2017-11-03 13:28:30 +03:00
// SPDX-License-Identifier: GPL-2.0+
2008-03-19 21:40:52 +03:00
/*
* Support for emulating SAT ( ata pass through ) on devices based
* on the Cypress USB / ATA bridge supporting ATACB .
*
* Copyright ( c ) 2008 Matthieu Castet ( castet . matthieu @ free . fr )
*/
2009-02-12 22:48:04 +03:00
# include <linux/module.h>
2008-03-19 21:40:52 +03:00
# include <scsi/scsi.h>
# include <scsi/scsi_cmnd.h>
# include <scsi/scsi_eh.h>
# include <linux/ata.h>
# include "usb.h"
# include "protocol.h"
# include "scsiglue.h"
# include "debug.h"
2015-05-06 12:24:21 +03:00
# define DRV_NAME "ums-cypress"
2009-02-28 23:39:20 +03:00
MODULE_DESCRIPTION ( " SAT support for Cypress USB/ATA bridges with ATACB " ) ;
MODULE_AUTHOR ( " Matthieu Castet <castet.matthieu@free.fr> " ) ;
MODULE_LICENSE ( " GPL " ) ;
2019-09-06 13:32:35 +03:00
MODULE_IMPORT_NS ( USB_STORAGE ) ;
2009-02-12 22:48:04 +03:00
/*
* The table of devices
*/
# define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
vendorName , productName , useProtocol , useTransport , \
initFunction , flags ) \
{ USB_DEVICE_VER ( id_vendor , id_product , bcdDeviceMin , bcdDeviceMax ) , \
2012-08-29 00:37:13 +04:00
. driver_info = ( flags ) }
2009-02-12 22:48:04 +03:00
2011-11-15 11:53:29 +04:00
static struct usb_device_id cypress_usb_ids [ ] = {
2009-02-12 22:48:04 +03:00
# include "unusual_cypress.h"
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , cypress_usb_ids ) ;
# undef UNUSUAL_DEV
/*
* The flags table
*/
# define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \
vendor_name , product_name , use_protocol , use_transport , \
init_function , Flags ) \
{ \
. vendorName = vendor_name , \
. productName = product_name , \
. useProtocol = use_protocol , \
. useTransport = use_transport , \
. initFunction = init_function , \
}
static struct us_unusual_dev cypress_unusual_dev_list [ ] = {
# include "unusual_cypress.h"
{ } /* Terminating entry */
} ;
# undef UNUSUAL_DEV
2008-03-19 21:40:52 +03:00
/*
* ATACB is a protocol used on cypress usb < - > ata bridge to
* send raw ATA command over mass storage
* There is a ATACB2 protocol that support LBA48 on newer chip .
* More info that be found on cy7c68310_8 . pdf and cy7c68300c_8 . pdf
* datasheet from cypress . com .
*/
2009-02-12 22:48:04 +03:00
static void cypress_atacb_passthrough ( struct scsi_cmnd * srb , struct us_data * us )
2008-03-19 21:40:52 +03:00
{
unsigned char save_cmnd [ MAX_COMMAND_SIZE ] ;
if ( likely ( srb - > cmnd [ 0 ] ! = ATA_16 & & srb - > cmnd [ 0 ] ! = ATA_12 ) ) {
usb_stor_transparent_scsi_command ( srb , us ) ;
return ;
}
memcpy ( save_cmnd , srb - > cmnd , sizeof ( save_cmnd ) ) ;
2008-04-30 12:19:47 +04:00
memset ( srb - > cmnd , 0 , MAX_COMMAND_SIZE ) ;
2008-03-19 21:40:52 +03:00
/* check if we support the command */
if ( save_cmnd [ 1 ] > > 5 ) /* MULTIPLE_COUNT */
goto invalid_fld ;
/* check protocol */
2015-02-08 01:42:43 +03:00
switch ( ( save_cmnd [ 1 ] > > 1 ) & 0xf ) {
case 3 : /*no DATA */
case 4 : /* PIO in */
case 5 : /* PIO out */
break ;
default :
goto invalid_fld ;
2008-03-19 21:40:52 +03:00
}
/* first build the ATACB command */
srb - > cmd_len = 16 ;
2016-04-18 13:09:11 +03:00
srb - > cmnd [ 0 ] = 0x24 ; /*
* bVSCBSignature : vendor - specific command
* this value can change , but most ( all ? ) manufacturers
* keep the cypress default : 0x24
*/
2008-03-19 21:40:52 +03:00
srb - > cmnd [ 1 ] = 0x24 ; /* bVSCBSubCommand : 0x24 for ATACB */
2016-04-18 13:09:11 +03:00
srb - > cmnd [ 3 ] = 0xff - 1 ; /*
* features , sector count , lba low , lba med
* lba high , device , command are valid
*/
2008-03-19 21:40:52 +03:00
srb - > cmnd [ 4 ] = 1 ; /* TransferBlockCount : 512 */
if ( save_cmnd [ 0 ] = = ATA_16 ) {
srb - > cmnd [ 6 ] = save_cmnd [ 4 ] ; /* features */
srb - > cmnd [ 7 ] = save_cmnd [ 6 ] ; /* sector count */
srb - > cmnd [ 8 ] = save_cmnd [ 8 ] ; /* lba low */
srb - > cmnd [ 9 ] = save_cmnd [ 10 ] ; /* lba med */
srb - > cmnd [ 10 ] = save_cmnd [ 12 ] ; /* lba high */
srb - > cmnd [ 11 ] = save_cmnd [ 13 ] ; /* device */
srb - > cmnd [ 12 ] = save_cmnd [ 14 ] ; /* command */
if ( save_cmnd [ 1 ] & 0x01 ) { /* extended bit set for LBA48 */
/* this could be supported by atacb2 */
if ( save_cmnd [ 3 ] | | save_cmnd [ 5 ] | | save_cmnd [ 7 ] | | save_cmnd [ 9 ]
| | save_cmnd [ 11 ] )
goto invalid_fld ;
}
2015-02-08 01:42:43 +03:00
} else { /* ATA12 */
2008-03-19 21:40:52 +03:00
srb - > cmnd [ 6 ] = save_cmnd [ 3 ] ; /* features */
srb - > cmnd [ 7 ] = save_cmnd [ 4 ] ; /* sector count */
srb - > cmnd [ 8 ] = save_cmnd [ 5 ] ; /* lba low */
srb - > cmnd [ 9 ] = save_cmnd [ 6 ] ; /* lba med */
srb - > cmnd [ 10 ] = save_cmnd [ 7 ] ; /* lba high */
srb - > cmnd [ 11 ] = save_cmnd [ 8 ] ; /* device */
srb - > cmnd [ 12 ] = save_cmnd [ 9 ] ; /* command */
}
/* Filter SET_FEATURES - XFER MODE command */
if ( ( srb - > cmnd [ 12 ] = = ATA_CMD_SET_FEATURES )
& & ( srb - > cmnd [ 6 ] = = SETFEATURES_XFER ) )
goto invalid_fld ;
if ( srb - > cmnd [ 12 ] = = ATA_CMD_ID_ATA | | srb - > cmnd [ 12 ] = = ATA_CMD_ID_ATAPI )
srb - > cmnd [ 2 ] | = ( 1 < < 7 ) ; /* set IdentifyPacketDevice for these cmds */
usb_stor_transparent_scsi_command ( srb , us ) ;
2016-04-18 13:09:11 +03:00
/* if the device doesn't support ATACB */
2008-03-19 21:40:52 +03:00
if ( srb - > result = = SAM_STAT_CHECK_CONDITION & &
memcmp ( srb - > sense_buffer , usb_stor_sense_invalidCDB ,
sizeof ( usb_stor_sense_invalidCDB ) ) = = 0 ) {
2013-04-19 22:44:00 +04:00
usb_stor_dbg ( us , " cypress atacb not supported ??? \n " ) ;
2008-03-19 21:40:52 +03:00
goto end ;
}
2016-04-18 13:09:11 +03:00
/*
* if ck_cond flags is set , and there wasn ' t critical error ,
2008-03-19 21:40:52 +03:00
* build the special sense
*/
if ( ( srb - > result ! = ( DID_ERROR < < 16 ) & &
srb - > result ! = ( DID_ABORT < < 16 ) ) & &
save_cmnd [ 2 ] & 0x20 ) {
struct scsi_eh_save ses ;
unsigned char regs [ 8 ] ;
unsigned char * sb = srb - > sense_buffer ;
unsigned char * desc = sb + 8 ;
int tmp_result ;
2016-04-18 13:09:11 +03:00
/* build the command for reading the ATA registers */
2009-02-11 10:54:31 +03:00
scsi_eh_prep_cmnd ( srb , & ses , NULL , 0 , sizeof ( regs ) ) ;
2016-04-18 13:09:11 +03:00
/*
* we use the same command as before , but we set
2008-03-19 21:40:52 +03:00
* the read taskfile bit , for not executing atacb command ,
* but reading register selected in srb - > cmnd [ 4 ]
*/
2009-02-11 10:54:31 +03:00
srb - > cmd_len = 16 ;
2008-03-19 21:40:52 +03:00
srb - > cmnd [ 2 ] = 1 ;
usb_stor_transparent_scsi_command ( srb , us ) ;
2009-02-11 10:54:31 +03:00
memcpy ( regs , srb - > sense_buffer , sizeof ( regs ) ) ;
2008-03-19 21:40:52 +03:00
tmp_result = srb - > result ;
scsi_eh_restore_cmnd ( srb , & ses ) ;
/* we fail to get registers, report invalid command */
if ( tmp_result ! = SAM_STAT_GOOD )
goto invalid_fld ;
/* build the sense */
memset ( sb , 0 , SCSI_SENSE_BUFFERSIZE ) ;
/* set sk, asc for a good command */
sb [ 1 ] = RECOVERED_ERROR ;
sb [ 2 ] = 0 ; /* ATA PASS THROUGH INFORMATION AVAILABLE */
sb [ 3 ] = 0x1D ;
2016-04-18 13:09:11 +03:00
/*
* XXX we should generate sk , asc , ascq from status and error
2008-03-19 21:40:52 +03:00
* regs
2009-02-11 10:54:31 +03:00
* ( see 11.1 Error translation ATA device error to SCSI error
2016-04-18 13:09:11 +03:00
* map , and ata_to_sense_error from libata . )
2008-03-19 21:40:52 +03:00
*/
/* Sense data is current and format is descriptor. */
sb [ 0 ] = 0x72 ;
desc [ 0 ] = 0x09 ; /* ATA_RETURN_DESCRIPTOR */
/* set length of additional sense data */
sb [ 7 ] = 14 ;
desc [ 1 ] = 12 ;
/* Copy registers into sense buffer. */
desc [ 2 ] = 0x00 ;
desc [ 3 ] = regs [ 1 ] ; /* features */
desc [ 5 ] = regs [ 2 ] ; /* sector count */
desc [ 7 ] = regs [ 3 ] ; /* lba low */
desc [ 9 ] = regs [ 4 ] ; /* lba med */
desc [ 11 ] = regs [ 5 ] ; /* lba high */
desc [ 12 ] = regs [ 6 ] ; /* device */
desc [ 13 ] = regs [ 7 ] ; /* command */
2021-04-27 11:30:15 +03:00
srb - > result = SAM_STAT_CHECK_CONDITION ;
2008-03-19 21:40:52 +03:00
}
goto end ;
invalid_fld :
2021-04-27 11:30:15 +03:00
srb - > result = SAM_STAT_CHECK_CONDITION ;
2008-03-19 21:40:52 +03:00
memcpy ( srb - > sense_buffer ,
usb_stor_sense_invalidCDB ,
sizeof ( usb_stor_sense_invalidCDB ) ) ;
end :
memcpy ( srb - > cmnd , save_cmnd , sizeof ( save_cmnd ) ) ;
if ( srb - > cmnd [ 0 ] = = ATA_12 )
srb - > cmd_len = 12 ;
}
2009-02-12 22:48:04 +03:00
2015-05-06 12:24:21 +03:00
static struct scsi_host_template cypress_host_template ;
2009-02-12 22:48:04 +03:00
static int cypress_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
struct us_data * us ;
int result ;
2013-04-20 16:24:04 +04:00
struct usb_device * device ;
2009-02-12 22:48:04 +03:00
result = usb_stor_probe1 ( & us , intf , id ,
2015-05-06 12:24:21 +03:00
( id - cypress_usb_ids ) + cypress_unusual_dev_list ,
& cypress_host_template ) ;
2009-02-12 22:48:04 +03:00
if ( result )
return result ;
2016-04-18 13:09:11 +03:00
/*
* Among CY7C68300 chips , the A revision does not support Cypress ATACB
2013-04-20 16:24:04 +04:00
* Filter out this revision from EEPROM default descriptor values
*/
device = interface_to_usbdev ( intf ) ;
if ( device - > descriptor . iManufacturer ! = 0x38 | |
device - > descriptor . iProduct ! = 0x4e | |
device - > descriptor . iSerialNumber ! = 0x64 ) {
us - > protocol_name = " Transparent SCSI with Cypress ATACB " ;
us - > proto_handler = cypress_atacb_passthrough ;
} else {
us - > protocol_name = " Transparent SCSI " ;
us - > proto_handler = usb_stor_transparent_scsi_command ;
}
2009-02-12 22:48:04 +03:00
result = usb_stor_probe2 ( us ) ;
return result ;
}
static struct usb_driver cypress_driver = {
2015-05-06 12:24:21 +03:00
. name = DRV_NAME ,
2009-02-12 22:48:04 +03:00
. probe = cypress_probe ,
. disconnect = usb_stor_disconnect ,
. suspend = usb_stor_suspend ,
. resume = usb_stor_resume ,
. reset_resume = usb_stor_reset_resume ,
. pre_reset = usb_stor_pre_reset ,
. post_reset = usb_stor_post_reset ,
. id_table = cypress_usb_ids ,
. soft_unbind = 1 ,
2012-01-14 06:15:21 +04:00
. no_dynamic_id = 1 ,
2009-02-12 22:48:04 +03:00
} ;
2015-05-06 12:24:21 +03:00
module_usb_stor_driver ( cypress_driver , cypress_host_template , DRV_NAME ) ;