diff options
Diffstat (limited to 'drivers/usb/storage/protocol.c')
| -rw-r--r-- | drivers/usb/storage/protocol.c | 171 |
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); |
