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