greybus: svc: revert svc changes to keep things working for a while.
authorGreg Kroah-Hartman <gregkh@google.com>
Wed, 29 Jul 2015 17:05:09 +0000 (10:05 -0700)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 29 Jul 2015 17:05:09 +0000 (10:05 -0700)
The firmware for the svc changes isn't quite ready, so revert the whole
set of patches in one hunk to get things back to a working state for the
other firmware developers.  The svc patches will be added back in a
separate branch.

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/Makefile
drivers/staging/greybus/ap.c [new file with mode: 0644]
drivers/staging/greybus/connection.c
drivers/staging/greybus/core.c
drivers/staging/greybus/es1.c
drivers/staging/greybus/es2.c
drivers/staging/greybus/greybus.h
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/interface.c
drivers/staging/greybus/svc.c
drivers/staging/greybus/svc_msg.h [new file with mode: 0644]

index d14bf4d21983cdcac1b6544301b32ea16e70e5bb..1467c5b3fcd85e87d62ef846ba72d3078ebe44bf 100644 (file)
@@ -1,5 +1,6 @@
 greybus-y :=   core.o          \
                debugfs.o       \
+               ap.o            \
                manifest.o      \
                endo.o          \
                module.o        \
diff --git a/drivers/staging/greybus/ap.c b/drivers/staging/greybus/ap.c
new file mode 100644 (file)
index 0000000..fc23817
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Greybus "AP" message loop handling
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 only.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include "svc_msg.h"
+#include "greybus_manifest.h"
+#include "greybus.h"
+
+struct ap_msg {
+       u8 *data;
+       size_t size;
+       struct greybus_host_device *hd;
+       struct work_struct event;
+};
+
+static struct workqueue_struct *ap_workqueue;
+
+static struct svc_msg *svc_msg_alloc(enum svc_function_id id)
+{
+       struct svc_msg *svc_msg;
+
+       svc_msg = kzalloc((sizeof *svc_msg), GFP_KERNEL);
+       if (!svc_msg)
+               return NULL;
+
+       // FIXME - verify we are only sending function IDs we should be
+       svc_msg->header.function_id = id;
+       return svc_msg;
+}
+
+static void svc_msg_free(struct svc_msg *svc_msg)
+{
+       kfree(svc_msg);
+}
+
+static int svc_msg_send(struct svc_msg *svc_msg, struct greybus_host_device *hd)
+{
+       int retval;
+
+       // FIXME - Do we need to do more than just pass it to the hd and then
+       // free it?
+       retval = hd->driver->submit_svc(svc_msg, hd);
+
+       svc_msg_free(svc_msg);
+       return retval;
+}
+
+static void svc_handshake(struct svc_function_handshake *handshake,
+                         int payload_length, struct greybus_host_device *hd)
+{
+       struct svc_msg *svc_msg;
+
+       if (payload_length != sizeof(*handshake)) {
+               dev_err(hd->parent,
+                       "Illegal size of svc handshake message %d\n",
+                       payload_length);
+               return;
+       }
+
+       /* A new SVC communication channel, let's verify a supported version */
+       if ((handshake->version_major != GREYBUS_VERSION_MAJOR) ||
+           (handshake->version_minor != GREYBUS_VERSION_MINOR)) {
+               dev_warn(hd->parent,
+                       "received invalid greybus version %u.%u\n",
+                       handshake->version_major, handshake->version_minor);
+               return;
+       }
+
+       /* Validate that the handshake came from the SVC */
+       if (handshake->handshake_type != SVC_HANDSHAKE_SVC_HELLO) {
+               /* we don't know what to do with this, log it and return */
+               dev_dbg(hd->parent, "received invalid handshake type %d\n",
+                       handshake->handshake_type);
+               return;
+       }
+
+       /* Send back a AP_HELLO message */
+       svc_msg = svc_msg_alloc(SVC_FUNCTION_HANDSHAKE);
+       if (!svc_msg)
+               return;
+
+       svc_msg->header.message_type = SVC_MSG_DATA;
+       svc_msg->header.payload_length =
+               cpu_to_le16(sizeof(*handshake));
+       svc_msg->handshake.version_major = GREYBUS_VERSION_MAJOR;
+       svc_msg->handshake.version_minor = GREYBUS_VERSION_MINOR;
+       svc_msg->handshake.handshake_type = SVC_HANDSHAKE_AP_HELLO;
+
+       (void)svc_msg_send(svc_msg, hd);
+}
+
+static void svc_management(struct svc_function_unipro_management *management,
+                          int payload_length, struct greybus_host_device *hd)
+{
+       struct gb_interface *intf;
+       int ret;
+
+       if (payload_length != sizeof(*management)) {
+               dev_err(hd->parent,
+                       "Illegal size of svc management message %d\n",
+                       payload_length);
+               return;
+       }
+
+       switch (management->management_packet_type) {
+       case SVC_MANAGEMENT_AP_ID:
+               hd->device_id = management->ap_id.device_id;
+               break;
+       case SVC_MANAGEMENT_LINK_UP:
+               intf = gb_interface_find(hd, management->link_up.interface_id);
+               if (!intf) {
+                       dev_err(hd->parent, "Interface ID %d not found\n",
+                               management->link_up.interface_id);
+                       return;
+               }
+               ret = gb_interface_init(intf, management->link_up.device_id);
+               if (ret) {
+                       dev_err(hd->parent,
+                               "error %d initializing interface %hhu\n",
+                               ret, management->link_up.interface_id);
+                       return;
+               }
+               break;
+       default:
+               dev_err(hd->parent, "Unhandled UniPro management message\n");
+       }
+}
+
+static void svc_hotplug(struct svc_function_hotplug *hotplug,
+                       int payload_length, struct greybus_host_device *hd)
+{
+       u8 interface_id = hotplug->interface_id;
+
+       switch (hotplug->hotplug_event) {
+       case SVC_HOTPLUG_EVENT:
+               /* Add a new interface to the system */
+               if (payload_length != sizeof(*hotplug)) {
+                       dev_err(hd->parent,
+                               "Illegal size of svc hotplug message %d\n",
+                               payload_length);
+                       return;
+               }
+               dev_dbg(hd->parent, "interface id %d added\n", interface_id);
+               gb_interface_create(hd, interface_id);
+               break;
+
+       case SVC_HOTUNPLUG_EVENT:
+               /* Remove a interface from the system */
+               if (payload_length != sizeof(*hotplug)) {
+                       dev_err(hd->parent,
+                               "Illegal size of svc hotunplug message %d\n",
+                               payload_length);
+                       return;
+               }
+               dev_dbg(hd->parent, "interface id %d removed\n", interface_id);
+               gb_interface_remove(hd, interface_id);
+               break;
+
+       default:
+               dev_err(hd->parent,
+                       "Received invalid hotplug message type %d\n",
+                       hotplug->hotplug_event);
+               break;
+       }
+}
+
+static void svc_power(struct svc_function_power *power,
+                     int payload_length, struct greybus_host_device *hd)
+{
+       u8 interface_id = power->interface_id;
+
+       /*
+        * The AP is only allowed to get a Battery Status message, not a Battery
+        * Status Request
+        */
+       if (power->power_type != SVC_POWER_BATTERY_STATUS) {
+               dev_err(hd->parent, "Received invalid power type %d\n",
+                       power->power_type);
+               return;
+       }
+
+       /*
+        * As struct struct svc_function_power_battery_status_request is 0 bytes
+        * big, we can just check the union of the whole structure to validate
+        * the size of this message.
+        */
+       if (payload_length != sizeof(*power)) {
+               dev_err(hd->parent,
+                       "Illegal size of svc power message %d\n",
+                       payload_length);
+               return;
+       }
+
+       dev_dbg(hd->parent, "power status for interface id %d is %d\n",
+               interface_id, power->status.status);
+
+       // FIXME - do something with the power information, like update our
+       // battery information...
+}
+
+static void svc_epm(struct svc_function_epm *epm,
+                   int payload_length, struct greybus_host_device *hd)
+{
+       /* What?  An AP should not get this message */
+       dev_err(hd->parent, "Got an EPM message???\n");
+}
+
+static void svc_suspend(struct svc_function_suspend *suspend,
+                       int payload_length, struct greybus_host_device *hd)
+{
+       /* What?  An AP should not get this message */
+       dev_err(hd->parent, "Got an suspend message???\n");
+}
+
+static struct svc_msg *convert_ap_message(struct ap_msg *ap_msg)
+{
+       struct svc_msg *svc_msg;
+       struct svc_msg_header *header;
+       struct greybus_host_device *hd = ap_msg->hd;
+
+       svc_msg = (struct svc_msg *)ap_msg->data;
+       header = &svc_msg->header;
+
+       /* Validate the message type */
+       if (header->message_type != SVC_MSG_DATA) {
+               dev_err(hd->parent, "message type %d received?\n",
+                       header->message_type);
+               return NULL;
+       }
+
+       /*
+        * The validation of the size of the message buffer happens in each
+        * svc_* function, due to the different types of messages, keeping the
+        * logic for each message only in one place.
+        */
+
+       return svc_msg;
+}
+
+static void ap_process_event(struct work_struct *work)
+{
+       struct svc_msg *svc_msg;
+       struct greybus_host_device *hd;
+       struct ap_msg *ap_msg;
+       int payload_length;
+
+       ap_msg = container_of(work, struct ap_msg, event);
+       hd = ap_msg->hd;
+
+       /* Turn the "raw" data into a real message */
+       svc_msg = convert_ap_message(ap_msg);
+       if (!svc_msg)
+               return;
+
+       payload_length = le16_to_cpu(svc_msg->header.payload_length);
+
+       /* Look at the message to figure out what to do with it */
+       switch (svc_msg->header.function_id) {
+       case SVC_FUNCTION_HANDSHAKE:
+               svc_handshake(&svc_msg->handshake, payload_length, hd);
+               break;
+       case SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT:
+               svc_management(&svc_msg->management, payload_length, hd);
+               break;
+       case SVC_FUNCTION_HOTPLUG:
+               svc_hotplug(&svc_msg->hotplug, payload_length, hd);
+               break;
+       case SVC_FUNCTION_POWER:
+               svc_power(&svc_msg->power, payload_length, hd);
+               break;
+       case SVC_FUNCTION_EPM:
+               svc_epm(&svc_msg->epm, payload_length, hd);
+               break;
+       case SVC_FUNCTION_SUSPEND:
+               svc_suspend(&svc_msg->suspend, payload_length, hd);
+               break;
+       default:
+               dev_err(hd->parent, "received invalid SVC function ID %d\n",
+                       svc_msg->header.function_id);
+       }
+
+       /* clean the message up */
+       kfree(ap_msg->data);
+       kfree(ap_msg);
+}
+
+int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int size)
+{
+       struct ap_msg *ap_msg;
+
+       /*
+        * Totally naive copy the message into a new structure that we slowly
+        * create and add it to the list.  Let's get this working, the odds of
+        * this being any "slow path" for AP messages is really low at this
+        * point in time, but you never know, so this comment is here to point
+        * out that maybe we should use a slab allocator, or even just not copy
+        * the data, but use it directly and force the urbs to be "new" each
+        * time.
+        */
+
+       /* Note - this can, and will, be called in interrupt context. */
+       ap_msg = kmalloc(sizeof(*ap_msg), GFP_ATOMIC);
+       if (!ap_msg)
+               return -ENOMEM;
+       ap_msg->data = kmalloc(size, GFP_ATOMIC);
+       if (!ap_msg->data) {
+               kfree(ap_msg);
+               return -ENOMEM;
+       }
+       memcpy(ap_msg->data, data, size);
+       ap_msg->size = size;
+       ap_msg->hd = hd;
+
+       INIT_WORK(&ap_msg->event, ap_process_event);
+       queue_work(ap_workqueue, &ap_msg->event);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(greybus_svc_in);
+
+int __init gb_ap_init(void)
+{
+       ap_workqueue = alloc_ordered_workqueue("greybus_ap", 0);
+       if (!ap_workqueue)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void gb_ap_exit(void)
+{
+       destroy_workqueue(ap_workqueue);
+       ap_workqueue = NULL;
+}
+
+
index 1a657f706b93124303151ac521d057e1a6e9cb14..b32da8af68f47a9adc55a9a087105166ddca1564 100644 (file)
@@ -251,12 +251,7 @@ gb_connection_create_range(struct greybus_host_device *hd,
 
        spin_unlock_irq(&gb_connections_lock);
 
-       if (hd_cport_id != GB_SVC_CPORT_ID) {
-               gb_svc_connection_create(hd->svc,
-                                        hd->endo->ap_intf_id, hd_cport_id,
-                                        bundle->intf->interface_id, cport_id);
-       }
-
+       /* XXX Will have to establish connections to get version */
        gb_connection_bind_protocol(connection);
        if (!connection->protocol)
                dev_warn(&connection->dev,
index 9f105fb12ede7db9e80c6a57af4dc68482b04b23..225fa4fb5268223aab67d7a2c149f82c84963535 100644 (file)
@@ -176,7 +176,8 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
         * Validate that the driver implements all of the callbacks
         * so that we don't have to every time we make them.
         */
-       if ((!driver->message_send) || (!driver->message_cancel)) {
+       if ((!driver->message_send) || (!driver->message_cancel) ||
+           (!driver->submit_svc)) {
                pr_err("Must implement all greybus_host_driver callbacks!\n");
                return ERR_PTR(-EINVAL);
        }
@@ -208,21 +209,6 @@ struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver
        ida_init(&hd->cport_id_map);
        hd->buffer_size_max = buffer_size_max;
 
-       /*
-        * Initialize AP's SVC protocol connection:
-        *
-        * This is required as part of early initialization of the host device
-        * as we need this connection in order to start any kind of message
-        * exchange between the AP and the SVC. SVC will start with a
-        * 'get-version' request followed by a 'svc-hello' message and at that
-        * time we will create a fully initialized svc-connection, as we need
-        * endo-id and AP's interface id for that.
-        */
-       if (!gb_ap_svc_connection_create(hd)) {
-               kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
-               return ERR_PTR(-ENOMEM);
-       }
-
        return hd;
 }
 EXPORT_SYMBOL_GPL(greybus_create_hd);
@@ -284,6 +270,12 @@ static int __init gb_init(void)
                goto error_bus;
        }
 
+       retval = gb_ap_init();
+       if (retval) {
+               pr_err("gb_ap_init failed (%d)\n", retval);
+               goto error_ap;
+       }
+
        retval = gb_operation_init();
        if (retval) {
                pr_err("gb_operation_init failed (%d)\n", retval);
@@ -317,6 +309,8 @@ error_control:
 error_endo:
        gb_operation_exit();
 error_operation:
+       gb_ap_exit();
+error_ap:
        bus_unregister(&greybus_bus_type);
 error_bus:
        gb_debugfs_cleanup();
@@ -331,6 +325,7 @@ static void __exit gb_exit(void)
        gb_control_protocol_exit();
        gb_endo_exit();
        gb_operation_exit();
+       gb_ap_exit();
        bus_unregister(&greybus_bus_type);
        gb_debugfs_cleanup();
 }
index c1fab375bb0b2eb191c49bc01e3238532990f417..0cb7a3c7ef72a0e7120aaba8cae79b1722e5b691 100644 (file)
 #include <asm/unaligned.h>
 
 #include "greybus.h"
+#include "svc_msg.h"
 #include "kernel_ver.h"
 
 /* Memory sizes for the buffers sent to/from the ES1 controller */
+#define ES1_SVC_MSG_SIZE       (sizeof(struct svc_msg) + SZ_64K)
 #define ES1_GBUF_MSG_SIZE_MAX  2048
 
 static const struct usb_device_id id_table[] = {
@@ -56,8 +58,11 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE);
  * @usb_intf: pointer to the USB interface we are bound to.
  * @hd: pointer to our greybus_host_device structure
  * @control_endpoint: endpoint to send data to SVC
+ * @svc_endpoint: endpoint for SVC data in
  * @cport_in_endpoint: bulk in endpoint for CPort data
  * @cport-out_endpoint: bulk out endpoint for CPort data
+ * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint
+ * @svc_urb: urb for SVC messages coming in on @svc_endpoint
  * @cport_in_urb: array of urbs for the CPort in messages
  * @cport_in_buffer: array of buffers for the @cport_in_urb urbs
  * @cport_out_urb: array of urbs for the CPort out messages
@@ -73,9 +78,13 @@ struct es1_ap_dev {
        struct greybus_host_device *hd;
 
        __u8 control_endpoint;
+       __u8 svc_endpoint;
        __u8 cport_in_endpoint;
        __u8 cport_out_endpoint;
 
+       u8 *svc_buffer;
+       struct urb *svc_urb;
+
        struct urb *cport_in_urb[NUM_CPORT_IN_URB];
        u8 *cport_in_buffer[NUM_CPORT_IN_URB];
        struct urb *cport_out_urb[NUM_CPORT_OUT_URB];
@@ -94,6 +103,26 @@ static void usb_log_enable(struct es1_ap_dev *es1);
 static void usb_log_disable(struct es1_ap_dev *es1);
 
 #define ES1_TIMEOUT    500     /* 500 ms for the SVC to do something */
+static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd)
+{
+       struct es1_ap_dev *es1 = hd_to_es1(hd);
+       int retval;
+
+       /* SVC messages go down our control pipe */
+       retval = usb_control_msg(es1->usb_dev,
+                                usb_sndctrlpipe(es1->usb_dev,
+                                                es1->control_endpoint),
+                                REQUEST_SVC,
+                                USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                                0x00, 0x00,
+                                (char *)svc_msg,
+                                sizeof(*svc_msg),
+                                ES1_TIMEOUT);
+       if (retval != sizeof(*svc_msg))
+               return retval;
+
+       return 0;
+}
 
 static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask)
 {
@@ -274,6 +303,7 @@ static struct greybus_host_driver es1_driver = {
        .hd_priv_size           = sizeof(struct es1_ap_dev),
        .message_send           = message_send,
        .message_cancel         = message_cancel,
+       .submit_svc             = submit_svc,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -337,6 +367,12 @@ static void ap_disconnect(struct usb_interface *interface)
                es1->cport_in_buffer[i] = NULL;
        }
 
+       usb_kill_urb(es1->svc_urb);
+       usb_free_urb(es1->svc_urb);
+       es1->svc_urb = NULL;
+       kfree(es1->svc_buffer);
+       es1->svc_buffer = NULL;
+
        usb_set_intfdata(interface, NULL);
        udev = es1->usb_dev;
        greybus_remove_hd(es1->hd);
@@ -344,6 +380,33 @@ static void ap_disconnect(struct usb_interface *interface)
        usb_put_dev(udev);
 }
 
+/* Callback for when we get a SVC message */
+static void svc_in_callback(struct urb *urb)
+{
+       struct greybus_host_device *hd = urb->context;
+       struct device *dev = &urb->dev->dev;
+       int status = check_urb_status(urb);
+       int retval;
+
+       if (status) {
+               if ((status == -EAGAIN) || (status == -EPROTO))
+                       goto exit;
+               dev_err(dev, "urb svc in error %d (dropped)\n", status);
+               return;
+       }
+
+       /* We have a message, create a new message structure, add it to the
+        * list, and wake up our thread that will process the messages.
+        */
+       greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length);
+
+exit:
+       /* resubmit the urb to get more messages */
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval)
+               dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
+}
+
 static void cport_in_callback(struct urb *urb)
 {
        struct greybus_host_device *hd = urb->context;
@@ -547,10 +610,14 @@ static int ap_probe(struct usb_interface *interface,
        struct usb_device *udev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
+       bool int_in_found = false;
        bool bulk_in_found = false;
        bool bulk_out_found = false;
        int retval = -ENOMEM;
        int i;
+       u16 endo_id = 0x4755;   // FIXME - get endo "ID" from the SVC
+       u8 ap_intf_id = 0x01;   // FIXME - get endo "ID" from the SVC
+       u8 svc_interval = 0;
 
        /* We need to fit a CPort ID in one byte of a message header */
        BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX);
@@ -579,7 +646,11 @@ static int ap_probe(struct usb_interface *interface,
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (usb_endpoint_is_bulk_in(endpoint)) {
+               if (usb_endpoint_is_int_in(endpoint)) {
+                       es1->svc_endpoint = endpoint->bEndpointAddress;
+                       svc_interval = endpoint->bInterval;
+                       int_in_found = true;
+               } else if (usb_endpoint_is_bulk_in(endpoint)) {
                        es1->cport_in_endpoint = endpoint->bEndpointAddress;
                        bulk_in_found = true;
                } else if (usb_endpoint_is_bulk_out(endpoint)) {
@@ -591,12 +662,27 @@ static int ap_probe(struct usb_interface *interface,
                                endpoint->bEndpointAddress);
                }
        }
-       if ((bulk_in_found == false) ||
+       if ((int_in_found == false) ||
+           (bulk_in_found == false) ||
            (bulk_out_found == false)) {
                dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
                goto error;
        }
 
+       /* Create our buffer and URB to get SVC messages, and start it up */
+       es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL);
+       if (!es1->svc_buffer)
+               goto error;
+
+       es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!es1->svc_urb)
+               goto error;
+
+       usb_fill_int_urb(es1->svc_urb, udev,
+                        usb_rcvintpipe(udev, es1->svc_endpoint),
+                        es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback,
+                        hd, svc_interval);
+
        /* Allocate buffers for our cport in messages and start them up */
        for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
                struct urb *urb;
@@ -632,6 +718,22 @@ static int ap_probe(struct usb_interface *interface,
                es1->cport_out_urb_busy[i] = false;     /* just to be anal */
        }
 
+       /*
+        * XXX Soon this will be initiated later, with a combination
+        * XXX of a Control protocol probe operation and a
+        * XXX subsequent Control protocol connected operation for
+        * XXX the SVC connection.  At that point we know we're
+        * XXX properly connected to an Endo.
+        */
+       retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
+       if (retval)
+               goto error;
+
+       /* Start up our svc urb, which allows events to start flowing */
+       retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
+       if (retval)
+               goto error;
+
        apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable",
                                                        (S_IWUSR | S_IRUGO),
                                                        gb_debugfs_get(), es1,
index 558345cd80af4385ae40042cd3d2c3c7df467aaa..323721a2e2aa0ec3ab22dab6468991061187e91f 100644 (file)
 #include <asm/unaligned.h>
 
 #include "greybus.h"
+#include "svc_msg.h"
 #include "kernel_ver.h"
 
 /* Memory sizes for the buffers sent to/from the ES1 controller */
+#define ES1_SVC_MSG_SIZE       (sizeof(struct svc_msg) + SZ_64K)
 #define ES1_GBUF_MSG_SIZE_MAX  2048
 
 static const struct usb_device_id id_table[] = {
@@ -83,7 +85,10 @@ struct es1_cport_out {
  * @usb_intf: pointer to the USB interface we are bound to.
  * @hd: pointer to our greybus_host_device structure
  * @control_endpoint: endpoint to send data to SVC
+ * @svc_endpoint: endpoint for SVC data in
 
+ * @svc_buffer: buffer for SVC messages coming in on @svc_endpoint
+ * @svc_urb: urb for SVC messages coming in on @svc_endpoint
  * @cport_in: endpoint, urbs and buffer for cport in messages
  * @cport_out: endpoint for for cport out messages
  * @cport_out_urb: array of urbs for the CPort out messages
@@ -99,6 +104,10 @@ struct es1_ap_dev {
        struct greybus_host_device *hd;
 
        __u8 control_endpoint;
+       __u8 svc_endpoint;
+
+       u8 *svc_buffer;
+       struct urb *svc_urb;
 
        struct es1_cport_in cport_in[NUM_BULKS];
        struct es1_cport_out cport_out[NUM_BULKS];
@@ -133,6 +142,26 @@ static int cport_to_ep(struct es1_ap_dev *es1, u16 cport_id)
 }
 
 #define ES1_TIMEOUT    500     /* 500 ms for the SVC to do something */
+static int submit_svc(struct svc_msg *svc_msg, struct greybus_host_device *hd)
+{
+       struct es1_ap_dev *es1 = hd_to_es1(hd);
+       int retval;
+
+       /* SVC messages go down our control pipe */
+       retval = usb_control_msg(es1->usb_dev,
+                                usb_sndctrlpipe(es1->usb_dev,
+                                                es1->control_endpoint),
+                                REQUEST_SVC,
+                                USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+                                0x00, 0x00,
+                                (char *)svc_msg,
+                                sizeof(*svc_msg),
+                                ES1_TIMEOUT);
+       if (retval != sizeof(*svc_msg))
+               return retval;
+
+       return 0;
+}
 
 static int ep_in_use(struct es1_ap_dev *es1, int bulk_ep_set)
 {
@@ -370,6 +399,7 @@ static struct greybus_host_driver es1_driver = {
        .hd_priv_size           = sizeof(struct es1_ap_dev),
        .message_send           = message_send,
        .message_cancel         = message_cancel,
+       .submit_svc             = submit_svc,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -437,6 +467,12 @@ static void ap_disconnect(struct usb_interface *interface)
                }
        }
 
+       usb_kill_urb(es1->svc_urb);
+       usb_free_urb(es1->svc_urb);
+       es1->svc_urb = NULL;
+       kfree(es1->svc_buffer);
+       es1->svc_buffer = NULL;
+
        usb_set_intfdata(interface, NULL);
        udev = es1->usb_dev;
        greybus_remove_hd(es1->hd);
@@ -444,6 +480,33 @@ static void ap_disconnect(struct usb_interface *interface)
        usb_put_dev(udev);
 }
 
+/* Callback for when we get a SVC message */
+static void svc_in_callback(struct urb *urb)
+{
+       struct greybus_host_device *hd = urb->context;
+       struct device *dev = &urb->dev->dev;
+       int status = check_urb_status(urb);
+       int retval;
+
+       if (status) {
+               if ((status == -EAGAIN) || (status == -EPROTO))
+                       goto exit;
+               dev_err(dev, "urb svc in error %d (dropped)\n", status);
+               return;
+       }
+
+       /* We have a message, create a new message structure, add it to the
+        * list, and wake up our thread that will process the messages.
+        */
+       greybus_svc_in(hd, urb->transfer_buffer, urb->actual_length);
+
+exit:
+       /* resubmit the urb to get more messages */
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval)
+               dev_err(dev, "Can not submit urb for AP data: %d\n", retval);
+}
+
 static void cport_in_callback(struct urb *urb)
 {
        struct greybus_host_device *hd = urb->context;
@@ -647,10 +710,14 @@ static int ap_probe(struct usb_interface *interface,
        struct usb_device *udev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
+       bool int_in_found = false;
        int bulk_in = 0;
        int bulk_out = 0;
        int retval = -ENOMEM;
        int i;
+       u16 endo_id = 0x4755;   // FIXME - get endo "ID" from the SVC
+       u8 ap_intf_id = 0x01;   // FIXME - get endo "ID" from the SVC
+       u8 svc_interval = 0;
 
        /* We need to fit a CPort ID in one byte of a message header */
        BUILD_BUG_ON(CPORT_ID_MAX > U8_MAX);
@@ -679,7 +746,11 @@ static int ap_probe(struct usb_interface *interface,
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
-               if (usb_endpoint_is_bulk_in(endpoint)) {
+               if (usb_endpoint_is_int_in(endpoint)) {
+                       es1->svc_endpoint = endpoint->bEndpointAddress;
+                       svc_interval = endpoint->bInterval;
+                       int_in_found = true;
+               } else if (usb_endpoint_is_bulk_in(endpoint)) {
                        es1->cport_in[bulk_in++].endpoint =
                                endpoint->bEndpointAddress;
                } else if (usb_endpoint_is_bulk_out(endpoint)) {
@@ -691,12 +762,27 @@ static int ap_probe(struct usb_interface *interface,
                                endpoint->bEndpointAddress);
                }
        }
-       if ((bulk_in == 0) ||
+       if ((int_in_found == false) ||
+           (bulk_in == 0) ||
            (bulk_out == 0)) {
                dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
                goto error;
        }
 
+       /* Create our buffer and URB to get SVC messages, and start it up */
+       es1->svc_buffer = kmalloc(ES1_SVC_MSG_SIZE, GFP_KERNEL);
+       if (!es1->svc_buffer)
+               goto error;
+
+       es1->svc_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!es1->svc_urb)
+               goto error;
+
+       usb_fill_int_urb(es1->svc_urb, udev,
+                        usb_rcvintpipe(udev, es1->svc_endpoint),
+                        es1->svc_buffer, ES1_SVC_MSG_SIZE, svc_in_callback,
+                        hd, svc_interval);
+
        /* Allocate buffers for our cport in messages and start them up */
        for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
                struct es1_cport_in *cport_in = &es1->cport_in[bulk_in];
@@ -736,6 +822,22 @@ static int ap_probe(struct usb_interface *interface,
                es1->cport_out_urb_busy[i] = false;     /* just to be anal */
        }
 
+       /*
+        * XXX Soon this will be initiated later, with a combination
+        * XXX of a Control protocol probe operation and a
+        * XXX subsequent Control protocol connected operation for
+        * XXX the SVC connection.  At that point we know we're
+        * XXX properly connected to an Endo.
+        */
+       retval = greybus_endo_setup(hd, endo_id, ap_intf_id);
+       if (retval)
+               goto error;
+
+       /* Start up our svc urb, which allows events to start flowing */
+       retval = usb_submit_urb(es1->svc_urb, GFP_KERNEL);
+       if (retval)
+               goto error;
+
        apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable",
                                                        (S_IWUSR | S_IRUGO),
                                                        gb_debugfs_get(), es1,
index cdade1e9b4c7c3c1274f6218065c3e062ea85264..2214f447df2bda8d5acfda1e937a563cbc2bc48e 100644 (file)
@@ -73,6 +73,7 @@
  */
 
 struct greybus_host_device;
+struct svc_msg;
 
 /* Greybus "Host driver" structure, needed by a host controller driver to be
  * able to handle both SVC control as well as "real" greybus messages
@@ -83,6 +84,8 @@ struct greybus_host_driver {
        int (*message_send)(struct greybus_host_device *hd, u16 dest_cport_id,
                        struct gb_message *message, gfp_t gfp_mask);
        void (*message_cancel)(struct gb_message *message);
+       int (*submit_svc)(struct svc_msg *svc_msg,
+                           struct greybus_host_device *hd);
 };
 
 struct greybus_host_device {
@@ -100,7 +103,6 @@ struct greybus_host_device {
 
        struct gb_endo *endo;
        struct gb_connection *initial_svc_connection;
-       struct gb_svc *svc;
 
        /* Private data for the host driver */
        unsigned long hd_priv[0] __aligned(sizeof(s64));
@@ -153,6 +155,9 @@ void greybus_deregister_driver(struct greybus_driver *driver);
 
 int greybus_disabled(void);
 
+int greybus_svc_in(struct greybus_host_device *hd, u8 *data, int length);
+int gb_ap_init(void);
+void gb_ap_exit(void);
 void gb_debugfs_init(void);
 void gb_debugfs_cleanup(void);
 struct dentry *gb_debugfs_get(void);
index e2d38dfe06b60f9740111968eaa0fb99f65e321c..61fe9dce6ec0d4ff7c533d1b8c9634f79e4bfcc5 100644 (file)
@@ -64,9 +64,9 @@
  * CONTROL and SVC protocols for communication between AP and SVC.
  */
 #define GB_SVC_BUNDLE_ID                       0
-#define GB_SVC_CPORT_ID                                0
+#define GB_SVC_CPORT_ID                                2
 #define GB_CONTROL_BUNDLE_ID                   0
-#define GB_CONTROL_CPORT_ID                    0
+#define GB_CONTROL_CPORT_ID                    2
 
 
 /* Control Protocol */
@@ -563,7 +563,6 @@ struct gb_spi_transfer_response {
 #define GB_SVC_TYPE_INTF_RESET         0x06
 #define GB_SVC_TYPE_CONN_CREATE                0x07
 #define GB_SVC_TYPE_CONN_DESTROY       0x08
-#define GB_SVC_TYPE_ROUTE_CREATE       0x0b
 
 /* SVC version request/response have same payload as gb_protocol_version_response */
 
@@ -606,8 +605,6 @@ struct gb_svc_conn_create_request {
        __u16   cport1_id;
        __u8    intf2_id;
        __u16   cport2_id;
-       __u8    tc;
-       __u8    flags;
 };
 /* connection create response has no payload */
 
@@ -619,13 +616,6 @@ struct gb_svc_conn_destroy_request {
 };
 /* connection destroy response has no payload */
 
-struct gb_svc_route_create_request {
-       __u8    intf1_id;
-       __u8    dev1_id;
-       __u8    intf2_id;
-       __u8    dev2_id;
-};
-
 /* UART */
 
 /* Version of the Greybus UART protocol we support */
index 4a26bf6f714ca79205d0abead9ce40c8feae183e..f1e2956b25a7a305e0f58567afad982716f9868e 100644 (file)
@@ -183,7 +183,7 @@ put_module:
 /*
  * Tear down a previously set up module.
  */
-static void interface_destroy(struct gb_interface *intf)
+void gb_interface_destroy(struct gb_interface *intf)
 {
        struct gb_module *module;
        struct gb_bundle *bundle;
@@ -279,7 +279,7 @@ void gb_interface_remove(struct greybus_host_device *hd, u8 interface_id)
        struct gb_interface *intf = gb_interface_find(hd, interface_id);
 
        if (intf)
-               interface_destroy(intf);
+               gb_interface_destroy(intf);
        else
                dev_err(hd->parent, "interface id %d not found\n",
                        interface_id);
@@ -290,5 +290,5 @@ void gb_interfaces_remove(struct greybus_host_device *hd)
        struct gb_interface *intf, *temp;
 
        list_for_each_entry_safe(intf, temp, &hd->interfaces, links)
-               interface_destroy(intf);
+               gb_interface_destroy(intf);
 }
index 784b7709b432210a109f3f8acac7b1759a7e0d67..5f2b2b43b939cd0bd1565ca2441ec3764b0caaaa 100644 (file)
@@ -9,10 +9,6 @@
 
 #include "greybus.h"
 
-#define CPORT_FLAGS_E2EFC       (1)
-#define CPORT_FLAGS_CSD_N       (2)
-#define CPORT_FLAGS_CSV_N       (4)
-
 struct gb_svc {
        struct gb_connection    *connection;
        u8                      version_major;
@@ -40,6 +36,7 @@ gb_ap_svc_connection_create(struct greybus_host_device *hd)
 
        return connection;
 }
+EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create);
 
 /*
  * We know endo-type and AP's interface id now, lets create a proper svc
@@ -101,12 +98,6 @@ static int connection_create_operation(struct gb_svc *svc,
        request.cport1_id = cport1_id;
        request.intf2_id = intf2_id;
        request.cport2_id = cport2_id;
-       /*
-        * XXX: fix connections paramaters to TC0 and all CPort flags
-        * for now.
-        */
-       request.tc = 0;
-       request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
 
        return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
                                 &request, sizeof(request), NULL, 0);
@@ -127,20 +118,6 @@ static int connection_destroy_operation(struct gb_svc *svc,
                                 &request, sizeof(request), NULL, 0);
 }
 
-static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
-                                 u8 intf2_id, u8 dev2_id)
-{
-       struct gb_svc_route_create_request request;
-
-       request.intf1_id = intf1_id;
-       request.dev1_id = dev1_id;
-       request.intf2_id = intf2_id;
-       request.dev2_id = dev2_id;
-
-       return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
-                                &request, sizeof(request), NULL, 0);
-}
-
 int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
 {
        return intf_device_id_operation(svc, intf_id, device_id);
@@ -171,13 +148,6 @@ int gb_svc_connection_destroy(struct gb_svc *svc,
 }
 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
 
-int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
-                       u8 intf2_id, u8 dev2_id) {
-       return route_create_operation(svc, intf1_id, dev1_id,
-                                     intf2_id, dev2_id);
-}
-EXPORT_SYMBOL_GPL(gb_svc_route_create);
-
 static int gb_svc_version_request(struct gb_operation *op)
 {
        struct gb_connection *connection = op->connection;
@@ -318,25 +288,6 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
                goto ida_put;
        }
 
-       /*
-        * Create a two-way route between the AP and the new interface
-        */
-       ret = route_create_operation(svc, hd->endo->ap_intf_id,
-                                    GB_DEVICE_ID_AP, intf_id, device_id);
-       if (ret) {
-               dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
-                       __func__, intf_id, device_id, ret);
-               goto ida_put;
-       }
-
-       ret = route_create_operation(svc, intf_id, device_id,
-                                    hd->endo->ap_intf_id, GB_DEVICE_ID_AP);
-       if (ret) {
-               dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
-                       __func__, intf_id, device_id, ret);
-               goto ida_put;
-       }
-
        ret = gb_interface_init(intf, device_id);
        if (ret) {
                dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
@@ -439,7 +390,6 @@ static int gb_svc_connection_init(struct gb_connection *connection)
        if (!svc)
                return -ENOMEM;
 
-       connection->hd->svc = svc;
        svc->connection = connection;
        connection->private = svc;
 
@@ -455,7 +405,6 @@ static void gb_svc_connection_exit(struct gb_connection *connection)
 {
        struct gb_svc *svc = connection->private;
 
-       connection->hd->svc = NULL;
        connection->private = NULL;
        kfree(svc);
 }
diff --git a/drivers/staging/greybus/svc_msg.h b/drivers/staging/greybus/svc_msg.h
new file mode 100644 (file)
index 0000000..3c628c5
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Greybus AP <-> SVC message structure format.
+ *
+ * See "Greybus Application Protocol" document (version 0.1) for
+ * details on these values and structures.
+ *
+ * Copyright 2014 Google Inc.
+ * Copyright 2014 Linaro Ltd.
+ *
+ * Released under the GPLv2 and BSD license.
+ */
+
+#ifndef __SVC_MSG_H
+#define __SVC_MSG_H
+
+enum svc_function_id {
+       SVC_FUNCTION_HANDSHAKE                  = 0x00,
+       SVC_FUNCTION_UNIPRO_NETWORK_MANAGEMENT  = 0x01,
+       SVC_FUNCTION_HOTPLUG                    = 0x02,
+       SVC_FUNCTION_POWER                      = 0x03,
+       SVC_FUNCTION_EPM                        = 0x04,
+       SVC_FUNCTION_SUSPEND                    = 0x05,
+};
+
+enum svc_msg_type {
+       SVC_MSG_DATA                            = 0x00,
+       SVC_MSG_ERROR                           = 0xff,
+};
+
+struct svc_msg_header {
+       __u8    function_id;    /* enum svc_function_id */
+       __u8    message_type;
+       __le16  payload_length;
+} __packed;
+
+enum svc_function_handshake_type {
+       SVC_HANDSHAKE_SVC_HELLO         = 0x00,
+       SVC_HANDSHAKE_AP_HELLO          = 0x01,
+       SVC_HANDSHAKE_MODULE_HELLO      = 0x02,
+};
+
+struct svc_function_handshake {
+       __u8    version_major;
+       __u8    version_minor;
+       __u8    handshake_type; /* enum svc_function_handshake_type */
+} __packed;
+
+struct svc_function_unipro_set_route {
+       __u8    device_id;
+} __packed;
+
+struct svc_function_unipro_link_up {
+       __u8    interface_id;   /* Interface id within the Endo */
+       __u8    device_id;
+} __packed;
+
+struct svc_function_ap_id {
+       __u8    interface_id;
+       __u8    device_id;
+} __packed;
+
+enum svc_function_management_event {
+       SVC_MANAGEMENT_AP_ID            = 0x00,
+       SVC_MANAGEMENT_LINK_UP          = 0x01,
+       SVC_MANAGEMENT_SET_ROUTE        = 0x02,
+};
+
+struct svc_function_unipro_management {
+       __u8    management_packet_type; /* enum svc_function_management_event */
+       union {
+               struct svc_function_ap_id               ap_id;
+               struct svc_function_unipro_link_up      link_up;
+               struct svc_function_unipro_set_route    set_route;
+       };
+} __packed;
+
+enum svc_function_hotplug_event {
+       SVC_HOTPLUG_EVENT       = 0x00,
+       SVC_HOTUNPLUG_EVENT     = 0x01,
+};
+
+struct svc_function_hotplug {
+       __u8    hotplug_event;  /* enum svc_function_hotplug_event */
+       __u8    interface_id;   /* Interface id within the Endo */
+} __packed;
+
+enum svc_function_power_type {
+       SVC_POWER_BATTERY_STATUS                = 0x00,
+       SVC_POWER_BATTERY_STATUS_REQUEST        = 0x01,
+};
+
+enum svc_function_battery_status {
+       SVC_BATTERY_UNKNOWN             = 0x00,
+       SVC_BATTERY_CHARGING            = 0x01,
+       SVC_BATTERY_DISCHARGING         = 0x02,
+       SVC_BATTERY_NOT_CHARGING        = 0x03,
+       SVC_BATTERY_FULL                = 0x04,
+};
+
+struct svc_function_power_battery_status {
+       __le16  charge_full;
+       __le16  charge_now;
+       __u8    status; /* enum svc_function_battery_status */
+} __packed;
+
+struct svc_function_power_battery_status_request {
+} __packed;
+
+/* XXX
+ * Each interface carries power, so it's possible these things
+ * are associated with each UniPro device and not just the module.
+ * For now it's safe to assume it's per-module.
+ */
+struct svc_function_power {
+       __u8    power_type;     /* enum svc_function_power_type */
+       __u8    interface_id;
+       union {
+               struct svc_function_power_battery_status                status;
+               struct svc_function_power_battery_status_request        request;
+       };
+} __packed;
+
+enum svc_function_epm_command_type {
+       SVC_EPM_ENABLE  = 0x00,
+       SVC_EPM_DISABLE = 0x01,
+};
+
+/* EPM's are associated with the module */
+struct svc_function_epm {
+       __u8    epm_command_type;       /* enum svc_function_epm_command_type */
+       __u8    module_id;
+} __packed;
+
+enum svc_function_suspend_command_type {
+       SVC_SUSPEND_FIXME_1     = 0x00, // FIXME
+       SVC_SUSPEND_FIXME_2     = 0x01,
+};
+
+/* We'll want independent control for multi-interface modules */
+struct svc_function_suspend {
+       __u8    suspend_command_type;   /* enum function_suspend_command_type */
+       __u8    device_id;
+} __packed;
+
+struct svc_msg {
+       struct svc_msg_header   header;
+       union {
+               struct svc_function_handshake           handshake;
+               struct svc_function_unipro_management   management;
+               struct svc_function_hotplug             hotplug;
+               struct svc_function_power               power;
+               struct svc_function_epm                 epm;
+               struct svc_function_suspend             suspend;
+       };
+} __packed;
+
+#endif /* __SVC_MSG_H */