diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/usb/image/microtek.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/usb/image/microtek.c')
-rw-r--r-- | drivers/usb/image/microtek.c | 862 |
1 files changed, 862 insertions, 0 deletions
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c new file mode 100644 index 000000000000..de0d32f6618d --- /dev/null +++ b/drivers/usb/image/microtek.c | |||
@@ -0,0 +1,862 @@ | |||
1 | /* Driver for Microtek Scanmaker X6 USB scanner, and possibly others. | ||
2 | * | ||
3 | * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com> | ||
4 | * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de> | ||
5 | * | ||
6 | * Parts shamelessly stolen from usb-storage and copyright by their | ||
7 | * authors. Thanks to Matt Dharm for giving us permission! | ||
8 | * | ||
9 | * This driver implements a SCSI host controller driver and a USB | ||
10 | * device driver. To avoid confusion, all the USB related stuff is | ||
11 | * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_. | ||
12 | * | ||
13 | * Microtek (www.microtek.com) did not release the specifications for | ||
14 | * their USB protocol to us, so we had to reverse engineer them. We | ||
15 | * don't know for which models they are valid. | ||
16 | * | ||
17 | * The X6 USB has three bulk endpoints, one output (0x1) down which | ||
18 | * commands and outgoing data are sent, and two input: 0x82 from which | ||
19 | * normal data is read from the scanner (in packets of maximum 32 | ||
20 | * bytes) and from which the status byte is read, and 0x83 from which | ||
21 | * the results of a scan (or preview) are read in up to 64 * 1024 byte | ||
22 | * chunks by the Windows driver. We don't know how much it is possible | ||
23 | * to read at a time from 0x83. | ||
24 | * | ||
25 | * It seems possible to read (with URB transfers) everything from 0x82 | ||
26 | * in one go, without bothering to read in 32 byte chunks. | ||
27 | * | ||
28 | * There seems to be an optimisation of a further READ implicit if | ||
29 | * you simply read from 0x83. | ||
30 | * | ||
31 | * Guessed protocol: | ||
32 | * | ||
33 | * Send raw SCSI command to EP 0x1 | ||
34 | * | ||
35 | * If there is data to receive: | ||
36 | * If the command was READ datatype=image: | ||
37 | * Read a lot of data from EP 0x83 | ||
38 | * Else: | ||
39 | * Read data from EP 0x82 | ||
40 | * Else: | ||
41 | * If there is data to transmit: | ||
42 | * Write it to EP 0x1 | ||
43 | * | ||
44 | * Read status byte from EP 0x82 | ||
45 | * | ||
46 | * References: | ||
47 | * | ||
48 | * The SCSI command set for the scanner is available from | ||
49 | * ftp://ftp.microtek.com/microtek/devpack/ | ||
50 | * | ||
51 | * Microtek NV sent us a more up to date version of the document. If | ||
52 | * you want it, just send mail. | ||
53 | * | ||
54 | * Status: | ||
55 | * | ||
56 | * Untested with multiple scanners. | ||
57 | * Untested on SMP. | ||
58 | * Untested on a bigendian machine. | ||
59 | * | ||
60 | * History: | ||
61 | * | ||
62 | * 20000417 starting history | ||
63 | * 20000417 fixed load oops | ||
64 | * 20000417 fixed unload oops | ||
65 | * 20000419 fixed READ IMAGE detection | ||
66 | * 20000424 started conversion to use URBs | ||
67 | * 20000502 handled short transfers as errors | ||
68 | * 20000513 rename and organisation of functions (john) | ||
69 | * 20000513 added IDs for all products supported by Windows driver (john) | ||
70 | * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john) | ||
71 | * 20000514 Version 0.0.8j | ||
72 | * 20000514 Fix reporting of non-existant devices to SCSI layer (john) | ||
73 | * 20000514 Added MTS_DEBUG_INT (john) | ||
74 | * 20000514 Changed "usb-microtek" to "microtek" for consistency (john) | ||
75 | * 20000514 Stupid bug fixes (john) | ||
76 | * 20000514 Version 0.0.9j | ||
77 | * 20000515 Put transfer context and URB in mts_desc (john) | ||
78 | * 20000515 Added prelim turn off debugging support (john) | ||
79 | * 20000515 Version 0.0.10j | ||
80 | * 20000515 Fixed up URB allocation (clear URB on alloc) (john) | ||
81 | * 20000515 Version 0.0.11j | ||
82 | * 20000516 Removed unnecessary spinlock in mts_transfer_context (john) | ||
83 | * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john) | ||
84 | * 20000516 Implemented (badly) scsi_abort (john) | ||
85 | * 20000516 Version 0.0.12j | ||
86 | * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john) | ||
87 | * 20000517 Added mts_debug_dump to print ll USB info (john) | ||
88 | * 20000518 Tweaks and documentation updates (john) | ||
89 | * 20000518 Version 0.0.13j | ||
90 | * 20000518 Cleaned up abort handling (john) | ||
91 | * 20000523 Removed scsi_command and various scsi_..._resets (john) | ||
92 | * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john) | ||
93 | * 20000523 Fixed last tiresome compile warning (john) | ||
94 | * 20000523 Version 0.0.14j (though version 0.1 has come out?) | ||
95 | * 20000602 Added primitive reset | ||
96 | * 20000602 Version 0.2.0 | ||
97 | * 20000603 various cosmetic changes | ||
98 | * 20000603 Version 0.2.1 | ||
99 | * 20000620 minor cosmetic changes | ||
100 | * 20000620 Version 0.2.2 | ||
101 | * 20000822 Hopefully fixed deadlock in mts_remove_nolock() | ||
102 | * 20000822 Fixed minor race in mts_transfer_cleanup() | ||
103 | * 20000822 Fixed deadlock on submission error in queuecommand | ||
104 | * 20000822 Version 0.2.3 | ||
105 | * 20000913 Reduced module size if debugging is off | ||
106 | * 20000913 Version 0.2.4 | ||
107 | * 20010210 New abort logic | ||
108 | * 20010210 Version 0.3.0 | ||
109 | * 20010217 Merged scatter/gather | ||
110 | * 20010218 Version 0.4.0 | ||
111 | * 20010218 Cosmetic fixes | ||
112 | * 20010218 Version 0.4.1 | ||
113 | * 20010306 Abort while using scatter/gather | ||
114 | * 20010306 Version 0.4.2 | ||
115 | * 20010311 Remove all timeouts and tidy up generally (john) | ||
116 | * 20010320 check return value of scsi_register() | ||
117 | * 20010320 Version 0.4.3 | ||
118 | * 20010408 Identify version on module load. | ||
119 | * 20011003 Fix multiple requests | ||
120 | */ | ||
121 | |||
122 | #include <linux/module.h> | ||
123 | #include <linux/kernel.h> | ||
124 | #include <linux/sched.h> | ||
125 | #include <linux/signal.h> | ||
126 | #include <linux/errno.h> | ||
127 | #include <linux/random.h> | ||
128 | #include <linux/poll.h> | ||
129 | #include <linux/init.h> | ||
130 | #include <linux/slab.h> | ||
131 | #include <linux/spinlock.h> | ||
132 | #include <linux/smp_lock.h> | ||
133 | #include <linux/usb.h> | ||
134 | #include <linux/proc_fs.h> | ||
135 | |||
136 | #include <asm/atomic.h> | ||
137 | #include <linux/blkdev.h> | ||
138 | #include "../../scsi/scsi.h" | ||
139 | #include <scsi/scsi_host.h> | ||
140 | |||
141 | #include "microtek.h" | ||
142 | |||
143 | /* | ||
144 | * Version Information | ||
145 | */ | ||
146 | #define DRIVER_VERSION "v0.4.3" | ||
147 | #define DRIVER_AUTHOR "John Fremlin <vii@penguinpowered.com>, Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>" | ||
148 | #define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver" | ||
149 | |||
150 | /* Should we do debugging? */ | ||
151 | |||
152 | //#define MTS_DO_DEBUG | ||
153 | |||
154 | /* USB layer driver interface */ | ||
155 | |||
156 | static int mts_usb_probe(struct usb_interface *intf, | ||
157 | const struct usb_device_id *id); | ||
158 | static void mts_usb_disconnect(struct usb_interface *intf); | ||
159 | |||
160 | static struct usb_device_id mts_usb_ids []; | ||
161 | |||
162 | static struct usb_driver mts_usb_driver = { | ||
163 | .owner = THIS_MODULE, | ||
164 | .name = "microtekX6", | ||
165 | .probe = mts_usb_probe, | ||
166 | .disconnect = mts_usb_disconnect, | ||
167 | .id_table = mts_usb_ids, | ||
168 | }; | ||
169 | |||
170 | |||
171 | /* Internal driver stuff */ | ||
172 | |||
173 | #define MTS_VERSION "0.4.3" | ||
174 | #define MTS_NAME "microtek usb (rev " MTS_VERSION "): " | ||
175 | |||
176 | #define MTS_WARNING(x...) \ | ||
177 | printk( KERN_WARNING MTS_NAME x ) | ||
178 | #define MTS_ERROR(x...) \ | ||
179 | printk( KERN_ERR MTS_NAME x ) | ||
180 | #define MTS_INT_ERROR(x...) \ | ||
181 | MTS_ERROR(x) | ||
182 | #define MTS_MESSAGE(x...) \ | ||
183 | printk( KERN_INFO MTS_NAME x ) | ||
184 | |||
185 | #if defined MTS_DO_DEBUG | ||
186 | |||
187 | #define MTS_DEBUG(x...) \ | ||
188 | printk( KERN_DEBUG MTS_NAME x ) | ||
189 | |||
190 | #define MTS_DEBUG_GOT_HERE() \ | ||
191 | MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ ) | ||
192 | #define MTS_DEBUG_INT() \ | ||
193 | do { MTS_DEBUG_GOT_HERE(); \ | ||
194 | MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \ | ||
195 | MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ | ||
196 | mts_debug_dump(context->instance);\ | ||
197 | } while(0) | ||
198 | #else | ||
199 | |||
200 | #define MTS_NUL_STATEMENT do { } while(0) | ||
201 | |||
202 | #define MTS_DEBUG(x...) MTS_NUL_STATEMENT | ||
203 | #define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT | ||
204 | #define MTS_DEBUG_INT() MTS_NUL_STATEMENT | ||
205 | |||
206 | #endif | ||
207 | |||
208 | |||
209 | |||
210 | #define MTS_INT_INIT()\ | ||
211 | struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \ | ||
212 | MTS_DEBUG_INT();\ | ||
213 | |||
214 | #ifdef MTS_DO_DEBUG | ||
215 | |||
216 | static inline void mts_debug_dump(struct mts_desc* desc) { | ||
217 | MTS_DEBUG("desc at 0x%x: toggle = %02x%02x\n", | ||
218 | (int)desc, | ||
219 | (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0] | ||
220 | ); | ||
221 | MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n", | ||
222 | usb_sndbulkpipe(desc->usb_dev,desc->ep_out), | ||
223 | usb_rcvbulkpipe(desc->usb_dev,desc->ep_response), | ||
224 | usb_rcvbulkpipe(desc->usb_dev,desc->ep_image) | ||
225 | ); | ||
226 | } | ||
227 | |||
228 | |||
229 | static inline void mts_show_command(Scsi_Cmnd *srb) | ||
230 | { | ||
231 | char *what = NULL; | ||
232 | |||
233 | switch (srb->cmnd[0]) { | ||
234 | case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; | ||
235 | case REZERO_UNIT: what = "REZERO_UNIT"; break; | ||
236 | case REQUEST_SENSE: what = "REQUEST_SENSE"; break; | ||
237 | case FORMAT_UNIT: what = "FORMAT_UNIT"; break; | ||
238 | case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; | ||
239 | case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; | ||
240 | case READ_6: what = "READ_6"; break; | ||
241 | case WRITE_6: what = "WRITE_6"; break; | ||
242 | case SEEK_6: what = "SEEK_6"; break; | ||
243 | case READ_REVERSE: what = "READ_REVERSE"; break; | ||
244 | case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; | ||
245 | case SPACE: what = "SPACE"; break; | ||
246 | case INQUIRY: what = "INQUIRY"; break; | ||
247 | case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; | ||
248 | case MODE_SELECT: what = "MODE_SELECT"; break; | ||
249 | case RESERVE: what = "RESERVE"; break; | ||
250 | case RELEASE: what = "RELEASE"; break; | ||
251 | case COPY: what = "COPY"; break; | ||
252 | case ERASE: what = "ERASE"; break; | ||
253 | case MODE_SENSE: what = "MODE_SENSE"; break; | ||
254 | case START_STOP: what = "START_STOP"; break; | ||
255 | case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; | ||
256 | case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; | ||
257 | case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; | ||
258 | case SET_WINDOW: what = "SET_WINDOW"; break; | ||
259 | case READ_CAPACITY: what = "READ_CAPACITY"; break; | ||
260 | case READ_10: what = "READ_10"; break; | ||
261 | case WRITE_10: what = "WRITE_10"; break; | ||
262 | case SEEK_10: what = "SEEK_10"; break; | ||
263 | case WRITE_VERIFY: what = "WRITE_VERIFY"; break; | ||
264 | case VERIFY: what = "VERIFY"; break; | ||
265 | case SEARCH_HIGH: what = "SEARCH_HIGH"; break; | ||
266 | case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; | ||
267 | case SEARCH_LOW: what = "SEARCH_LOW"; break; | ||
268 | case SET_LIMITS: what = "SET_LIMITS"; break; | ||
269 | case READ_POSITION: what = "READ_POSITION"; break; | ||
270 | case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; | ||
271 | case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; | ||
272 | case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; | ||
273 | case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; | ||
274 | case COMPARE: what = "COMPARE"; break; | ||
275 | case COPY_VERIFY: what = "COPY_VERIFY"; break; | ||
276 | case WRITE_BUFFER: what = "WRITE_BUFFER"; break; | ||
277 | case READ_BUFFER: what = "READ_BUFFER"; break; | ||
278 | case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; | ||
279 | case READ_LONG: what = "READ_LONG"; break; | ||
280 | case WRITE_LONG: what = "WRITE_LONG"; break; | ||
281 | case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; | ||
282 | case WRITE_SAME: what = "WRITE_SAME"; break; | ||
283 | case READ_TOC: what = "READ_TOC"; break; | ||
284 | case LOG_SELECT: what = "LOG_SELECT"; break; | ||
285 | case LOG_SENSE: what = "LOG_SENSE"; break; | ||
286 | case MODE_SELECT_10: what = "MODE_SELECT_10"; break; | ||
287 | case MODE_SENSE_10: what = "MODE_SENSE_10"; break; | ||
288 | case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; | ||
289 | case READ_12: what = "READ_12"; break; | ||
290 | case WRITE_12: what = "WRITE_12"; break; | ||
291 | case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; | ||
292 | case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; | ||
293 | case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; | ||
294 | case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; | ||
295 | case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; | ||
296 | case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; | ||
297 | case WRITE_LONG_2: what = "WRITE_LONG_2"; break; | ||
298 | default: | ||
299 | MTS_DEBUG("can't decode command\n"); | ||
300 | goto out; | ||
301 | break; | ||
302 | } | ||
303 | MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len); | ||
304 | |||
305 | out: | ||
306 | MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", | ||
307 | srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], | ||
308 | srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); | ||
309 | } | ||
310 | |||
311 | #else | ||
312 | |||
313 | static inline void mts_show_command(Scsi_Cmnd * dummy) | ||
314 | { | ||
315 | } | ||
316 | |||
317 | static inline void mts_debug_dump(struct mts_desc* dummy) | ||
318 | { | ||
319 | } | ||
320 | |||
321 | #endif | ||
322 | |||
323 | static inline void mts_urb_abort(struct mts_desc* desc) { | ||
324 | MTS_DEBUG_GOT_HERE(); | ||
325 | mts_debug_dump(desc); | ||
326 | |||
327 | usb_kill_urb( desc->urb ); | ||
328 | } | ||
329 | |||
330 | static int mts_scsi_abort (Scsi_Cmnd *srb) | ||
331 | { | ||
332 | struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); | ||
333 | |||
334 | MTS_DEBUG_GOT_HERE(); | ||
335 | |||
336 | mts_urb_abort(desc); | ||
337 | |||
338 | return SCSI_ABORT_PENDING; | ||
339 | } | ||
340 | |||
341 | static int mts_scsi_host_reset (Scsi_Cmnd *srb) | ||
342 | { | ||
343 | struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); | ||
344 | int result, rc; | ||
345 | |||
346 | MTS_DEBUG_GOT_HERE(); | ||
347 | mts_debug_dump(desc); | ||
348 | |||
349 | rc = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf); | ||
350 | if (rc < 0) | ||
351 | return FAILED; | ||
352 | result = usb_reset_device(desc->usb_dev);; | ||
353 | if (rc) | ||
354 | usb_unlock_device(desc->usb_dev); | ||
355 | return result ? FAILED : SUCCESS; | ||
356 | } | ||
357 | |||
358 | static | ||
359 | int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ); | ||
360 | |||
361 | static void mts_transfer_cleanup( struct urb *transfer ); | ||
362 | static void mts_do_sg(struct urb * transfer, struct pt_regs *regs); | ||
363 | |||
364 | |||
365 | inline static | ||
366 | void mts_int_submit_urb (struct urb* transfer, | ||
367 | int pipe, | ||
368 | void* data, | ||
369 | unsigned length, | ||
370 | usb_complete_t callback ) | ||
371 | /* Interrupt context! */ | ||
372 | |||
373 | /* Holding transfer->context->lock! */ | ||
374 | { | ||
375 | int res; | ||
376 | |||
377 | MTS_INT_INIT(); | ||
378 | |||
379 | usb_fill_bulk_urb(transfer, | ||
380 | context->instance->usb_dev, | ||
381 | pipe, | ||
382 | data, | ||
383 | length, | ||
384 | callback, | ||
385 | context | ||
386 | ); | ||
387 | |||
388 | transfer->status = 0; | ||
389 | |||
390 | res = usb_submit_urb( transfer, GFP_ATOMIC ); | ||
391 | if ( unlikely(res) ) { | ||
392 | MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); | ||
393 | context->srb->result = DID_ERROR << 16; | ||
394 | mts_transfer_cleanup(transfer); | ||
395 | } | ||
396 | return; | ||
397 | } | ||
398 | |||
399 | |||
400 | static void mts_transfer_cleanup( struct urb *transfer ) | ||
401 | /* Interrupt context! */ | ||
402 | { | ||
403 | MTS_INT_INIT(); | ||
404 | |||
405 | if ( likely(context->final_callback != NULL) ) | ||
406 | context->final_callback(context->srb); | ||
407 | |||
408 | } | ||
409 | |||
410 | static void mts_transfer_done( struct urb *transfer, struct pt_regs *regs ) | ||
411 | { | ||
412 | MTS_INT_INIT(); | ||
413 | |||
414 | context->srb->result &= MTS_SCSI_ERR_MASK; | ||
415 | context->srb->result |= (unsigned)context->status<<1; | ||
416 | |||
417 | mts_transfer_cleanup(transfer); | ||
418 | |||
419 | return; | ||
420 | } | ||
421 | |||
422 | |||
423 | static void mts_get_status( struct urb *transfer ) | ||
424 | /* Interrupt context! */ | ||
425 | { | ||
426 | MTS_INT_INIT(); | ||
427 | |||
428 | mts_int_submit_urb(transfer, | ||
429 | usb_rcvbulkpipe(context->instance->usb_dev, | ||
430 | context->instance->ep_response), | ||
431 | &context->status, | ||
432 | 1, | ||
433 | mts_transfer_done ); | ||
434 | } | ||
435 | |||
436 | static void mts_data_done( struct urb* transfer, struct pt_regs *regs ) | ||
437 | /* Interrupt context! */ | ||
438 | { | ||
439 | MTS_INT_INIT(); | ||
440 | |||
441 | if ( context->data_length != transfer->actual_length ) { | ||
442 | context->srb->resid = context->data_length - transfer->actual_length; | ||
443 | } else if ( unlikely(transfer->status) ) { | ||
444 | context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16; | ||
445 | } | ||
446 | |||
447 | mts_get_status(transfer); | ||
448 | |||
449 | return; | ||
450 | } | ||
451 | |||
452 | |||
453 | static void mts_command_done( struct urb *transfer, struct pt_regs *regs ) | ||
454 | /* Interrupt context! */ | ||
455 | { | ||
456 | MTS_INT_INIT(); | ||
457 | |||
458 | if ( unlikely(transfer->status) ) { | ||
459 | if (transfer->status == -ENOENT) { | ||
460 | /* We are being killed */ | ||
461 | MTS_DEBUG_GOT_HERE(); | ||
462 | context->srb->result = DID_ABORT<<16; | ||
463 | } else { | ||
464 | /* A genuine error has occurred */ | ||
465 | MTS_DEBUG_GOT_HERE(); | ||
466 | |||
467 | context->srb->result = DID_ERROR<<16; | ||
468 | } | ||
469 | mts_transfer_cleanup(transfer); | ||
470 | |||
471 | return; | ||
472 | } | ||
473 | |||
474 | if (context->srb->cmnd[0] == REQUEST_SENSE) { | ||
475 | mts_int_submit_urb(transfer, | ||
476 | context->data_pipe, | ||
477 | context->srb->sense_buffer, | ||
478 | context->data_length, | ||
479 | mts_data_done); | ||
480 | } else { if ( context->data ) { | ||
481 | mts_int_submit_urb(transfer, | ||
482 | context->data_pipe, | ||
483 | context->data, | ||
484 | context->data_length, | ||
485 | context->srb->use_sg ? mts_do_sg : mts_data_done); | ||
486 | } else { | ||
487 | mts_get_status(transfer); | ||
488 | } | ||
489 | } | ||
490 | |||
491 | return; | ||
492 | } | ||
493 | |||
494 | static void mts_do_sg (struct urb* transfer, struct pt_regs *regs) | ||
495 | { | ||
496 | struct scatterlist * sg; | ||
497 | MTS_INT_INIT(); | ||
498 | |||
499 | MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,context->srb->use_sg); | ||
500 | |||
501 | if (unlikely(transfer->status)) { | ||
502 | context->srb->result = (transfer->status == -ENOENT ? DID_ABORT : DID_ERROR)<<16; | ||
503 | mts_transfer_cleanup(transfer); | ||
504 | } | ||
505 | |||
506 | sg = context->srb->buffer; | ||
507 | context->fragment++; | ||
508 | mts_int_submit_urb(transfer, | ||
509 | context->data_pipe, | ||
510 | page_address(sg[context->fragment].page) + | ||
511 | sg[context->fragment].offset, | ||
512 | sg[context->fragment].length, | ||
513 | context->fragment + 1 == context->srb->use_sg ? mts_data_done : mts_do_sg); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 }; | ||
518 | static const u8 mts_read_image_sig_len = 4; | ||
519 | static const unsigned char mts_direction[256/8] = { | ||
520 | 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, | ||
521 | 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, | ||
522 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, | ||
523 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | ||
524 | }; | ||
525 | |||
526 | |||
527 | #define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1) | ||
528 | |||
529 | static void | ||
530 | mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) | ||
531 | { | ||
532 | int pipe; | ||
533 | struct scatterlist * sg; | ||
534 | |||
535 | MTS_DEBUG_GOT_HERE(); | ||
536 | |||
537 | desc->context.instance = desc; | ||
538 | desc->context.srb = srb; | ||
539 | desc->context.fragment = 0; | ||
540 | |||
541 | if (!srb->use_sg) { | ||
542 | if ( !srb->bufflen ){ | ||
543 | desc->context.data = NULL; | ||
544 | desc->context.data_length = 0; | ||
545 | return; | ||
546 | } else { | ||
547 | desc->context.data = srb->buffer; | ||
548 | desc->context.data_length = srb->bufflen; | ||
549 | MTS_DEBUG("length = %d or %d\n", | ||
550 | srb->request_bufflen, srb->bufflen); | ||
551 | } | ||
552 | } else { | ||
553 | MTS_DEBUG("Using scatter/gather\n"); | ||
554 | sg = srb->buffer; | ||
555 | desc->context.data = page_address(sg[0].page) + sg[0].offset; | ||
556 | desc->context.data_length = sg[0].length; | ||
557 | } | ||
558 | |||
559 | |||
560 | /* can't rely on srb->sc_data_direction */ | ||
561 | |||
562 | /* Brutally ripped from usb-storage */ | ||
563 | |||
564 | if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len ) | ||
565 | ) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image); | ||
566 | MTS_DEBUG( "transfering from desc->ep_image == %d\n", | ||
567 | (int)desc->ep_image ); | ||
568 | } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) { | ||
569 | pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response); | ||
570 | MTS_DEBUG( "transfering from desc->ep_response == %d\n", | ||
571 | (int)desc->ep_response); | ||
572 | } else { | ||
573 | MTS_DEBUG("transfering to desc->ep_out == %d\n", | ||
574 | (int)desc->ep_out); | ||
575 | pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out); | ||
576 | } | ||
577 | desc->context.data_pipe = pipe; | ||
578 | } | ||
579 | |||
580 | |||
581 | static | ||
582 | int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ) | ||
583 | { | ||
584 | struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]); | ||
585 | int err = 0; | ||
586 | int res; | ||
587 | |||
588 | MTS_DEBUG_GOT_HERE(); | ||
589 | mts_show_command(srb); | ||
590 | mts_debug_dump(desc); | ||
591 | |||
592 | if ( srb->device->lun || srb->device->id || srb->device->channel ) { | ||
593 | |||
594 | MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel ); | ||
595 | |||
596 | MTS_DEBUG("this device doesn't exist\n"); | ||
597 | |||
598 | srb->result = DID_BAD_TARGET << 16; | ||
599 | |||
600 | if(likely(callback != NULL)) | ||
601 | callback(srb); | ||
602 | |||
603 | goto out; | ||
604 | } | ||
605 | |||
606 | |||
607 | usb_fill_bulk_urb(desc->urb, | ||
608 | desc->usb_dev, | ||
609 | usb_sndbulkpipe(desc->usb_dev,desc->ep_out), | ||
610 | srb->cmnd, | ||
611 | srb->cmd_len, | ||
612 | mts_command_done, | ||
613 | &desc->context | ||
614 | ); | ||
615 | |||
616 | |||
617 | mts_build_transfer_context( srb, desc ); | ||
618 | desc->context.final_callback = callback; | ||
619 | |||
620 | /* here we need ATOMIC as we are called with the iolock */ | ||
621 | res=usb_submit_urb(desc->urb, GFP_ATOMIC); | ||
622 | |||
623 | if(unlikely(res)){ | ||
624 | MTS_ERROR("error %d submitting URB\n",(int)res); | ||
625 | srb->result = DID_ERROR << 16; | ||
626 | |||
627 | if(likely(callback != NULL)) | ||
628 | callback(srb); | ||
629 | |||
630 | } | ||
631 | |||
632 | out: | ||
633 | return err; | ||
634 | } | ||
635 | |||
636 | static Scsi_Host_Template mts_scsi_host_template = { | ||
637 | .module = THIS_MODULE, | ||
638 | .name = "microtekX6", | ||
639 | .proc_name = "microtekX6", | ||
640 | .queuecommand = mts_scsi_queuecommand, | ||
641 | .eh_abort_handler = mts_scsi_abort, | ||
642 | .eh_host_reset_handler = mts_scsi_host_reset, | ||
643 | .sg_tablesize = SG_ALL, | ||
644 | .can_queue = 1, | ||
645 | .this_id = -1, | ||
646 | .cmd_per_lun = 1, | ||
647 | .use_clustering = 1, | ||
648 | .emulated = 1, | ||
649 | }; | ||
650 | |||
651 | struct vendor_product | ||
652 | { | ||
653 | char* name; | ||
654 | enum | ||
655 | { | ||
656 | mts_sup_unknown=0, | ||
657 | mts_sup_alpha, | ||
658 | mts_sup_full | ||
659 | } | ||
660 | support_status; | ||
661 | } ; | ||
662 | |||
663 | |||
664 | /* These are taken from the msmUSB.inf file on the Windows driver CD */ | ||
665 | const static struct vendor_product mts_supported_products[] = | ||
666 | { | ||
667 | { "Phantom 336CX", mts_sup_unknown}, | ||
668 | { "Phantom 336CX", mts_sup_unknown}, | ||
669 | { "Scanmaker X6", mts_sup_alpha}, | ||
670 | { "Phantom C6", mts_sup_unknown}, | ||
671 | { "Phantom 336CX", mts_sup_unknown}, | ||
672 | { "ScanMaker V6USL", mts_sup_unknown}, | ||
673 | { "ScanMaker V6USL", mts_sup_unknown}, | ||
674 | { "Scanmaker V6UL", mts_sup_unknown}, | ||
675 | { "Scanmaker V6UPL", mts_sup_alpha}, | ||
676 | }; | ||
677 | |||
678 | /* The entries of microtek_table must correspond, line-by-line to | ||
679 | the entries of mts_supported_products[]. */ | ||
680 | |||
681 | static struct usb_device_id mts_usb_ids [] = | ||
682 | { | ||
683 | { USB_DEVICE(0x4ce, 0x0300) }, | ||
684 | { USB_DEVICE(0x5da, 0x0094) }, | ||
685 | { USB_DEVICE(0x5da, 0x0099) }, | ||
686 | { USB_DEVICE(0x5da, 0x009a) }, | ||
687 | { USB_DEVICE(0x5da, 0x00a0) }, | ||
688 | { USB_DEVICE(0x5da, 0x00a3) }, | ||
689 | { USB_DEVICE(0x5da, 0x80a3) }, | ||
690 | { USB_DEVICE(0x5da, 0x80ac) }, | ||
691 | { USB_DEVICE(0x5da, 0x00b6) }, | ||
692 | { } /* Terminating entry */ | ||
693 | }; | ||
694 | |||
695 | MODULE_DEVICE_TABLE (usb, mts_usb_ids); | ||
696 | |||
697 | |||
698 | static int mts_usb_probe(struct usb_interface *intf, | ||
699 | const struct usb_device_id *id) | ||
700 | { | ||
701 | int i; | ||
702 | int ep_out = -1; | ||
703 | int ep_in_set[3]; /* this will break if we have more than three endpoints | ||
704 | which is why we check */ | ||
705 | int *ep_in_current = ep_in_set; | ||
706 | int err_retval = -ENOMEM; | ||
707 | |||
708 | struct mts_desc * new_desc; | ||
709 | struct vendor_product const* p; | ||
710 | struct usb_device *dev = interface_to_usbdev (intf); | ||
711 | |||
712 | /* the current altsetting on the interface we're probing */ | ||
713 | struct usb_host_interface *altsetting; | ||
714 | |||
715 | MTS_DEBUG_GOT_HERE(); | ||
716 | MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); | ||
717 | |||
718 | MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", | ||
719 | le16_to_cpu(dev->descriptor.idProduct), | ||
720 | le16_to_cpu(dev->descriptor.idVendor) ); | ||
721 | |||
722 | MTS_DEBUG_GOT_HERE(); | ||
723 | |||
724 | p = &mts_supported_products[id - mts_usb_ids]; | ||
725 | |||
726 | MTS_DEBUG_GOT_HERE(); | ||
727 | |||
728 | MTS_DEBUG( "found model %s\n", p->name ); | ||
729 | if ( p->support_status != mts_sup_full ) | ||
730 | MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", | ||
731 | p->name ); | ||
732 | |||
733 | /* the current altsetting on the interface we're probing */ | ||
734 | altsetting = intf->cur_altsetting; | ||
735 | |||
736 | |||
737 | /* Check if the config is sane */ | ||
738 | |||
739 | if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) { | ||
740 | MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", | ||
741 | (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints ); | ||
742 | return -ENODEV; | ||
743 | } | ||
744 | |||
745 | for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) { | ||
746 | if ((altsetting->endpoint[i].desc.bmAttributes & | ||
747 | USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { | ||
748 | |||
749 | MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", | ||
750 | (int)altsetting->endpoint[i].desc.bEndpointAddress ); | ||
751 | } else { | ||
752 | if (altsetting->endpoint[i].desc.bEndpointAddress & | ||
753 | USB_DIR_IN) | ||
754 | *ep_in_current++ | ||
755 | = altsetting->endpoint[i].desc.bEndpointAddress & | ||
756 | USB_ENDPOINT_NUMBER_MASK; | ||
757 | else { | ||
758 | if ( ep_out != -1 ) { | ||
759 | MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); | ||
760 | return -ENODEV; | ||
761 | } | ||
762 | |||
763 | ep_out = altsetting->endpoint[i].desc.bEndpointAddress & | ||
764 | USB_ENDPOINT_NUMBER_MASK; | ||
765 | } | ||
766 | } | ||
767 | |||
768 | } | ||
769 | |||
770 | |||
771 | if ( ep_out == -1 ) { | ||
772 | MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); | ||
773 | return -ENODEV; | ||
774 | } | ||
775 | |||
776 | |||
777 | new_desc = kmalloc(sizeof(struct mts_desc), GFP_KERNEL); | ||
778 | if (!new_desc) | ||
779 | goto out; | ||
780 | |||
781 | memset(new_desc, 0, sizeof(*new_desc)); | ||
782 | new_desc->urb = usb_alloc_urb(0, GFP_KERNEL); | ||
783 | if (!new_desc->urb) | ||
784 | goto out_kfree; | ||
785 | |||
786 | new_desc->usb_dev = dev; | ||
787 | new_desc->usb_intf = intf; | ||
788 | init_MUTEX(&new_desc->lock); | ||
789 | |||
790 | /* endpoints */ | ||
791 | new_desc->ep_out = ep_out; | ||
792 | new_desc->ep_response = ep_in_set[0]; | ||
793 | new_desc->ep_image = ep_in_set[1]; | ||
794 | |||
795 | if ( new_desc->ep_out != MTS_EP_OUT ) | ||
796 | MTS_WARNING( "will this work? Command EP is not usually %d\n", | ||
797 | (int)new_desc->ep_out ); | ||
798 | |||
799 | if ( new_desc->ep_response != MTS_EP_RESPONSE ) | ||
800 | MTS_WARNING( "will this work? Response EP is not usually %d\n", | ||
801 | (int)new_desc->ep_response ); | ||
802 | |||
803 | if ( new_desc->ep_image != MTS_EP_IMAGE ) | ||
804 | MTS_WARNING( "will this work? Image data EP is not usually %d\n", | ||
805 | (int)new_desc->ep_image ); | ||
806 | |||
807 | new_desc->host = scsi_host_alloc(&mts_scsi_host_template, | ||
808 | sizeof(new_desc)); | ||
809 | if (!new_desc->host) | ||
810 | goto out_free_urb; | ||
811 | |||
812 | new_desc->host->hostdata[0] = (unsigned long)new_desc; | ||
813 | if (scsi_add_host(new_desc->host, NULL)) { | ||
814 | err_retval = -EIO; | ||
815 | goto out_free_urb; | ||
816 | } | ||
817 | scsi_scan_host(new_desc->host); | ||
818 | |||
819 | usb_set_intfdata(intf, new_desc); | ||
820 | return 0; | ||
821 | |||
822 | out_free_urb: | ||
823 | usb_free_urb(new_desc->urb); | ||
824 | out_kfree: | ||
825 | kfree(new_desc); | ||
826 | out: | ||
827 | return err_retval; | ||
828 | } | ||
829 | |||
830 | static void mts_usb_disconnect (struct usb_interface *intf) | ||
831 | { | ||
832 | struct mts_desc *desc = usb_get_intfdata(intf); | ||
833 | |||
834 | usb_set_intfdata(intf, NULL); | ||
835 | |||
836 | usb_kill_urb(desc->urb); | ||
837 | scsi_remove_host(desc->host); | ||
838 | |||
839 | scsi_host_put(desc->host); | ||
840 | usb_free_urb(desc->urb); | ||
841 | kfree(desc); | ||
842 | } | ||
843 | |||
844 | |||
845 | static int __init microtek_drv_init(void) | ||
846 | { | ||
847 | return usb_register(&mts_usb_driver); | ||
848 | } | ||
849 | |||
850 | static void __exit microtek_drv_exit(void) | ||
851 | { | ||
852 | usb_deregister(&mts_usb_driver); | ||
853 | } | ||
854 | |||
855 | module_init(microtek_drv_init); | ||
856 | module_exit(microtek_drv_exit); | ||
857 | |||
858 | MODULE_AUTHOR( DRIVER_AUTHOR ); | ||
859 | MODULE_DESCRIPTION( DRIVER_DESC ); | ||
860 | MODULE_LICENSE("GPL"); | ||
861 | |||
862 | |||