greybus: es2: clean up CDSI CPort reservation
[cascardo/linux.git] / drivers / staging / greybus / es2.c
index cd33aa7..c8ee779 100644 (file)
 #include "greybus.h"
 #include "kernel_ver.h"
 #include "connection.h"
+#include "greybus_trace.h"
 
-/* Memory sizes for the buffers sent to/from the ES1 controller */
-#define ES1_GBUF_MSG_SIZE_MAX  2048
+
+/* Fixed CPort numbers */
+#define ES2_CPORT_CDSI0                16
+#define ES2_CPORT_CDSI1                17
+
+/* Memory sizes for the buffers sent to/from the ES2 controller */
+#define ES2_GBUF_MSG_SIZE_MAX  2048
 
 static const struct usb_device_id id_table[] = {
-       /* Made up numbers for the SVC USB Bridge in ES2 */
-       { USB_DEVICE(0xffff, 0x0002) },
+       { USB_DEVICE(0x18d1, 0x1eaf) },
        { },
 };
 MODULE_DEVICE_TABLE(usb, id_table);
 
 #define APB1_LOG_SIZE          SZ_16K
-static struct dentry *apb1_log_dentry;
-static struct dentry *apb1_log_enable_dentry;
-static struct task_struct *apb1_log_task;
-static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE);
 
 /* Number of bulk in and bulk out couple */
 #define NUM_BULKS              7
@@ -48,21 +49,12 @@ static DEFINE_KFIFO(apb1_log_fifo, char, APB1_LOG_SIZE);
  */
 #define NUM_CPORT_OUT_URB      (8 * NUM_BULKS)
 
-/* vendor request APB1 log */
-#define REQUEST_LOG            0x02
-
-/* vendor request to map a cport to bulk in and bulk out endpoints */
-#define REQUEST_EP_MAPPING     0x03
-
-/* vendor request to get the number of cports available */
-#define REQUEST_CPORT_COUNT    0x04
-
 /*
  * @endpoint: bulk in endpoint for CPort data
  * @urb: array of urbs for the CPort in messages
  * @buffer: array of buffers for the @cport_in_urb urbs
  */
-struct es1_cport_in {
+struct es2_cport_in {
        __u8 endpoint;
        struct urb *urb[NUM_CPORT_IN_URB];
        u8 *buffer[NUM_CPORT_IN_URB];
@@ -71,15 +63,15 @@ struct es1_cport_in {
 /*
  * @endpoint: bulk out endpoint for CPort data
  */
-struct es1_cport_out {
+struct es2_cport_out {
        __u8 endpoint;
 };
 
 /**
- * es1_ap_dev - ES1 USB Bridge to AP structure
+ * es2_ap_dev - ES2 USB Bridge to AP structure
  * @usb_dev: pointer to the USB device we are.
  * @usb_intf: pointer to the USB interface we are bound to.
- * @hd: pointer to our greybus_host_device structure
+ * @hd: pointer to our gb_host_device structure
 
  * @cport_in: endpoint, urbs and buffer for cport in messages
  * @cport_out: endpoint for for cport out messages
@@ -89,20 +81,30 @@ struct es1_cport_out {
  * @cport_out_urb_cancelled: array of flags indicating whether the
  *                     corresponding @cport_out_urb is being cancelled
  * @cport_out_urb_lock: locks the @cport_out_urb_busy "list"
+ *
+ * @apb_log_task: task pointer for logging thread
+ * @apb_log_dentry: file system entry for the log file interface
+ * @apb_log_enable_dentry: file system entry for enabling logging
+ * @apb_log_fifo: kernel FIFO to carry logged data
  */
-struct es1_ap_dev {
+struct es2_ap_dev {
        struct usb_device *usb_dev;
        struct usb_interface *usb_intf;
-       struct greybus_host_device *hd;
+       struct gb_host_device *hd;
 
-       struct es1_cport_in cport_in[NUM_BULKS];
-       struct es1_cport_out cport_out[NUM_BULKS];
+       struct es2_cport_in cport_in[NUM_BULKS];
+       struct es2_cport_out cport_out[NUM_BULKS];
        struct urb *cport_out_urb[NUM_CPORT_OUT_URB];
        bool cport_out_urb_busy[NUM_CPORT_OUT_URB];
        bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB];
        spinlock_t cport_out_urb_lock;
 
        int *cport_to_ep;
+
+       struct task_struct *apb_log_task;
+       struct dentry *apb_log_dentry;
+       struct dentry *apb_log_enable_dentry;
+       DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE);
 };
 
 /**
@@ -117,39 +119,41 @@ struct cport_to_ep {
        __u8 endpoint_out;
 };
 
-static inline struct es1_ap_dev *hd_to_es1(struct greybus_host_device *hd)
+static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
 {
-       return (struct es1_ap_dev *)&hd->hd_priv;
+       return (struct es2_ap_dev *)&hd->hd_priv;
 }
 
 static void cport_out_callback(struct urb *urb);
-static void usb_log_enable(struct es1_ap_dev *es1);
-static void usb_log_disable(struct es1_ap_dev *es1);
+static void usb_log_enable(struct es2_ap_dev *es2);
+static void usb_log_disable(struct es2_ap_dev *es2);
 
 /* Get the endpoints pair mapped to the cport */
-static int cport_to_ep_pair(struct es1_ap_dev *es1, u16 cport_id)
+static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id)
 {
-       if (cport_id >= es1->hd->num_cports)
+       if (cport_id >= es2->hd->num_cports)
                return 0;
-       return es1->cport_to_ep[cport_id];
+       return es2->cport_to_ep[cport_id];
 }
 
-#define ES1_TIMEOUT    500     /* 500 ms for the SVC to do something */
+#define ES2_TIMEOUT    500     /* 500 ms for the SVC to do something */
 
+/* Disable for now until we work all of this out to keep a warning-free build */
+#if 0
 /* Test if the endpoints pair is already mapped to a cport */
-static int ep_pair_in_use(struct es1_ap_dev *es1, int ep_pair)
+static int ep_pair_in_use(struct es2_ap_dev *es2, int ep_pair)
 {
        int i;
 
-       for (i = 0; i < es1->hd->num_cports; i++) {
-               if (es1->cport_to_ep[i] == ep_pair)
+       for (i = 0; i < es2->hd->num_cports; i++) {
+               if (es2->cport_to_ep[i] == ep_pair)
                        return 1;
        }
        return 0;
 }
 
 /* Configure the endpoint mapping and send the request to APBridge */
-static int map_cport_to_ep(struct es1_ap_dev *es1,
+static int map_cport_to_ep(struct es2_ap_dev *es2,
                                u16 cport_id, int ep_pair)
 {
        int retval;
@@ -157,28 +161,28 @@ static int map_cport_to_ep(struct es1_ap_dev *es1,
 
        if (ep_pair < 0 || ep_pair >= NUM_BULKS)
                return -EINVAL;
-       if (cport_id >= es1->hd->num_cports)
+       if (cport_id >= es2->hd->num_cports)
                return -EINVAL;
-       if (ep_pair && ep_pair_in_use(es1, ep_pair))
+       if (ep_pair && ep_pair_in_use(es2, ep_pair))
                return -EINVAL;
 
        cport_to_ep = kmalloc(sizeof(*cport_to_ep), GFP_KERNEL);
        if (!cport_to_ep)
                return -ENOMEM;
 
-       es1->cport_to_ep[cport_id] = ep_pair;
+       es2->cport_to_ep[cport_id] = ep_pair;
        cport_to_ep->cport_id = cpu_to_le16(cport_id);
-       cport_to_ep->endpoint_in = es1->cport_in[ep_pair].endpoint;
-       cport_to_ep->endpoint_out = es1->cport_out[ep_pair].endpoint;
+       cport_to_ep->endpoint_in = es2->cport_in[ep_pair].endpoint;
+       cport_to_ep->endpoint_out = es2->cport_out[ep_pair].endpoint;
 
-       retval = usb_control_msg(es1->usb_dev,
-                                usb_sndctrlpipe(es1->usb_dev, 0),
-                                REQUEST_EP_MAPPING,
+       retval = usb_control_msg(es2->usb_dev,
+                                usb_sndctrlpipe(es2->usb_dev, 0),
+                                GB_APB_REQUEST_EP_MAPPING,
                                 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                                 0x00, 0x00,
                                 (char *)cport_to_ep,
                                 sizeof(*cport_to_ep),
-                                ES1_TIMEOUT);
+                                ES2_TIMEOUT);
        if (retval == sizeof(*cport_to_ep))
                retval = 0;
        kfree(cport_to_ep);
@@ -187,29 +191,153 @@ static int map_cport_to_ep(struct es1_ap_dev *es1,
 }
 
 /* Unmap a cport: use the muxed endpoints pair */
-static int unmap_cport(struct es1_ap_dev *es1, u16 cport_id)
+static int unmap_cport(struct es2_ap_dev *es2, u16 cport_id)
+{
+       return map_cport_to_ep(es2, cport_id, 0);
+}
+#endif
+
+static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
+{
+       struct usb_device *udev = es2->usb_dev;
+       u8 *data;
+       int retval;
+
+       data = kmalloc(size, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       memcpy(data, req, size);
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                cmd,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE,
+                                0, 0, data, size, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
+       else
+               retval = 0;
+
+       kfree(data);
+       return retval;
+}
+
+static void ap_urb_complete(struct urb *urb)
+{
+       struct usb_ctrlrequest *dr = urb->context;
+
+       kfree(dr);
+       usb_free_urb(urb);
+}
+
+static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
 {
-       return map_cport_to_ep(es1, cport_id, 0);
+       struct usb_device *udev = es2->usb_dev;
+       struct urb *urb;
+       struct usb_ctrlrequest *dr;
+       u8 *buf;
+       int retval;
+
+       urb = usb_alloc_urb(0, GFP_ATOMIC);
+       if (!urb)
+               return -ENOMEM;
+
+       dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC);
+       if (!dr) {
+               usb_free_urb(urb);
+               return -ENOMEM;
+       }
+
+       buf = (u8 *)dr + sizeof(*dr);
+       memcpy(buf, req, size);
+
+       dr->bRequest = cmd;
+       dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE;
+       dr->wValue = 0;
+       dr->wIndex = 0;
+       dr->wLength = cpu_to_le16(size);
+
+       usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0),
+                            (unsigned char *)dr, buf, size,
+                            ap_urb_complete, dr);
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval) {
+               usb_free_urb(urb);
+               kfree(dr);
+       }
+       return retval;
 }
 
-static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask)
+static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd,
+                    bool async)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+
+       if (async)
+               return output_async(es2, req, size, cmd);
+
+       return output_sync(es2, req, size, cmd);
+}
+
+static int es2_cport_in_enable(struct es2_ap_dev *es2,
+                               struct es2_cport_in *cport_in)
+{
+       struct urb *urb;
+       int ret;
+       int i;
+
+       for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
+               urb = cport_in->urb[i];
+
+               ret = usb_submit_urb(urb, GFP_KERNEL);
+               if (ret) {
+                       dev_err(&es2->usb_dev->dev,
+                                       "failed to submit in-urb: %d\n", ret);
+                       goto err_kill_urbs;
+               }
+       }
+
+       return 0;
+
+err_kill_urbs:
+       for (--i; i >= 0; --i) {
+               urb = cport_in->urb[i];
+               usb_kill_urb(urb);
+       }
+
+       return ret;
+}
+
+static void es2_cport_in_disable(struct es2_ap_dev *es2,
+                               struct es2_cport_in *cport_in)
+{
+       struct urb *urb;
+       int i;
+
+       for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
+               urb = cport_in->urb[i];
+               usb_kill_urb(urb);
+       }
+}
+
+static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask)
 {
        struct urb *urb = NULL;
        unsigned long flags;
        int i;
 
-       spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
+       spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
 
        /* Look in our pool of allocated urbs first, as that's the "fastest" */
        for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
-               if (es1->cport_out_urb_busy[i] == false &&
-                               es1->cport_out_urb_cancelled[i] == false) {
-                       es1->cport_out_urb_busy[i] = true;
-                       urb = es1->cport_out_urb[i];
+               if (es2->cport_out_urb_busy[i] == false &&
+                               es2->cport_out_urb_cancelled[i] == false) {
+                       es2->cport_out_urb_busy[i] = true;
+                       urb = es2->cport_out_urb[i];
                        break;
                }
        }
-       spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
+       spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
        if (urb)
                return urb;
 
@@ -217,12 +345,12 @@ static struct urb *next_free_urb(struct es1_ap_dev *es1, gfp_t gfp_mask)
         * Crap, pool is empty, complain to the syslog and go allocate one
         * dynamically as we have to succeed.
         */
-       dev_err(&es1->usb_dev->dev,
+       dev_dbg(&es2->usb_dev->dev,
                "No free CPort OUT urbs, having to dynamically allocate one!\n");
        return usb_alloc_urb(0, gfp_mask);
 }
 
-static void free_urb(struct es1_ap_dev *es1, struct urb *urb)
+static void free_urb(struct es2_ap_dev *es2, struct urb *urb)
 {
        unsigned long flags;
        int i;
@@ -230,15 +358,15 @@ static void free_urb(struct es1_ap_dev *es1, struct urb *urb)
         * See if this was an urb in our pool, if so mark it "free", otherwise
         * we need to free it ourselves.
         */
-       spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
+       spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
        for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
-               if (urb == es1->cport_out_urb[i]) {
-                       es1->cport_out_urb_busy[i] = false;
+               if (urb == es2->cport_out_urb[i]) {
+                       es2->cport_out_urb_busy[i] = false;
                        urb = NULL;
                        break;
                }
        }
-       spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
+       spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
 
        /* If urb is not NULL, then we need to free this urb */
        usb_free_urb(urb);
@@ -274,11 +402,11 @@ static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header)
  * Returns zero if the message was successfully queued, or a negative errno
  * otherwise.
  */
-static int message_send(struct greybus_host_device *hd, u16 cport_id,
+static int message_send(struct gb_host_device *hd, u16 cport_id,
                        struct gb_message *message, gfp_t gfp_mask)
 {
-       struct es1_ap_dev *es1 = hd_to_es1(hd);
-       struct usb_device *udev = es1->usb_dev;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
        size_t buffer_size;
        int retval;
        struct urb *urb;
@@ -291,41 +419,41 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id,
         * the target CPort id before filling it in.
         */
        if (!cport_id_valid(hd, cport_id)) {
-               pr_err("invalid destination cport 0x%02x\n", cport_id);
+               dev_err(&udev->dev, "invalid cport %u\n", cport_id);
                return -EINVAL;
        }
 
        /* Find a free urb */
-       urb = next_free_urb(es1, gfp_mask);
+       urb = next_free_urb(es2, gfp_mask);
        if (!urb)
                return -ENOMEM;
 
-       spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
+       spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
        message->hcpriv = urb;
-       spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
+       spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
 
        /* Pack the cport id into the message header */
        gb_message_cport_pack(message->header, cport_id);
 
        buffer_size = sizeof(*message->header) + message->payload_size;
 
-       ep_pair = cport_to_ep_pair(es1, cport_id);
+       ep_pair = cport_to_ep_pair(es2, cport_id);
        usb_fill_bulk_urb(urb, udev,
                          usb_sndbulkpipe(udev,
-                                         es1->cport_out[ep_pair].endpoint),
+                                         es2->cport_out[ep_pair].endpoint),
                          message->buffer, buffer_size,
                          cport_out_callback, message);
        urb->transfer_flags |= URB_ZERO_PACKET;
-       gb_connection_push_timestamp(message->operation->connection);
+       trace_gb_host_device_send(hd, cport_id, buffer_size);
        retval = usb_submit_urb(urb, gfp_mask);
        if (retval) {
-               pr_err("error %d submitting URB\n", retval);
+               dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval);
 
-               spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
+               spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
                message->hcpriv = NULL;
-               spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
+               spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
 
-               free_urb(es1, urb);
+               free_urb(es2, urb);
                gb_message_cport_clear(message->header);
 
                return retval;
@@ -339,14 +467,14 @@ static int message_send(struct greybus_host_device *hd, u16 cport_id,
  */
 static void message_cancel(struct gb_message *message)
 {
-       struct greybus_host_device *hd = message->operation->connection->hd;
-       struct es1_ap_dev *es1 = hd_to_es1(hd);
+       struct gb_host_device *hd = message->operation->connection->hd;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
        struct urb *urb;
        int i;
 
        might_sleep();
 
-       spin_lock_irq(&es1->cport_out_urb_lock);
+       spin_lock_irq(&es2->cport_out_urb_lock);
        urb = message->hcpriv;
 
        /* Prevent dynamically allocated urb from being deallocated. */
@@ -354,28 +482,148 @@ static void message_cancel(struct gb_message *message)
 
        /* Prevent pre-allocated urb from being reused. */
        for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
-               if (urb == es1->cport_out_urb[i]) {
-                       es1->cport_out_urb_cancelled[i] = true;
+               if (urb == es2->cport_out_urb[i]) {
+                       es2->cport_out_urb_cancelled[i] = true;
                        break;
                }
        }
-       spin_unlock_irq(&es1->cport_out_urb_lock);
+       spin_unlock_irq(&es2->cport_out_urb_lock);
 
        usb_kill_urb(urb);
 
        if (i < NUM_CPORT_OUT_URB) {
-               spin_lock_irq(&es1->cport_out_urb_lock);
-               es1->cport_out_urb_cancelled[i] = false;
-               spin_unlock_irq(&es1->cport_out_urb_lock);
+               spin_lock_irq(&es2->cport_out_urb_lock);
+               es2->cport_out_urb_cancelled[i] = false;
+               spin_unlock_irq(&es2->cport_out_urb_lock);
        }
 
        usb_free_urb(urb);
 }
 
-static struct greybus_host_driver es1_driver = {
-       .hd_priv_size           = sizeof(struct es1_ap_dev),
+static int cport_reset(struct gb_host_device *hd, u16 cport_id)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+       int retval;
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_RESET_CPORT,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0,
+                                NULL, 0, ES2_TIMEOUT);
+       if (retval < 0) {
+               dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id,
+                       retval);
+               return retval;
+       }
+
+       return 0;
+}
+
+static int cport_enable(struct gb_host_device *hd, u16 cport_id)
+{
+       int retval;
+
+       if (cport_id != GB_SVC_CPORT_ID) {
+               retval = cport_reset(hd, cport_id);
+               if (retval)
+                       return retval;
+       }
+
+       return 0;
+}
+
+static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+
+       if (!cport_id_valid(hd, cport_id)) {
+               dev_err(&udev->dev, "invalid cport %u\n", cport_id);
+               return -EINVAL;
+       }
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_LATENCY_TAG_EN,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n",
+                       cport_id);
+       return retval;
+}
+
+static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+
+       if (!cport_id_valid(hd, cport_id)) {
+               dev_err(&udev->dev, "invalid cport %u\n", cport_id);
+               return -EINVAL;
+       }
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_LATENCY_TAG_DIS,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n",
+                       cport_id);
+       return retval;
+}
+
+static int cport_features_enable(struct gb_host_device *hd, u16 cport_id)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_CPORT_FEAT_EN,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev, "Cannot enable CPort features for cport %u: %d\n",
+                       cport_id, retval);
+       return retval;
+}
+
+static int cport_features_disable(struct gb_host_device *hd, u16 cport_id)
+{
+       int retval;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_CPORT_FEAT_DIS,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE, cport_id, 0, NULL,
+                                0, ES2_TIMEOUT);
+       if (retval < 0)
+               dev_err(&udev->dev,
+                       "Cannot disable CPort features for cport %u: %d\n",
+                       cport_id, retval);
+       return retval;
+}
+
+static struct gb_hd_driver es2_driver = {
+       .hd_priv_size           = sizeof(struct es2_ap_dev),
        .message_send           = message_send,
        .message_cancel         = message_cancel,
+       .cport_enable           = cport_enable,
+       .latency_tag_enable     = latency_tag_enable,
+       .latency_tag_disable    = latency_tag_disable,
+       .output                 = output,
+       .cport_features_enable  = cport_features_enable,
+       .cport_features_disable = cport_features_disable,
 };
 
 /* Common function to report consistent warnings based on URB status */
@@ -404,56 +652,52 @@ static int check_urb_status(struct urb *urb)
        return -EAGAIN;
 }
 
-static void ap_disconnect(struct usb_interface *interface)
+static void es2_destroy(struct es2_ap_dev *es2)
 {
-       struct es1_ap_dev *es1;
        struct usb_device *udev;
        int bulk_in;
        int i;
 
-       es1 = usb_get_intfdata(interface);
-       if (!es1)
-               return;
-
-       usb_log_disable(es1);
+       debugfs_remove(es2->apb_log_enable_dentry);
+       usb_log_disable(es2);
 
        /* Tear down everything! */
        for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
-               struct urb *urb = es1->cport_out_urb[i];
+               struct urb *urb = es2->cport_out_urb[i];
 
                if (!urb)
                        break;
                usb_kill_urb(urb);
                usb_free_urb(urb);
-               es1->cport_out_urb[i] = NULL;
-               es1->cport_out_urb_busy[i] = false;     /* just to be anal */
+               es2->cport_out_urb[i] = NULL;
+               es2->cport_out_urb_busy[i] = false;     /* just to be anal */
        }
 
        for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
-               struct es1_cport_in *cport_in = &es1->cport_in[bulk_in];
+               struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
+
                for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
                        struct urb *urb = cport_in->urb[i];
 
                        if (!urb)
                                break;
-                       usb_kill_urb(urb);
                        usb_free_urb(urb);
                        kfree(cport_in->buffer[i]);
                        cport_in->buffer[i] = NULL;
                }
        }
 
-       usb_set_intfdata(interface, NULL);
-       udev = es1->usb_dev;
-       greybus_remove_hd(es1->hd);
-       kfree(es1->cport_to_ep);
+       kfree(es2->cport_to_ep);
+
+       udev = es2->usb_dev;
+       gb_hd_put(es2->hd);
 
        usb_put_dev(udev);
 }
 
 static void cport_in_callback(struct urb *urb)
 {
-       struct greybus_host_device *hd = urb->context;
+       struct gb_host_device *hd = urb->context;
        struct device *dev = &urb->dev->dev;
        struct gb_operation_msg_hdr *header;
        int status = check_urb_status(urb);
@@ -463,12 +707,17 @@ static void cport_in_callback(struct urb *urb)
        if (status) {
                if ((status == -EAGAIN) || (status == -EPROTO))
                        goto exit;
+
+               /* The urb is being unlinked */
+               if (status == -ENOENT || status == -ESHUTDOWN)
+                       return;
+
                dev_err(dev, "urb cport in error %d (dropped)\n", status);
                return;
        }
 
        if (urb->actual_length < sizeof(*header)) {
-               dev_err(dev, "%s: short message received\n", __func__);
+               dev_err(dev, "short message received\n");
                goto exit;
        }
 
@@ -476,66 +725,66 @@ static void cport_in_callback(struct urb *urb)
        header = urb->transfer_buffer;
        cport_id = gb_message_cport_unpack(header);
 
-       if (cport_id_valid(hd, cport_id))
+       if (cport_id_valid(hd, cport_id)) {
+               trace_gb_host_device_recv(hd, cport_id, urb->actual_length);
                greybus_data_rcvd(hd, cport_id, urb->transfer_buffer,
                                                        urb->actual_length);
-       else
-               dev_err(dev, "%s: invalid cport id 0x%02x received\n",
-                               __func__, cport_id);
+       } else {
+               dev_err(dev, "invalid cport id %u received\n", cport_id);
+       }
 exit:
        /* put our urb back in the request pool */
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
-               dev_err(dev, "%s: error %d in submitting urb.\n",
-                       __func__, retval);
+               dev_err(dev, "failed to resubmit in-urb: %d\n", retval);
 }
 
 static void cport_out_callback(struct urb *urb)
 {
        struct gb_message *message = urb->context;
-       struct greybus_host_device *hd = message->operation->connection->hd;
-       struct es1_ap_dev *es1 = hd_to_es1(hd);
+       struct gb_host_device *hd = message->operation->connection->hd;
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
        int status = check_urb_status(urb);
        unsigned long flags;
 
        gb_message_cport_clear(message->header);
 
+       spin_lock_irqsave(&es2->cport_out_urb_lock, flags);
+       message->hcpriv = NULL;
+       spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags);
+
        /*
         * Tell the submitter that the message send (attempt) is
         * complete, and report the status.
         */
        greybus_message_sent(hd, message, status);
 
-       spin_lock_irqsave(&es1->cport_out_urb_lock, flags);
-       message->hcpriv = NULL;
-       spin_unlock_irqrestore(&es1->cport_out_urb_lock, flags);
-
-       free_urb(es1, urb);
+       free_urb(es2, urb);
 }
 
 #define APB1_LOG_MSG_SIZE      64
-static void apb1_log_get(struct es1_ap_dev *es1, char *buf)
+static void apb_log_get(struct es2_ap_dev *es2, char *buf)
 {
        int retval;
 
        /* SVC messages go down our control pipe */
        do {
-               retval = usb_control_msg(es1->usb_dev,
-                                       usb_rcvctrlpipe(es1->usb_dev, 0),
-                                       REQUEST_LOG,
+               retval = usb_control_msg(es2->usb_dev,
+                                       usb_rcvctrlpipe(es2->usb_dev, 0),
+                                       GB_APB_REQUEST_LOG,
                                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
                                        0x00, 0x00,
                                        buf,
                                        APB1_LOG_MSG_SIZE,
-                                       ES1_TIMEOUT);
+                                       ES2_TIMEOUT);
                if (retval > 0)
-                       kfifo_in(&apb1_log_fifo, buf, retval);
+                       kfifo_in(&es2->apb_log_fifo, buf, retval);
        } while (retval > 0);
 }
 
-static int apb1_log_poll(void *data)
+static int apb_log_poll(void *data)
 {
-       struct es1_ap_dev *es1 = data;
+       struct es2_ap_dev *es2 = data;
        char *buf;
 
        buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL);
@@ -544,7 +793,7 @@ static int apb1_log_poll(void *data)
 
        while (!kthread_should_stop()) {
                msleep(1000);
-               apb1_log_get(es1, buf);
+               apb_log_get(es2, buf);
        }
 
        kfree(buf);
@@ -552,9 +801,10 @@ static int apb1_log_poll(void *data)
        return 0;
 }
 
-static ssize_t apb1_log_read(struct file *f, char __user *buf,
+static ssize_t apb_log_read(struct file *f, char __user *buf,
                                size_t count, loff_t *ppos)
 {
+       struct es2_ap_dev *es2 = f->f_inode->i_private;
        ssize_t ret;
        size_t copied;
        char *tmp_buf;
@@ -566,7 +816,7 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf,
        if (!tmp_buf)
                return -ENOMEM;
 
-       copied = kfifo_out(&apb1_log_fifo, tmp_buf, count);
+       copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count);
        ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied);
 
        kfree(tmp_buf);
@@ -574,87 +824,93 @@ static ssize_t apb1_log_read(struct file *f, char __user *buf,
        return ret;
 }
 
-static const struct file_operations apb1_log_fops = {
-       .read   = apb1_log_read,
+static const struct file_operations apb_log_fops = {
+       .read   = apb_log_read,
 };
 
-static void usb_log_enable(struct es1_ap_dev *es1)
+static void usb_log_enable(struct es2_ap_dev *es2)
 {
-       if (!IS_ERR_OR_NULL(apb1_log_task))
+       if (!IS_ERR_OR_NULL(es2->apb_log_task))
                return;
 
        /* get log from APB1 */
-       apb1_log_task = kthread_run(apb1_log_poll, es1, "apb1_log");
-       if (IS_ERR(apb1_log_task))
+       es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log");
+       if (IS_ERR(es2->apb_log_task))
                return;
-       apb1_log_dentry = debugfs_create_file("apb1_log", S_IRUGO,
-                                               gb_debugfs_get(), NULL,
-                                               &apb1_log_fops);
+       /* XXX We will need to rename this per APB */
+       es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO,
+                                               gb_debugfs_get(), es2,
+                                               &apb_log_fops);
 }
 
-static void usb_log_disable(struct es1_ap_dev *es1)
+static void usb_log_disable(struct es2_ap_dev *es2)
 {
-       if (IS_ERR_OR_NULL(apb1_log_task))
+       if (IS_ERR_OR_NULL(es2->apb_log_task))
                return;
 
-       debugfs_remove(apb1_log_dentry);
-       apb1_log_dentry = NULL;
+       debugfs_remove(es2->apb_log_dentry);
+       es2->apb_log_dentry = NULL;
 
-       kthread_stop(apb1_log_task);
-       apb1_log_task = NULL;
+       kthread_stop(es2->apb_log_task);
+       es2->apb_log_task = NULL;
 }
 
-static ssize_t apb1_log_enable_read(struct file *f, char __user *buf,
+static ssize_t apb_log_enable_read(struct file *f, char __user *buf,
                                size_t count, loff_t *ppos)
 {
+       struct es2_ap_dev *es2 = f->f_inode->i_private;
+       int enable = !IS_ERR_OR_NULL(es2->apb_log_task);
        char tmp_buf[3];
-       int enable = !IS_ERR_OR_NULL(apb1_log_task);
 
        sprintf(tmp_buf, "%d\n", enable);
        return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3);
 }
 
-static ssize_t apb1_log_enable_write(struct file *f, const char __user *buf,
+static ssize_t apb_log_enable_write(struct file *f, const char __user *buf,
                                size_t count, loff_t *ppos)
 {
        int enable;
        ssize_t retval;
-       struct es1_ap_dev *es1 = (struct es1_ap_dev *)f->f_inode->i_private;
+       struct es2_ap_dev *es2 = f->f_inode->i_private;
 
        retval = kstrtoint_from_user(buf, count, 10, &enable);
        if (retval)
                return retval;
 
        if (enable)
-               usb_log_enable(es1);
+               usb_log_enable(es2);
        else
-               usb_log_disable(es1);
+               usb_log_disable(es2);
 
        return count;
 }
 
-static const struct file_operations apb1_log_enable_fops = {
-       .read   = apb1_log_enable_read,
-       .write  = apb1_log_enable_write,
+static const struct file_operations apb_log_enable_fops = {
+       .read   = apb_log_enable_read,
+       .write  = apb_log_enable_write,
 };
 
-static int apb1_get_cport_count(struct usb_device *udev)
+static int apb_get_cport_count(struct usb_device *udev)
 {
        int retval;
        __le16 *cport_count;
 
-       cport_count = kmalloc(sizeof(*cport_count), GFP_KERNEL);
+       cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL);
        if (!cport_count)
                return -ENOMEM;
 
        retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-                                REQUEST_CPORT_COUNT,
+                                GB_APB_REQUEST_CPORT_COUNT,
                                 USB_DIR_IN | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, cport_count,
-                                sizeof(*cport_count), ES1_TIMEOUT);
-       if (retval < 0) {
+                                sizeof(*cport_count), ES2_TIMEOUT);
+       if (retval != sizeof(*cport_count)) {
                dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
                        retval);
+
+               if (retval >= 0)
+                       retval = -EIO;
+
                goto out;
        }
 
@@ -672,29 +928,28 @@ out:
 }
 
 /*
- * The ES1 USB Bridge device contains 4 endpoints
- * 1 Control - usual USB stuff + AP -> SVC messages
- * 1 Interrupt IN - SVC -> AP messages
- * 1 Bulk IN - CPort data in
- * 1 Bulk OUT - CPort data out
+ * The ES2 USB Bridge device has 15 endpoints
+ * 1 Control - usual USB stuff + AP -> APBridgeA messages
+ * 7 Bulk IN - CPort data in
+ * 7 Bulk OUT - CPort data out
  */
 static int ap_probe(struct usb_interface *interface,
                    const struct usb_device_id *id)
 {
-       struct es1_ap_dev *es1;
-       struct greybus_host_device *hd;
+       struct es2_ap_dev *es2;
+       struct gb_host_device *hd;
        struct usb_device *udev;
        struct usb_host_interface *iface_desc;
        struct usb_endpoint_descriptor *endpoint;
        int bulk_in = 0;
        int bulk_out = 0;
-       int retval = -ENOMEM;
+       int retval;
        int i;
        int num_cports;
 
        udev = usb_get_dev(interface_to_usbdev(interface));
 
-       num_cports = apb1_get_cport_count(udev);
+       num_cports = apb_get_cport_count(udev);
        if (num_cports < 0) {
                usb_put_dev(udev);
                dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
@@ -702,74 +957,88 @@ static int ap_probe(struct usb_interface *interface,
                return num_cports;
        }
 
-       hd = greybus_create_hd(&es1_driver, &udev->dev, ES1_GBUF_MSG_SIZE_MAX,
-                              num_cports);
+       hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX,
+                               num_cports);
        if (IS_ERR(hd)) {
                usb_put_dev(udev);
                return PTR_ERR(hd);
        }
 
-       es1 = hd_to_es1(hd);
-       es1->hd = hd;
-       es1->usb_intf = interface;
-       es1->usb_dev = udev;
-       spin_lock_init(&es1->cport_out_urb_lock);
-       usb_set_intfdata(interface, es1);
+       es2 = hd_to_es2(hd);
+       es2->hd = hd;
+       es2->usb_intf = interface;
+       es2->usb_dev = udev;
+       spin_lock_init(&es2->cport_out_urb_lock);
+       INIT_KFIFO(es2->apb_log_fifo);
+       usb_set_intfdata(interface, es2);
+
+       /*
+        * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated
+        * dynamically.
+        */
+       retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0);
+       if (retval)
+               goto error;
+       retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1);
+       if (retval)
+               goto error;
 
-       es1->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es1->cport_to_ep),
+       es2->cport_to_ep = kcalloc(hd->num_cports, sizeof(*es2->cport_to_ep),
                                   GFP_KERNEL);
-       if (!es1->cport_to_ep) {
+       if (!es2->cport_to_ep) {
                retval = -ENOMEM;
                goto error;
        }
 
-       /* find all 3 of our endpoints */
+       /* find all bulk endpoints */
        iface_desc = interface->cur_altsetting;
        for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (usb_endpoint_is_bulk_in(endpoint)) {
-                       es1->cport_in[bulk_in++].endpoint =
+                       es2->cport_in[bulk_in++].endpoint =
                                endpoint->bEndpointAddress;
                } else if (usb_endpoint_is_bulk_out(endpoint)) {
-                       es1->cport_out[bulk_out++].endpoint =
+                       es2->cport_out[bulk_out++].endpoint =
                                endpoint->bEndpointAddress;
                } else {
                        dev_err(&udev->dev,
-                               "Unknown endpoint type found, address %x\n",
+                               "Unknown endpoint type found, address 0x%02x\n",
                                endpoint->bEndpointAddress);
                }
        }
-       if ((bulk_in == 0) ||
-           (bulk_out == 0)) {
+       if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
                dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
+               retval = -ENODEV;
                goto error;
        }
 
-       /* Allocate buffers for our cport in messages and start them up */
+       /* Allocate buffers for our cport in messages */
        for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
-               struct es1_cport_in *cport_in = &es1->cport_in[bulk_in];
+               struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
+
                for (i = 0; i < NUM_CPORT_IN_URB; ++i) {
                        struct urb *urb;
                        u8 *buffer;
 
                        urb = usb_alloc_urb(0, GFP_KERNEL);
-                       if (!urb)
+                       if (!urb) {
+                               retval = -ENOMEM;
                                goto error;
-                       buffer = kmalloc(ES1_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
-                       if (!buffer)
+                       }
+                       buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL);
+                       if (!buffer) {
+                               retval = -ENOMEM;
                                goto error;
+                       }
 
                        usb_fill_bulk_urb(urb, udev,
                                          usb_rcvbulkpipe(udev,
                                                          cport_in->endpoint),
-                                         buffer, ES1_GBUF_MSG_SIZE_MAX,
+                                         buffer, ES2_GBUF_MSG_SIZE_MAX,
                                          cport_in_callback, hd);
                        cport_in->urb[i] = urb;
                        cport_in->buffer[i] = buffer;
-                       retval = usb_submit_urb(urb, GFP_KERNEL);
-                       if (retval)
-                               goto error;
                }
        }
 
@@ -778,32 +1047,65 @@ static int ap_probe(struct usb_interface *interface,
                struct urb *urb;
 
                urb = usb_alloc_urb(0, GFP_KERNEL);
-               if (!urb)
+               if (!urb) {
+                       retval = -ENOMEM;
                        goto error;
+               }
 
-               es1->cport_out_urb[i] = urb;
-               es1->cport_out_urb_busy[i] = false;     /* just to be anal */
+               es2->cport_out_urb[i] = urb;
+               es2->cport_out_urb_busy[i] = false;     /* just to be anal */
        }
 
-       apb1_log_enable_dentry = debugfs_create_file("apb1_log_enable",
+       /* XXX We will need to rename this per APB */
+       es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable",
                                                        (S_IWUSR | S_IRUGO),
-                                                       gb_debugfs_get(), es1,
-                                                       &apb1_log_enable_fops);
+                                                       gb_debugfs_get(), es2,
+                                                       &apb_log_enable_fops);
+
+       retval = gb_hd_add(hd);
+       if (retval)
+               goto error;
+
+       for (i = 0; i < NUM_BULKS; ++i) {
+               retval = es2_cport_in_enable(es2, &es2->cport_in[i]);
+               if (retval)
+                       goto err_disable_cport_in;
+       }
+
        return 0;
+
+err_disable_cport_in:
+       for (--i; i >= 0; --i)
+               es2_cport_in_disable(es2, &es2->cport_in[i]);
+       gb_hd_del(hd);
 error:
-       ap_disconnect(interface);
+       es2_destroy(es2);
 
        return retval;
 }
 
-static struct usb_driver es1_ap_driver = {
+static void ap_disconnect(struct usb_interface *interface)
+{
+       struct es2_ap_dev *es2 = usb_get_intfdata(interface);
+       int i;
+
+       gb_hd_del(es2->hd);
+
+       for (i = 0; i < NUM_BULKS; ++i)
+               es2_cport_in_disable(es2, &es2->cport_in[i]);
+
+       es2_destroy(es2);
+}
+
+static struct usb_driver es2_ap_driver = {
        .name =         "es2_ap_driver",
        .probe =        ap_probe,
        .disconnect =   ap_disconnect,
        .id_table =     id_table,
+       .soft_unbind =  1,
 };
 
-module_usb_driver(es1_ap_driver);
+module_usb_driver(es2_ap_driver);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");