diff options
Diffstat (limited to 'drivers/usb/storage/cypress_atacb.c')
-rw-r--r-- | drivers/usb/storage/cypress_atacb.c | 106 |
1 files changed, 97 insertions, 9 deletions
diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 898e67d30e5..c8447182118 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * 675 Mass Ave, Cambridge, MA 02139, USA. | 19 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/module.h> | ||
22 | #include <scsi/scsi.h> | 23 | #include <scsi/scsi.h> |
23 | #include <scsi/scsi_cmnd.h> | 24 | #include <scsi/scsi_cmnd.h> |
24 | #include <scsi/scsi_eh.h> | 25 | #include <scsi/scsi_eh.h> |
@@ -29,6 +30,49 @@ | |||
29 | #include "scsiglue.h" | 30 | #include "scsiglue.h" |
30 | #include "debug.h" | 31 | #include "debug.h" |
31 | 32 | ||
33 | MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); | ||
34 | MODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | /* | ||
38 | * The table of devices | ||
39 | */ | ||
40 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | ||
41 | vendorName, productName, useProtocol, useTransport, \ | ||
42 | initFunction, flags) \ | ||
43 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ | ||
44 | .driver_info = (flags)|(USB_US_TYPE_STOR<<24) } | ||
45 | |||
46 | struct usb_device_id cypress_usb_ids[] = { | ||
47 | # include "unusual_cypress.h" | ||
48 | { } /* Terminating entry */ | ||
49 | }; | ||
50 | MODULE_DEVICE_TABLE(usb, cypress_usb_ids); | ||
51 | |||
52 | #undef UNUSUAL_DEV | ||
53 | |||
54 | /* | ||
55 | * The flags table | ||
56 | */ | ||
57 | #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ | ||
58 | vendor_name, product_name, use_protocol, use_transport, \ | ||
59 | init_function, Flags) \ | ||
60 | { \ | ||
61 | .vendorName = vendor_name, \ | ||
62 | .productName = product_name, \ | ||
63 | .useProtocol = use_protocol, \ | ||
64 | .useTransport = use_transport, \ | ||
65 | .initFunction = init_function, \ | ||
66 | } | ||
67 | |||
68 | static struct us_unusual_dev cypress_unusual_dev_list[] = { | ||
69 | # include "unusual_cypress.h" | ||
70 | { } /* Terminating entry */ | ||
71 | }; | ||
72 | |||
73 | #undef UNUSUAL_DEV | ||
74 | |||
75 | |||
32 | /* | 76 | /* |
33 | * ATACB is a protocol used on cypress usb<->ata bridge to | 77 | * ATACB is a protocol used on cypress usb<->ata bridge to |
34 | * send raw ATA command over mass storage | 78 | * send raw ATA command over mass storage |
@@ -36,7 +80,7 @@ | |||
36 | * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf | 80 | * More info that be found on cy7c68310_8.pdf and cy7c68300c_8.pdf |
37 | * datasheet from cypress.com. | 81 | * datasheet from cypress.com. |
38 | */ | 82 | */ |
39 | void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) | 83 | static void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) |
40 | { | 84 | { |
41 | unsigned char save_cmnd[MAX_COMMAND_SIZE]; | 85 | unsigned char save_cmnd[MAX_COMMAND_SIZE]; |
42 | 86 | ||
@@ -133,19 +177,18 @@ void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) | |||
133 | 177 | ||
134 | /* build the command for | 178 | /* build the command for |
135 | * reading the ATA registers */ | 179 | * reading the ATA registers */ |
136 | scsi_eh_prep_cmnd(srb, &ses, NULL, 0, 0); | 180 | scsi_eh_prep_cmnd(srb, &ses, NULL, 0, sizeof(regs)); |
137 | srb->sdb.length = sizeof(regs); | 181 | |
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 | 182 | /* we use the same command as before, but we set |
143 | * the read taskfile bit, for not executing atacb command, | 183 | * the read taskfile bit, for not executing atacb command, |
144 | * but reading register selected in srb->cmnd[4] | 184 | * but reading register selected in srb->cmnd[4] |
145 | */ | 185 | */ |
186 | srb->cmd_len = 16; | ||
187 | srb->cmnd = ses.cmnd; | ||
146 | srb->cmnd[2] = 1; | 188 | srb->cmnd[2] = 1; |
147 | 189 | ||
148 | usb_stor_transparent_scsi_command(srb, us); | 190 | usb_stor_transparent_scsi_command(srb, us); |
191 | memcpy(regs, srb->sense_buffer, sizeof(regs)); | ||
149 | tmp_result = srb->result; | 192 | tmp_result = srb->result; |
150 | scsi_eh_restore_cmnd(srb, &ses); | 193 | scsi_eh_restore_cmnd(srb, &ses); |
151 | /* we fail to get registers, report invalid command */ | 194 | /* we fail to get registers, report invalid command */ |
@@ -162,8 +205,8 @@ void cypress_atacb_passthrough(struct scsi_cmnd *srb, struct us_data *us) | |||
162 | 205 | ||
163 | /* XXX we should generate sk, asc, ascq from status and error | 206 | /* XXX we should generate sk, asc, ascq from status and error |
164 | * regs | 207 | * regs |
165 | * (see 11.1 Error translation ATA device error to SCSI error map) | 208 | * (see 11.1 Error translation ATA device error to SCSI error |
166 | * and ata_to_sense_error from libata. | 209 | * map, and ata_to_sense_error from libata.) |
167 | */ | 210 | */ |
168 | 211 | ||
169 | /* Sense data is current and format is descriptor. */ | 212 | /* Sense data is current and format is descriptor. */ |
@@ -198,3 +241,48 @@ end: | |||
198 | if (srb->cmnd[0] == ATA_12) | 241 | if (srb->cmnd[0] == ATA_12) |
199 | srb->cmd_len = 12; | 242 | srb->cmd_len = 12; |
200 | } | 243 | } |
244 | |||
245 | |||
246 | static int cypress_probe(struct usb_interface *intf, | ||
247 | const struct usb_device_id *id) | ||
248 | { | ||
249 | struct us_data *us; | ||
250 | int result; | ||
251 | |||
252 | result = usb_stor_probe1(&us, intf, id, | ||
253 | (id - cypress_usb_ids) + cypress_unusual_dev_list); | ||
254 | if (result) | ||
255 | return result; | ||
256 | |||
257 | us->protocol_name = "Transparent SCSI with Cypress ATACB"; | ||
258 | us->proto_handler = cypress_atacb_passthrough; | ||
259 | |||
260 | result = usb_stor_probe2(us); | ||
261 | return result; | ||
262 | } | ||
263 | |||
264 | static struct usb_driver cypress_driver = { | ||
265 | .name = "ums-cypress", | ||
266 | .probe = cypress_probe, | ||
267 | .disconnect = usb_stor_disconnect, | ||
268 | .suspend = usb_stor_suspend, | ||
269 | .resume = usb_stor_resume, | ||
270 | .reset_resume = usb_stor_reset_resume, | ||
271 | .pre_reset = usb_stor_pre_reset, | ||
272 | .post_reset = usb_stor_post_reset, | ||
273 | .id_table = cypress_usb_ids, | ||
274 | .soft_unbind = 1, | ||
275 | }; | ||
276 | |||
277 | static int __init cypress_init(void) | ||
278 | { | ||
279 | return usb_register(&cypress_driver); | ||
280 | } | ||
281 | |||
282 | static void __exit cypress_exit(void) | ||
283 | { | ||
284 | usb_deregister(&cypress_driver); | ||
285 | } | ||
286 | |||
287 | module_init(cypress_init); | ||
288 | module_exit(cypress_exit); | ||