summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/f_sdp.c123
1 files changed, 107 insertions, 16 deletions
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c
index 9b7372815d..c5b35945c1 100644
--- a/drivers/usb/gadget/f_sdp.c
+++ b/drivers/usb/gadget/f_sdp.c
@@ -71,6 +71,8 @@ struct hid_report {
#define SDP_COMMAND_LEN 16
+#define SDP_HID_PACKET_SIZE_EP1 1024
+
#define SDP_EXIT 1
struct sdp_command {
@@ -84,8 +86,10 @@ struct sdp_command {
enum sdp_state {
SDP_STATE_IDLE,
+ SDP_STATE_RX_CMD,
SDP_STATE_RX_DCD_DATA,
SDP_STATE_RX_FILE_DATA,
+ SDP_STATE_RX_FILE_DATA_BUSY,
SDP_STATE_TX_SEC_CONF,
SDP_STATE_TX_SEC_CONF_BUSY,
SDP_STATE_TX_REGISTER,
@@ -116,8 +120,12 @@ struct f_sdp {
/* EP1 IN */
struct usb_ep *in_ep;
struct usb_request *in_req;
+ /* EP1 OUT */
+ struct usb_ep *out_ep;
+ struct usb_request *out_req;
bool configuration_done;
+ bool ep_int_enable;
};
static struct f_sdp *sdp_func;
@@ -131,7 +139,7 @@ static struct usb_interface_descriptor sdp_intf_runtime = {
.bLength = sizeof(sdp_intf_runtime),
.bDescriptorType = USB_DT_INTERFACE,
.bAlternateSetting = 0,
- .bNumEndpoints = 1,
+ .bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
@@ -161,6 +169,16 @@ static struct usb_endpoint_descriptor in_desc = {
.bInterval = 1,
};
+static struct usb_endpoint_descriptor out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = 64,
+ .bInterval = 1,
+};
+
static struct usb_endpoint_descriptor in_hs_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
@@ -171,10 +189,21 @@ static struct usb_endpoint_descriptor in_hs_desc = {
.bInterval = 1,
};
+static struct usb_endpoint_descriptor out_hs_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
+
+ .bEndpointAddress = 1 | USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = SDP_HID_PACKET_SIZE_EP1,
+ .bInterval = 1,
+};
+
static struct usb_descriptor_header *sdp_runtime_descs[] = {
(struct usb_descriptor_header *)&sdp_intf_runtime,
(struct usb_descriptor_header *)&sdp_hid_desc,
(struct usb_descriptor_header *)&in_desc,
+ (struct usb_descriptor_header *)&out_desc,
NULL,
};
@@ -182,6 +211,7 @@ static struct usb_descriptor_header *sdp_runtime_hs_descs[] = {
(struct usb_descriptor_header *)&sdp_intf_runtime,
(struct usb_descriptor_header *)&sdp_hid_desc,
(struct usb_descriptor_header *)&in_hs_desc,
+ (struct usb_descriptor_header *)&out_hs_desc,
NULL,
};
@@ -347,7 +377,7 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
int status = req->status;
u8 *data = req->buf;
u8 report = data[0];
- int datalen = req->length - 1;
+ int datalen = req->actual - 1;
if (status != 0) {
pr_err("Status: %d\n", status);
@@ -370,13 +400,15 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
sdp->dnl_bytes_remaining -= datalen;
}
- if (sdp->state == SDP_STATE_RX_FILE_DATA) {
+ if (sdp->state == SDP_STATE_RX_FILE_DATA_BUSY) {
memcpy(sdp_ptr(sdp->dnl_address), req->buf + 1, datalen);
sdp->dnl_address += datalen;
}
- if (sdp->dnl_bytes_remaining)
+ if (sdp->dnl_bytes_remaining) {
+ sdp->state = SDP_STATE_RX_FILE_DATA;
return;
+ }
#ifndef CONFIG_SPL_BUILD
env_set_hex("filesize", sdp->dnl_bytes);
@@ -384,7 +416,7 @@ static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req)
printf("done\n");
switch (sdp->state) {
- case SDP_STATE_RX_FILE_DATA:
+ case SDP_STATE_RX_FILE_DATA_BUSY:
sdp->state = SDP_STATE_TX_SEC_CONF;
break;
case SDP_STATE_RX_DCD_DATA:
@@ -465,10 +497,12 @@ static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
case 1:
value = SDP_COMMAND_LEN + 1;
req->complete = sdp_rx_command_complete;
+ sdp_func->ep_int_enable = false;
break;
case 2:
value = len;
req->complete = sdp_rx_data_complete;
+ sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY;
break;
}
}
@@ -499,11 +533,17 @@ static int sdp_bind(struct usb_configuration *c, struct usb_function *f)
return id;
sdp_intf_runtime.bInterfaceNumber = id;
- struct usb_ep *ep;
+ struct usb_ep *ep_in, *ep_out;
/* allocate instance-specific endpoints */
- ep = usb_ep_autoconfig(gadget, &in_desc);
- if (!ep) {
+ ep_in = usb_ep_autoconfig(gadget, &in_desc);
+ if (!ep_in) {
+ rv = -ENODEV;
+ goto error;
+ }
+
+ ep_out = usb_ep_autoconfig(gadget, &out_desc);
+ if (!ep_out) {
rv = -ENODEV;
goto error;
}
@@ -511,9 +551,11 @@ static int sdp_bind(struct usb_configuration *c, struct usb_function *f)
if (gadget_is_dualspeed(gadget)) {
/* Assume endpoint addresses are the same for both speeds */
in_hs_desc.bEndpointAddress = in_desc.bEndpointAddress;
+ out_hs_desc.bEndpointAddress = out_desc.bEndpointAddress;
}
- sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
+ sdp->in_ep = ep_in; /* Store IN EP for enabling @ setup */
+ sdp->out_ep = ep_out;
cdev->req->context = sdp;
@@ -546,18 +588,29 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
}
-static struct usb_request *sdp_start_ep(struct usb_ep *ep)
+static struct usb_request *sdp_start_ep(struct usb_ep *ep, bool in)
{
struct usb_request *req;
- req = alloc_ep_req(ep, 65);
+ if (in)
+ req = alloc_ep_req(ep, 65);
+ else
+ req = alloc_ep_req(ep, 2048);
+/*
+ * OUT endpoint request length should be an integral multiple of
+ * maxpacket size 1024, else we break on certain controllers like
+ * DWC3 that expect bulk OUT requests to be divisible by maxpacket size.
+ */
debug("%s: ep:%p req:%p\n", __func__, ep, req);
if (!req)
return NULL;
memset(req->buf, 0, req->length);
- req->complete = sdp_tx_complete;
+ if (in)
+ req->complete = sdp_tx_complete;
+ else
+ req->complete = sdp_rx_command_complete;
return req;
}
@@ -570,19 +623,27 @@ static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
- if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH)
+ if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH) {
result = usb_ep_enable(sdp->in_ep, &in_hs_desc);
- else
+ result |= usb_ep_enable(sdp->out_ep, &out_hs_desc);
+ } else {
result = usb_ep_enable(sdp->in_ep, &in_desc);
+ result |= usb_ep_enable(sdp->out_ep, &out_desc);
+ }
if (result)
return result;
- sdp->in_req = sdp_start_ep(sdp->in_ep);
+
+ sdp->in_req = sdp_start_ep(sdp->in_ep, true);
sdp->in_req->context = sdp;
+ sdp->out_req = sdp_start_ep(sdp->out_ep, false);
+ sdp->out_req->context = sdp;
sdp->in_ep->driver_data = cdev; /* claim */
+ sdp->out_ep->driver_data = cdev; /* claim */
sdp->altsetting = alt;
sdp->state = SDP_STATE_IDLE;
+ sdp->ep_int_enable = true;
return 0;
}
@@ -599,11 +660,18 @@ static void sdp_disable(struct usb_function *f)
struct f_sdp *sdp = func_to_sdp(f);
usb_ep_disable(sdp->in_ep);
+ usb_ep_disable(sdp->out_ep);
if (sdp->in_req) {
- free(sdp->in_req);
+ free(sdp->in_req->buf);
+ usb_ep_free_request(sdp->in_ep, sdp->in_req);
sdp->in_req = NULL;
}
+ if (sdp->out_req) {
+ free(sdp->out_req->buf);
+ usb_ep_free_request(sdp->out_ep, sdp->out_req);
+ sdp->out_req = NULL;
+ }
}
static int sdp_bind_config(struct usb_configuration *c)
@@ -818,6 +886,27 @@ static int sdp_handle_in_ep(struct spl_image_info *spl_image)
return 0;
}
+static void sdp_handle_out_ep(void)
+{
+ int rc;
+
+ if (sdp_func->state == SDP_STATE_IDLE) {
+ sdp_func->out_req->complete = sdp_rx_command_complete;
+ rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0);
+ if (rc)
+ printf("error in submission: %s\n",
+ sdp_func->out_ep->name);
+ sdp_func->state = SDP_STATE_RX_CMD;
+ } else if (sdp_func->state == SDP_STATE_RX_FILE_DATA) {
+ sdp_func->out_req->complete = sdp_rx_data_complete;
+ rc = usb_ep_queue(sdp_func->out_ep, sdp_func->out_req, 0);
+ if (rc)
+ printf("error in submission: %s\n",
+ sdp_func->out_ep->name);
+ sdp_func->state = SDP_STATE_RX_FILE_DATA_BUSY;
+ }
+}
+
#ifndef CONFIG_SPL_BUILD
int sdp_handle(int controller_index)
#else
@@ -843,6 +932,8 @@ int spl_sdp_handle(int controller_index, struct spl_image_info *spl_image)
#else
flag = sdp_handle_in_ep(NULL);
#endif
+ if (sdp_func->ep_int_enable)
+ sdp_handle_out_ep();
}
}