Commit | Line | Data |
---|---|---|
f0183a33 FB |
1 | /* |
2 | * Driver for Freecom USB/IDE adaptor | |
1da177e4 LT |
3 | * |
4 | * Freecom v0.1: | |
5 | * | |
6 | * First release | |
7 | * | |
8 | * Current development and maintenance by: | |
9 | * (C) 2000 David Brown <usb-storage@davidb.org> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the | |
13 | * Free Software Foundation; either version 2, or (at your option) any | |
14 | * later version. | |
15 | * | |
16 | * This program is distributed in the hope that it will be useful, but | |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | * General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along | |
22 | * with this program; if not, write to the Free Software Foundation, Inc., | |
23 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
24 | * | |
25 | * This driver was developed with information provided in FREECOM's USB | |
26 | * Programmers Reference Guide. For further information contact Freecom | |
27 | * (http://www.freecom.de/) | |
28 | */ | |
29 | ||
0d62939f | 30 | #include <linux/module.h> |
1da177e4 LT |
31 | #include <scsi/scsi.h> |
32 | #include <scsi/scsi_cmnd.h> | |
33 | ||
34 | #include "usb.h" | |
35 | #include "transport.h" | |
36 | #include "protocol.h" | |
37 | #include "debug.h" | |
aa519be3 AM |
38 | #include "scsiglue.h" |
39 | ||
40 | #define DRV_NAME "ums-freecom" | |
1da177e4 | 41 | |
4246b06a MG |
42 | MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); |
43 | MODULE_AUTHOR("David Brown <usb-storage@davidb.org>"); | |
44 | MODULE_LICENSE("GPL"); | |
45 | ||
1da177e4 | 46 | #ifdef CONFIG_USB_STORAGE_DEBUG |
191648d0 | 47 | static void pdump(struct us_data *us, void *ibuffer, int length); |
1da177e4 LT |
48 | #endif |
49 | ||
50 | /* Bits of HD_STATUS */ | |
51 | #define ERR_STAT 0x01 | |
52 | #define DRQ_STAT 0x08 | |
53 | ||
54 | /* All of the outgoing packets are 64 bytes long. */ | |
55 | struct freecom_cb_wrap { | |
56 | u8 Type; /* Command type. */ | |
57 | u8 Timeout; /* Timeout in seconds. */ | |
58 | u8 Atapi[12]; /* An ATAPI packet. */ | |
59 | u8 Filler[50]; /* Padding Data. */ | |
60 | }; | |
61 | ||
62 | struct freecom_xfer_wrap { | |
63 | u8 Type; /* Command type. */ | |
64 | u8 Timeout; /* Timeout in seconds. */ | |
65 | __le32 Count; /* Number of bytes to transfer. */ | |
66 | u8 Pad[58]; | |
67 | } __attribute__ ((packed)); | |
68 | ||
69 | struct freecom_ide_out { | |
70 | u8 Type; /* Type + IDE register. */ | |
71 | u8 Pad; | |
72 | __le16 Value; /* Value to write. */ | |
73 | u8 Pad2[60]; | |
74 | }; | |
75 | ||
76 | struct freecom_ide_in { | |
77 | u8 Type; /* Type | IDE register. */ | |
78 | u8 Pad[63]; | |
79 | }; | |
80 | ||
81 | struct freecom_status { | |
82 | u8 Status; | |
83 | u8 Reason; | |
84 | __le16 Count; | |
85 | u8 Pad[60]; | |
86 | }; | |
87 | ||
f0183a33 FB |
88 | /* |
89 | * Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide | |
90 | * register. | |
91 | */ | |
1da177e4 LT |
92 | #define FCM_INT_STATUS 0x02 /* INDEX_STAT */ |
93 | #define FCM_STATUS_BUSY 0x80 | |
94 | ||
f0183a33 FB |
95 | /* |
96 | * These are the packet types. The low bit indicates that this command | |
97 | * should wait for an interrupt. | |
98 | */ | |
1da177e4 LT |
99 | #define FCM_PACKET_ATAPI 0x21 |
100 | #define FCM_PACKET_STATUS 0x20 | |
101 | ||
f0183a33 FB |
102 | /* |
103 | * Receive data from the IDE interface. The ATAPI packet has already | |
104 | * waited, so the data should be immediately available. | |
105 | */ | |
1da177e4 LT |
106 | #define FCM_PACKET_INPUT 0x81 |
107 | ||
108 | /* Send data to the IDE interface. */ | |
109 | #define FCM_PACKET_OUTPUT 0x01 | |
110 | ||
f0183a33 FB |
111 | /* |
112 | * Write a value to an ide register. Or the ide register to write after | |
113 | * munging the address a bit. | |
114 | */ | |
1da177e4 LT |
115 | #define FCM_PACKET_IDE_WRITE 0x40 |
116 | #define FCM_PACKET_IDE_READ 0xC0 | |
117 | ||
118 | /* All packets (except for status) are 64 bytes long. */ | |
119 | #define FCM_PACKET_LENGTH 64 | |
120 | #define FCM_STATUS_PACKET_LENGTH 4 | |
121 | ||
0d62939f AS |
122 | static int init_freecom(struct us_data *us); |
123 | ||
124 | ||
125 | /* | |
126 | * The table of devices | |
127 | */ | |
128 | #define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \ | |
129 | vendorName, productName, useProtocol, useTransport, \ | |
130 | initFunction, flags) \ | |
131 | { USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \ | |
f61870ee | 132 | .driver_info = (flags) } |
0d62939f | 133 | |
2053f075 | 134 | static struct usb_device_id freecom_usb_ids[] = { |
0d62939f AS |
135 | # include "unusual_freecom.h" |
136 | { } /* Terminating entry */ | |
137 | }; | |
138 | MODULE_DEVICE_TABLE(usb, freecom_usb_ids); | |
139 | ||
140 | #undef UNUSUAL_DEV | |
141 | ||
142 | /* | |
143 | * The flags table | |
144 | */ | |
145 | #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ | |
146 | vendor_name, product_name, use_protocol, use_transport, \ | |
147 | init_function, Flags) \ | |
148 | { \ | |
149 | .vendorName = vendor_name, \ | |
150 | .productName = product_name, \ | |
151 | .useProtocol = use_protocol, \ | |
152 | .useTransport = use_transport, \ | |
153 | .initFunction = init_function, \ | |
154 | } | |
155 | ||
156 | static struct us_unusual_dev freecom_unusual_dev_list[] = { | |
157 | # include "unusual_freecom.h" | |
158 | { } /* Terminating entry */ | |
159 | }; | |
160 | ||
161 | #undef UNUSUAL_DEV | |
162 | ||
1da177e4 LT |
163 | static int |
164 | freecom_readdata (struct scsi_cmnd *srb, struct us_data *us, | |
165 | unsigned int ipipe, unsigned int opipe, int count) | |
166 | { | |
167 | struct freecom_xfer_wrap *fxfr = | |
168 | (struct freecom_xfer_wrap *) us->iobuf; | |
169 | int result; | |
170 | ||
171 | fxfr->Type = FCM_PACKET_INPUT | 0x00; | |
172 | fxfr->Timeout = 0; /* Short timeout for debugging. */ | |
173 | fxfr->Count = cpu_to_le32 (count); | |
174 | memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); | |
175 | ||
191648d0 | 176 | usb_stor_dbg(us, "Read data Freecom! (c=%d)\n", count); |
1da177e4 LT |
177 | |
178 | /* Issue the transfer command. */ | |
179 | result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, | |
180 | FCM_PACKET_LENGTH, NULL); | |
181 | if (result != USB_STOR_XFER_GOOD) { | |
191648d0 | 182 | usb_stor_dbg(us, "Freecom readdata transport error\n"); |
1da177e4 LT |
183 | return USB_STOR_TRANSPORT_ERROR; |
184 | } | |
185 | ||
186 | /* Now transfer all of our blocks. */ | |
191648d0 | 187 | usb_stor_dbg(us, "Start of read\n"); |
41c2497b | 188 | result = usb_stor_bulk_srb(us, ipipe, srb); |
191648d0 | 189 | usb_stor_dbg(us, "freecom_readdata done!\n"); |
1da177e4 LT |
190 | |
191 | if (result > USB_STOR_XFER_SHORT) | |
192 | return USB_STOR_TRANSPORT_ERROR; | |
193 | return USB_STOR_TRANSPORT_GOOD; | |
194 | } | |
195 | ||
196 | static int | |
197 | freecom_writedata (struct scsi_cmnd *srb, struct us_data *us, | |
198 | int unsigned ipipe, unsigned int opipe, int count) | |
199 | { | |
200 | struct freecom_xfer_wrap *fxfr = | |
201 | (struct freecom_xfer_wrap *) us->iobuf; | |
202 | int result; | |
203 | ||
204 | fxfr->Type = FCM_PACKET_OUTPUT | 0x00; | |
205 | fxfr->Timeout = 0; /* Short timeout for debugging. */ | |
206 | fxfr->Count = cpu_to_le32 (count); | |
207 | memset (fxfr->Pad, 0, sizeof (fxfr->Pad)); | |
208 | ||
191648d0 | 209 | usb_stor_dbg(us, "Write data Freecom! (c=%d)\n", count); |
1da177e4 LT |
210 | |
211 | /* Issue the transfer command. */ | |
212 | result = usb_stor_bulk_transfer_buf (us, opipe, fxfr, | |
213 | FCM_PACKET_LENGTH, NULL); | |
214 | if (result != USB_STOR_XFER_GOOD) { | |
191648d0 | 215 | usb_stor_dbg(us, "Freecom writedata transport error\n"); |
1da177e4 LT |
216 | return USB_STOR_TRANSPORT_ERROR; |
217 | } | |
218 | ||
219 | /* Now transfer all of our blocks. */ | |
191648d0 | 220 | usb_stor_dbg(us, "Start of write\n"); |
41c2497b | 221 | result = usb_stor_bulk_srb(us, opipe, srb); |
1da177e4 | 222 | |
191648d0 | 223 | usb_stor_dbg(us, "freecom_writedata done!\n"); |
1da177e4 LT |
224 | if (result > USB_STOR_XFER_SHORT) |
225 | return USB_STOR_TRANSPORT_ERROR; | |
226 | return USB_STOR_TRANSPORT_GOOD; | |
227 | } | |
228 | ||
229 | /* | |
230 | * Transport for the Freecom USB/IDE adaptor. | |
231 | * | |
232 | */ | |
0d62939f | 233 | static int freecom_transport(struct scsi_cmnd *srb, struct us_data *us) |
1da177e4 LT |
234 | { |
235 | struct freecom_cb_wrap *fcb; | |
236 | struct freecom_status *fst; | |
237 | unsigned int ipipe, opipe; /* We need both pipes. */ | |
238 | int result; | |
239 | unsigned int partial; | |
240 | int length; | |
241 | ||
242 | fcb = (struct freecom_cb_wrap *) us->iobuf; | |
243 | fst = (struct freecom_status *) us->iobuf; | |
244 | ||
191648d0 | 245 | usb_stor_dbg(us, "Freecom TRANSPORT STARTED\n"); |
1da177e4 LT |
246 | |
247 | /* Get handles for both transports. */ | |
248 | opipe = us->send_bulk_pipe; | |
249 | ipipe = us->recv_bulk_pipe; | |
250 | ||
251 | /* The ATAPI Command always goes out first. */ | |
252 | fcb->Type = FCM_PACKET_ATAPI | 0x00; | |
253 | fcb->Timeout = 0; | |
254 | memcpy (fcb->Atapi, srb->cmnd, 12); | |
255 | memset (fcb->Filler, 0, sizeof (fcb->Filler)); | |
256 | ||
191648d0 | 257 | US_DEBUG(pdump(us, srb->cmnd, 12)); |
1da177e4 LT |
258 | |
259 | /* Send it out. */ | |
260 | result = usb_stor_bulk_transfer_buf (us, opipe, fcb, | |
261 | FCM_PACKET_LENGTH, NULL); | |
262 | ||
f0183a33 FB |
263 | /* |
264 | * The Freecom device will only fail if there is something wrong in | |
1da177e4 | 265 | * USB land. It returns the status in its own registers, which |
f0183a33 FB |
266 | * come back in the bulk pipe. |
267 | */ | |
1da177e4 | 268 | if (result != USB_STOR_XFER_GOOD) { |
191648d0 | 269 | usb_stor_dbg(us, "freecom transport error\n"); |
1da177e4 LT |
270 | return USB_STOR_TRANSPORT_ERROR; |
271 | } | |
272 | ||
f0183a33 FB |
273 | /* |
274 | * There are times we can optimize out this status read, but it | |
275 | * doesn't hurt us to always do it now. | |
276 | */ | |
1da177e4 LT |
277 | result = usb_stor_bulk_transfer_buf (us, ipipe, fst, |
278 | FCM_STATUS_PACKET_LENGTH, &partial); | |
191648d0 | 279 | usb_stor_dbg(us, "foo Status result %d %u\n", result, partial); |
1da177e4 LT |
280 | if (result != USB_STOR_XFER_GOOD) |
281 | return USB_STOR_TRANSPORT_ERROR; | |
282 | ||
191648d0 | 283 | US_DEBUG(pdump(us, (void *)fst, partial)); |
1da177e4 | 284 | |
f0183a33 FB |
285 | /* |
286 | * The firmware will time-out commands after 20 seconds. Some commands | |
1da177e4 LT |
287 | * can legitimately take longer than this, so we use a different |
288 | * command that only waits for the interrupt and then sends status, | |
7c7e2d00 | 289 | * without having to send a new ATAPI command to the device. |
1da177e4 LT |
290 | * |
291 | * NOTE: There is some indication that a data transfer after a timeout | |
292 | * may not work, but that is a condition that should never happen. | |
293 | */ | |
294 | while (fst->Status & FCM_STATUS_BUSY) { | |
191648d0 JP |
295 | usb_stor_dbg(us, "20 second USB/ATAPI bridge TIMEOUT occurred!\n"); |
296 | usb_stor_dbg(us, "fst->Status is %x\n", fst->Status); | |
1da177e4 LT |
297 | |
298 | /* Get the status again */ | |
299 | fcb->Type = FCM_PACKET_STATUS; | |
300 | fcb->Timeout = 0; | |
301 | memset (fcb->Atapi, 0, sizeof(fcb->Atapi)); | |
302 | memset (fcb->Filler, 0, sizeof (fcb->Filler)); | |
303 | ||
304 | /* Send it out. */ | |
305 | result = usb_stor_bulk_transfer_buf (us, opipe, fcb, | |
306 | FCM_PACKET_LENGTH, NULL); | |
307 | ||
f0183a33 FB |
308 | /* |
309 | * The Freecom device will only fail if there is something | |
1da177e4 LT |
310 | * wrong in USB land. It returns the status in its own |
311 | * registers, which come back in the bulk pipe. | |
312 | */ | |
313 | if (result != USB_STOR_XFER_GOOD) { | |
191648d0 | 314 | usb_stor_dbg(us, "freecom transport error\n"); |
1da177e4 LT |
315 | return USB_STOR_TRANSPORT_ERROR; |
316 | } | |
317 | ||
318 | /* get the data */ | |
319 | result = usb_stor_bulk_transfer_buf (us, ipipe, fst, | |
320 | FCM_STATUS_PACKET_LENGTH, &partial); | |
321 | ||
191648d0 | 322 | usb_stor_dbg(us, "bar Status result %d %u\n", result, partial); |
1da177e4 LT |
323 | if (result != USB_STOR_XFER_GOOD) |
324 | return USB_STOR_TRANSPORT_ERROR; | |
325 | ||
191648d0 | 326 | US_DEBUG(pdump(us, (void *)fst, partial)); |
1da177e4 LT |
327 | } |
328 | ||
329 | if (partial != 4) | |
330 | return USB_STOR_TRANSPORT_ERROR; | |
331 | if ((fst->Status & 1) != 0) { | |
191648d0 | 332 | usb_stor_dbg(us, "operation failed\n"); |
1da177e4 LT |
333 | return USB_STOR_TRANSPORT_FAILED; |
334 | } | |
335 | ||
f0183a33 FB |
336 | /* |
337 | * The device might not have as much data available as we | |
1da177e4 | 338 | * requested. If you ask for more than the device has, this reads |
f0183a33 FB |
339 | * and such will hang. |
340 | */ | |
191648d0 JP |
341 | usb_stor_dbg(us, "Device indicates that it has %d bytes available\n", |
342 | le16_to_cpu(fst->Count)); | |
343 | usb_stor_dbg(us, "SCSI requested %d\n", scsi_bufflen(srb)); | |
1da177e4 LT |
344 | |
345 | /* Find the length we desire to read. */ | |
346 | switch (srb->cmnd[0]) { | |
7c7e2d00 ME |
347 | case INQUIRY: |
348 | case REQUEST_SENSE: /* 16 or 18 bytes? spec says 18, lots of devices only have 16 */ | |
349 | case MODE_SENSE: | |
350 | case MODE_SENSE_10: | |
351 | length = le16_to_cpu(fst->Count); | |
352 | break; | |
353 | default: | |
354 | length = scsi_bufflen(srb); | |
1da177e4 LT |
355 | } |
356 | ||
357 | /* verify that this amount is legal */ | |
41c2497b BH |
358 | if (length > scsi_bufflen(srb)) { |
359 | length = scsi_bufflen(srb); | |
191648d0 JP |
360 | usb_stor_dbg(us, "Truncating request to match buffer length: %d\n", |
361 | length); | |
1da177e4 LT |
362 | } |
363 | ||
f0183a33 FB |
364 | /* |
365 | * What we do now depends on what direction the data is supposed to | |
366 | * move in. | |
367 | */ | |
1da177e4 LT |
368 | |
369 | switch (us->srb->sc_data_direction) { | |
370 | case DMA_FROM_DEVICE: | |
371 | /* catch bogus "read 0 length" case */ | |
372 | if (!length) | |
373 | break; | |
f0183a33 FB |
374 | /* |
375 | * Make sure that the status indicates that the device | |
376 | * wants data as well. | |
377 | */ | |
1da177e4 | 378 | if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) { |
191648d0 | 379 | usb_stor_dbg(us, "SCSI wants data, drive doesn't have any\n"); |
1da177e4 LT |
380 | return USB_STOR_TRANSPORT_FAILED; |
381 | } | |
382 | result = freecom_readdata (srb, us, ipipe, opipe, length); | |
383 | if (result != USB_STOR_TRANSPORT_GOOD) | |
384 | return result; | |
385 | ||
191648d0 | 386 | usb_stor_dbg(us, "Waiting for status\n"); |
1da177e4 LT |
387 | result = usb_stor_bulk_transfer_buf (us, ipipe, fst, |
388 | FCM_PACKET_LENGTH, &partial); | |
191648d0 | 389 | US_DEBUG(pdump(us, (void *)fst, partial)); |
1da177e4 LT |
390 | |
391 | if (partial != 4 || result > USB_STOR_XFER_SHORT) | |
392 | return USB_STOR_TRANSPORT_ERROR; | |
393 | if ((fst->Status & ERR_STAT) != 0) { | |
191648d0 | 394 | usb_stor_dbg(us, "operation failed\n"); |
1da177e4 LT |
395 | return USB_STOR_TRANSPORT_FAILED; |
396 | } | |
397 | if ((fst->Reason & 3) != 3) { | |
191648d0 | 398 | usb_stor_dbg(us, "Drive seems still hungry\n"); |
1da177e4 LT |
399 | return USB_STOR_TRANSPORT_FAILED; |
400 | } | |
191648d0 | 401 | usb_stor_dbg(us, "Transfer happy\n"); |
1da177e4 LT |
402 | break; |
403 | ||
404 | case DMA_TO_DEVICE: | |
405 | /* catch bogus "write 0 length" case */ | |
406 | if (!length) | |
407 | break; | |
f0183a33 FB |
408 | /* |
409 | * Make sure the status indicates that the device wants to | |
410 | * send us data. | |
411 | */ | |
1da177e4 LT |
412 | /* !!IMPLEMENT!! */ |
413 | result = freecom_writedata (srb, us, ipipe, opipe, length); | |
414 | if (result != USB_STOR_TRANSPORT_GOOD) | |
415 | return result; | |
416 | ||
191648d0 | 417 | usb_stor_dbg(us, "Waiting for status\n"); |
1da177e4 LT |
418 | result = usb_stor_bulk_transfer_buf (us, ipipe, fst, |
419 | FCM_PACKET_LENGTH, &partial); | |
420 | ||
421 | if (partial != 4 || result > USB_STOR_XFER_SHORT) | |
422 | return USB_STOR_TRANSPORT_ERROR; | |
423 | if ((fst->Status & ERR_STAT) != 0) { | |
191648d0 | 424 | usb_stor_dbg(us, "operation failed\n"); |
1da177e4 LT |
425 | return USB_STOR_TRANSPORT_FAILED; |
426 | } | |
427 | if ((fst->Reason & 3) != 3) { | |
191648d0 | 428 | usb_stor_dbg(us, "Drive seems still hungry\n"); |
1da177e4 LT |
429 | return USB_STOR_TRANSPORT_FAILED; |
430 | } | |
431 | ||
191648d0 | 432 | usb_stor_dbg(us, "Transfer happy\n"); |
1da177e4 LT |
433 | break; |
434 | ||
435 | ||
436 | case DMA_NONE: | |
437 | /* Easy, do nothing. */ | |
438 | break; | |
439 | ||
440 | default: | |
441 | /* should never hit here -- filtered in usb.c */ | |
191648d0 JP |
442 | usb_stor_dbg(us, "freecom unimplemented direction: %d\n", |
443 | us->srb->sc_data_direction); | |
7c7e2d00 | 444 | /* Return fail, SCSI seems to handle this better. */ |
1da177e4 LT |
445 | return USB_STOR_TRANSPORT_FAILED; |
446 | break; | |
447 | } | |
448 | ||
449 | return USB_STOR_TRANSPORT_GOOD; | |
450 | } | |
451 | ||
0d62939f | 452 | static int init_freecom(struct us_data *us) |
1da177e4 LT |
453 | { |
454 | int result; | |
455 | char *buffer = us->iobuf; | |
456 | ||
f0183a33 FB |
457 | /* |
458 | * The DMA-mapped I/O buffer is 64 bytes long, just right for | |
1da177e4 LT |
459 | * all our packets. No need to allocate any extra buffer space. |
460 | */ | |
461 | ||
462 | result = usb_stor_control_msg(us, us->recv_ctrl_pipe, | |
463 | 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20, 3*HZ); | |
464 | buffer[32] = '\0'; | |
191648d0 | 465 | usb_stor_dbg(us, "String returned from FC init is: %s\n", buffer); |
1da177e4 | 466 | |
f0183a33 FB |
467 | /* |
468 | * Special thanks to the people at Freecom for providing me with | |
1da177e4 LT |
469 | * this "magic sequence", which they use in their Windows and MacOS |
470 | * drivers to make sure that all the attached perhiperals are | |
471 | * properly reset. | |
472 | */ | |
473 | ||
474 | /* send reset */ | |
475 | result = usb_stor_control_msg(us, us->send_ctrl_pipe, | |
476 | 0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ); | |
191648d0 | 477 | usb_stor_dbg(us, "result from activate reset is %d\n", result); |
1da177e4 LT |
478 | |
479 | /* wait 250ms */ | |
480 | mdelay(250); | |
481 | ||
482 | /* clear reset */ | |
483 | result = usb_stor_control_msg(us, us->send_ctrl_pipe, | |
484 | 0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ); | |
191648d0 | 485 | usb_stor_dbg(us, "result from clear reset is %d\n", result); |
1da177e4 LT |
486 | |
487 | /* wait 3 seconds */ | |
488 | mdelay(3 * 1000); | |
489 | ||
490 | return USB_STOR_TRANSPORT_GOOD; | |
491 | } | |
492 | ||
0d62939f | 493 | static int usb_stor_freecom_reset(struct us_data *us) |
1da177e4 LT |
494 | { |
495 | printk (KERN_CRIT "freecom reset called\n"); | |
496 | ||
497 | /* We don't really have this feature. */ | |
498 | return FAILED; | |
499 | } | |
500 | ||
501 | #ifdef CONFIG_USB_STORAGE_DEBUG | |
191648d0 | 502 | static void pdump(struct us_data *us, void *ibuffer, int length) |
1da177e4 LT |
503 | { |
504 | static char line[80]; | |
505 | int offset = 0; | |
506 | unsigned char *buffer = (unsigned char *) ibuffer; | |
507 | int i, j; | |
508 | int from, base; | |
509 | ||
510 | offset = 0; | |
511 | for (i = 0; i < length; i++) { | |
512 | if ((i & 15) == 0) { | |
513 | if (i > 0) { | |
514 | offset += sprintf (line+offset, " - "); | |
515 | for (j = i - 16; j < i; j++) { | |
516 | if (buffer[j] >= 32 && buffer[j] <= 126) | |
517 | line[offset++] = buffer[j]; | |
518 | else | |
519 | line[offset++] = '.'; | |
520 | } | |
521 | line[offset] = 0; | |
191648d0 | 522 | usb_stor_dbg(us, "%s\n", line); |
1da177e4 LT |
523 | offset = 0; |
524 | } | |
525 | offset += sprintf (line+offset, "%08x:", i); | |
7c7e2d00 | 526 | } else if ((i & 7) == 0) { |
1da177e4 LT |
527 | offset += sprintf (line+offset, " -"); |
528 | } | |
529 | offset += sprintf (line+offset, " %02x", buffer[i] & 0xff); | |
530 | } | |
531 | ||
532 | /* Add the last "chunk" of data. */ | |
533 | from = (length - 1) % 16; | |
534 | base = ((length - 1) / 16) * 16; | |
535 | ||
536 | for (i = from + 1; i < 16; i++) | |
537 | offset += sprintf (line+offset, " "); | |
538 | if (from < 8) | |
539 | offset += sprintf (line+offset, " "); | |
540 | offset += sprintf (line+offset, " - "); | |
541 | ||
542 | for (i = 0; i <= from; i++) { | |
543 | if (buffer[base+i] >= 32 && buffer[base+i] <= 126) | |
544 | line[offset++] = buffer[base+i]; | |
545 | else | |
546 | line[offset++] = '.'; | |
547 | } | |
548 | line[offset] = 0; | |
191648d0 | 549 | usb_stor_dbg(us, "%s\n", line); |
1da177e4 LT |
550 | offset = 0; |
551 | } | |
552 | #endif | |
553 | ||
aa519be3 AM |
554 | static struct scsi_host_template freecom_host_template; |
555 | ||
0d62939f AS |
556 | static int freecom_probe(struct usb_interface *intf, |
557 | const struct usb_device_id *id) | |
558 | { | |
559 | struct us_data *us; | |
560 | int result; | |
561 | ||
562 | result = usb_stor_probe1(&us, intf, id, | |
aa519be3 AM |
563 | (id - freecom_usb_ids) + freecom_unusual_dev_list, |
564 | &freecom_host_template); | |
0d62939f AS |
565 | if (result) |
566 | return result; | |
567 | ||
568 | us->transport_name = "Freecom"; | |
569 | us->transport = freecom_transport; | |
570 | us->transport_reset = usb_stor_freecom_reset; | |
571 | us->max_lun = 0; | |
572 | ||
573 | result = usb_stor_probe2(us); | |
574 | return result; | |
575 | } | |
576 | ||
577 | static struct usb_driver freecom_driver = { | |
aa519be3 | 578 | .name = DRV_NAME, |
0d62939f AS |
579 | .probe = freecom_probe, |
580 | .disconnect = usb_stor_disconnect, | |
581 | .suspend = usb_stor_suspend, | |
582 | .resume = usb_stor_resume, | |
583 | .reset_resume = usb_stor_reset_resume, | |
584 | .pre_reset = usb_stor_pre_reset, | |
585 | .post_reset = usb_stor_post_reset, | |
586 | .id_table = freecom_usb_ids, | |
587 | .soft_unbind = 1, | |
e73b2db6 | 588 | .no_dynamic_id = 1, |
0d62939f AS |
589 | }; |
590 | ||
aa519be3 | 591 | module_usb_stor_driver(freecom_driver, freecom_host_template, DRV_NAME); |