aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/es2.c
diff options
context:
space:
mode:
authorAlexandre Bailon <abailon@baylibre.com>2016-07-07 07:41:00 -0500
committerAlex Elder <elder@linaro.org>2016-07-11 15:54:17 -0500
commitc14118a8411c4d7ad1dd6dd501beb33ae1268b08 (patch)
tree13e1d5d7c583f3419bf3c9e790ba416d299d6792 /drivers/staging/greybus/es2.c
parentgreybus: es2: Add a new bulk in endpoint for APBridgeA RPC (diff)
downloadlinux-dev-c14118a8411c4d7ad1dd6dd501beb33ae1268b08.tar.xz
linux-dev-c14118a8411c4d7ad1dd6dd501beb33ae1268b08.zip
greybus: es2: Implement APBridgeA RPC (ARPC)
Implement ARPC. In first time, we are going to use it to implement new request but the goal is to update all existing vendor request to use ARPC. In addition, Convert the current USB Vendor request for CPort Reset to ARPC so that we can be sure that the port has been fully reset by the time the request completes. Testing Done: AP can reset APBA Cports by using the ARPC command. In addition, tested with a hacked firmware that cause error during the Cport reset, and Greybus printed the error "failed to reset cport". Signed-off-by: Alexandre Bailon <abailon@baylibre.com> Reviewed-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Alex Elder <elder@linaro.org>
Diffstat (limited to 'drivers/staging/greybus/es2.c')
-rw-r--r--drivers/staging/greybus/es2.c203
1 files changed, 194 insertions, 9 deletions
diff --git a/drivers/staging/greybus/es2.c b/drivers/staging/greybus/es2.c
index 8ebbd704f3b8..b763b27ce9ef 100644
--- a/drivers/staging/greybus/es2.c
+++ b/drivers/staging/greybus/es2.c
@@ -11,6 +11,7 @@
#include <linux/usb.h>
#include <linux/kfifo.h>
#include <linux/debugfs.h>
+#include <linux/list.h>
#include <asm/unaligned.h>
#include "greybus.h"
@@ -26,6 +27,7 @@
#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[] = {
@@ -102,6 +104,9 @@ struct es2_cport_out {
* @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;
@@ -127,6 +132,10 @@ struct es2_ap_dev {
__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;
};
/**
@@ -164,6 +173,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;
@@ -172,6 +189,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)
@@ -590,7 +609,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;
int retval;
+ int result;
switch (cport_id) {
case GB_SVC_CPORT_ID:
@@ -599,18 +620,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_CPORT_RESET, &req, sizeof(req),
+ &result, ES2_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,
@@ -1061,10 +1079,154 @@ 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->req)
+ 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, u8 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 = (u16)(es2->arpc_id_cycle++);
+ list_add_tail(&es2->arpcs, &rpc->list);
+}
+
+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),
+ APBA_REQUEST_ARPC_RUN,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_INTERFACE,
+ 0, 0,
+ rpc->req, rpc->req->size,
+ ES2_TIMEOUT);
+ if (retval != 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;
+
+ 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;
+ }
+
+ *result = rpc->resp->result;
+ if (*result)
+ retval = -EREMOTEIO;
+
+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) {
@@ -1079,6 +1241,26 @@ static void arpc_in_callback(struct urb *urb)
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: %d\n",
+ 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);
@@ -1417,6 +1599,9 @@ 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;