2005-04-17 02:20:36 +04:00
/* Driver for USB Mass Storage compliant devices
*
* 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 0213 9 , 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.
2007-09-09 21:22:23 +04:00
* Update the * * sgptr and * offset variables so that the next copy will
2008-02-20 22:15:58 +03:00
* pick up from where this one left off .
*/
2005-04-17 02:20:36 +04:00
unsigned int usb_stor_access_xfer_buf ( unsigned char * buffer ,
2007-05-11 14:33:09 +04:00
unsigned int buflen , struct scsi_cmnd * srb , struct scatterlist * * sgptr ,
2005-04-17 02:20:36 +04:00
unsigned int * offset , enum xfer_buf_dir dir )
{
unsigned int cnt ;
2008-02-20 22:15:58 +03:00
struct scatterlist * sg = * sgptr ;
2005-04-17 02:20:36 +04:00
2007-09-09 21:22:23 +04:00
/* We have to go through the list one entry
2005-04-17 02:20:36 +04:00
* 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
2008-02-20 22:15:58 +03:00
* position in the kernel ' s virtual address space .
*/
2007-09-09 21:22:23 +04:00
if ( ! sg )
sg = scsi_sglist ( srb ) ;
/* This loop handles a single s-g list entry, which may
2008-02-20 22:15:58 +03:00
* 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 .
*/
2007-09-09 21:22:23 +04:00
cnt = 0 ;
2008-02-20 22:15:58 +03:00
while ( cnt < buflen & & sg ) {
2007-09-09 21:22:23 +04:00
struct page * page = sg_page ( sg ) +
( ( sg - > offset + * offset ) > > PAGE_SHIFT ) ;
2008-02-20 22:15:58 +03:00
unsigned int poff = ( sg - > offset + * offset ) & ( PAGE_SIZE - 1 ) ;
2007-09-09 21:22:23 +04:00
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 ;
2005-04-17 02:20:36 +04:00
}
}
2007-09-09 21:22:23 +04:00
* sgptr = sg ;
2005-04-17 02:20:36 +04:00
/* Return the amount actually transferred */
return cnt ;
}
/* Store the contents of buffer into srb's transfer buffer and set the
2008-02-20 22:15:58 +03:00
* SCSI residue .
*/
2005-04-17 02:20:36 +04:00
void usb_stor_set_xfer_buf ( unsigned char * buffer ,
unsigned int buflen , struct scsi_cmnd * srb )
{
2007-05-11 14:33:09 +04:00
unsigned int offset = 0 ;
struct scatterlist * sg = NULL ;
2005-04-17 02:20:36 +04:00
2008-02-23 01:00:06 +03:00
buflen = min ( buflen , scsi_bufflen ( srb ) ) ;
2008-02-20 22:15:58 +03:00
buflen = usb_stor_access_xfer_buf ( buffer , buflen , srb , & sg , & offset ,
2005-04-17 02:20:36 +04:00
TO_XFER_BUF ) ;
2007-09-09 21:22:23 +04:00
if ( buflen < scsi_bufflen ( srb ) )
scsi_set_resid ( srb , scsi_bufflen ( srb ) - buflen ) ;
2005-04-17 02:20:36 +04:00
}