2008-12-14 20:39:22 +03:00
/*
* Driver for Option High Speed Mobile Devices .
*
* ( c ) 2008 Dan Williams < dcbw @ redhat . com >
*
* Inspiration taken from sierra_ms . c by Kevin Lloyd < klloyd @ sierrawireless . com >
*
* 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 of the License , 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/usb.h>
# include "usb.h"
# include "transport.h"
# include "option_ms.h"
# include "debug.h"
# define ZCD_FORCE_MODEM 0x01
# define ZCD_ALLOW_MS 0x02
static unsigned int option_zero_cd = ZCD_FORCE_MODEM ;
module_param ( option_zero_cd , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( option_zero_cd , " ZeroCD mode (1=Force Modem (default), "
" 2=Allow CD-Rom " ) ;
# define RESPONSE_LEN 1024
2009-05-25 01:21:42 +04:00
static int option_rezero ( struct us_data * us )
2008-12-14 20:39:22 +03:00
{
const unsigned char rezero_msg [ ] = {
0x55 , 0x53 , 0x42 , 0x43 , 0x78 , 0x56 , 0x34 , 0x12 ,
0x01 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x06 , 0x01 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
char * buffer ;
int result ;
US_DEBUGP ( " Option MS: %s " , " DEVICE MODE SWITCH \n " ) ;
buffer = kzalloc ( RESPONSE_LEN , GFP_KERNEL ) ;
if ( buffer = = NULL )
return USB_STOR_TRANSPORT_ERROR ;
2009-05-25 01:21:42 +04:00
memcpy ( buffer , rezero_msg , sizeof ( rezero_msg ) ) ;
2008-12-14 20:39:22 +03:00
result = usb_stor_bulk_transfer_buf ( us ,
2009-05-25 01:21:42 +04:00
us - > send_bulk_pipe ,
buffer , sizeof ( rezero_msg ) , NULL ) ;
2008-12-14 20:39:22 +03:00
if ( result ! = USB_STOR_XFER_GOOD ) {
result = USB_STOR_XFER_ERROR ;
goto out ;
}
/* Some of the devices need to be asked for a response, but we don't
* care what that response is .
*/
2009-05-25 01:21:42 +04:00
usb_stor_bulk_transfer_buf ( us ,
us - > recv_bulk_pipe ,
2008-12-14 20:39:22 +03:00
buffer , RESPONSE_LEN , NULL ) ;
2009-05-25 01:21:42 +04:00
/* Read the CSW */
usb_stor_bulk_transfer_buf ( us ,
us - > recv_bulk_pipe ,
buffer , 13 , NULL ) ;
2008-12-14 20:39:22 +03:00
result = USB_STOR_XFER_GOOD ;
out :
kfree ( buffer ) ;
return result ;
}
2009-05-25 01:21:42 +04:00
static int option_inquiry ( struct us_data * us )
2008-12-14 20:39:22 +03:00
{
2009-05-25 01:21:42 +04:00
const unsigned char inquiry_msg [ ] = {
0x55 , 0x53 , 0x42 , 0x43 , 0x12 , 0x34 , 0x56 , 0x78 ,
0x24 , 0x00 , 0x00 , 0x00 , 0x80 , 0x00 , 0x06 , 0x12 ,
0x00 , 0x00 , 0x00 , 0x24 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
char * buffer ;
int result ;
2008-12-14 20:39:22 +03:00
2009-05-25 01:21:42 +04:00
US_DEBUGP ( " Option MS: %s " , " device inquiry for vendor name \n " ) ;
2008-12-14 20:39:22 +03:00
2009-05-25 01:21:42 +04:00
buffer = kzalloc ( 0x24 , GFP_KERNEL ) ;
if ( buffer = = NULL )
return USB_STOR_TRANSPORT_ERROR ;
memcpy ( buffer , inquiry_msg , sizeof ( inquiry_msg ) ) ;
result = usb_stor_bulk_transfer_buf ( us ,
us - > send_bulk_pipe ,
buffer , sizeof ( inquiry_msg ) , NULL ) ;
if ( result ! = USB_STOR_XFER_GOOD ) {
result = USB_STOR_XFER_ERROR ;
goto out ;
2008-12-14 20:39:22 +03:00
}
2009-05-25 01:21:42 +04:00
result = usb_stor_bulk_transfer_buf ( us ,
us - > recv_bulk_pipe ,
buffer , 0x24 , NULL ) ;
if ( result ! = USB_STOR_XFER_GOOD ) {
result = USB_STOR_XFER_ERROR ;
goto out ;
2008-12-14 20:39:22 +03:00
}
2009-05-25 01:21:42 +04:00
result = memcmp ( buffer + 8 , " Option " , 6 ) ;
/* Read the CSW */
usb_stor_bulk_transfer_buf ( us ,
us - > recv_bulk_pipe ,
buffer , 13 , NULL ) ;
out :
kfree ( buffer ) ;
return result ;
}
int option_ms_init ( struct us_data * us )
{
int result ;
US_DEBUGP ( " Option MS: option_ms_init called \n " ) ;
/* Additional test for vendor information via INQUIRY,
* because some vendor / product IDs are ambiguous
*/
result = option_inquiry ( us ) ;
if ( result ! = 0 ) {
US_DEBUGP ( " Option MS: vendor is not Option or not determinable, "
" no action taken \n " ) ;
2009-05-22 01:37:58 +04:00
return 0 ;
2009-05-25 01:21:42 +04:00
} else
US_DEBUGP ( " Option MS: this is a genuine Option device, "
" proceeding \n " ) ;
2008-12-14 20:39:22 +03:00
/* Force Modem mode */
if ( option_zero_cd = = ZCD_FORCE_MODEM ) {
US_DEBUGP ( " Option MS: %s " , " Forcing Modem Mode \n " ) ;
2009-05-25 01:21:42 +04:00
result = option_rezero ( us ) ;
2008-12-14 20:39:22 +03:00
if ( result ! = USB_STOR_XFER_GOOD )
US_DEBUGP ( " Option MS: Failed to switch to modem mode. \n " ) ;
return - EIO ;
} else if ( option_zero_cd = = ZCD_ALLOW_MS ) {
/* Allow Mass Storage mode (keep CD-Rom) */
US_DEBUGP ( " Option MS: %s " , " Allowing Mass Storage Mode if device "
" requests it \n " ) ;
}
2009-05-22 01:37:58 +04:00
return 0 ;
2008-12-14 20:39:22 +03:00
}