diff options
| -rw-r--r-- | drivers/usb/storage/Kconfig | 11 | ||||
| -rw-r--r-- | drivers/usb/storage/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/storage/cypress_atacb.c | 200 | ||||
| -rw-r--r-- | drivers/usb/storage/cypress_atacb.h | 25 | ||||
| -rw-r--r-- | drivers/usb/storage/scsiglue.c | 2 | ||||
| -rw-r--r-- | drivers/usb/storage/transport.c | 3 | ||||
| -rw-r--r-- | drivers/usb/storage/unusual_devs.h | 8 | ||||
| -rw-r--r-- | drivers/usb/storage/usb.c | 10 | ||||
| -rw-r--r-- | include/linux/usb_usual.h | 1 | 
9 files changed, 259 insertions, 2 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 05cfc8473bd..d3e5f889f68 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -145,6 +145,17 @@ config USB_STORAGE_KARMA  	  on the resulting scsi device node returns the Karma to normal  	  operation. +config USB_STORAGE_CYPRESS_ATACB +	bool "SAT emulation on Cypress USB/ATA Bridge with ATACB" +	depends on USB_STORAGE +	---help--- +	  Say Y here if you want to use SAT (ata pass through) on devices based +	  on the Cypress USB/ATA bridge supporting ATACB. This will allow you +	  to use tools to tune and monitor your drive (like hdparm or smartctl). + +	  If you say no here your device will still work with the standard usb +	  mass storage class. +  config USB_LIBUSUAL  	bool "The shared table of common (or usual) storage devices"  	depends on USB diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 023969b4385..4c596c766c5 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -21,6 +21,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT)	+= jumpshot.o  usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA)	+= alauda.o  usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH)	+= onetouch.o  usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA)	+= karma.o +usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o  usb-storage-objs :=	scsiglue.o protocol.o transport.o usb.o \  			initializers.o $(usb-storage-obj-y) diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c new file mode 100644 index 00000000000..d88824b3511 --- /dev/null +++ b/drivers/usb/storage/cypress_atacb.c @@ -0,0 +1,200 @@ +/* + * 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) + * + * 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 <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" + +/* + * 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. + */ +void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) +{ +	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)); +	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: /*no DATA */ +		case 4: /* PIO in */ +		case 5: /* PIO out */ +			break; +		default: +			goto invalid_fld; +	} + +	/* first build the ATACB command */ +	srb->cmd_len = 16; + +	srb->cmnd[0] = 0x24; /* bVSCBSignature : vendor-specific command +	                        this value can change, but most(all ?) manufacturers +							keep the cypress default : 0x24 */ +	srb->cmnd[1] = 0x24; /* bVSCBSubCommand : 0x24 for ATACB */ + +	srb->cmnd[3] = 0xff - 1; /* features, sector count, lba low, lba med +								lba high, device, command are valid */ +	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; +		} +	} +	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; + +	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); + +	/* if the device doesn't support ATACB +	 */ +	if (srb->result == SAM_STAT_CHECK_CONDITION && +			memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB, +				sizeof(usb_stor_sense_invalidCDB)) == 0) { +		US_DEBUGP("cypress atacb not supported ???\n"); +		goto end; +	} + +	/* if ck_cond flags is set, and there wasn't critical error, +	 * 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; + +		/* build the command for +		 * reading the ATA registers */ +		scsi_eh_prep_cmnd(srb, &ses, NULL, 0, 0); +		srb->sdb.length = sizeof(regs); +		sg_init_one(&ses.sense_sgl, regs, srb->sdb.length); +		srb->sdb.table.sgl = &ses.sense_sgl; +		srb->sc_data_direction = DMA_FROM_DEVICE; +		srb->sdb.table.nents = 1; +		/* we use the same command as before, but we set +		 * the read taskfile bit, for not executing atacb command, +		 * but reading register selected in srb->cmnd[4] +		 */ +		srb->cmnd[2] = 1; + +		usb_stor_transparent_scsi_command(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, 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; + +		/* XXX we should generate sk, asc, ascq from status and error +		 * regs +		 * (see 11.1 Error translation  ATA device error to SCSI error map) +		 * and ata_to_sense_error from libata. +		 */ + +		/* 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 */ + +		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; +} diff --git a/drivers/usb/storage/cypress_atacb.h b/drivers/usb/storage/cypress_atacb.h new file mode 100644 index 00000000000..fbada898d56 --- /dev/null +++ b/drivers/usb/storage/cypress_atacb.h @@ -0,0 +1,25 @@ +/* + * 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) + * + * 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. + */ + +#ifndef _CYPRESS_ATACB_H_ +#define _CYPRESS_ATACB_H_ +extern void cypress_atacb_passthrough(struct scsi_cmnd*, struct us_data*); +#endif diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 8c1e2954f3b..5405ba8cd9d 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -132,7 +132,7 @@ static int slave_configure(struct scsi_device *sdev)  		/* Disk-type devices use MODE SENSE(6) if the protocol  		 * (SubClass) is Transparent SCSI, otherwise they use  		 * MODE SENSE(10). */ -		if (us->subclass != US_SC_SCSI) +		if (us->subclass != US_SC_SCSI && us->subclass != US_SC_CYP_ATACB)  			sdev->use_10_for_ms = 1;  		/* Many disks only accept MODE SENSE transfer lengths of diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index bdd4334bed5..4628f03b13b 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -603,7 +603,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)  		scsi_eh_prep_cmnd(srb, &ses, NULL, 0, US_SENSE_SIZE);  		/* FIXME: we must do the protocol translation here */ -		if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI) +		if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI || +				us->subclass == US_SC_CYP_ATACB)  			srb->cmd_len = 6;  		else  			srb->cmd_len = 12; diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 91252075e6e..732bf52a775 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1719,6 +1719,14 @@ UNUSUAL_DEV(  0xed06, 0x4500, 0x0001, 0x0001,  		US_SC_DEVICE, US_PR_DEVICE, NULL,  		US_FL_CAPACITY_HEURISTICS), +#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB +UNUSUAL_DEV(  0x04b4, 0x6830, 0x0000, 0x9999, +		"Cypress", +		"Cypress AT2LP", +		US_SC_CYP_ATACB, US_PR_BULK, NULL, +		0), +#endif +  /* 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), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index ac6114eea0c..f59593de3b8 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -101,6 +101,9 @@  #ifdef CONFIG_USB_STORAGE_KARMA  #include "karma.h"  #endif +#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB +#include "cypress_atacb.h" +#endif  /* Some informational data */  MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); @@ -708,6 +711,13 @@ static int get_protocol(struct us_data *us)  		break;  #endif +#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB +	case US_SC_CYP_ATACB: +		us->protocol_name = "Transparent SCSI with Cypress ATACB"; +		us->proto_handler = cypress_atacb_passthrough; +		break; +#endif +  	default:  		return -EIO;  	} diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 0a40dfa44c9..d9a3bbe38e6 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -85,6 +85,7 @@ enum { US_DO_ALL_FLAGS };  #define US_SC_LOCKABLE	0x07		/* Password-protected */  #define US_SC_ISD200    0xf0		/* ISD200 ATA */ +#define US_SC_CYP_ATACB 0xf1		/* Cypress ATACB */  #define US_SC_DEVICE	0xff		/* Use device's value */  /* Protocols */  |