aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/storage/protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/storage/protocol.c')
-rw-r--r--drivers/usb/storage/protocol.c171
1 files changed, 55 insertions, 116 deletions
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index 9ad30428d2d..12e3c2fac64 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -1,7 +1,5 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
@@ -45,6 +43,7 @@
*/
#include <linux/highmem.h>
+#include <linux/export.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -58,54 +57,33 @@
* Protocol routines
***********************************************************************/
-void usb_stor_qic157_command(struct scsi_cmnd *srb, struct us_data *us)
+void usb_stor_pad12_command(struct scsi_cmnd *srb, struct us_data *us)
{
- /* Pad the ATAPI command with zeros
+ /*
+ * Pad the SCSI command with zeros out to 12 bytes. If the
+ * command already is 12 bytes or longer, leave it alone.
*
* NOTE: This only works because a scsi_cmnd struct field contains
* a unsigned char cmnd[16], so we know we have storage available
*/
- for (; srb->cmd_len<12; srb->cmd_len++)
+ for (; srb->cmd_len < 12; srb->cmd_len++)
srb->cmnd[srb->cmd_len] = 0;
- /* set command length to 12 bytes */
- srb->cmd_len = 12;
-
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
}
-void usb_stor_ATAPI_command(struct scsi_cmnd *srb, struct us_data *us)
-{
- /* Pad the ATAPI command with zeros
- *
- * NOTE: This only works because a scsi_cmnd struct field contains
- * a unsigned char cmnd[16], so we know we have storage available
- */
-
- /* Pad the ATAPI command with zeros */
- for (; srb->cmd_len<12; srb->cmd_len++)
- srb->cmnd[srb->cmd_len] = 0;
-
- /* set command length to 12 bytes */
- srb->cmd_len = 12;
-
- /* send the command to the transport layer */
- usb_stor_invoke_transport(srb, us);
-}
-
-
void usb_stor_ufi_command(struct scsi_cmnd *srb, struct us_data *us)
{
/* fix some commands -- this is a form of mode translation
- * UFI devices only accept 12 byte long commands
+ * UFI devices only accept 12 byte long commands
*
* NOTE: This only works because a scsi_cmnd struct field contains
* a unsigned char cmnd[16], so we know we have storage available
*/
/* Pad the ATAPI command with zeros */
- for (; srb->cmd_len<12; srb->cmd_len++)
+ for (; srb->cmd_len < 12; srb->cmd_len++)
srb->cmnd[srb->cmd_len] = 0;
/* set command length to 12 bytes (this affects the transport layer) */
@@ -143,112 +121,73 @@ void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb,
/* send the command to the transport layer */
usb_stor_invoke_transport(srb, us);
}
+EXPORT_SYMBOL_GPL(usb_stor_transparent_scsi_command);
/***********************************************************************
* Scatter-gather transfer buffer access routines
***********************************************************************/
/* Copy a buffer of length buflen to/from the srb's transfer buffer.
- * (Note: for scatter-gather transfers (srb->use_sg > 0), srb->request_buffer
- * points to a list of s-g entries and we ignore srb->request_bufflen.
- * For non-scatter-gather transfers, srb->request_buffer points to the
- * transfer buffer itself and srb->request_bufflen is the buffer's length.)
- * Update the *index and *offset variables so that the next copy will
- * pick up from where this one left off. */
-
+ * Update the **sgptr and *offset variables so that the next copy will
+ * pick up from where this one left off.
+ */
unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
- unsigned int buflen, struct scsi_cmnd *srb, unsigned int *index,
+ unsigned int buflen, struct scsi_cmnd *srb, struct scatterlist **sgptr,
unsigned int *offset, enum xfer_buf_dir dir)
{
- unsigned int cnt;
-
- /* If not using scatter-gather, just transfer the data directly.
- * Make certain it will fit in the available buffer space. */
- if (srb->use_sg == 0) {
- if (*offset >= srb->request_bufflen)
- return 0;
- cnt = min(buflen, srb->request_bufflen - *offset);
- if (dir == TO_XFER_BUF)
- memcpy((unsigned char *) srb->request_buffer + *offset,
- buffer, cnt);
+ unsigned int cnt = 0;
+ struct scatterlist *sg = *sgptr;
+ struct sg_mapping_iter miter;
+ unsigned int nents = scsi_sg_count(srb);
+
+ if (sg)
+ nents = sg_nents(sg);
+ else
+ sg = scsi_sglist(srb);
+
+ sg_miter_start(&miter, sg, nents, dir == FROM_XFER_BUF ?
+ SG_MITER_FROM_SG: SG_MITER_TO_SG);
+
+ if (!sg_miter_skip(&miter, *offset))
+ return cnt;
+
+ while (sg_miter_next(&miter) && cnt < buflen) {
+ unsigned int len = min_t(unsigned int, miter.length,
+ buflen - cnt);
+
+ if (dir == FROM_XFER_BUF)
+ memcpy(buffer + cnt, miter.addr, len);
else
- memcpy(buffer, (unsigned char *) srb->request_buffer +
- *offset, cnt);
- *offset += cnt;
-
- /* Using scatter-gather. We have to go through the list one entry
- * at a time. Each s-g entry contains some number of pages, and
- * each page has to be kmap()'ed separately. If the page is already
- * in kernel-addressable memory then kmap() will return its address.
- * If the page is not directly accessible -- such as a user buffer
- * located in high memory -- then kmap() will map it to a temporary
- * position in the kernel's virtual address space. */
- } else {
- struct scatterlist *sg =
- (struct scatterlist *) srb->request_buffer
- + *index;
-
- /* This loop handles a single s-g list entry, which may
- * include multiple pages. Find the initial page structure
- * and the starting offset within the page, and update
- * the *offset and *index values for the next loop. */
- cnt = 0;
- while (cnt < buflen && *index < srb->use_sg) {
- struct page *page = sg->page +
- ((sg->offset + *offset) >> PAGE_SHIFT);
- unsigned int poff =
- (sg->offset + *offset) & (PAGE_SIZE-1);
- unsigned int sglen = sg->length - *offset;
-
- if (sglen > buflen - cnt) {
-
- /* Transfer ends within this s-g entry */
- sglen = buflen - cnt;
- *offset += sglen;
- } else {
-
- /* Transfer continues to next s-g entry */
- *offset = 0;
- ++*index;
- ++sg;
- }
-
- /* Transfer the data for all the pages in this
- * s-g entry. For each page: call kmap(), do the
- * transfer, and call kunmap() immediately after. */
- while (sglen > 0) {
- unsigned int plen = min(sglen, (unsigned int)
- PAGE_SIZE - poff);
- unsigned char *ptr = kmap(page);
-
- if (dir == TO_XFER_BUF)
- memcpy(ptr + poff, buffer + cnt, plen);
- else
- memcpy(buffer + cnt, ptr + poff, plen);
- kunmap(page);
-
- /* Start at the beginning of the next page */
- poff = 0;
- ++page;
- cnt += plen;
- sglen -= plen;
- }
+ memcpy(miter.addr, buffer + cnt, len);
+
+ if (*offset + len < miter.piter.sg->length) {
+ *offset += len;
+ *sgptr = miter.piter.sg;
+ } else {
+ *offset = 0;
+ *sgptr = sg_next(miter.piter.sg);
}
+ cnt += len;
}
+ sg_miter_stop(&miter);
- /* Return the amount actually transferred */
return cnt;
}
+EXPORT_SYMBOL_GPL(usb_stor_access_xfer_buf);
/* Store the contents of buffer into srb's transfer buffer and set the
- * SCSI residue. */
+ * SCSI residue.
+ */
void usb_stor_set_xfer_buf(unsigned char *buffer,
unsigned int buflen, struct scsi_cmnd *srb)
{
- unsigned int index = 0, offset = 0;
+ unsigned int offset = 0;
+ struct scatterlist *sg = NULL;
- usb_stor_access_xfer_buf(buffer, buflen, srb, &index, &offset,
+ buflen = min(buflen, scsi_bufflen(srb));
+ buflen = usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset,
TO_XFER_BUF);
- if (buflen < srb->request_bufflen)
- srb->resid = srb->request_bufflen - buflen;
+ if (buflen < scsi_bufflen(srb))
+ scsi_set_resid(srb, scsi_bufflen(srb) - buflen);
}
+EXPORT_SYMBOL_GPL(usb_stor_set_xfer_buf);