aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormatthieu castet <castet.matthieu@free.fr>2008-03-19 14:40:52 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-25 00:16:42 -0400
commitd277064e7e16d02e0078a6bc1820764ae00dea87 (patch)
tree18f78d06f8bc3d5a57a04ac46d4f75ca5757faad
parentdda43a0e03a33dd716fb34f812b1af614f74daff (diff)
USB: mass storage: emulation of sat scsi_pass_thru with ATACB
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). Atacb support can be added for each application, but there is some disadvantages : - all application need to be patched - 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 (if ck_cond is set). I have implemented the emulation in usb-storage with a special proto_handler, and an unsual entry. Signed-off-by: Matthieu CASTET <castet.matthieu@free.fr> Signed-off-by: Matthew Dharm <mdharm-usb@one-eyed-alien.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/usb/storage/Kconfig11
-rw-r--r--drivers/usb/storage/Makefile1
-rw-r--r--drivers/usb/storage/cypress_atacb.c200
-rw-r--r--drivers/usb/storage/cypress_atacb.h25
-rw-r--r--drivers/usb/storage/scsiglue.c2
-rw-r--r--drivers/usb/storage/transport.c3
-rw-r--r--drivers/usb/storage/unusual_devs.h8
-rw-r--r--drivers/usb/storage/usb.c10
-rw-r--r--include/linux/usb_usual.h1
9 files changed, 259 insertions, 2 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 05cfc8473bd2..d3e5f889f68d 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -145,6 +145,17 @@ config USB_STORAGE_KARMA
145 on the resulting scsi device node returns the Karma to normal 145 on the resulting scsi device node returns the Karma to normal
146 operation. 146 operation.
147 147
148config USB_STORAGE_CYPRESS_ATACB
149 bool "SAT emulation on Cypress USB/ATA Bridge with ATACB"
150 depends on USB_STORAGE
151 ---help---
152 Say Y here if you want to use SAT (ata pass through) on devices based
153 on the Cypress USB/ATA bridge supporting ATACB. This will allow you
154 to use tools to tune and monitor your drive (like hdparm or smartctl).
155
156 If you say no here your device will still work with the standard usb
157 mass storage class.
158
148config USB_LIBUSUAL 159config USB_LIBUSUAL
149 bool "The shared table of common (or usual) storage devices" 160 bool "The shared table of common (or usual) storage devices"
150 depends on USB 161 depends on USB
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 023969b4385b..4c596c766c53 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -21,6 +21,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
21usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o 21usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
22usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o 22usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
23usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o 23usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
24usb-storage-obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += cypress_atacb.o
24 25
25usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ 26usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
26 initializers.o $(usb-storage-obj-y) 27 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 000000000000..d88824b3511c
--- /dev/null
+++ b/drivers/usb/storage/cypress_atacb.c
@@ -0,0 +1,200 @@
1/*
2 * Support for emulating SAT (ata pass through) on devices based
3 * on the Cypress USB/ATA bridge supporting ATACB.
4 *
5 * Copyright (c) 2008 Matthieu Castet (castet.matthieu@free.fr)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#include <scsi/scsi.h>
23#include <scsi/scsi_cmnd.h>
24#include <scsi/scsi_eh.h>
25#include <linux/ata.h>
26
27#include "usb.h"
28#include "protocol.h"
29#include "scsiglue.h"
30#include "debug.h"
31
32/*
33 * ATACB is a protocol used on cypress usb<->ata bridge to
34 * send raw ATA command over mass storage
35 * There is a ATACB2 protocol that support LBA48 on newer chip.
36 * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf
37 * datasheet from cypress.com.
38 */
39void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us)
40{
41 unsigned char save_cmnd[MAX_COMMAND_SIZE];
42
43 if (likely(srb->cmnd[0] != ATA_16 && srb->cmnd[0] != ATA_12)) {
44 usb_stor_transparent_scsi_command(srb, us);
45 return;
46 }
47
48 memcpy(save_cmnd, srb->cmnd, sizeof(save_cmnd));
49 memset(srb->cmnd, 0, sizeof(srb->cmnd));
50
51 /* check if we support the command */
52 if (save_cmnd[1] >> 5) /* MULTIPLE_COUNT */
53 goto invalid_fld;
54 /* check protocol */
55 switch((save_cmnd[1] >> 1) & 0xf) {
56 case 3: /*no DATA */
57 case 4: /* PIO in */
58 case 5: /* PIO out */
59 break;
60 default:
61 goto invalid_fld;
62 }
63
64 /* first build the ATACB command */
65 srb->cmd_len = 16;
66
67 srb->cmnd[0] = 0x24; /* bVSCBSignature : vendor-specific command
68 this value can change, but most(all ?) manufacturers
69 keep the cypress default : 0x24 */
70 srb->cmnd[1] = 0x24; /* bVSCBSubCommand : 0x24 for ATACB */
71
72 srb->cmnd[3] = 0xff - 1; /* features, sector count, lba low, lba med
73 lba high, device, command are valid */
74 srb->cmnd[4] = 1; /* TransferBlockCount : 512 */
75
76 if (save_cmnd[0] == ATA_16) {
77 srb->cmnd[ 6] = save_cmnd[ 4]; /* features */
78 srb->cmnd[ 7] = save_cmnd[ 6]; /* sector count */
79 srb->cmnd[ 8] = save_cmnd[ 8]; /* lba low */
80 srb->cmnd[ 9] = save_cmnd[10]; /* lba med */
81 srb->cmnd[10] = save_cmnd[12]; /* lba high */
82 srb->cmnd[11] = save_cmnd[13]; /* device */
83 srb->cmnd[12] = save_cmnd[14]; /* command */
84
85 if (save_cmnd[1] & 0x01) {/* extended bit set for LBA48 */
86 /* this could be supported by atacb2 */
87 if (save_cmnd[3] || save_cmnd[5] || save_cmnd[7] || save_cmnd[9]
88 || save_cmnd[11])
89 goto invalid_fld;
90 }
91 }
92 else { /* ATA12 */
93 srb->cmnd[ 6] = save_cmnd[3]; /* features */
94 srb->cmnd[ 7] = save_cmnd[4]; /* sector count */
95 srb->cmnd[ 8] = save_cmnd[5]; /* lba low */
96 srb->cmnd[ 9] = save_cmnd[6]; /* lba med */
97 srb->cmnd[10] = save_cmnd[7]; /* lba high */
98 srb->cmnd[11] = save_cmnd[8]; /* device */
99 srb->cmnd[12] = save_cmnd[9]; /* command */
100
101 }
102 /* Filter SET_FEATURES - XFER MODE command */
103 if ((srb->cmnd[12] == ATA_CMD_SET_FEATURES)
104 && (srb->cmnd[6] == SETFEATURES_XFER))
105 goto invalid_fld;
106
107 if (srb->cmnd[12] == ATA_CMD_ID_ATA || srb->cmnd[12] == ATA_CMD_ID_ATAPI)
108 srb->cmnd[2] |= (1<<7); /* set IdentifyPacketDevice for these cmds */
109
110
111 usb_stor_transparent_scsi_command(srb, us);
112
113 /* if the device doesn't support ATACB
114 */
115 if (srb->result == SAM_STAT_CHECK_CONDITION &&
116 memcmp(srb->sense_buffer, usb_stor_sense_invalidCDB,
117 sizeof(usb_stor_sense_invalidCDB)) == 0) {
118 US_DEBUGP("cypress atacb not supported ???\n");
119 goto end;
120 }
121
122 /* if ck_cond flags is set, and there wasn't critical error,
123 * build the special sense
124 */
125 if ((srb->result != (DID_ERROR << 16) &&
126 srb->result != (DID_ABORT << 16)) &&
127 save_cmnd[2] & 0x20) {
128 struct scsi_eh_save ses;
129 unsigned char regs[8];
130 unsigned char *sb = srb->sense_buffer;
131 unsigned char *desc = sb + 8;
132 int tmp_result;
133
134 /* build the command for
135 * reading the ATA registers */
136 scsi_eh_prep_cmnd(srb, &ses, NULL, 0, 0);
137 srb->sdb.length = sizeof(regs);
138 sg_init_one(&ses.sense_sgl, regs, srb->sdb.length);
139 srb->sdb.table.sgl = &ses.sense_sgl;
140 srb->sc_data_direction = DMA_FROM_DEVICE;
141 srb->sdb.table.nents = 1;
142 /* we use the same command as before, but we set
143 * the read taskfile bit, for not executing atacb command,
144 * but reading register selected in srb->cmnd[4]
145 */
146 srb->cmnd[2] = 1;
147
148 usb_stor_transparent_scsi_command(srb, us);
149 tmp_result = srb->result;
150 scsi_eh_restore_cmnd(srb, &ses);
151 /* we fail to get registers, report invalid command */
152 if (tmp_result != SAM_STAT_GOOD)
153 goto invalid_fld;
154
155 /* build the sense */
156 memset(sb, 0, SCSI_SENSE_BUFFERSIZE);
157
158 /* set sk, asc for a good command */
159 sb[1] = RECOVERED_ERROR;
160 sb[2] = 0; /* ATA PASS THROUGH INFORMATION AVAILABLE */
161 sb[3] = 0x1D;
162
163 /* XXX we should generate sk, asc, ascq from status and error
164 * regs
165 * (see 11.1 Error translation ­ ATA device error to SCSI error map)
166 * and ata_to_sense_error from libata.
167 */
168
169 /* Sense data is current and format is descriptor. */
170 sb[0] = 0x72;
171 desc[0] = 0x09; /* ATA_RETURN_DESCRIPTOR */
172
173 /* set length of additional sense data */
174 sb[7] = 14;
175 desc[1] = 12;
176
177 /* Copy registers into sense buffer. */
178 desc[ 2] = 0x00;
179 desc[ 3] = regs[1]; /* features */
180 desc[ 5] = regs[2]; /* sector count */
181 desc[ 7] = regs[3]; /* lba low */
182 desc[ 9] = regs[4]; /* lba med */
183 desc[11] = regs[5]; /* lba high */
184 desc[12] = regs[6]; /* device */
185 desc[13] = regs[7]; /* command */
186
187 srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
188 }
189 goto end;
190invalid_fld:
191 srb->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
192
193 memcpy(srb->sense_buffer,
194 usb_stor_sense_invalidCDB,
195 sizeof(usb_stor_sense_invalidCDB));
196end:
197 memcpy(srb->cmnd, save_cmnd, sizeof(save_cmnd));
198 if (srb->cmnd[0] == ATA_12)
199 srb->cmd_len = 12;
200}
diff --git a/drivers/usb/storage/cypress_atacb.h b/drivers/usb/storage/cypress_atacb.h
new file mode 100644
index 000000000000..fbada898d56b
--- /dev/null
+++ b/drivers/usb/storage/cypress_atacb.h
@@ -0,0 +1,25 @@
1/*
2 * Support for emulating SAT (ata pass through) on devices based
3 * on the Cypress USB/ATA bridge supporting ATACB.
4 *
5 * Copyright (c) 2008 Matthieu Castet (castet.matthieu@free.fr)
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2, or (at your option) any
10 * later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22#ifndef _CYPRESS_ATACB_H_
23#define _CYPRESS_ATACB_H_
24extern void cypress_atacb_passthrough(struct scsi_cmnd*, struct us_data*);
25#endif
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 8c1e2954f3b9..5405ba8cd9d2 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)
132 /* Disk-type devices use MODE SENSE(6) if the protocol 132 /* Disk-type devices use MODE SENSE(6) if the protocol
133 * (SubClass) is Transparent SCSI, otherwise they use 133 * (SubClass) is Transparent SCSI, otherwise they use
134 * MODE SENSE(10). */ 134 * MODE SENSE(10). */
135 if (us->subclass != US_SC_SCSI) 135 if (us->subclass != US_SC_SCSI && us->subclass != US_SC_CYP_ATACB)
136 sdev->use_10_for_ms = 1; 136 sdev->use_10_for_ms = 1;
137 137
138 /* Many disks only accept MODE SENSE transfer lengths of 138 /* Many disks only accept MODE SENSE transfer lengths of
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index bdd4334bed5a..4628f03b13bf 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)
603 scsi_eh_prep_cmnd(srb, &ses, NULL, 0, US_SENSE_SIZE); 603 scsi_eh_prep_cmnd(srb, &ses, NULL, 0, US_SENSE_SIZE);
604 604
605 /* FIXME: we must do the protocol translation here */ 605 /* FIXME: we must do the protocol translation here */
606 if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI) 606 if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI ||
607 us->subclass == US_SC_CYP_ATACB)
607 srb->cmd_len = 6; 608 srb->cmd_len = 6;
608 else 609 else
609 srb->cmd_len = 12; 610 srb->cmd_len = 12;
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 91252075e6e1..732bf52a775e 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,
1719 US_SC_DEVICE, US_PR_DEVICE, NULL, 1719 US_SC_DEVICE, US_PR_DEVICE, NULL,
1720 US_FL_CAPACITY_HEURISTICS), 1720 US_FL_CAPACITY_HEURISTICS),
1721 1721
1722#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
1723UNUSUAL_DEV( 0x04b4, 0x6830, 0x0000, 0x9999,
1724 "Cypress",
1725 "Cypress AT2LP",
1726 US_SC_CYP_ATACB, US_PR_BULK, NULL,
1727 0),
1728#endif
1729
1722/* Control/Bulk transport for all SubClass values */ 1730/* Control/Bulk transport for all SubClass values */
1723USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR), 1731USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR),
1724USUAL_DEV(US_SC_8020, US_PR_CB, USB_US_TYPE_STOR), 1732USUAL_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 ac6114eea0c3..f59593de3b8f 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -101,6 +101,9 @@
101#ifdef CONFIG_USB_STORAGE_KARMA 101#ifdef CONFIG_USB_STORAGE_KARMA
102#include "karma.h" 102#include "karma.h"
103#endif 103#endif
104#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
105#include "cypress_atacb.h"
106#endif
104 107
105/* Some informational data */ 108/* Some informational data */
106MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); 109MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -708,6 +711,13 @@ static int get_protocol(struct us_data *us)
708 break; 711 break;
709#endif 712#endif
710 713
714#ifdef CONFIG_USB_STORAGE_CYPRESS_ATACB
715 case US_SC_CYP_ATACB:
716 us->protocol_name = "Transparent SCSI with Cypress ATACB";
717 us->proto_handler = cypress_atacb_passthrough;
718 break;
719#endif
720
711 default: 721 default:
712 return -EIO; 722 return -EIO;
713 } 723 }
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index 0a40dfa44c9f..d9a3bbe38e6b 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -85,6 +85,7 @@ enum { US_DO_ALL_FLAGS };
85#define US_SC_LOCKABLE 0x07 /* Password-protected */ 85#define US_SC_LOCKABLE 0x07 /* Password-protected */
86 86
87#define US_SC_ISD200 0xf0 /* ISD200 ATA */ 87#define US_SC_ISD200 0xf0 /* ISD200 ATA */
88#define US_SC_CYP_ATACB 0xf1 /* Cypress ATACB */
88#define US_SC_DEVICE 0xff /* Use device's value */ 89#define US_SC_DEVICE 0xff /* Use device's value */
89 90
90/* Protocols */ 91/* Protocols */