greybus: hd/es2: add cport_clear callback and ARPC
[cascardo/linux.git] / drivers / staging / greybus / es2.c
index fd59c14..7f46f2a 100644 (file)
 #include <linux/usb.h>
 #include <linux/kfifo.h>
 #include <linux/debugfs.h>
+#include <linux/list.h>
 #include <asm/unaligned.h>
 
+#include "arpc.h"
 #include "greybus.h"
+#include "greybus_trace.h"
 #include "kernel_ver.h"
 #include "connection.h"
 
+
+/* Default timeout for USB vendor requests. */
+#define ES2_USB_CTRL_TIMEOUT   500
+
+/* Default timeout for ARPC CPort requests */
+#define ES2_ARPC_CPORT_TIMEOUT 500
+
 /* 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
 
+/* Memory sizes for the ARPC buffers */
+#define ARPC_OUT_SIZE_MAX      U16_MAX
+#define ARPC_IN_SIZE_MAX       128
+
 static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x18d1, 0x1eaf) },
        { },
@@ -35,6 +49,12 @@ MODULE_DEVICE_TABLE(usb, id_table);
 /* Number of bulk in and bulk out couple */
 #define NUM_BULKS              7
 
+/* Expected number of bulk out endpoints */
+#define NUM_BULKS_OUT          NUM_BULKS
+
+/* Expected number of bulk in endpoints (including ARPC endpoint) */
+#define NUM_BULKS_IN           (NUM_BULKS + 1)
+
 /*
  * Number of CPort IN urbs in flight at any point in time.
  * Adjust if we are having stalls in the USB buffer due to not enough urbs in
@@ -47,6 +67,11 @@ MODULE_DEVICE_TABLE(usb, id_table);
  */
 #define NUM_CPORT_OUT_URB      (8 * NUM_BULKS)
 
+/*
+ * Number of ARPC in urbs in flight at any point in time.
+ */
+#define NUM_ARPC_IN_URB                2
+
 /*
  * @endpoint: bulk in endpoint for CPort data
  * @urb: array of urbs for the CPort in messages
@@ -84,6 +109,12 @@ struct es2_cport_out {
  * @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
+ * @arpc_urb: array of urbs for the ARPC in messages
+ * @arpc_buffer: array of buffers for the @arpc_urb urbs
+ * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC
+ * @arpc_id_cycle: gives an unique id to ARPC
+ * @arpc_lock: locks ARPC list
+ * @arpcs: list of in progress ARPCs
  */
 struct es2_ap_dev {
        struct usb_device *usb_dev;
@@ -105,6 +136,14 @@ struct es2_ap_dev {
        struct dentry *apb_log_dentry;
        struct dentry *apb_log_enable_dentry;
        DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE);
+
+       __u8 arpc_endpoint_in;
+       struct urb *arpc_urb[NUM_ARPC_IN_URB];
+       u8 *arpc_buffer[NUM_ARPC_IN_URB];
+
+       int arpc_id_cycle;
+       spinlock_t arpc_lock;
+       struct list_head arpcs;
 };
 
 /**
@@ -142,6 +181,14 @@ struct timesync_authoritative_request {
        __le64  frame_time[GB_TIMESYNC_MAX_STROBES];
 } __packed;
 
+struct arpc {
+       struct list_head list;
+       struct arpc_request_message *req;
+       struct arpc_response_message *resp;
+       struct completion response_received;
+       bool active;
+};
+
 static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
 {
        return (struct es2_ap_dev *)&hd->hd_priv;
@@ -150,6 +197,8 @@ static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd)
 static void cport_out_callback(struct urb *urb);
 static void usb_log_enable(struct es2_ap_dev *es2);
 static void usb_log_disable(struct es2_ap_dev *es2);
+static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload,
+                    size_t size, int *result, unsigned int timeout);
 
 /* Get the endpoints pair mapped to the cport */
 static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id)
@@ -159,8 +208,6 @@ static int cport_to_ep_pair(struct es2_ap_dev *es2, u16 cport_id)
        return es2->cport_to_ep[cport_id];
 }
 
-#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 */
@@ -205,7 +252,7 @@ static int map_cport_to_ep(struct es2_ap_dev *es2,
                                 0x00, 0x00,
                                 (char *)cport_to_ep,
                                 sizeof(*cport_to_ep),
-                                ES2_TIMEOUT);
+                                ES2_USB_CTRL_TIMEOUT);
        if (retval == sizeof(*cport_to_ep))
                retval = 0;
        kfree(cport_to_ep);
@@ -235,7 +282,7 @@ static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd)
                                 cmd,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE,
-                                0, 0, data, size, ES2_TIMEOUT);
+                                0, 0, data, size, ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev, "%s: return error %d\n", __func__, retval);
        else
@@ -343,6 +390,45 @@ static void es2_cport_in_disable(struct es2_ap_dev *es2,
        }
 }
 
+static int es2_arpc_in_enable(struct es2_ap_dev *es2)
+{
+       struct urb *urb;
+       int ret;
+       int i;
+
+       for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
+               urb = es2->arpc_urb[i];
+
+               ret = usb_submit_urb(urb, GFP_KERNEL);
+               if (ret) {
+                       dev_err(&es2->usb_dev->dev,
+                               "failed to submit arpc in-urb: %d\n", ret);
+                       goto err_kill_urbs;
+               }
+       }
+
+       return 0;
+
+err_kill_urbs:
+       for (--i; i >= 0; --i) {
+               urb = es2->arpc_urb[i];
+               usb_kill_urb(urb);
+       }
+
+       return ret;
+}
+
+static void es2_arpc_in_disable(struct es2_ap_dev *es2)
+{
+       struct urb *urb;
+       int i;
+
+       for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
+               urb = es2->arpc_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;
@@ -467,6 +553,9 @@ static int message_send(struct gb_host_device *hd, u16 cport_id,
                          message->buffer, buffer_size,
                          cport_out_callback, message);
        urb->transfer_flags |= URB_ZERO_PACKET;
+
+       trace_gb_message_submit(message);
+
        retval = usb_submit_urb(urb, gfp_mask);
        if (retval) {
                dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval);
@@ -526,7 +615,9 @@ 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;
+       struct arpc_cport_reset_req req;
        int retval;
+       int result;
 
        switch (cport_id) {
        case GB_SVC_CPORT_ID:
@@ -535,18 +626,15 @@ static int cport_reset(struct gb_host_device *hd, u16 cport_id)
                return 0;
        }
 
-       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) {
+       req.cport_id = cpu_to_le16(cport_id);
+       retval = arpc_sync(es2, ARPC_TYPE_CPORT_RESET, &req, sizeof(req),
+                          &result, ES2_ARPC_CPORT_TIMEOUT);
+       if (retval == -EREMOTEIO) {
                dev_err(&udev->dev, "failed to reset cport %u: %d\n", cport_id,
-                       retval);
-               return retval;
+                       result);
        }
 
-       return 0;
+       return retval;
 }
 
 static int es2_cport_allocate(struct gb_host_device *hd, int cport_id,
@@ -602,7 +690,52 @@ static void es2_cport_release(struct gb_host_device *hd, u16 cport_id)
        ida_simple_remove(&hd->cport_id_map, cport_id);
 }
 
-static int cport_enable(struct gb_host_device *hd, u16 cport_id)
+static int cport_enable(struct gb_host_device *hd, u16 cport_id,
+                       unsigned long flags)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct usb_device *udev = es2->usb_dev;
+       struct gb_apb_request_cport_flags *req;
+       u32 connection_flags;
+       int ret;
+
+       req = kzalloc(sizeof(*req), GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       connection_flags = 0;
+       if (flags & GB_CONNECTION_FLAG_CONTROL)
+               connection_flags |= GB_APB_CPORT_FLAG_CONTROL;
+       if (flags & GB_CONNECTION_FLAG_HIGH_PRIO)
+               connection_flags |= GB_APB_CPORT_FLAG_HIGH_PRIO;
+
+       req->flags = cpu_to_le32(connection_flags);
+
+       dev_dbg(&hd->dev, "%s - cport = %u, flags = %02x\n", __func__,
+                       cport_id, connection_flags);
+
+       ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                               GB_APB_REQUEST_CPORT_FLAGS,
+                               USB_DIR_OUT | USB_TYPE_VENDOR |
+                               USB_RECIP_INTERFACE, cport_id, 0,
+                               req, sizeof(*req), ES2_USB_CTRL_TIMEOUT);
+       if (ret != sizeof(*req)) {
+               dev_err(&udev->dev, "failed to set cport flags for port %d\n",
+                               cport_id);
+               if (ret >= 0)
+                       ret = -EIO;
+
+               goto out;
+       }
+
+       ret = 0;
+out:
+       kfree(req);
+
+       return ret;
+}
+
+static int cport_disable(struct gb_host_device *hd, u16 cport_id)
 {
        int retval;
 
@@ -613,6 +746,72 @@ static int cport_enable(struct gb_host_device *hd, u16 cport_id)
        return 0;
 }
 
+static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct device *dev = &es2->usb_dev->dev;
+       struct arpc_cport_connected_req req;
+       int ret;
+
+       req.cport_id = cpu_to_le16(cport_id);
+       ret = arpc_sync(es2, ARPC_TYPE_CPORT_CONNECTED, &req, sizeof(req),
+                       NULL, ES2_ARPC_CPORT_TIMEOUT);
+       if (ret) {
+               dev_err(dev, "failed to set connected state for cport %u: %d\n",
+                               cport_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id,
+                               size_t peer_space, unsigned int timeout)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct device *dev = &es2->usb_dev->dev;
+       struct arpc_cport_quiesce_req req;
+       int result;
+       int ret;
+
+       if (peer_space > U16_MAX)
+               return -EINVAL;
+
+       if (timeout > U16_MAX)
+               return -EINVAL;
+
+       req.cport_id = cpu_to_le16(cport_id);
+       req.peer_space = cpu_to_le16(peer_space);
+       req.timeout = cpu_to_le16(timeout);
+       ret = arpc_sync(es2, ARPC_TYPE_CPORT_QUIESCE, &req, sizeof(req),
+                       &result, ES2_ARPC_CPORT_TIMEOUT + timeout);
+       if (ret) {
+               dev_err(dev, "failed to quiesce cport %u: %d (%d)\n",
+                               cport_id, ret, result);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int es2_cport_clear(struct gb_host_device *hd, u16 cport_id)
+{
+       struct es2_ap_dev *es2 = hd_to_es2(hd);
+       struct device *dev = &es2->usb_dev->dev;
+       struct arpc_cport_clear_req req;
+       int ret;
+
+       req.cport_id = cpu_to_le16(cport_id);
+       ret = arpc_sync(es2, ARPC_TYPE_CPORT_CLEAR, &req, sizeof(req),
+                       NULL, ES2_ARPC_CPORT_TIMEOUT);
+       if (ret) {
+               dev_err(dev, "failed to clear cport %u: %d\n", cport_id, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
 {
        int retval;
@@ -623,7 +822,7 @@ static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id)
                                 GB_APB_REQUEST_LATENCY_TAG_EN,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
-                                0, ES2_TIMEOUT);
+                                0, ES2_USB_CTRL_TIMEOUT);
 
        if (retval < 0)
                dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n",
@@ -641,7 +840,7 @@ static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id)
                                 GB_APB_REQUEST_LATENCY_TAG_DIS,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
-                                0, ES2_TIMEOUT);
+                                0, ES2_USB_CTRL_TIMEOUT);
 
        if (retval < 0)
                dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n",
@@ -659,7 +858,7 @@ static int cport_features_enable(struct gb_host_device *hd, u16 cport_id)
                                 GB_APB_REQUEST_CPORT_FEAT_EN,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
-                                0, ES2_TIMEOUT);
+                                0, ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev, "Cannot enable CPort features for cport %u: %d\n",
                        cport_id, retval);
@@ -676,7 +875,7 @@ static int cport_features_disable(struct gb_host_device *hd, u16 cport_id)
                                 GB_APB_REQUEST_CPORT_FEAT_DIS,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, cport_id, 0, NULL,
-                                0, ES2_TIMEOUT);
+                                0, ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev,
                        "Cannot disable CPort features for cport %u: %d\n",
@@ -701,10 +900,10 @@ static int timesync_enable(struct gb_host_device *hd, u8 count,
        request->strobe_delay = cpu_to_le32(strobe_delay);
        request->refclk = cpu_to_le32(refclk);
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_TIMESYNC_ENABLE,
+                                GB_APB_REQUEST_TIMESYNC_ENABLE,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, request,
-                                sizeof(*request), ES2_TIMEOUT);
+                                sizeof(*request), ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev, "Cannot enable timesync %d\n", retval);
 
@@ -719,10 +918,10 @@ static int timesync_disable(struct gb_host_device *hd)
        struct usb_device *udev = es2->usb_dev;
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_TIMESYNC_DISABLE,
+                                GB_APB_REQUEST_TIMESYNC_DISABLE,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, NULL,
-                                0, ES2_TIMEOUT);
+                                0, ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev, "Cannot disable timesync %d\n", retval);
 
@@ -744,10 +943,10 @@ static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time)
                request->frame_time[i] = cpu_to_le64(frame_time[i]);
 
        retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-                                REQUEST_TIMESYNC_AUTHORITATIVE,
+                                GB_APB_REQUEST_TIMESYNC_AUTHORITATIVE,
                                 USB_DIR_OUT | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, request,
-                                sizeof(*request), ES2_TIMEOUT);
+                                sizeof(*request), ES2_USB_CTRL_TIMEOUT);
        if (retval < 0)
                dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval);
 
@@ -767,10 +966,11 @@ static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time)
                return -ENOMEM;
 
        retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-                                REQUEST_TIMESYNC_GET_LAST_EVENT,
+                                GB_APB_REQUEST_TIMESYNC_GET_LAST_EVENT,
                                 USB_DIR_IN | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, response_frame_time,
-                                sizeof(*response_frame_time), ES2_TIMEOUT);
+                                sizeof(*response_frame_time),
+                                ES2_USB_CTRL_TIMEOUT);
 
        if (retval != sizeof(*response_frame_time)) {
                dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n",
@@ -795,6 +995,10 @@ static struct gb_hd_driver es2_driver = {
        .cport_allocate                 = es2_cport_allocate,
        .cport_release                  = es2_cport_release,
        .cport_enable                   = cport_enable,
+       .cport_disable                  = cport_disable,
+       .cport_connected                = es2_cport_connected,
+       .cport_quiesce                  = es2_cport_quiesce,
+       .cport_clear                    = es2_cport_clear,
        .latency_tag_enable             = latency_tag_enable,
        .latency_tag_disable            = latency_tag_disable,
        .output                         = output,
@@ -853,6 +1057,16 @@ static void es2_destroy(struct es2_ap_dev *es2)
                es2->cport_out_urb_busy[i] = false;     /* just to be anal */
        }
 
+       for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
+               struct urb *urb = es2->arpc_urb[i];
+
+               if (!urb)
+                       break;
+               usb_free_urb(urb);
+               kfree(es2->arpc_buffer[i]);
+               es2->arpc_buffer[i] = NULL;
+       }
+
        for (bulk_in = 0; bulk_in < NUM_BULKS; bulk_in++) {
                struct es2_cport_in *cport_in = &es2->cport_in[bulk_in];
 
@@ -945,12 +1159,207 @@ static void cport_out_callback(struct urb *urb)
        free_urb(es2, urb);
 }
 
+static struct arpc *arpc_alloc(void *payload, u16 size, u8 type)
+{
+       struct arpc *rpc;
+
+       if (size + sizeof(*rpc->req) > ARPC_OUT_SIZE_MAX)
+               return NULL;
+
+       rpc = kzalloc(sizeof(*rpc), GFP_KERNEL);
+       if (!rpc)
+               return NULL;
+
+       INIT_LIST_HEAD(&rpc->list);
+       rpc->req = kzalloc(sizeof(*rpc->req) + size, GFP_KERNEL);
+       if (!rpc->req)
+               goto err_free_rpc;
+
+       rpc->resp = kzalloc(sizeof(*rpc->resp), GFP_KERNEL);
+       if (!rpc->resp)
+               goto err_free_req;
+
+       rpc->req->type = type;
+       rpc->req->size = cpu_to_le16(sizeof(rpc->req) + size);
+       memcpy(rpc->req->data, payload, size);
+
+       init_completion(&rpc->response_received);
+
+       return rpc;
+
+err_free_req:
+       kfree(rpc->req);
+err_free_rpc:
+       kfree(rpc);
+
+       return NULL;
+}
+
+static void arpc_free(struct arpc *rpc)
+{
+       kfree(rpc->req);
+       kfree(rpc->resp);
+       kfree(rpc);
+}
+
+static struct arpc *arpc_find(struct es2_ap_dev *es2, __le16 id)
+{
+       struct arpc *rpc;
+
+       list_for_each_entry(rpc, &es2->arpcs, list) {
+               if (rpc->req->id == id)
+                       return rpc;
+       }
+
+       return NULL;
+}
+
+static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc)
+{
+       rpc->active = true;
+       rpc->req->id = cpu_to_le16(es2->arpc_id_cycle++);
+       list_add_tail(&rpc->list, &es2->arpcs);
+}
+
+static void arpc_del(struct es2_ap_dev *es2, struct arpc *rpc)
+{
+       if (rpc->active) {
+               rpc->active = false;
+               list_del(&rpc->list);
+       }
+}
+
+static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout)
+{
+       struct usb_device *udev = es2->usb_dev;
+       int retval;
+
+       retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+                                GB_APB_REQUEST_ARPC_RUN,
+                                USB_DIR_OUT | USB_TYPE_VENDOR |
+                                USB_RECIP_INTERFACE,
+                                0, 0,
+                                rpc->req, le16_to_cpu(rpc->req->size),
+                                ES2_USB_CTRL_TIMEOUT);
+       if (retval != le16_to_cpu(rpc->req->size)) {
+               dev_err(&udev->dev,
+                       "failed to send ARPC request %d: %d\n",
+                       rpc->req->type, retval);
+               if (retval > 0)
+                       retval = -EIO;
+               return retval;
+       }
+
+       return 0;
+}
+
+static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload,
+                    size_t size, int *result, unsigned int timeout)
+{
+       struct arpc *rpc;
+       unsigned long flags;
+       int retval;
+
+       if (result)
+               *result = 0;
+
+       rpc = arpc_alloc(payload, size, type);
+       if (!rpc)
+               return -ENOMEM;
+
+       spin_lock_irqsave(&es2->arpc_lock, flags);
+       arpc_add(es2, rpc);
+       spin_unlock_irqrestore(&es2->arpc_lock, flags);
+
+       retval = arpc_send(es2, rpc, timeout);
+       if (retval)
+               goto out_arpc_del;
+
+       retval = wait_for_completion_interruptible_timeout(
+                                               &rpc->response_received,
+                                               msecs_to_jiffies(timeout));
+       if (retval <= 0) {
+               if (!retval)
+                       retval = -ETIMEDOUT;
+               goto out_arpc_del;
+       }
+
+       if (rpc->resp->result) {
+               retval = -EREMOTEIO;
+               if (result)
+                       *result = rpc->resp->result;
+       } else {
+               retval = 0;
+       }
+
+out_arpc_del:
+       spin_lock_irqsave(&es2->arpc_lock, flags);
+       arpc_del(es2, rpc);
+       spin_unlock_irqrestore(&es2->arpc_lock, flags);
+       arpc_free(rpc);
+
+       if (retval < 0 && retval != -EREMOTEIO) {
+               dev_err(&es2->usb_dev->dev,
+                       "failed to execute ARPC: %d\n", retval);
+       }
+
+       return retval;
+}
+
+static void arpc_in_callback(struct urb *urb)
+{
+       struct es2_ap_dev *es2 = urb->context;
+       struct device *dev = &urb->dev->dev;
+       int status = check_urb_status(urb);
+       struct arpc *rpc;
+       struct arpc_response_message *resp;
+       unsigned long flags;
+       int retval;
+
+       if (status) {
+               if ((status == -EAGAIN) || (status == -EPROTO))
+                       goto exit;
+
+               /* The urb is being unlinked */
+               if (status == -ENOENT || status == -ESHUTDOWN)
+                       return;
+
+               dev_err(dev, "arpc in-urb error %d (dropped)\n", status);
+               return;
+       }
+
+       if (urb->actual_length < sizeof(*resp)) {
+               dev_err(dev, "short aprc response received\n");
+               goto exit;
+       }
+
+       resp = urb->transfer_buffer;
+       spin_lock_irqsave(&es2->arpc_lock, flags);
+       rpc = arpc_find(es2, resp->id);
+       if (!rpc) {
+               dev_err(dev, "invalid arpc response id received: %u\n",
+                       le16_to_cpu(resp->id));
+               spin_unlock_irqrestore(&es2->arpc_lock, flags);
+               goto exit;
+       }
+
+       arpc_del(es2, rpc);
+       memcpy(rpc->resp, resp, sizeof(*resp));
+       complete(&rpc->response_received);
+       spin_unlock_irqrestore(&es2->arpc_lock, flags);
+
+exit:
+       /* put our urb back in the request pool */
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval)
+               dev_err(dev, "failed to resubmit arpc in-urb: %d\n", retval);
+}
+
 #define APB1_LOG_MSG_SIZE      64
 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(es2->usb_dev,
                                        usb_rcvctrlpipe(es2->usb_dev, 0),
@@ -959,7 +1368,7 @@ static void apb_log_get(struct es2_ap_dev *es2, char *buf)
                                        0x00, 0x00,
                                        buf,
                                        APB1_LOG_MSG_SIZE,
-                                       ES2_TIMEOUT);
+                                       ES2_USB_CTRL_TIMEOUT);
                if (retval > 0)
                        kfifo_in(&es2->apb_log_fifo, buf, retval);
        } while (retval > 0);
@@ -1086,7 +1495,7 @@ static int apb_get_cport_count(struct usb_device *udev)
                                 GB_APB_REQUEST_CPORT_COUNT,
                                 USB_DIR_IN | USB_TYPE_VENDOR |
                                 USB_RECIP_INTERFACE, 0, 0, cport_count,
-                                sizeof(*cport_count), ES2_TIMEOUT);
+                                sizeof(*cport_count), ES2_USB_CTRL_TIMEOUT);
        if (retval != sizeof(*cport_count)) {
                dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n",
                        retval);
@@ -1179,8 +1588,13 @@ static int ap_probe(struct usb_interface *interface,
                endpoint = &iface_desc->endpoint[i].desc;
 
                if (usb_endpoint_is_bulk_in(endpoint)) {
-                       es2->cport_in[bulk_in++].endpoint =
-                               endpoint->bEndpointAddress;
+                       if (bulk_in < NUM_BULKS)
+                               es2->cport_in[bulk_in].endpoint =
+                                       endpoint->bEndpointAddress;
+                       else
+                               es2->arpc_endpoint_in =
+                                       endpoint->bEndpointAddress;
+                       bulk_in++;
                } else if (usb_endpoint_is_bulk_out(endpoint)) {
                        es2->cport_out[bulk_out++].endpoint =
                                endpoint->bEndpointAddress;
@@ -1190,7 +1604,7 @@ static int ap_probe(struct usb_interface *interface,
                                endpoint->bEndpointAddress);
                }
        }
-       if (bulk_in != NUM_BULKS || bulk_out != NUM_BULKS) {
+       if (bulk_in != NUM_BULKS_IN || bulk_out != NUM_BULKS_OUT) {
                dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n");
                retval = -ENODEV;
                goto error;
@@ -1225,6 +1639,32 @@ static int ap_probe(struct usb_interface *interface,
                }
        }
 
+       /* Allocate buffers for ARPC in messages */
+       for (i = 0; i < NUM_ARPC_IN_URB; ++i) {
+               struct urb *urb;
+               u8 *buffer;
+
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       retval = -ENOMEM;
+                       goto error;
+               }
+               buffer = kmalloc(ARPC_IN_SIZE_MAX, GFP_KERNEL);
+               if (!buffer) {
+                       retval = -ENOMEM;
+                       goto error;
+               }
+
+               usb_fill_bulk_urb(urb, udev,
+                                 usb_rcvbulkpipe(udev,
+                                                 es2->arpc_endpoint_in),
+                                 buffer, ARPC_IN_SIZE_MAX,
+                                 arpc_in_callback, es2);
+
+               es2->arpc_urb[i] = urb;
+               es2->arpc_buffer[i] = buffer;
+       }
+
        /* Allocate urbs for our CPort OUT messages */
        for (i = 0; i < NUM_CPORT_OUT_URB; ++i) {
                struct urb *urb;
@@ -1245,9 +1685,15 @@ static int ap_probe(struct usb_interface *interface,
                                                        gb_debugfs_get(), es2,
                                                        &apb_log_enable_fops);
 
+       INIT_LIST_HEAD(&es2->arpcs);
+       spin_lock_init(&es2->arpc_lock);
+
+       if (es2_arpc_in_enable(es2))
+               goto error;
+
        retval = gb_hd_add(hd);
        if (retval)
-               goto error;
+               goto err_disable_arpc_in;
 
        for (i = 0; i < NUM_BULKS; ++i) {
                retval = es2_cport_in_enable(es2, &es2->cport_in[i]);
@@ -1261,6 +1707,8 @@ err_disable_cport_in:
        for (--i; i >= 0; --i)
                es2_cport_in_disable(es2, &es2->cport_in[i]);
        gb_hd_del(hd);
+err_disable_arpc_in:
+       es2_arpc_in_disable(es2);
 error:
        es2_destroy(es2);
 
@@ -1276,6 +1724,7 @@ static void ap_disconnect(struct usb_interface *interface)
 
        for (i = 0; i < NUM_BULKS; ++i)
                es2_cport_in_disable(es2, &es2->cport_in[i]);
+       es2_arpc_in_disable(es2);
 
        es2_destroy(es2);
 }