aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/function/f_tcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget/function/f_tcm.c')
-rw-r--r--drivers/usb/gadget/function/f_tcm.c311
1 files changed, 225 insertions, 86 deletions
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 41a10bcc2efc..60e40c118d13 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -43,14 +43,17 @@ static inline struct f_uas *to_f_uas(struct usb_function *f)
/* Start bot.c code */
+static struct usbg_cdb *acquire_cmd_request(struct f_uas *fu);
+static void release_cmd_request(struct f_uas *fu, struct usb_request *req);
static int bot_enqueue_cmd_cbw(struct f_uas *fu)
{
int ret;
+ struct usbg_cdb *cmd = acquire_cmd_request(fu);
if (fu->flags & USBG_BOT_CMD_PEND)
return 0;
- ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC);
+ ret = usb_ep_queue(fu->ep_out, cmd->req, GFP_ATOMIC);
if (!ret)
fu->flags |= USBG_BOT_CMD_PEND;
return ret;
@@ -61,6 +64,7 @@ static void bot_status_complete(struct usb_ep *ep, struct usb_request *req)
struct usbg_cmd *cmd = req->context;
struct f_uas *fu = cmd->fu;
+ release_cmd_request(fu, req);
transport_generic_free_cmd(&cmd->se_cmd, 0);
if (req->status < 0) {
pr_err("ERR %s(%d)\n", __func__, __LINE__);
@@ -136,7 +140,7 @@ static void bot_send_bad_status(struct usbg_cmd *cmd)
}
req->complete = bot_err_compl;
req->context = cmd;
- req->buf = fu->cmd.buf;
+ req->buf = fu->cmd[0]->buf;
usb_ep_queue(ep, req, GFP_KERNEL);
} else {
bot_enqueue_sense_code(fu, cmd);
@@ -245,7 +249,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd)
{
struct f_uas *fu = cmd->fu;
struct se_cmd *se_cmd = &cmd->se_cmd;
- struct usb_gadget *gadget = fuas_to_gadget(fu);
int ret;
init_completion(&cmd->write_complete);
@@ -256,22 +259,6 @@ static int bot_send_write_request(struct usbg_cmd *cmd)
return -EINVAL;
}
- if (!gadget->sg_supported) {
- cmd->data_buf = kmalloc(se_cmd->data_length, GFP_KERNEL);
- if (!cmd->data_buf)
- return -ENOMEM;
-
- fu->bot_req_out->buf = cmd->data_buf;
- } else {
- fu->bot_req_out->buf = NULL;
- fu->bot_req_out->num_sgs = se_cmd->t_data_nents;
- fu->bot_req_out->sg = se_cmd->t_data_sg;
- }
-
- fu->bot_req_out->complete = usbg_data_write_cmpl;
- fu->bot_req_out->length = se_cmd->data_length;
- fu->bot_req_out->context = cmd;
-
ret = usbg_prepare_w_request(cmd, fu->bot_req_out);
if (ret)
goto cleanup;
@@ -297,11 +284,84 @@ static void bot_cmd_complete(struct usb_ep *ep, struct usb_request *req)
if (req->status < 0)
return;
+ release_cmd_request(fu, req);
ret = bot_submit_command(fu, req->buf, req->actual);
if (ret)
pr_err("%s(%d): %d\n", __func__, __LINE__, ret);
}
+static struct usbg_cdb *acquire_cmd_request(struct f_uas *fu)
+{
+ int i;
+
+ for (i = 0; i < fu->ncmd; i++) {
+ if (!fu->cmd[i]->claimed) {
+ fu->cmd[i]->claimed = true;
+ return fu->cmd[i];
+ }
+ }
+ return NULL;
+}
+
+static void release_cmd_request(struct f_uas *fu, struct usb_request *req)
+{
+ int i;
+
+ for (i = 0; i < fu->ncmd; i++) {
+ if (fu->cmd[i]->req == req)
+ fu->cmd[i]->claimed = false;
+ }
+}
+
+static void free_cmd_resource(struct f_uas *fu, struct usb_ep *ep)
+{
+ int i;
+
+ for (i = 0; i < fu->ncmd; i++) {
+ if (fu->cmd[i]->req)
+ usb_ep_free_request(ep, fu->cmd[i]->req);
+
+ kfree(fu->cmd[i]->buf);
+ fu->cmd[i]->buf = NULL;
+
+ kfree(fu->cmd[i]);
+ fu->cmd[i] = NULL;
+ }
+}
+
+static int alloc_cmd_resource(struct f_uas *fu, int num, struct usb_ep *ep,
+ void (*complete)(struct usb_ep *ep,
+ struct usb_request *req))
+{
+ int i;
+
+ fu->ncmd = num;
+ for (i = 0; i < fu->ncmd; i++) {
+ fu->cmd[i] = kcalloc(fu->ncmd, sizeof(struct usbg_cdb),
+ GFP_KERNEL);
+ if (!fu->cmd)
+ goto err_cmd;
+
+ fu->cmd[i]->req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!fu->cmd[i]->req)
+ goto err_cmd;
+
+ fu->cmd[i]->buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
+ if (!fu->cmd[i]->buf)
+ goto err_cmd;
+
+ fu->cmd[i]->req->complete = complete;
+ fu->cmd[i]->req->buf = fu->cmd[i]->buf;
+ fu->cmd[i]->req->length = fu->ep_out->maxpacket;
+ fu->cmd[i]->req->context = fu;
+ }
+
+ return 0;
+err_cmd:
+ free_cmd_resource(fu, ep);
+ return -ENOMEM;
+}
+
static int bot_prepare_reqs(struct f_uas *fu)
{
int ret;
@@ -314,10 +374,6 @@ static int bot_prepare_reqs(struct f_uas *fu)
if (!fu->bot_req_out)
goto err_out;
- fu->cmd.req = usb_ep_alloc_request(fu->ep_out, GFP_KERNEL);
- if (!fu->cmd.req)
- goto err_cmd;
-
fu->bot_status.req = usb_ep_alloc_request(fu->ep_in, GFP_KERNEL);
if (!fu->bot_status.req)
goto err_sts;
@@ -327,28 +383,20 @@ static int bot_prepare_reqs(struct f_uas *fu)
fu->bot_status.req->complete = bot_status_complete;
fu->bot_status.csw.Signature = cpu_to_le32(US_BULK_CS_SIGN);
- fu->cmd.buf = kmalloc(fu->ep_out->maxpacket, GFP_KERNEL);
- if (!fu->cmd.buf)
- goto err_buf;
-
- fu->cmd.req->complete = bot_cmd_complete;
- fu->cmd.req->buf = fu->cmd.buf;
- fu->cmd.req->length = fu->ep_out->maxpacket;
- fu->cmd.req->context = fu;
+ ret = alloc_cmd_resource(fu, BOT_MAX_COMMANDS, fu->ep_out,
+ bot_cmd_complete);
+ if (ret)
+ goto err_cmd;
ret = bot_enqueue_cmd_cbw(fu);
if (ret)
goto err_queue;
return 0;
err_queue:
- kfree(fu->cmd.buf);
- fu->cmd.buf = NULL;
-err_buf:
+ free_cmd_resource(fu, fu->ep_out);
+err_cmd:
usb_ep_free_request(fu->ep_in, fu->bot_status.req);
err_sts:
- usb_ep_free_request(fu->ep_out, fu->cmd.req);
- fu->cmd.req = NULL;
-err_cmd:
usb_ep_free_request(fu->ep_out, fu->bot_req_out);
fu->bot_req_out = NULL;
err_out:
@@ -372,16 +420,13 @@ static void bot_cleanup_old_alt(struct f_uas *fu)
usb_ep_free_request(fu->ep_in, fu->bot_req_in);
usb_ep_free_request(fu->ep_out, fu->bot_req_out);
- usb_ep_free_request(fu->ep_out, fu->cmd.req);
usb_ep_free_request(fu->ep_in, fu->bot_status.req);
- kfree(fu->cmd.buf);
+ free_cmd_resource(fu, fu->ep_out);
fu->bot_req_in = NULL;
fu->bot_req_out = NULL;
- fu->cmd.req = NULL;
fu->bot_status.req = NULL;
- fu->cmd.buf = NULL;
}
static void bot_set_alt(struct f_uas *fu)
@@ -480,14 +525,6 @@ static void uasp_cleanup_one_stream(struct f_uas *fu, struct uas_stream *stream)
stream->req_status = NULL;
}
-static void uasp_free_cmdreq(struct f_uas *fu)
-{
- usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
- kfree(fu->cmd.buf);
- fu->cmd.req = NULL;
- fu->cmd.buf = NULL;
-}
-
static void uasp_cleanup_old_alt(struct f_uas *fu)
{
int i;
@@ -502,7 +539,7 @@ static void uasp_cleanup_old_alt(struct f_uas *fu)
for (i = 0; i < UASP_SS_EP_COMP_NUM_STREAMS; i++)
uasp_cleanup_one_stream(fu, &fu->stream[i]);
- uasp_free_cmdreq(fu);
+ free_cmd_resource(fu, fu->ep_cmd);
}
static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req);
@@ -565,6 +602,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
struct usbg_cmd *cmd = req->context;
struct uas_stream *stream = cmd->stream;
struct f_uas *fu = cmd->fu;
+ struct usbg_cdb *cmd_cdb;
int ret;
if (req->status < 0)
@@ -599,7 +637,8 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
case UASP_QUEUE_COMMAND:
transport_generic_free_cmd(&cmd->se_cmd, 0);
- usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ cmd_cdb = acquire_cmd_request(fu);
+ usb_ep_queue(fu->ep_cmd, cmd_cdb->req, GFP_ATOMIC);
break;
default:
@@ -719,11 +758,13 @@ static int usbg_submit_command(struct f_uas *, void *, unsigned int);
static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_uas *fu = req->context;
+ struct usbg_cdb *cmd;
int ret;
if (req->status < 0)
return;
+ release_cmd_request(fu, req);
ret = usbg_submit_command(fu, req->buf, req->actual);
/*
* Once we tune for performance enqueue the command req here again so
@@ -733,7 +774,8 @@ static void uasp_cmd_complete(struct usb_ep *ep, struct usb_request *req)
*/
if (!ret)
return;
- usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
+ cmd = acquire_cmd_request(fu);
+ usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC);
}
static int uasp_alloc_stream_res(struct f_uas *fu, struct uas_stream *stream)
@@ -762,28 +804,6 @@ out:
return -ENOMEM;
}
-static int uasp_alloc_cmd(struct f_uas *fu)
-{
- fu->cmd.req = usb_ep_alloc_request(fu->ep_cmd, GFP_KERNEL);
- if (!fu->cmd.req)
- goto err;
-
- fu->cmd.buf = kmalloc(fu->ep_cmd->maxpacket, GFP_KERNEL);
- if (!fu->cmd.buf)
- goto err_buf;
-
- fu->cmd.req->complete = uasp_cmd_complete;
- fu->cmd.req->buf = fu->cmd.buf;
- fu->cmd.req->length = fu->ep_cmd->maxpacket;
- fu->cmd.req->context = fu;
- return 0;
-
-err_buf:
- usb_ep_free_request(fu->ep_cmd, fu->cmd.req);
-err:
- return -ENOMEM;
-}
-
static void uasp_setup_stream_res(struct f_uas *fu, int max_streams)
{
int i;
@@ -801,12 +821,15 @@ static int uasp_prepare_reqs(struct f_uas *fu)
{
int ret;
int i;
- int max_streams;
+ int max_streams, max_commands;
- if (fu->flags & USBG_USE_STREAMS)
+ if (fu->flags & USBG_USE_STREAMS) {
+ max_commands = UASP_MAX_COMMANDS;
max_streams = UASP_SS_EP_COMP_NUM_STREAMS;
- else
+ } else {
+ max_commands = 1;
max_streams = 1;
+ }
for (i = 0; i < max_streams; i++) {
ret = uasp_alloc_stream_res(fu, &fu->stream[i]);
@@ -814,19 +837,25 @@ static int uasp_prepare_reqs(struct f_uas *fu)
goto err_cleanup;
}
- ret = uasp_alloc_cmd(fu);
+ ret = alloc_cmd_resource(fu, max_commands, fu->ep_cmd,
+ uasp_cmd_complete);
if (ret)
goto err_free_stream;
uasp_setup_stream_res(fu, max_streams);
- ret = usb_ep_queue(fu->ep_cmd, fu->cmd.req, GFP_ATOMIC);
- if (ret)
- goto err_free_stream;
+ /* queue number of commands */
+ for (i = 0; i < fu->ncmd; i++) {
+ struct usbg_cdb *cmd = acquire_cmd_request(fu);
+
+ ret = usb_ep_queue(fu->ep_cmd, cmd->req, GFP_ATOMIC);
+ if (ret)
+ goto err_free_stream;
+ }
return 0;
err_free_stream:
- uasp_free_cmdreq(fu);
+ free_cmd_resource(fu, fu->ep_cmd);
err_cleanup:
if (i) {
@@ -839,16 +868,28 @@ err_cleanup:
return ret;
}
+#define SS_BOT_INTERFACE_DESC_NO 5
static void uasp_set_alt(struct f_uas *fu)
{
struct usb_function *f = &fu->function;
struct usb_gadget *gadget = f->config->cdev->gadget;
+ struct usb_descriptor_header **ss_uasp_backup = f->ss_descriptors;
int ret;
fu->flags = USBG_IS_UAS;
- if (gadget->speed == USB_SPEED_SUPER)
+ if (gadget->speed == USB_SPEED_SUPER) {
fu->flags |= USBG_USE_STREAMS;
+ /* If device connect in SS then comp_descriptor with stream
+ * should be attached to descriptor. Since BOT and UAS using
+ * same endpoint, config_ep_by_speed will returns first match
+ * with comp_descriptor without stream. This is just workaround
+ * proper fix need to be introduced. Here advancing descritor
+ * header ss_descriptors with number of descriptor present in
+ * BOT mode.
+ */
+ f->ss_descriptors += SS_BOT_INTERFACE_DESC_NO;
+ }
config_ep_by_speed(gadget, f, fu->ep_in);
ret = usb_ep_enable(fu->ep_in);
@@ -874,6 +915,10 @@ static void uasp_set_alt(struct f_uas *fu)
goto err_wq;
fu->flags |= USBG_ENABLED;
+ /* restore ss_descriptors */
+ if (gadget->speed == USB_SPEED_SUPER)
+ f->ss_descriptors = ss_uasp_backup;
+
pr_info("Using the UAS protocol\n");
return;
err_wq:
@@ -885,6 +930,9 @@ err_cmd:
err_b_out:
usb_ep_disable(fu->ep_in);
err_b_in:
+ /* restore ss_descriptors */
+ if (gadget->speed == USB_SPEED_SUPER)
+ f->ss_descriptors = ss_uasp_backup;
fu->flags = 0;
}
@@ -950,6 +998,56 @@ static int get_cmd_dir(const unsigned char *cdb)
return ret;
}
+static void recover_w_length_with_maxpacket(struct usbg_cmd *cmd,
+ struct usb_request *req)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int rem;
+
+ rem = se_cmd->data_length % fu->ep_out->maxpacket;
+ if (rem) {
+ /* recover paded data length */
+ cmd->data_len -= fu->ep_out->maxpacket - rem;
+
+ if (gadget->sg_supported) {
+ struct scatterlist *s = sg_last(se_cmd->t_data_sg,
+ se_cmd->t_data_nents);
+
+ s->length -= fu->ep_out->maxpacket - rem;
+ }
+ }
+}
+
+static void adjust_w_length_with_maxpacket(struct usbg_cmd *cmd,
+ struct usb_request *req)
+{
+ struct se_cmd *se_cmd = &cmd->se_cmd;
+ struct f_uas *fu = cmd->fu;
+ struct usb_gadget *gadget = fuas_to_gadget(fu);
+ int rem;
+
+ cmd->data_len = se_cmd->data_length;
+ rem = cmd->data_len % fu->ep_out->maxpacket;
+ if (rem) {
+ /* pad data length so that transfer size can be in multiple of
+ * max packet size
+ */
+ cmd->data_len += fu->ep_out->maxpacket - rem;
+
+ if (gadget->sg_supported) {
+ /* if sg is supported and data length in page also need
+ * to be adjusted as multiple of max packet size.
+ */
+ struct scatterlist *s = sg_last(se_cmd->t_data_sg,
+ se_cmd->t_data_nents);
+
+ s->length += fu->ep_out->maxpacket - rem;
+ }
+ }
+}
+
static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
{
struct usbg_cmd *cmd = req->context;
@@ -960,6 +1058,8 @@ static void usbg_data_write_cmpl(struct usb_ep *ep, struct usb_request *req)
goto cleanup;
}
+ recover_w_length_with_maxpacket(cmd, req);
+
if (req->num_sgs == 0) {
sg_copy_from_buffer(se_cmd->t_data_sg,
se_cmd->t_data_nents,
@@ -980,8 +1080,10 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
struct f_uas *fu = cmd->fu;
struct usb_gadget *gadget = fuas_to_gadget(fu);
+ adjust_w_length_with_maxpacket(cmd, req);
+
if (!gadget->sg_supported) {
- cmd->data_buf = kmalloc(se_cmd->data_length, GFP_ATOMIC);
+ cmd->data_buf = kmalloc(cmd->data_len, GFP_ATOMIC);
if (!cmd->data_buf)
return -ENOMEM;
@@ -993,7 +1095,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
}
req->complete = usbg_data_write_cmpl;
- req->length = se_cmd->data_length;
+ req->length = cmd->data_len;
req->context = cmd;
return 0;
}
@@ -1186,7 +1288,8 @@ static void bot_cmd_work(struct work_struct *work)
if (target_submit_cmd(se_cmd, tv_nexus->tvn_se_sess,
cmd->cmd_buf, cmd->sense_iu.sense, cmd->unpacked_lun,
- cmd->data_len, cmd->prio_attr, dir, 0) < 0)
+ cmd->data_len, cmd->prio_attr, dir,
+ TARGET_SCF_ACK_KREF) < 0)
goto out;
return;
@@ -1675,9 +1778,11 @@ static ssize_t tcm_usbg_tpg_nexus_store(struct config_item *item,
CONFIGFS_ATTR(tcm_usbg_tpg_, enable);
CONFIGFS_ATTR(tcm_usbg_tpg_, nexus);
+static struct configfs_attribute tcm_usbg_tpg_attr_maxburst;
static struct configfs_attribute *usbg_base_attrs[] = {
&tcm_usbg_tpg_attr_enable,
&tcm_usbg_tpg_attr_nexus,
+ &tcm_usbg_tpg_attr_maxburst,
NULL,
};
@@ -1985,6 +2090,32 @@ static struct usb_gadget_strings *tcm_strings[] = {
NULL,
};
+static ssize_t tcm_usbg_tpg_maxburst_show(struct config_item *item, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%u\n", uasp_cmd_comp_desc.bMaxBurst);
+}
+
+static ssize_t tcm_usbg_tpg_maxburst_store(struct config_item *item,
+ const char *page, size_t count)
+{
+ int value;
+ int ret;
+
+ ret = kstrtouint(page, 10, &value);
+ if (ret)
+ return ret;
+
+ uasp_bi_ep_comp_desc.bMaxBurst = value;
+ uasp_bo_ep_comp_desc.bMaxBurst = value;
+ uasp_status_in_ep_comp_desc.bMaxBurst = value;
+ uasp_cmd_comp_desc.bMaxBurst = value;
+ bot_bi_ep_comp_desc.bMaxBurst = value;
+ bot_bo_ep_comp_desc.bMaxBurst = value;
+
+ return count;
+}
+CONFIGFS_ATTR(tcm_usbg_tpg_, maxburst);
+
static int tcm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct f_uas *fu = to_f_uas(f);
@@ -2114,6 +2245,13 @@ static int tcm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
return -EOPNOTSUPP;
}
+static int tcm_get_alt(struct usb_function *f, unsigned int intf)
+{
+ struct f_uas *fu = to_f_uas(f);
+
+ return fu->flags & USBG_IS_UAS ? 1 : 0;
+}
+
static void tcm_disable(struct usb_function *f)
{
struct f_uas *fu = to_f_uas(f);
@@ -2302,6 +2440,7 @@ static struct usb_function *tcm_alloc(struct usb_function_instance *fi)
fu->function.bind = tcm_bind;
fu->function.unbind = tcm_unbind;
fu->function.set_alt = tcm_set_alt;
+ fu->function.get_alt = tcm_get_alt;
fu->function.setup = tcm_setup;
fu->function.disable = tcm_disable;
fu->function.free_func = tcm_free;