[RFC/PATCH 3/4] uasp: COMMAND IU implementation

From: Tatyana Brokhman
Date: Fri Jan 21 2011 - 02:45:56 EST


This patch implements the handling of different SCSI commands received
in a COMMAND IU packets

Signed-off-by: Tatyana Brokhman <tlinder@xxxxxxxxxxxxxx>
---
drivers/usb/gadget/f_uasp.c | 2 +-
drivers/usb/gadget/f_uasp.h | 9 +
drivers/usb/gadget/uasp_cmdiu.c | 1206 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 1201 insertions(+), 16 deletions(-)

diff --git a/drivers/usb/gadget/f_uasp.c b/drivers/usb/gadget/f_uasp.c
index a0d878f..82f7f8f 100644
--- a/drivers/usb/gadget/f_uasp.c
+++ b/drivers/usb/gadget/f_uasp.c
@@ -1858,7 +1858,7 @@ sleep:
* This function should be called when fsg_common->filesem is
* taken!
*/
-static void close_lun(struct uasp_lun *ulun)
+void close_lun(struct uasp_lun *ulun)
{
struct fsg_lun *fsglun = ulun->lun;
/*TODO: stop lun task and clear all queues!*/
diff --git a/drivers/usb/gadget/f_uasp.h b/drivers/usb/gadget/f_uasp.h
index e7cadbc..1e092e3 100644
--- a/drivers/usb/gadget/f_uasp.h
+++ b/drivers/usb/gadget/f_uasp.h
@@ -411,4 +411,13 @@ void run_lun_threads(struct uasp_dev *udev, int state);
*/
int all_lun_state_non_processing(struct uasp_dev *udev);

+/**
+ * close_lun() - Close the backing file of the given LUN
+ * @ulun: pointer to the LUn to close
+ *
+ * This function should be called when fsg_common->filesem is
+ * taken!
+ */
+void close_lun(struct uasp_lun *ulun);
+
#endif /* _F_UASP_H */
diff --git a/drivers/usb/gadget/uasp_cmdiu.c b/drivers/usb/gadget/uasp_cmdiu.c
index 1444d56..8e10517 100644
--- a/drivers/usb/gadget/uasp_cmdiu.c
+++ b/drivers/usb/gadget/uasp_cmdiu.c
@@ -137,7 +137,80 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct fsg_common *common = udev->ucommon->common;
+ struct usb_request *req = bh->inreq;
+ u8 *buf = (u8 *)bh->buf;
+ u8 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ ERROR(common, "%s() - Error condition\n", __func__);
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ memset(buf, 0, FSG_BUFLEN);
+ if (!curlun) {
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ } else {
+ buf[0] = curlun->lun->cdrom ?
+ TYPE_ROM : TYPE_DISK;
+ buf[1] = curlun->lun->removable ? 0x80 : 0;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ buf[5] = 0; /* No special options */
+ buf[6] = 0;
+ buf[7] = 0;
+ memcpy(buf + 8, common->inquiry_string,
+ sizeof(common->inquiry_string));
+ }
+ fill_usb_request(req, bh->buf, cmdiu->cdb[4], 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ IUGETW(cmdiu->ip_tag), status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}


@@ -150,12 +223,123 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
*
* Returns 1 if usb request should be submitted to PCD after cmdiu processing,
* 0 otherwise.
+ *
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ * If a REQUEST SENSE command is received from an initiator with a pending unit
+ * attention condition (before the target generates the contingent allegiance
+ * condition), then the target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * We implement option a).
+ *
*/
static int do_uasp_request_sense(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ u8 *buf = (u8 *)cmdiu->bh->buf;
+ u32 sdinfo;
+ u32 sd;
+ int valid, rc = 0;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ if (!curlun) {
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->lun->sense_data;
+ sdinfo = curlun->lun->sense_data_info;
+ valid = curlun->lun->info_valid << 7;
+
+ /*
+ * If sense data exists, send it and preserve
+ * unit attention data, then clear sent sense
+ * data.
+ */
+ if (sd) {
+ curlun->lun->sense_data = SS_NO_SENSE;
+ curlun->lun->sense_data_info = 0;
+ curlun->lun->info_valid = 0;
+ /*
+ * If no sense data, sent unit attention data
+ * then clear the sent unit attention data.
+ */
+ } else {
+ sd = curlun->lun->unit_attention_data;
+ sdinfo = 0;
+ valid = 0;
+ curlun->lun->unit_attention_data =
+ SS_NO_SENSE;
+ }
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ /* Sense information */
+ put_unaligned_be32(sdinfo, &buf[3]);
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf, 18,
+ 0, (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)cmdiu->bh->buf,
+ IUGETW(cmdiu->ip_tag),
+ status,
+ sense);
+
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
+ UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}

/**
@@ -172,7 +356,29 @@ static int do_uasp_test_unit_ready(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf, IUGETW(cmdiu->ip_tag),
+ status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0, cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ return 1;
}

/**
@@ -190,7 +396,131 @@ static int do_uasp_mode_sense(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u8 *buf = (u8 *)bh->buf;
+ u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit, rc = 0;
+ int mscmnd = cmdiu->cdb[0];
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+ page_code = cmdiu->cdb[2] & 0x3f;
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if ((cmdiu->cdb[1] & ~0x08) != 0) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if ((cmdiu->cdb[2] >> 6) == 3) {
+ sense = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (page_code != 0x08 && page_code != 0x3f) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ pc = cmdiu->cdb[2] >> 6;
+ page_code = cmdiu->cdb[2] & 0x3f;
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+ memset(buf, 0, 8);
+
+ if (mscmnd == MODE_SENSE) {
+ buf[2] = (curlun->lun->ro ? 0x80 : 0x00);
+ buf += 4;
+ limit = 255;
+ } else { /* SC_MODE_SENSE_10 */
+ buf[3] = (curlun->lun->ro ? 0x80 : 0x00);
+ buf += 8;
+ limit = FSG_BUFLEN;
+ }
+ /*
+ * The mode pages, in numerical order.
+ * The only page we support is the Caching page.
+ */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10);
+ /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /*
+ * Check that a valid page was requested and the mode
+ * data length isn't too long.
+ */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (mscmnd == MODE_SENSE)
+ /* Store the mode data length */
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+
+ fill_usb_request(req, buf0, len, 0, cmdiu, 0,
+ IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ IUGETW(cmdiu->ip_tag), status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}

/**
@@ -207,7 +537,41 @@ static int do_uasp_prevent_allow(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ int prevent;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ prevent = cmdiu->cdb[4] & 0x01;
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (!curlun->lun->removable) {
+ status = STATUS_CHECK_CONDITION;
+ sense = SS_INVALID_COMMAND;
+ } else if ((cmdiu->cdb[4] & ~0x01) != 0) { /* Mask away Prevent */
+ status = STATUS_CHECK_CONDITION;
+ sense = SS_INVALID_FIELD_IN_CDB;
+ } else {
+ if (curlun->lun->prevent_medium_removal && !prevent)
+ fsg_lun_fsync_sub(curlun->lun);
+ curlun->lun->prevent_medium_removal = prevent;
+ }
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ IUGETW(cmdiu->ip_tag), status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0, cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ return 1;
}

/**
@@ -224,7 +588,194 @@ static int do_uasp_read(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u8 mscmnd = cmdiu->cdb[0];
+ loff_t file_offset_tmp;
+ u32 amount, lba;
+ ssize_t nread;
+ unsigned int partial_page;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ if (!curlun) {
+ ERROR(udev->ucommon->common,
+ "%s() - Error condition - curlun = NULL\n",
+ __func__);
+ sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto switch_cmdiu_state;
+ }
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ if (mscmnd == READ_6) {
+ lba = get_unaligned_be24(&cmdiu->cdb[1]);
+ cmdiu->xfer_len =
+ ((cmdiu->cdb[4] == 0 ? 256 : cmdiu->cdb[4]) << 9);
+ } else {
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ /*
+ * We allow DPO (Disable Page Out = don't save data in
+ * the cache) and FUA (Force Unit Access = don't read
+ * from the cache), but we don't implement them.
+ */
+ if ((cmdiu->cdb[1] & ~0x18) != 0) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto switch_cmdiu_state;
+ }
+
+ if (mscmnd == READ_10)
+ cmdiu->xfer_len =
+ (get_unaligned_be16(&cmdiu->cdb[7]) << 9);
+ else
+ cmdiu->xfer_len =
+ (get_unaligned_be32(&cmdiu->cdb[6]) << 9);
+ }
+ cmdiu->file_offset = ((loff_t) lba) << 9;
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (lba >= curlun->lun->num_sectors) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ curlun->lun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ " xfer_len = %d\n",
+ __func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
+ }
+
+switch_cmdiu_state:
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+send_more_data: /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ * Finally, if we're not at a page boundary, don't read
+ * past the next page.
+ * If this means reading 0 then we were asked to read
+ * past the end of file.
+ */
+ amount = min((unsigned int)cmdiu->xfer_len, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - cmdiu->file_offset);
+ partial_page = cmdiu->file_offset &
+ (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int) PAGE_CACHE_SIZE -
+ partial_page);
+
+ /*
+ * If we were asked to read past the end of file,
+ * end with an empty buffer.
+ */
+ if (amount == 0) {
+ curlun->lun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ cmdiu->xfer_len = 0;
+ nread = 0;
+ } else {
+ /* Perform the read */
+ file_offset_tmp = cmdiu->file_offset;
+ nread = vfs_read(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+
+ if (nread < 0) {
+ LDBG(curlun->lun,
+ "error in file read: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun->lun,
+ "partial file read: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511);
+ /* Round down to a block */
+ }
+
+ cmdiu->file_offset += nread;
+ cmdiu->xfer_len -= nread;
+
+ /*
+ * If an error occurred, report it and
+ * its position
+ */
+ if (nread < amount) {
+ curlun->lun->sense_data = sense =
+ SS_UNRECOVERED_READ_ERROR;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto send_status;
+ }
+ }
+
+ fill_usb_request(req, bh->buf, nread, 0,
+ cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
+ /* Completion of sent data is not received yet */
+ DBG(udev->ucommon->common,
+ "%s() - completion for bh is not received",
+ __func__);
+ break;
+ } else {
+ /* Completion of the sent data is done */
+ DBG(udev->ucommon->common,
+ "%s() - COMMAND_STATE_DATA for bh\n", __func__);
+ if (cmdiu->xfer_len == 0)
+ goto send_status;
+ else
+ goto send_more_data;
+ }
+send_status:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag),
+ status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}

/**
@@ -242,7 +793,71 @@ static int do_uasp_read_capacity(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u8 *buf = (u8 *)bh->buf;
+ u32 lba;
+ int pmi, rc = 0;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ lba = IUGETDW(&cmdiu->cdb[2]);
+ pmi = cmdiu->cdb[8];
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ IUSETDW(&buf[0], curlun->lun->num_sectors - 1);
+ IUSETDW(&buf[4], 512);
+
+ fill_usb_request(req, bh->buf, 8, 0,
+ cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag),
+ status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
}

/**
@@ -260,7 +875,73 @@ static int do_uasp_read_format_capacities(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u8 *buf = (u8 *)bh->buf;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /*
+ * Only the Current/Maximum
+ * Capacity Descriptor
+ */
+ buf += 4;
+
+ IUSETDW(&buf[0], curlun->lun->num_sectors);
+ /* Number of blocks */
+ IUSETDW(&buf[4], 512); /* Block length */
+ buf[4] = 0x02; /* Current capacity */
+
+ fill_usb_request(req, bh->buf, 12, 0,
+ cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ uasp_bulk_in_complete);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag),
+ status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}

/**
@@ -278,7 +959,106 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+ int start, loej;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ start = cmdiu->cdb[4] & 0x01;
+ loej = cmdiu->cdb[4] & 0x02;
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (!curlun->lun->removable) {
+ sense = SS_INVALID_COMMAND;
+ status = STATUS_CHECK_CONDITION;
+ } else if ((cmdiu->cdb[1] & ~0x01) != 0 || /* Mask away Immed */
+ (cmdiu->cdb[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ }
+
+ if (status)
+ goto do_uasp_start_stop_done;
+
+ if (!loej) {
+ /*
+ * No action should be taken regarding loading or ejecting of
+ * the medium
+ */
+ /*
+ * Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded.
+ */
+ if (start && !fsg_lun_is_open(curlun->lun)) {
+ sense = SS_MEDIUM_NOT_PRESENT;
+ status = STATUS_CHECK_CONDITION;
+ }
+ } else {
+ /*
+ * LOEJ = 1 & START = 0 -> requests that the medium
+ * shall be unloaded
+ */
+ if (start && !fsg_lun_is_open(curlun->lun)) {
+ sense = SS_MEDIUM_NOT_PRESENT;
+ status = STATUS_CHECK_CONDITION;
+ } else {
+ /* Are we allowed to unload the media? */
+ if (curlun->lun->prevent_medium_removal) {
+ DBG(udev->ucommon->common,
+ "%s(): unload attempt prevented\n",
+ __func__);
+ sense = SS_MEDIUM_REMOVAL_PREVENTED;
+ status = STATUS_CHECK_CONDITION;
+ goto do_uasp_start_stop_done;
+ }
+
+ /* Simulate an unload/eject */
+ if (udev->ucommon->common->ops &&
+ udev->ucommon->common->ops->pre_eject) {
+ int r = udev->ucommon->common->ops->pre_eject(
+ udev->ucommon->common, curlun->lun,
+ curlun - udev->ucommon->uluns);
+ if (unlikely(r < 0))
+ status = STATUS_CHECK_CONDITION;
+ else if (r) /* r > 0 means don't aject */
+ goto do_uasp_start_stop_done;
+ }
+
+ up_read(&(udev->ucommon->common->filesem));
+ down_write(&(udev->ucommon->common->filesem));
+ close_lun(curlun);
+ up_write(&(udev->ucommon->common->filesem));
+ down_read(&(udev->ucommon->common->filesem));
+
+ if (udev->ucommon->common->ops &&
+ udev->ucommon->common->ops->post_eject) {
+ if (udev->ucommon->common->ops->
+ post_eject(udev->ucommon->common,
+ curlun->lun,
+ curlun - udev->ucommon->uluns)
+ < 0)
+ status = STATUS_CHECK_CONDITION;
+ }
+ }
+ }
+
+do_uasp_start_stop_done:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag), status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}

/**
@@ -288,14 +1068,112 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
* addressed to a valid LUN, 0 otherwise.
* @cmdiu: COMMAND IU to be performed.
*
- * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
- * 0 otherwise.
+ * Returns 1 if usb request should be submitted to PCD after
+ * cmdiu processing, 0 otherwise.
+ *
+ * Although optional, this command is used by MS-Windows. We support a minimal
+ * version: BytChk must be 0.
+ *
*/
static int do_uasp_verify(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ loff_t file_offset_tmp, file_offset;
+ u32 ver_len, amount;
+ ssize_t nread;
+ u32 sense = SS_NO_SENSE;
+ u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ file_offset = (IUGETDW(&cmdiu->cdb[2]) << 9);
+ ver_len = (IUGETW(&cmdiu->cdb[7]) << 9);
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (file_offset + ver_len > curlun->lun->file_length) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ } else if ((cmdiu->cdb[1] & ~0x10) != 0) {
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it.
+ */
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ } else if (ver_len == 0)
+ status = STATUS_CHECK_CONDITION;
+ else {
+ /* Write out all the dirty buffers before invalidating them */
+ fsg_lun_fsync_sub(curlun->lun);
+ invalidate_sub(curlun->lun);
+
+ /* Just try to read the requested blocks */
+ while (ver_len > 0) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ * If this means reading 0 then we were asked to read
+ * past the end of file.
+ */
+ amount = min((unsigned int) ver_len, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - file_offset);
+ if (amount == 0) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun->lun, "file read %u @ %llu -> %d\n",
+ amount, (unsigned long long) file_offset,
+ (int)nread);
+
+ if (nread < 0) {
+ LDBG(curlun->lun, "error in file verify: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun->lun,
+ "partial file verify: %d/%u\n",
+ (int) nread, amount);
+ /* Round down to a sector */
+ nread -= (nread & 511);
+ }
+
+ if (nread == 0) {
+ sense = SS_UNRECOVERED_READ_ERROR;
+ status = STATUS_CHECK_CONDITION;
+ break;
+ }
+
+ file_offset += nread;
+ ver_len -= nread;
+ }
+ }
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag), status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}

/**
@@ -314,7 +1192,272 @@ static int do_uasp_write(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->outreq;
+ loff_t usb_offset = 0;
+ loff_t file_offset_tmp = 0;
+ u32 amount_left_to_req = 0;
+ unsigned int partial_page;
+ int get_some_more = 0;
+ u32 amount = 0;
+ ssize_t nwritten = 0;
+ u32 sense = SS_NO_SENSE;
+ u32 lba;
+ u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (!curlun) {
+ ERROR(udev->ucommon->common,
+ "%s() - Error condition - curlun = NULL\n",
+ __func__);
+ sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto send_status;
+ }
+
+ if (curlun->lun->ro) {
+ sense = SS_WRITE_PROTECTED;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ spin_lock(&curlun->lun->filp->f_lock);
+ /* Default is not to wait */
+ curlun->lun->filp->f_flags &= ~O_SYNC;
+ spin_unlock(&curlun->lun->filp->f_lock);
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ switch (cmdiu->cdb[0]) {
+ case WRITE_6:
+ lba = get_unaligned_be24(&cmdiu->cdb[1]);
+ amount_left_to_req = cmdiu->xfer_len =
+ (cmdiu->cdb[4] == 0 ? 256 : cmdiu->cdb[4]) << 9;
+ break;
+ case WRITE_10:
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ amount_left_to_req = cmdiu->xfer_len =
+ get_unaligned_be16(&cmdiu->cdb[7]) << 9;
+ break;
+ case WRITE_12:
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ amount_left_to_req = cmdiu->xfer_len =
+ get_unaligned_be32(&cmdiu->cdb[6]) << 9;
+ break;
+ default:
+ sense = SS_INVALID_COMMAND;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->cdb[0] != WRITE_6) {
+ /*
+ * We allow DPO (Disable Page Out = don't save data in
+ * the cache) and FUA (Force Unit Access = write
+ * directly to the medium). We don't implement DPO; we
+ * implement FUA by performing synchronous output.
+ */
+ if (cmdiu->cdb[1] & ~0x18) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+ if (!curlun->lun->nofua && (cmdiu->cdb[1] & 0x08)) {
+ /* FUA */
+ spin_lock(&curlun->lun->filp->f_lock);
+ curlun->lun->filp->f_flags |= O_SYNC;
+ spin_unlock(&curlun->lun->filp->f_lock);
+ }
+ }
+
+ if (lba >= curlun->lun->num_sectors) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ get_some_more = 1;
+ cmdiu->file_offset = usb_offset = ((loff_t) lba) << 9;
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ " xfer_len = %d\n",
+ __func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_DATA:
+ /* Queue a request for more data from the host */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED &&
+ get_some_more) {
+ /*
+ * Figure out how much we want to get:
+ * Try to get the remaining amount.
+ * But don't get more than the buffer size.
+ * And don't try to go past the end of the file.
+ * If we're not at a page boundary, don't go past the
+ * next page.
+ * If this means getting 0, then we were asked to write
+ * past the end of file.
+ * Finally, round down to a block boundary.
+ */
+ amount = min(amount_left_to_req, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - usb_offset);
+ partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int)PAGE_CACHE_SIZE -
+ partial_page);
+
+ if (amount == 0) {
+ get_some_more = 0;
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->lun->sense_data_info = usb_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ amount -= (amount & 511);
+ if (amount == 0)
+ /*
+ * Why were we were asked to transfer a
+ * partial block?
+ */
+ goto send_status;
+
+ /* Get the next buffer */
+ usb_offset += amount;
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ fill_usb_request(req, bh->buf, amount, 0,
+ cmdiu, 0, IUGETW(cmdiu->ip_tag),
+ uasp_bulk_out_complete);
+ DBG(udev->ucommon->common, "%s() fill_usb_request for"
+ " out endpoint, amout = %d",
+ __func__, amount);
+
+ cmdiu->ep = udev->fsg_dev.bulk_out;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 2;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else { /* Completion of the sent data is done */
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ sense = SS_COMMUNICATION_FAILURE;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+ if (req->actual != req->length) {
+ /*
+ * Host decide abort the command
+ * Note: if we going to submit more than one
+ * request for this command, we should abort
+ * all submitted requests for this command
+ */
+ DBG(udev->ucommon->common,
+ "%s() - Host aborted the command\n",
+ __func__);
+ goto send_status;
+ }
+
+ amount = req->actual;
+ if (curlun->lun->file_length - cmdiu->file_offset <
+ amount) {
+ ERROR(udev->ucommon->common,
+ "%s(): write %u @ %llu beyond end %llu\n",
+ __func__, amount,
+ (unsigned long long)cmdiu->file_offset,
+ (unsigned long long)
+ curlun->lun->file_length);
+ amount = curlun->lun->file_length -
+ cmdiu->file_offset;
+ }
+
+ /* Perform the write */
+ file_offset_tmp = cmdiu->file_offset;
+ nwritten = vfs_write(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ DBG(udev->ucommon->common,
+ "%s(): file write %u @ %llu -> %d\n", __func__,
+ amount, (unsigned long long)cmdiu->file_offset,
+ (int)nwritten);
+
+ if (nwritten < 0) {
+ ERROR(udev->ucommon->common,
+ "%s(): error in file write: %d\n",
+ __func__, (int)nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ DBG(udev->ucommon->common,
+ "%s(): partial file write: %d/%u\n",
+ __func__, (int)nwritten, amount);
+ nwritten -= (nwritten & 511);
+ /* Round down to a block */
+ }
+
+ cmdiu->file_offset += nwritten;
+ cmdiu->xfer_len -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ sense = SS_WRITE_ERROR;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->xfer_len == 0) {
+ DBG(udev->ucommon->common,
+ "%s() - cmdiu->xferlen = 0, "
+ "send status\n", __func__);
+ goto send_status;
+ }
+
+ rc = 1;
+ break;
+send_status:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ }
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag), status,
+ sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}

/**
@@ -325,13 +1468,46 @@ static int do_uasp_write(struct uasp_dev *udev,
* @cmdiu: COMMAND IU to be performed.
*
* Returns 1 if usb request should be submitted to PCD after cmdiu processing,
- * 0 otherwise.
+ * 0 otherwise.
+ *
*/
static int do_uasp_synchronize_cache(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ uint32_t sense = SS_NO_SENSE;
+ uint8_t status = STATUS_GOOD;
+ int rc;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /*
+ * We ignore the requested LBA and write out all file's
+ * dirty data buffers.
+ */
+ rc = fsg_lun_fsync_sub(curlun->lun);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (rc) {
+ sense = SS_WRITE_ERROR;
+ status = STATUS_CHECK_CONDITION;
+ }
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+ fill_sense_iu(udev, bh->buf, IUGETW(cmdiu->ip_tag), status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, IUGETW(cmdiu->ip_tag), status_complete);
+ cmdiu->ep = udev->status;
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}

/**
--
1.6.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-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/