| /* 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) | |
| * | |
| * Developed with the assistance of: | |
| * (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org) | |
| * (c) 2002 Alan Stern (stern@rowland.org) | |
| * | |
| * Initial work by: | |
| * (c) 1999 Michael Gee (michael@linuxspecific.com) | |
| * | |
| * This driver is based on the 'USB Mass Storage Class' document. This | |
| * describes in detail the protocol used to communicate with such | |
| * devices. Clearly, the designers had SCSI and ATAPI commands in | |
| * mind when they created this document. The commands are all very | |
| * similar to commands in the SCSI-II and ATAPI specifications. | |
| * | |
| * It is important to note that in a number of cases this class | |
| * exhibits class-specific exemptions from the USB specification. | |
| * Notably the usage of NAK, STALL and ACK differs from the norm, in | |
| * that they are used to communicate wait, failed and OK on commands. | |
| * | |
| * Also, for certain devices, the interrupt endpoint is used to convey | |
| * status of a command. | |
| * | |
| * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more | |
| * information about this driver. | |
| * | |
| * This program is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License as published by the | |
| * Free Software Foundation; either version 2, or (at your option) any | |
| * later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, but | |
| * WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License along | |
| * with this program; if not, write to the Free Software Foundation, Inc., | |
| * 675 Mass Ave, Cambridge, MA 02139, USA. | |
| */ | |
| #include <linux/highmem.h> | |
| #include <scsi/scsi.h> | |
| #include <scsi/scsi_cmnd.h> | |
| #include "usb.h" | |
| #include "protocol.h" | |
| #include "debug.h" | |
| #include "scsiglue.h" | |
| #include "transport.h" | |
| /*********************************************************************** | |
| * Protocol routines | |
| ***********************************************************************/ | |
| void usb_stor_qic157_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 | |
| */ | |
| 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 | |
| * | |
| * 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 (this affects the transport layer) */ | |
| srb->cmd_len = 12; | |
| /* XXX We should be constantly re-evaluating the need for these */ | |
| /* determine the correct data length for these commands */ | |
| switch (srb->cmnd[0]) { | |
| /* for INQUIRY, UFI devices only ever return 36 bytes */ | |
| case INQUIRY: | |
| srb->cmnd[4] = 36; | |
| break; | |
| /* again, for MODE_SENSE_10, we get the minimum (8) */ | |
| case MODE_SENSE_10: | |
| srb->cmnd[7] = 0; | |
| srb->cmnd[8] = 8; | |
| break; | |
| /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */ | |
| case REQUEST_SENSE: | |
| srb->cmnd[4] = 18; | |
| break; | |
| } /* end switch on cmnd[0] */ | |
| /* send the command to the transport layer */ | |
| usb_stor_invoke_transport(srb, us); | |
| } | |
| void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, | |
| struct us_data *us) | |
| { | |
| /* send the command to the transport layer */ | |
| usb_stor_invoke_transport(srb, us); | |
| } | |
| /*********************************************************************** | |
| * Scatter-gather transfer buffer access routines | |
| ***********************************************************************/ | |
| /* Copy a buffer of length buflen to/from the srb's transfer buffer. | |
| * 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, struct scatterlist **sgptr, | |
| unsigned int *offset, enum xfer_buf_dir dir) | |
| { | |
| unsigned int cnt; | |
| struct scatterlist *sg = *sgptr; | |
| /* 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. | |
| */ | |
| if (!sg) | |
| sg = scsi_sglist(srb); | |
| buflen = min(buflen, scsi_bufflen(srb)); | |
| /* 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 **sgptr values for the next loop. | |
| */ | |
| cnt = 0; | |
| while (cnt < buflen && sg) { | |
| struct page *page = sg_page(sg) + | |
| ((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; | |
| sg = sg_next(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; | |
| } | |
| } | |
| *sgptr = sg; | |
| /* Return the amount actually transferred */ | |
| return cnt; | |
| } | |
| /* Store the contents of buffer into srb's transfer buffer and set the | |
| * SCSI residue. | |
| */ | |
| void usb_stor_set_xfer_buf(unsigned char *buffer, | |
| unsigned int buflen, struct scsi_cmnd *srb) | |
| { | |
| unsigned int offset = 0; | |
| struct scatterlist *sg = NULL; | |
| buflen = usb_stor_access_xfer_buf(buffer, buflen, srb, &sg, &offset, | |
| TO_XFER_BUF); | |
| if (buflen < scsi_bufflen(srb)) | |
| scsi_set_resid(srb, scsi_bufflen(srb) - buflen); | |
| } |