This patch implements the handling of most of the TM IUs defined in table 20 of the UAS Spec Signed-off-by: Tatyana Brokhman <tlinder@xxxxxxxxxxxxxx> diff --git a/drivers/usb/gadget/uasp_tmiu.c b/drivers/usb/gadget/uasp_tmiu.c index c25c293..0b4b417 100644 --- a/drivers/usb/gadget/uasp_tmiu.c +++ b/drivers/usb/gadget/uasp_tmiu.c @@ -60,10 +60,38 @@ void fill_response_iu(struct uasp_dev *udev, * commands. */ static void reset_lun(struct uasp_dev *udev, - struct uasp_lun *curlun, - struct tm_iu *tmiu) + struct uasp_lun *curlun, + struct tm_iu *tmiu) { + struct response_iu *riu; + uint8_t status; + unsigned long flags; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto res_lun_fill_response; + } + + abort_commands(udev, &curlun->cmd_queue, &curlun->tm_func_queue, + &(curlun->lock)); + + spin_lock_irqsave(&(curlun->lock), flags); + curlun->pending_requests = 0; + spin_unlock_irqrestore(&(curlun->lock), flags); + + curlun->lun->unit_attention_data = SS_RESET_OCCURRED; + status = RESPONSE_TM_FUNCTION_COMPLETE; + +res_lun_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, status); + + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; } /** @@ -73,15 +101,68 @@ static void reset_lun(struct uasp_dev *udev, * addressed to a valid LUN, 0 otherwise. * @tmiu: TM FUNCTION IU to be processed. * - * This function aborts the command with the same ip_tag as in the - * tmiu->task_tag. It's valid only for command that are handled by a specific - * LUN . + * This function aborts the command with the same tag as in the + * tmiu->task_tag. It's valid only for command that are handled + * by a specific LUN . */ static void abort_task(struct uasp_dev *udev, - struct uasp_lun *curlun, - struct tm_iu *tmiu) + struct uasp_lun *curlun, + struct tm_iu *tmiu) { + struct cmd_iu *cmdiu, *tmp; + struct response_iu *riu; + unsigned long flags; + uint8_t status; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto abrt_task_fill_response; + } + + /* Try to find the command in curlun */ + list_for_each_entry_safe(cmdiu, tmp, &curlun->cmd_queue, node) + if (cmdiu->tag == tmiu->task_tag) + goto found; + + /* Command with specified ipt_tag not found */ + DBG(udev->ucommon->common, "%s(): cmdiu with tag %04x wasn't found\n", + __func__, tmiu->task_tag); + cmdiu = 0; + +found: + if (cmdiu) { + spin_lock_irqsave(&(curlun->lock), flags); + if (cmdiu->state == COMMAND_STATE_DATA) { + if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) { + spin_unlock_irqrestore(&(curlun->lock), flags); + if (cmdiu->bh->inreq_busy) + usb_ep_dequeue(cmdiu->ep, + cmdiu->bh->inreq); + if (cmdiu->bh->outreq_busy) + usb_ep_dequeue(cmdiu->ep, + cmdiu->bh->outreq); + spin_lock_irqsave(&(curlun->lock), flags); + } + } else if (cmdiu->state == COMMAND_STATE_STATUS) { + spin_unlock_irqrestore(&(curlun->lock), flags); + usb_ep_dequeue(cmdiu->ep, cmdiu->bh->inreq); + spin_lock_irqsave(&(curlun->lock), flags); + } else + cmdiu->state = COMMAND_STATE_ABORTED; + spin_unlock_irqrestore(&(curlun->lock), flags); + } + + status = RESPONSE_TM_FUNCTION_COMPLETE; + +abrt_task_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, status); + + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; } /** @@ -94,10 +175,36 @@ static void abort_task(struct uasp_dev *udev, * This function aborts all the commands pending for the specified LUN. */ static void abort_task_set(struct uasp_dev *udev, - struct uasp_lun *curlun, - struct tm_iu *tmiu) + struct uasp_lun *curlun, + struct tm_iu *tmiu) { + struct response_iu *riu; + uint8_t status; + unsigned long flags; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto abrt_ts_fill_response; + } + + abort_commands(udev, &curlun->cmd_queue, 0, &(curlun->lock)); + + spin_lock_irqsave(&(curlun->lock), flags); + curlun->pending_requests = 0; + spin_unlock_irqrestore(&(curlun->lock), flags); + + status = RESPONSE_TM_FUNCTION_COMPLETE; + +abrt_ts_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, status); + + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; } /** @@ -106,9 +213,54 @@ static void abort_task_set(struct uasp_dev *udev, * @tmiu: TM FUNCTION IU to be processed. */ static void reset_nexus(struct uasp_dev *udev, - struct tm_iu *tmiu) + struct tm_iu *tmiu) { + struct response_iu *riu; + unsigned long flags; + uint8_t status; + int rc = 0; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + + riu = (struct response_iu *)tmiu->bh->buf; + + run_lun_threads(udev, LUN_STATE_RESET); + + /* + * Wait for luns completing the nexus reset. + * Sleep if luns are in processing + */ + while (!all_lun_state_non_processing(udev)) { + DBG(udev->ucommon->common, + "%s() - Luns are in process. Going to sleep\n", __func__); + rc = sleep_thread(udev->ucommon->common); + if (rc) { + ERROR(udev->ucommon->common, + "%s() - sleep_thread failed! (%d)", __func__, rc); + status = RESPONSE_TM_FUNCTION_FAILED; + goto reset_nexus_fill_response; + } + DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__); + rc = 0; + } + + /* Abort general commands and tmius */ + abort_commands(udev, &udev->cmd_queue, &udev->tm_func_queue, + &(udev->ucommon->common->lock)); + + spin_lock_irqsave(&(udev->ucommon->common->lock), flags); + udev->pending_requests = 0; + spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags); + + status = RESPONSE_TM_FUNCTION_COMPLETE; +reset_nexus_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, + RESPONSE_TM_FUNCTION_COMPLETE); + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; + DBG(udev->ucommon->common, "%s() - Exit\n", __func__); } /** @@ -126,7 +278,37 @@ static void query_unit_attention(struct uasp_dev *udev, struct uasp_lun *curlun, struct tm_iu *tmiu) { + struct response_iu *riu; + uint8_t status; + uint32_t resp_info = 0; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto qut_fill_response; + } + + status = RESPONSE_TM_FUNCTION_COMPLETE; + + if (curlun->lun->unit_attention_data) { + status = RESPONSE_TM_FUNCTION_SUCCEEDED; + /* + * We don't keep queue of unit attention conditions, + * and deferred errors also. We only keep unit attention + * condition with higher precedence level. + */ + resp_info = curlun->lun->unit_attention_data | (1 << 20); + } + +qut_fill_response: + fill_response_iu(udev, riu, tmiu->tag, resp_info, status); + + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + + tmiu->ep = udev->status; } @@ -141,7 +323,39 @@ static void query_task(struct uasp_dev *udev, struct uasp_lun *curlun, struct tm_iu *tmiu) { + struct cmd_iu *cmdiu = 0; + struct cmd_iu *tmp_cmdiu; + struct response_iu *riu; + unsigned long flags; + uint8_t status = RESPONSE_TM_FUNCTION_COMPLETE; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto q_task_fill_response; + } + + /* Try to find in command in curlun */ + spin_lock_irqsave(&(curlun->lock), flags); + list_for_each_entry_safe(cmdiu, tmp_cmdiu, &curlun->cmd_queue, node) { + if (cmdiu->tag == tmiu->task_tag) { + if (cmdiu->state == COMMAND_STATE_IDLE || + cmdiu->state == COMMAND_STATE_DATA || + cmdiu->state == COMMAND_STATE_STATUS) + status = RESPONSE_TM_FUNCTION_SUCCEEDED; + spin_unlock_irqrestore(&(curlun->lock), flags); + goto q_task_fill_response; + } + } + spin_unlock_irqrestore(&(curlun->lock), flags); + +q_task_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, status); + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; } /** @@ -155,7 +369,42 @@ static void query_task_set(struct uasp_dev *udev, struct uasp_lun *curlun, struct tm_iu *tmiu) { + struct cmd_iu *cmdiu = 0; + struct cmd_iu *tmp_cmdiu; + struct response_iu *riu; + unsigned long flags; + uint8_t status; + DBG(udev->ucommon->common, "%s() - Enter\n", __func__); + riu = (struct response_iu *)tmiu->bh->buf; + if (!curlun) { + status = RESPONSE_INCORRECT_LUN; + goto q_task_set_fill_response; + } + + /* Try to find none-completed command in curlun */ + spin_lock_irqsave(&(curlun->lock), flags); + list_for_each_entry_safe(cmdiu, tmp_cmdiu, &curlun->cmd_queue, node) { + if (cmdiu->state == COMMAND_STATE_IDLE || + cmdiu->state == COMMAND_STATE_RR_WR || + cmdiu->state == COMMAND_STATE_DATA || + cmdiu->state == COMMAND_STATE_STATUS) { + status = RESPONSE_TM_FUNCTION_SUCCEEDED; + spin_unlock_irqrestore(&(curlun->lock), flags); + goto q_task_set_fill_response; + } + } + + spin_unlock_irqrestore(&(curlun->lock), flags); + status = RESPONSE_TM_FUNCTION_COMPLETE; + +q_task_set_fill_response: + fill_response_iu(udev, riu, tmiu->tag, 0, status); + fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU, + 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag), + status_complete); + tmiu->ep = udev->status; + DBG(udev->ucommon->common, "%s() - Exit\n", __func__); } /** -- 1.7.3.3 -- Sent by a Consultant for Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html