mass storage : emulation of sat scsi_pass_thru with ATACB
Subsystems (3)
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
Quoted 39 lines · matthieu castet
Hi,
I have got a cypress usb-ide bridge and I would like to tune or monitor my disk with tools like hdparm, hddtemp or smartctl.
My controller support a way to send raw ATA command to the disk with something call atacb (see
http://download.cypress.com.edgesuite.net/design_resources/datasheets/contents/cy7c68300c_8.pdf).First I start to add support for atacb in applications like smartctl. But there is some disadvantages :
- I need to patch all application
- A race is possible if there other accesses, because the emulation can be split in 2 atacb scsi transactions. One for sending the command, one for reading the register (ifck_condis set).So the idea to implement a SAT emulation for such device came to my mind.
First I want to ask, are there usb mass storage device that support SAT ? After some reading it seems impossible. Mass storage transparent scsi requires spc2 that only support fixed sense (starting with
0x70or0x71). But SAT need a sense with descriptor defined in spc3 (starting with0x72or0x73).So SAT is not compatible with the sense format of mass storage ?
For doing the emulation, I want to provide a special
proto_handler.But I don't know what the best way to send command in my emulation layer :
should I useusb_stor_invoke_transportor directly useus->transport. usingusb_stor_invoke_transportsave me lot's trouble of implementing error handling, but I can't control what I do.What should I do if I got an error after sending the first command and
ck_condis set ? Should I still try to read the register ?
Quoted 8 lines
Is it safe to try to read the register if the first command did autosense ? Or should I only send the command, and read the register and compute the sense key, asc and ascq from registers ?
For the moment, my "draft" code look something like [1].
Any comments ?
I attach a updated version of the driver.
I register the handler with an unsual entry. I don't know if it is the best way to do that (we need an entry for each device supporting atacb).
Another solution could have been to put the emulation in usb_stor_transparent_scsi_command, and if the device report invalidCDB don't try to use it again.
Any comments ?
Matthieu
drivers/usb/storage/protocol.c +175 -0
@@ -47,6 +47,8 @@#include <linux/highmem.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_eh.h> +#include <linux/ata.h> #include "usb.h" #include "protocol.h"
@@ -144,6 +146,179 @@usb_stor_invoke_transport (srb, us); } +static void emulate_pass_thru_with_atacb (struct scsi_cmnd *srb, +struct us_data *us) +{ +unsigned char save_cmnd[MAX_COMMAND_SIZE ]; +memcpy (save_cmnd, srb->cmnd,sizeof (save_cmnd)); +memset (srb->cmnd,0 ,sizeof (srb->cmnd)); + +/* check if we support the command */ +if (save_cmnd[1 ] >>5 )/* MULTIPLE_COUNT */ + goto invalid_fld; +/* check protocol */ +switch ((save_cmnd[1 ] >>1 ) &0xf ) { +case 3 : +case 4 : +case 5 : +break ; +default : + goto invalid_fld; + } + +/* first build the ATACB command */ + srb->cmd_len =16 ; + + srb->cmnd[0 ] =0x24 ;/* XXX this value can change */ + srb->cmnd[1 ] =0x24 ; + srb->cmnd[2 ] =0 ; + + srb->cmnd[3 ] =0xff -1 ; + srb->cmnd[4 ] =1 ; + +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 */ +if (save_cmnd[3 ] || save_cmnd[5 ] || save_cmnd[7 ] || save_cmnd[9 ] + || save_cmnd[11 ]) + goto invalid_fld; + } + } +else {/* ATA12 */ + 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; + +/* realy needed ? isd200 don't do that */ +if (srb->cmnd[12 ] ==ATA_CMD_ID_ATA || srb->cmnd[12 ] ==ATA_CMD_ID_ATAPI ) + srb->cmnd[2 ] |= (1 <<7 ); + + +usb_stor_invoke_transport (srb, us); + +/* if the device doesn't support ATACB +* abort and register usb_stor_transparent_scsi_command +* callback +*/ +if (srb->result ==SAM_STAT_CHECK_CONDITION && +memcmp (srb->sense_buffer, usb_stor_sense_invalidCDB, +sizeof (usb_stor_sense_invalidCDB)) ==0 ) { + us->proto_handler = usb_stor_transparent_scsi_command; + goto end; + } + +if ((srb->result != (DID_ERROR <<16 ) && + srb->result != (DID_ABORT <<16 )) && + save_cmnd[2 ] &0x20 ) {/* ck_cond */ +/* read regs and put them in sense */ +struct scsi_eh_save ses; +unsigned char sk, asc, ascq; +unsigned char regs[8 ]; +unsigned char *sb = srb->sense_buffer; +unsigned char *desc = sb +8 ; +int tmp_result; + +if (srb->result ==SAM_STAT_GOOD ) { +/* OK */ + sk =RECOVERED_ERROR ; + asc =0 ;/* ATA PASS THROUGH INFORMATION AVAILABLE */ + ascq =0x1D ; + } +else { +/* save sense info */ +/* XXX we get only 0, we should use regs status and error +*/ +#if 0 + sk = sb[2 ]; + asc = sb[12 ]; + ascq = sb[13 ]; +#else + sk =ILLEGAL_REQUEST ; + asc =0x24 ; + ascq =0 ; +#endif + } + + +/* read registers */ + srb->cmnd[2 ] =1 ; +scsi_eh_prep_cmnd (srb, &ses,NULL ,0 ,0 ); + srb->request_buffer = regs; + srb->request_bufflen =sizeof (regs); + srb->sc_data_direction =DMA_FROM_DEVICE ; + +usb_stor_invoke_transport (srb, us); + 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 ,sizeof (srb->sense_buffer)); +/* XXX we should generate sk, asc, ascq from status and error +* regs +* (see 11.1 Error translation � ATA device error to SCSI error map) +*/ + sb[1 ] = sk; + sb[2 ] = asc; + sb[3 ] = ascq; + sb[0 ] =0x72 ; + desc[0 ] =0x09 ;/* ATA_RETURN_DESCRIPTOR */ + + sb[7 ] =14 ; + desc[1 ] =12 ; + + desc[2 ] =0x00 ; + desc[3 ] = regs[1 ]; + desc[5 ] = regs[2 ]; + desc[7 ] = regs[3 ]; + desc[9 ] = regs[4 ]; + desc[11 ] = regs[5 ]; + desc[12 ] = regs[6 ]; + desc[13 ] = regs[7 ]; + + srb->result = (DRIVER_SENSE <<24 ) |SAM_STAT_CHECK_CONDITION ; + } + goto end; +invalid_fld: + srb->result = (DRIVER_SENSE <<24 ) |SAM_STAT_CHECK_CONDITION ; + +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 ; +} + +void usb_stor_transparent_scsi_command_atacb (struct scsi_cmnd *srb, +struct us_data *us) +{ +if (srb->cmnd[0 ] !=ATA_16 && srb->cmnd[0 ] !=ATA_12 ) +usb_stor_invoke_transport (srb, us); +else +emulate_pass_thru_with_atacb (srb, us); +} +/*********************************************************************** * Scatter-gather transfer buffer access routines ***********************************************************************/
drivers/usb/storage/initializers.c +11 -0
@@ -43,6 +43,7 @@#include "initializers.h" #include "debug.h" #include "transport.h" +#include "protocol.h" /* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target * mode */
@@ -104,3 +105,13 @@US_DEBUGP ("usb_control_msg performing result is %d\n" , result);return (result ?0 :-1 ); } + +/* This places register the atacb proto callback */ +int usb_atacb_init (struct us_data *us) +{ +/* XXX get atacb command from eeprom +* ATM we assume the default : 0x24 +*/ + us->proto_handler = usb_stor_transparent_scsi_command_atacb; +return 0 ; +}
drivers/usb/storage/initializers.h +3 -0
@@ -50,3 +50,6 @@/* This places the HUAWEI E220 devices in multi-port mode */ int usb_stor_huawei_e220_init (struct us_data *us); + +/* This places register the atacb proto callback */ +int usb_atacb_init (struct us_data *us);
drivers/usb/storage/protocol.h +2 -0
@@ -47,6 +47,8 @@extern void usb_stor_ufi_command (struct scsi_cmnd *,struct us_data *);extern void usb_stor_transparent_scsi_command (struct scsi_cmnd *,struct us_data *); +extern void usb_stor_transparent_scsi_command_atacb (struct scsi_cmnd *, +struct us_data *);/* struct scsi_cmnd transfer buffer access utilities */ enum xfer_buf_dir {TO_XFER_BUF ,FROM_XFER_BUF };
drivers/usb/storage/unusual_devs.h +6 -0
@@ -1588,6 +1588,12 @@ US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_CAPACITY_HEURISTICS), +UNUSUAL_DEV (0x04b4 ,0x6830 ,0x0000 ,0x9999 , +"Cypress" , +"Cypress AT2LP" , +US_SC_SCSI ,US_PR_BULK , usb_atacb_init, +US_FL_NEED_OVERRIDE ), +/* Control/Bulk transport for all SubClass values */ USUAL_DEV (US_SC_RBC ,US_PR_CB ,USB_US_TYPE_STOR ), USUAL_DEV(US_SC_8020, US_PR_CB, USB_US_TYPE_STOR),