[RFC/PATCH 4/5] uas: TASK MANAGEMENT IU implementation

From: Tatyana Brokhman
Date: Mon Mar 07 2011 - 11:48:46 EST


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.0.4

--
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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/