diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_tcm.c')
-rw-r--r-- | drivers/usb/gadget/function/f_tcm.c | 311 |
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; |