xref: /openbmc/linux/drivers/usb/image/microtek.c (revision 04d1fa43)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
51da177e4SLinus Torvalds  * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Parts shamelessly stolen from usb-storage and copyright by their
81da177e4SLinus Torvalds  * authors. Thanks to Matt Dharm for giving us permission!
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  * This driver implements a SCSI host controller driver and a USB
111da177e4SLinus Torvalds  * device driver. To avoid confusion, all the USB related stuff is
121da177e4SLinus Torvalds  * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * Microtek (www.microtek.com) did not release the specifications for
151da177e4SLinus Torvalds  * their USB protocol to us, so we had to reverse engineer them. We
161da177e4SLinus Torvalds  * don't know for which models they are valid.
171da177e4SLinus Torvalds  *
181da177e4SLinus Torvalds  * The X6 USB has three bulk endpoints, one output (0x1) down which
191da177e4SLinus Torvalds  * commands and outgoing data are sent, and two input: 0x82 from which
201da177e4SLinus Torvalds  * normal data is read from the scanner (in packets of maximum 32
211da177e4SLinus Torvalds  * bytes) and from which the status byte is read, and 0x83 from which
221da177e4SLinus Torvalds  * the results of a scan (or preview) are read in up to 64 * 1024 byte
231da177e4SLinus Torvalds  * chunks by the Windows driver. We don't know how much it is possible
241da177e4SLinus Torvalds  * to read at a time from 0x83.
251da177e4SLinus Torvalds  *
261da177e4SLinus Torvalds  * It seems possible to read (with URB transfers) everything from 0x82
271da177e4SLinus Torvalds  * in one go, without bothering to read in 32 byte chunks.
281da177e4SLinus Torvalds  *
291da177e4SLinus Torvalds  * There seems to be an optimisation of a further READ implicit if
301da177e4SLinus Torvalds  * you simply read from 0x83.
311da177e4SLinus Torvalds  *
321da177e4SLinus Torvalds  * Guessed protocol:
331da177e4SLinus Torvalds  *
341da177e4SLinus Torvalds  *	Send raw SCSI command to EP 0x1
351da177e4SLinus Torvalds  *
361da177e4SLinus Torvalds  *	If there is data to receive:
371da177e4SLinus Torvalds  *		If the command was READ datatype=image:
381da177e4SLinus Torvalds  *			Read a lot of data from EP 0x83
391da177e4SLinus Torvalds  *		Else:
401da177e4SLinus Torvalds  *			Read data from EP 0x82
411da177e4SLinus Torvalds  *	Else:
421da177e4SLinus Torvalds  *		If there is data to transmit:
431da177e4SLinus Torvalds  *			Write it to EP 0x1
441da177e4SLinus Torvalds  *
451da177e4SLinus Torvalds  *	Read status byte from EP 0x82
461da177e4SLinus Torvalds  *
471da177e4SLinus Torvalds  * References:
481da177e4SLinus Torvalds  *
491da177e4SLinus Torvalds  * The SCSI command set for the scanner is available from
501da177e4SLinus Torvalds  *	ftp://ftp.microtek.com/microtek/devpack/
511da177e4SLinus Torvalds  *
521da177e4SLinus Torvalds  * Microtek NV sent us a more up to date version of the document. If
531da177e4SLinus Torvalds  * you want it, just send mail.
541da177e4SLinus Torvalds  *
551da177e4SLinus Torvalds  * Status:
561da177e4SLinus Torvalds  *
571da177e4SLinus Torvalds  *	Untested with multiple scanners.
581da177e4SLinus Torvalds  *	Untested on SMP.
591da177e4SLinus Torvalds  *	Untested on a bigendian machine.
601da177e4SLinus Torvalds  *
611da177e4SLinus Torvalds  * History:
621da177e4SLinus Torvalds  *
631da177e4SLinus Torvalds  *	20000417 starting history
641da177e4SLinus Torvalds  *	20000417 fixed load oops
651da177e4SLinus Torvalds  *	20000417 fixed unload oops
661da177e4SLinus Torvalds  *	20000419 fixed READ IMAGE detection
671da177e4SLinus Torvalds  *	20000424 started conversion to use URBs
681da177e4SLinus Torvalds  *	20000502 handled short transfers as errors
691da177e4SLinus Torvalds  *	20000513 rename and organisation of functions (john)
701da177e4SLinus Torvalds  *	20000513 added IDs for all products supported by Windows driver (john)
711da177e4SLinus Torvalds  *	20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
721da177e4SLinus Torvalds  *	20000514 Version 0.0.8j
7325985edcSLucas De Marchi  *      20000514 Fix reporting of non-existent devices to SCSI layer (john)
741da177e4SLinus Torvalds  *	20000514 Added MTS_DEBUG_INT (john)
751da177e4SLinus Torvalds  *	20000514 Changed "usb-microtek" to "microtek" for consistency (john)
761da177e4SLinus Torvalds  *	20000514 Stupid bug fixes (john)
771da177e4SLinus Torvalds  *	20000514 Version 0.0.9j
781da177e4SLinus Torvalds  *	20000515 Put transfer context and URB in mts_desc (john)
791da177e4SLinus Torvalds  *	20000515 Added prelim turn off debugging support (john)
801da177e4SLinus Torvalds  *	20000515 Version 0.0.10j
811da177e4SLinus Torvalds  *      20000515 Fixed up URB allocation (clear URB on alloc) (john)
821da177e4SLinus Torvalds  *      20000515 Version 0.0.11j
831da177e4SLinus Torvalds  *	20000516 Removed unnecessary spinlock in mts_transfer_context (john)
841da177e4SLinus Torvalds  *	20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
851da177e4SLinus Torvalds  *	20000516 Implemented (badly) scsi_abort (john)
861da177e4SLinus Torvalds  *	20000516 Version 0.0.12j
871da177e4SLinus Torvalds  *      20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
881da177e4SLinus Torvalds  *      20000517 Added mts_debug_dump to print ll USB info (john)
891da177e4SLinus Torvalds  *	20000518 Tweaks and documentation updates (john)
901da177e4SLinus Torvalds  *	20000518 Version 0.0.13j
911da177e4SLinus Torvalds  *	20000518 Cleaned up abort handling (john)
921da177e4SLinus Torvalds  *	20000523 Removed scsi_command and various scsi_..._resets (john)
931da177e4SLinus Torvalds  *	20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
941da177e4SLinus Torvalds  *	20000523 Fixed last tiresome compile warning (john)
951da177e4SLinus Torvalds  *	20000523 Version 0.0.14j (though version 0.1 has come out?)
961da177e4SLinus Torvalds  *	20000602 Added primitive reset
971da177e4SLinus Torvalds  *	20000602 Version 0.2.0
981da177e4SLinus Torvalds  *	20000603 various cosmetic changes
991da177e4SLinus Torvalds  *	20000603 Version 0.2.1
1001da177e4SLinus Torvalds  *	20000620 minor cosmetic changes
1011da177e4SLinus Torvalds  *	20000620 Version 0.2.2
1021da177e4SLinus Torvalds  *	20000822 Hopefully fixed deadlock in mts_remove_nolock()
1031da177e4SLinus Torvalds  *	20000822 Fixed minor race in mts_transfer_cleanup()
1041da177e4SLinus Torvalds  *	20000822 Fixed deadlock on submission error in queuecommand
1051da177e4SLinus Torvalds  *	20000822 Version 0.2.3
1061da177e4SLinus Torvalds  *	20000913 Reduced module size if debugging is off
1071da177e4SLinus Torvalds  *	20000913 Version 0.2.4
1081da177e4SLinus Torvalds  *      20010210 New abort logic
1091da177e4SLinus Torvalds  *      20010210 Version 0.3.0
1101da177e4SLinus Torvalds  *	20010217 Merged scatter/gather
1111da177e4SLinus Torvalds  *	20010218 Version 0.4.0
1121da177e4SLinus Torvalds  *	20010218 Cosmetic fixes
1131da177e4SLinus Torvalds  *	20010218 Version 0.4.1
1141da177e4SLinus Torvalds  *      20010306 Abort while using scatter/gather
1151da177e4SLinus Torvalds  *      20010306 Version 0.4.2
1161da177e4SLinus Torvalds  *      20010311 Remove all timeouts and tidy up generally (john)
1171da177e4SLinus Torvalds  *	20010320 check return value of scsi_register()
1181da177e4SLinus Torvalds  *	20010320 Version 0.4.3
1191da177e4SLinus Torvalds  *	20010408 Identify version on module load.
1201da177e4SLinus Torvalds  *	20011003 Fix multiple requests
1211da177e4SLinus Torvalds  */
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds #include <linux/module.h>
1241da177e4SLinus Torvalds #include <linux/kernel.h>
1251da177e4SLinus Torvalds #include <linux/signal.h>
1261da177e4SLinus Torvalds #include <linux/errno.h>
1271da177e4SLinus Torvalds #include <linux/random.h>
1281da177e4SLinus Torvalds #include <linux/poll.h>
1291da177e4SLinus Torvalds #include <linux/slab.h>
1301da177e4SLinus Torvalds #include <linux/spinlock.h>
1311da177e4SLinus Torvalds #include <linux/usb.h>
1321da177e4SLinus Torvalds #include <linux/proc_fs.h>
13360063497SArun Sharma #include <linux/atomic.h>
1341da177e4SLinus Torvalds #include <linux/blkdev.h>
13553555fb7SBart Van Assche 
13653555fb7SBart Van Assche #include <scsi/scsi.h>
13753555fb7SBart Van Assche #include <scsi/scsi_cmnd.h>
13853555fb7SBart Van Assche #include <scsi/scsi_device.h>
13953555fb7SBart Van Assche #include <scsi/scsi_eh.h>
1401da177e4SLinus Torvalds #include <scsi/scsi_host.h>
14153555fb7SBart Van Assche #include <scsi/scsi_tcq.h>
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds #include "microtek.h"
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds #define DRIVER_AUTHOR "John Fremlin <vii@penguinpowered.com>, Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>"
1461da177e4SLinus Torvalds #define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver"
1471da177e4SLinus Torvalds 
1481da177e4SLinus Torvalds /* Should we do debugging? */
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds //#define MTS_DO_DEBUG
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds /* USB layer driver interface */
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds static int mts_usb_probe(struct usb_interface *intf,
1551da177e4SLinus Torvalds 			 const struct usb_device_id *id);
1561da177e4SLinus Torvalds static void mts_usb_disconnect(struct usb_interface *intf);
1571da177e4SLinus Torvalds 
1589052127fSNémeth Márton static const struct usb_device_id mts_usb_ids[];
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds static struct usb_driver mts_usb_driver = {
1611da177e4SLinus Torvalds 	.name =		"microtekX6",
1621da177e4SLinus Torvalds 	.probe =	mts_usb_probe,
1631da177e4SLinus Torvalds 	.disconnect =	mts_usb_disconnect,
1641da177e4SLinus Torvalds 	.id_table =	mts_usb_ids,
1651da177e4SLinus Torvalds };
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds /* Internal driver stuff */
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds #define MTS_VERSION	"0.4.3"
1711da177e4SLinus Torvalds #define MTS_NAME	"microtek usb (rev " MTS_VERSION "): "
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds #define MTS_WARNING(x...) \
1741da177e4SLinus Torvalds 	printk( KERN_WARNING MTS_NAME x )
1751da177e4SLinus Torvalds #define MTS_ERROR(x...) \
1761da177e4SLinus Torvalds 	printk( KERN_ERR MTS_NAME x )
1771da177e4SLinus Torvalds #define MTS_INT_ERROR(x...) \
1781da177e4SLinus Torvalds 	MTS_ERROR(x)
1791da177e4SLinus Torvalds #define MTS_MESSAGE(x...) \
1801da177e4SLinus Torvalds 	printk( KERN_INFO MTS_NAME x )
1811da177e4SLinus Torvalds 
1821da177e4SLinus Torvalds #if defined MTS_DO_DEBUG
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds #define MTS_DEBUG(x...) \
1851da177e4SLinus Torvalds 	printk( KERN_DEBUG MTS_NAME x )
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds #define MTS_DEBUG_GOT_HERE() \
18896e12fceSHarvey Harrison 	MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ )
1891da177e4SLinus Torvalds #define MTS_DEBUG_INT() \
1901da177e4SLinus Torvalds 	do { MTS_DEBUG_GOT_HERE(); \
1911da177e4SLinus Torvalds 	     MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
192b44cd112SGreg Kroah-Hartman 	     MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
1931da177e4SLinus Torvalds              mts_debug_dump(context->instance);\
1941da177e4SLinus Torvalds 	   } while(0)
1951da177e4SLinus Torvalds #else
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds #define MTS_NUL_STATEMENT do { } while(0)
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds #define MTS_DEBUG(x...)	MTS_NUL_STATEMENT
2001da177e4SLinus Torvalds #define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
2011da177e4SLinus Torvalds #define MTS_DEBUG_INT() MTS_NUL_STATEMENT
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds #endif
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds #define MTS_INT_INIT()\
2081da177e4SLinus Torvalds 	struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
2091da177e4SLinus Torvalds 	MTS_DEBUG_INT();\
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds #ifdef MTS_DO_DEBUG
2121da177e4SLinus Torvalds 
mts_debug_dump(struct mts_desc * desc)2131da177e4SLinus Torvalds static inline void mts_debug_dump(struct mts_desc* desc) {
2141da177e4SLinus Torvalds 	MTS_DEBUG("desc at 0x%x: toggle = %02x%02x\n",
2151da177e4SLinus Torvalds 		  (int)desc,
2161da177e4SLinus Torvalds 		  (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
2171da177e4SLinus Torvalds 		);
2181da177e4SLinus Torvalds 	MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
2191da177e4SLinus Torvalds 		  usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
2201da177e4SLinus Torvalds 		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
2211da177e4SLinus Torvalds 		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
2221da177e4SLinus Torvalds 		);
2231da177e4SLinus Torvalds }
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 
mts_show_command(struct scsi_cmnd * srb)2260eebe6acSHenrik Kretzschmar static inline void mts_show_command(struct scsi_cmnd *srb)
2271da177e4SLinus Torvalds {
2281da177e4SLinus Torvalds 	char *what = NULL;
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	switch (srb->cmnd[0]) {
2311da177e4SLinus Torvalds 	case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
2321da177e4SLinus Torvalds 	case REZERO_UNIT: what = "REZERO_UNIT"; break;
2331da177e4SLinus Torvalds 	case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
2341da177e4SLinus Torvalds 	case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
2351da177e4SLinus Torvalds 	case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
2361da177e4SLinus Torvalds 	case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
2371da177e4SLinus Torvalds 	case READ_6: what = "READ_6"; break;
2381da177e4SLinus Torvalds 	case WRITE_6: what = "WRITE_6"; break;
2391da177e4SLinus Torvalds 	case SEEK_6: what = "SEEK_6"; break;
2401da177e4SLinus Torvalds 	case READ_REVERSE: what = "READ_REVERSE"; break;
2411da177e4SLinus Torvalds 	case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
2421da177e4SLinus Torvalds 	case SPACE: what = "SPACE"; break;
2431da177e4SLinus Torvalds 	case INQUIRY: what = "INQUIRY"; break;
2441da177e4SLinus Torvalds 	case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
2451da177e4SLinus Torvalds 	case MODE_SELECT: what = "MODE_SELECT"; break;
2461da177e4SLinus Torvalds 	case RESERVE: what = "RESERVE"; break;
2471da177e4SLinus Torvalds 	case RELEASE: what = "RELEASE"; break;
2481da177e4SLinus Torvalds 	case COPY: what = "COPY"; break;
2491da177e4SLinus Torvalds 	case ERASE: what = "ERASE"; break;
2501da177e4SLinus Torvalds 	case MODE_SENSE: what = "MODE_SENSE"; break;
2511da177e4SLinus Torvalds 	case START_STOP: what = "START_STOP"; break;
2521da177e4SLinus Torvalds 	case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
2531da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
2541da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
2551da177e4SLinus Torvalds 	case SET_WINDOW: what = "SET_WINDOW"; break;
2561da177e4SLinus Torvalds 	case READ_CAPACITY: what = "READ_CAPACITY"; break;
2571da177e4SLinus Torvalds 	case READ_10: what = "READ_10"; break;
2581da177e4SLinus Torvalds 	case WRITE_10: what = "WRITE_10"; break;
2591da177e4SLinus Torvalds 	case SEEK_10: what = "SEEK_10"; break;
2601da177e4SLinus Torvalds 	case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
2611da177e4SLinus Torvalds 	case VERIFY: what = "VERIFY"; break;
2621da177e4SLinus Torvalds 	case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
2631da177e4SLinus Torvalds 	case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
2641da177e4SLinus Torvalds 	case SEARCH_LOW: what = "SEARCH_LOW"; break;
2651da177e4SLinus Torvalds 	case SET_LIMITS: what = "SET_LIMITS"; break;
2661da177e4SLinus Torvalds 	case READ_POSITION: what = "READ_POSITION"; break;
2671da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
2681da177e4SLinus Torvalds 	case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
2691da177e4SLinus Torvalds 	case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
2701da177e4SLinus Torvalds 	case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
2711da177e4SLinus Torvalds 	case COMPARE: what = "COMPARE"; break;
2721da177e4SLinus Torvalds 	case COPY_VERIFY: what = "COPY_VERIFY"; break;
2731da177e4SLinus Torvalds 	case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
2741da177e4SLinus Torvalds 	case READ_BUFFER: what = "READ_BUFFER"; break;
2751da177e4SLinus Torvalds 	case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
2761da177e4SLinus Torvalds 	case READ_LONG: what = "READ_LONG"; break;
2771da177e4SLinus Torvalds 	case WRITE_LONG: what = "WRITE_LONG"; break;
2781da177e4SLinus Torvalds 	case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
2791da177e4SLinus Torvalds 	case WRITE_SAME: what = "WRITE_SAME"; break;
2801da177e4SLinus Torvalds 	case READ_TOC: what = "READ_TOC"; break;
2811da177e4SLinus Torvalds 	case LOG_SELECT: what = "LOG_SELECT"; break;
2821da177e4SLinus Torvalds 	case LOG_SENSE: what = "LOG_SENSE"; break;
2831da177e4SLinus Torvalds 	case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
2841da177e4SLinus Torvalds 	case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
2851da177e4SLinus Torvalds 	case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
2861da177e4SLinus Torvalds 	case READ_12: what = "READ_12"; break;
2871da177e4SLinus Torvalds 	case WRITE_12: what = "WRITE_12"; break;
2881da177e4SLinus Torvalds 	case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
2891da177e4SLinus Torvalds 	case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
2901da177e4SLinus Torvalds 	case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
2911da177e4SLinus Torvalds 	case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
2921da177e4SLinus Torvalds 	case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
2931da177e4SLinus Torvalds 	case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
2941da177e4SLinus Torvalds 	case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
2951da177e4SLinus Torvalds 	default:
2961da177e4SLinus Torvalds 		MTS_DEBUG("can't decode command\n");
2971da177e4SLinus Torvalds 		goto out;
2981da177e4SLinus Torvalds 		break;
2991da177e4SLinus Torvalds 	}
3001da177e4SLinus Torvalds 	MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
3011da177e4SLinus Torvalds 
3021da177e4SLinus Torvalds  out:
30378a629e9SAndy Shevchenko 	MTS_DEBUG( "  %10ph\n", srb->cmnd);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds #else
3071da177e4SLinus Torvalds 
mts_show_command(struct scsi_cmnd * dummy)3080eebe6acSHenrik Kretzschmar static inline void mts_show_command(struct scsi_cmnd * dummy)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
mts_debug_dump(struct mts_desc * dummy)3121da177e4SLinus Torvalds static inline void mts_debug_dump(struct mts_desc* dummy)
3131da177e4SLinus Torvalds {
3141da177e4SLinus Torvalds }
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds #endif
3171da177e4SLinus Torvalds 
mts_urb_abort(struct mts_desc * desc)3181da177e4SLinus Torvalds static inline void mts_urb_abort(struct mts_desc* desc) {
3191da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
3201da177e4SLinus Torvalds 	mts_debug_dump(desc);
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	usb_kill_urb( desc->urb );
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds 
mts_slave_alloc(struct scsi_device * s)3259142d59aSOliver Neukum static int mts_slave_alloc (struct scsi_device *s)
3269142d59aSOliver Neukum {
3279142d59aSOliver Neukum 	s->inquiry_len = 0x24;
3289142d59aSOliver Neukum 	return 0;
3299142d59aSOliver Neukum }
3309142d59aSOliver Neukum 
mts_slave_configure(struct scsi_device * s)3319142d59aSOliver Neukum static int mts_slave_configure (struct scsi_device *s)
3329142d59aSOliver Neukum {
3339142d59aSOliver Neukum 	blk_queue_dma_alignment(s->request_queue, (512 - 1));
3349142d59aSOliver Neukum 	return 0;
3359142d59aSOliver Neukum }
3369142d59aSOliver Neukum 
mts_scsi_abort(struct scsi_cmnd * srb)3370eebe6acSHenrik Kretzschmar static int mts_scsi_abort(struct scsi_cmnd *srb)
3381da177e4SLinus Torvalds {
3391da177e4SLinus Torvalds 	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
3401da177e4SLinus Torvalds 
3411da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 	mts_urb_abort(desc);
3441da177e4SLinus Torvalds 
345275cfdf4SHideaki YOSHIFUJI 	return FAILED;
3461da177e4SLinus Torvalds }
3471da177e4SLinus Torvalds 
mts_scsi_host_reset(struct scsi_cmnd * srb)3480eebe6acSHenrik Kretzschmar static int mts_scsi_host_reset(struct scsi_cmnd *srb)
3491da177e4SLinus Torvalds {
3501da177e4SLinus Torvalds 	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
351011b15dfSAlan Stern 	int result;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
3541da177e4SLinus Torvalds 	mts_debug_dump(desc);
3551da177e4SLinus Torvalds 
356011b15dfSAlan Stern 	result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
357011b15dfSAlan Stern 	if (result == 0) {
35853b3531bSAlexey Dobriyan 		result = usb_reset_device(desc->usb_dev);
3591da177e4SLinus Torvalds 		usb_unlock_device(desc->usb_dev);
360011b15dfSAlan Stern 	}
3611da177e4SLinus Torvalds 	return result ? FAILED : SUCCESS;
3621da177e4SLinus Torvalds }
3631da177e4SLinus Torvalds 
3640eebe6acSHenrik Kretzschmar static int
365f281233dSJeff Garzik mts_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *srb);
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds static void mts_transfer_cleanup( struct urb *transfer );
3687d12e780SDavid Howells static void mts_do_sg(struct urb * transfer);
3691da177e4SLinus Torvalds 
37077933d72SJesper Juhl static inline
mts_int_submit_urb(struct urb * transfer,int pipe,void * data,unsigned length,usb_complete_t callback)3711da177e4SLinus Torvalds void mts_int_submit_urb (struct urb* transfer,
3721da177e4SLinus Torvalds 			int pipe,
3731da177e4SLinus Torvalds 			void* data,
3741da177e4SLinus Torvalds 			unsigned length,
3751da177e4SLinus Torvalds 			usb_complete_t callback )
3761da177e4SLinus Torvalds /* Interrupt context! */
3771da177e4SLinus Torvalds 
3781da177e4SLinus Torvalds /* Holding transfer->context->lock! */
3791da177e4SLinus Torvalds {
3801da177e4SLinus Torvalds 	int res;
3811da177e4SLinus Torvalds 
3821da177e4SLinus Torvalds 	MTS_INT_INIT();
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds 	usb_fill_bulk_urb(transfer,
3851da177e4SLinus Torvalds 		      context->instance->usb_dev,
3861da177e4SLinus Torvalds 		      pipe,
3871da177e4SLinus Torvalds 		      data,
3881da177e4SLinus Torvalds 		      length,
3891da177e4SLinus Torvalds 		      callback,
3901da177e4SLinus Torvalds 		      context
3911da177e4SLinus Torvalds 		);
3921da177e4SLinus Torvalds 
3931da177e4SLinus Torvalds 	res = usb_submit_urb( transfer, GFP_ATOMIC );
3941da177e4SLinus Torvalds 	if ( unlikely(res) ) {
3951da177e4SLinus Torvalds 		MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
396be171e48SOliver Neukum 		set_host_byte(context->srb, DID_ERROR);
3971da177e4SLinus Torvalds 		mts_transfer_cleanup(transfer);
3981da177e4SLinus Torvalds 	}
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
4011da177e4SLinus Torvalds 
mts_transfer_cleanup(struct urb * transfer)4021da177e4SLinus Torvalds static void mts_transfer_cleanup( struct urb *transfer )
4031da177e4SLinus Torvalds /* Interrupt context! */
4041da177e4SLinus Torvalds {
4051da177e4SLinus Torvalds 	MTS_INT_INIT();
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds 	if ( likely(context->final_callback != NULL) )
4081da177e4SLinus Torvalds 		context->final_callback(context->srb);
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
mts_transfer_done(struct urb * transfer)4117d12e780SDavid Howells static void mts_transfer_done( struct urb *transfer )
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	MTS_INT_INIT();
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	context->srb->result &= MTS_SCSI_ERR_MASK;
4169142d59aSOliver Neukum 	context->srb->result |= (unsigned)(*context->scsi_status)<<1;
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	mts_transfer_cleanup(transfer);
4191da177e4SLinus Torvalds }
4201da177e4SLinus Torvalds 
4211da177e4SLinus Torvalds 
mts_get_status(struct urb * transfer)4221da177e4SLinus Torvalds static void mts_get_status( struct urb *transfer )
4231da177e4SLinus Torvalds /* Interrupt context! */
4241da177e4SLinus Torvalds {
4251da177e4SLinus Torvalds 	MTS_INT_INIT();
4261da177e4SLinus Torvalds 
4271da177e4SLinus Torvalds 	mts_int_submit_urb(transfer,
4281da177e4SLinus Torvalds 			   usb_rcvbulkpipe(context->instance->usb_dev,
4291da177e4SLinus Torvalds 					   context->instance->ep_response),
4309142d59aSOliver Neukum 			   context->scsi_status,
4311da177e4SLinus Torvalds 			   1,
4321da177e4SLinus Torvalds 			   mts_transfer_done );
4331da177e4SLinus Torvalds }
4341da177e4SLinus Torvalds 
mts_data_done(struct urb * transfer)4357d12e780SDavid Howells static void mts_data_done( struct urb* transfer )
4361da177e4SLinus Torvalds /* Interrupt context! */
4371da177e4SLinus Torvalds {
438b44cd112SGreg Kroah-Hartman 	int status = transfer->status;
4391da177e4SLinus Torvalds 	MTS_INT_INIT();
4401da177e4SLinus Torvalds 
4411da177e4SLinus Torvalds 	if ( context->data_length != transfer->actual_length ) {
442afd9a033SBoaz Harrosh 		scsi_set_resid(context->srb, context->data_length -
443afd9a033SBoaz Harrosh 			       transfer->actual_length);
444b44cd112SGreg Kroah-Hartman 	} else if ( unlikely(status) ) {
445be171e48SOliver Neukum 		set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
4461da177e4SLinus Torvalds 	}
4471da177e4SLinus Torvalds 
4481da177e4SLinus Torvalds 	mts_get_status(transfer);
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds 
4511da177e4SLinus Torvalds 
mts_command_done(struct urb * transfer)4527d12e780SDavid Howells static void mts_command_done( struct urb *transfer )
4531da177e4SLinus Torvalds /* Interrupt context! */
4541da177e4SLinus Torvalds {
455b44cd112SGreg Kroah-Hartman 	int status = transfer->status;
4561da177e4SLinus Torvalds 	MTS_INT_INIT();
4571da177e4SLinus Torvalds 
458b44cd112SGreg Kroah-Hartman 	if ( unlikely(status) ) {
459b44cd112SGreg Kroah-Hartman 	        if (status == -ENOENT) {
4601da177e4SLinus Torvalds 		        /* We are being killed */
4611da177e4SLinus Torvalds 			MTS_DEBUG_GOT_HERE();
462be171e48SOliver Neukum 			set_host_byte(context->srb, DID_ABORT);
4631da177e4SLinus Torvalds                 } else {
4641da177e4SLinus Torvalds 		        /* A genuine error has occurred */
4651da177e4SLinus Torvalds 			MTS_DEBUG_GOT_HERE();
4661da177e4SLinus Torvalds 
467be171e48SOliver Neukum 		        set_host_byte(context->srb, DID_ERROR);
4681da177e4SLinus Torvalds                 }
4691da177e4SLinus Torvalds 		mts_transfer_cleanup(transfer);
4701da177e4SLinus Torvalds 
4711da177e4SLinus Torvalds 		return;
4721da177e4SLinus Torvalds 	}
4731da177e4SLinus Torvalds 
4741da177e4SLinus Torvalds 	if (context->srb->cmnd[0] == REQUEST_SENSE) {
4751da177e4SLinus Torvalds 		mts_int_submit_urb(transfer,
4761da177e4SLinus Torvalds 				   context->data_pipe,
4771da177e4SLinus Torvalds 				   context->srb->sense_buffer,
4781da177e4SLinus Torvalds 				   context->data_length,
4791da177e4SLinus Torvalds 				   mts_data_done);
4801da177e4SLinus Torvalds 	} else { if ( context->data ) {
4811da177e4SLinus Torvalds 			mts_int_submit_urb(transfer,
4821da177e4SLinus Torvalds 					   context->data_pipe,
4831da177e4SLinus Torvalds 					   context->data,
4841da177e4SLinus Torvalds 					   context->data_length,
485afd9a033SBoaz Harrosh 					   scsi_sg_count(context->srb) > 1 ?
486afd9a033SBoaz Harrosh 					           mts_do_sg : mts_data_done);
4871da177e4SLinus Torvalds 		} else {
4881da177e4SLinus Torvalds 			mts_get_status(transfer);
4891da177e4SLinus Torvalds 		}
4901da177e4SLinus Torvalds 	}
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds 
mts_do_sg(struct urb * transfer)4937d12e780SDavid Howells static void mts_do_sg (struct urb* transfer)
4941da177e4SLinus Torvalds {
495b44cd112SGreg Kroah-Hartman 	int status = transfer->status;
4961da177e4SLinus Torvalds 	MTS_INT_INIT();
4971da177e4SLinus Torvalds 
498afd9a033SBoaz Harrosh 	MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,
499afd9a033SBoaz Harrosh 	                                          scsi_sg_count(context->srb));
5001da177e4SLinus Torvalds 
501b44cd112SGreg Kroah-Hartman 	if (unlikely(status)) {
502be171e48SOliver Neukum                 set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
5031da177e4SLinus Torvalds 		mts_transfer_cleanup(transfer);
5041da177e4SLinus Torvalds         }
5051da177e4SLinus Torvalds 
5061194b5ceSMing Lei 	context->curr_sg = sg_next(context->curr_sg);
5071da177e4SLinus Torvalds 	mts_int_submit_urb(transfer,
5081da177e4SLinus Torvalds 			   context->data_pipe,
5091194b5ceSMing Lei 			   sg_virt(context->curr_sg),
5101194b5ceSMing Lei 			   context->curr_sg->length,
5111194b5ceSMing Lei 			   sg_is_last(context->curr_sg) ?
512afd9a033SBoaz Harrosh 			   mts_data_done : mts_do_sg);
5131da177e4SLinus Torvalds }
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
5161da177e4SLinus Torvalds static const u8 mts_read_image_sig_len = 4;
5171da177e4SLinus Torvalds static const unsigned char mts_direction[256/8] = {
5181da177e4SLinus Torvalds 	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
5191da177e4SLinus Torvalds 	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
5201da177e4SLinus Torvalds 	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
5211da177e4SLinus Torvalds 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5221da177e4SLinus Torvalds };
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds #define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
5261da177e4SLinus Torvalds 
5271da177e4SLinus Torvalds static void
mts_build_transfer_context(struct scsi_cmnd * srb,struct mts_desc * desc)5280eebe6acSHenrik Kretzschmar mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	int pipe;
5311da177e4SLinus Torvalds 
5321da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
5331da177e4SLinus Torvalds 
5341da177e4SLinus Torvalds 	desc->context.instance = desc;
5351da177e4SLinus Torvalds 	desc->context.srb = srb;
5361da177e4SLinus Torvalds 
537afd9a033SBoaz Harrosh 	if (!scsi_bufflen(srb)) {
5381da177e4SLinus Torvalds 		desc->context.data = NULL;
5391da177e4SLinus Torvalds 		desc->context.data_length = 0;
5401da177e4SLinus Torvalds 		return;
5411da177e4SLinus Torvalds 	} else {
5421194b5ceSMing Lei 		desc->context.curr_sg = scsi_sglist(srb);
5431194b5ceSMing Lei 		desc->context.data = sg_virt(desc->context.curr_sg);
5441194b5ceSMing Lei 		desc->context.data_length = desc->context.curr_sg->length;
5451da177e4SLinus Torvalds 	}
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	/* can't rely on srb->sc_data_direction */
5491da177e4SLinus Torvalds 
5501da177e4SLinus Torvalds 	/* Brutally ripped from usb-storage */
5511da177e4SLinus Torvalds 
5521da177e4SLinus Torvalds 	if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
5531da177e4SLinus Torvalds ) { 		pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
55425985edcSLucas De Marchi 		MTS_DEBUG( "transferring from desc->ep_image == %d\n",
5551da177e4SLinus Torvalds 			   (int)desc->ep_image );
5561da177e4SLinus Torvalds 	} else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
5571da177e4SLinus Torvalds 			pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
55825985edcSLucas De Marchi 			MTS_DEBUG( "transferring from desc->ep_response == %d\n",
5591da177e4SLinus Torvalds 				   (int)desc->ep_response);
5601da177e4SLinus Torvalds 	} else {
56125985edcSLucas De Marchi 		MTS_DEBUG("transferring to desc->ep_out == %d\n",
5621da177e4SLinus Torvalds 			  (int)desc->ep_out);
5631da177e4SLinus Torvalds 		pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
5641da177e4SLinus Torvalds 	}
5651da177e4SLinus Torvalds 	desc->context.data_pipe = pipe;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
mts_scsi_queuecommand_lck(struct scsi_cmnd * srb)568af049dfdSBart Van Assche static int mts_scsi_queuecommand_lck(struct scsi_cmnd *srb)
5691da177e4SLinus Torvalds {
570af049dfdSBart Van Assche 	mts_scsi_cmnd_callback callback = scsi_done;
5711da177e4SLinus Torvalds 	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
5721da177e4SLinus Torvalds 	int res;
5731da177e4SLinus Torvalds 
5741da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
5751da177e4SLinus Torvalds 	mts_show_command(srb);
5761da177e4SLinus Torvalds 	mts_debug_dump(desc);
5771da177e4SLinus Torvalds 
5781da177e4SLinus Torvalds 	if ( srb->device->lun || srb->device->id || srb->device->channel ) {
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds 		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 );
5811da177e4SLinus Torvalds 
5821da177e4SLinus Torvalds 		MTS_DEBUG("this device doesn't exist\n");
5831da177e4SLinus Torvalds 
584be171e48SOliver Neukum 		set_host_byte(srb, DID_BAD_TARGET);
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 		if(likely(callback != NULL))
5871da177e4SLinus Torvalds 			callback(srb);
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 		goto out;
5901da177e4SLinus Torvalds 	}
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds 	usb_fill_bulk_urb(desc->urb,
5941da177e4SLinus Torvalds 		      desc->usb_dev,
5951da177e4SLinus Torvalds 		      usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
5961da177e4SLinus Torvalds 		      srb->cmnd,
5971da177e4SLinus Torvalds 		      srb->cmd_len,
5981da177e4SLinus Torvalds 		      mts_command_done,
5991da177e4SLinus Torvalds 		      &desc->context
6001da177e4SLinus Torvalds 		      );
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 
6031da177e4SLinus Torvalds 	mts_build_transfer_context( srb, desc );
6041da177e4SLinus Torvalds 	desc->context.final_callback = callback;
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	/* here we need ATOMIC as we are called with the iolock */
6071da177e4SLinus Torvalds 	res=usb_submit_urb(desc->urb, GFP_ATOMIC);
6081da177e4SLinus Torvalds 
6091da177e4SLinus Torvalds 	if(unlikely(res)){
6101da177e4SLinus Torvalds 		MTS_ERROR("error %d submitting URB\n",(int)res);
611be171e48SOliver Neukum 		set_host_byte(srb, DID_ERROR);
6121da177e4SLinus Torvalds 
6131da177e4SLinus Torvalds 		if(likely(callback != NULL))
6141da177e4SLinus Torvalds 			callback(srb);
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds 	}
6171da177e4SLinus Torvalds out:
618a570ec79SSaurav Girepunje 	return 0;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
621f281233dSJeff Garzik static DEF_SCSI_QCMD(mts_scsi_queuecommand)
622f281233dSJeff Garzik 
623*04d1fa43SBart Van Assche static const struct scsi_host_template mts_scsi_host_template = {
6241da177e4SLinus Torvalds 	.module			= THIS_MODULE,
6251da177e4SLinus Torvalds 	.name			= "microtekX6",
6261da177e4SLinus Torvalds 	.proc_name		= "microtekX6",
6271da177e4SLinus Torvalds 	.queuecommand		= mts_scsi_queuecommand,
6281da177e4SLinus Torvalds 	.eh_abort_handler	= mts_scsi_abort,
6291da177e4SLinus Torvalds 	.eh_host_reset_handler	= mts_scsi_host_reset,
6301da177e4SLinus Torvalds 	.sg_tablesize =		SG_ALL,
6311da177e4SLinus Torvalds 	.can_queue =		1,
6321da177e4SLinus Torvalds 	.this_id =		-1,
6331da177e4SLinus Torvalds 	.emulated =		1,
6349142d59aSOliver Neukum 	.slave_alloc =		mts_slave_alloc,
6359142d59aSOliver Neukum 	.slave_configure =	mts_slave_configure,
6369142d59aSOliver Neukum 	.max_sectors=		256, /* 128 K */
6371da177e4SLinus Torvalds };
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds /* The entries of microtek_table must correspond, line-by-line to
6401da177e4SLinus Torvalds    the entries of mts_supported_products[]. */
6411da177e4SLinus Torvalds 
6429052127fSNémeth Márton static const struct usb_device_id mts_usb_ids[] =
6431da177e4SLinus Torvalds {
6441da177e4SLinus Torvalds 	{ USB_DEVICE(0x4ce, 0x0300) },
6451da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x0094) },
6461da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x0099) },
6471da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x009a) },
6481da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x00a0) },
6491da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x00a3) },
6501da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x80a3) },
6511da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x80ac) },
6521da177e4SLinus Torvalds 	{ USB_DEVICE(0x5da, 0x00b6) },
6531da177e4SLinus Torvalds 	{ }						/* Terminating entry */
6541da177e4SLinus Torvalds };
6551da177e4SLinus Torvalds 
6561da177e4SLinus Torvalds MODULE_DEVICE_TABLE (usb, mts_usb_ids);
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 
mts_usb_probe(struct usb_interface * intf,const struct usb_device_id * id)6591da177e4SLinus Torvalds static int mts_usb_probe(struct usb_interface *intf,
6601da177e4SLinus Torvalds 			 const struct usb_device_id *id)
6611da177e4SLinus Torvalds {
6621da177e4SLinus Torvalds 	int i;
6631da177e4SLinus Torvalds 	int ep_out = -1;
6641da177e4SLinus Torvalds 	int ep_in_set[3]; /* this will break if we have more than three endpoints
6651da177e4SLinus Torvalds 			   which is why we check */
6661da177e4SLinus Torvalds 	int *ep_in_current = ep_in_set;
6671da177e4SLinus Torvalds 	int err_retval = -ENOMEM;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	struct mts_desc * new_desc;
6701da177e4SLinus Torvalds 	struct usb_device *dev = interface_to_usbdev (intf);
6711da177e4SLinus Torvalds 
6721da177e4SLinus Torvalds 	/* the current altsetting on the interface we're probing */
6731da177e4SLinus Torvalds 	struct usb_host_interface *altsetting;
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
6761da177e4SLinus Torvalds 	MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds 	MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
6791da177e4SLinus Torvalds 		   le16_to_cpu(dev->descriptor.idProduct),
6801da177e4SLinus Torvalds 		   le16_to_cpu(dev->descriptor.idVendor) );
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds 	MTS_DEBUG_GOT_HERE();
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	/* the current altsetting on the interface we're probing */
6851da177e4SLinus Torvalds 	altsetting = intf->cur_altsetting;
6861da177e4SLinus Torvalds 
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 	/* Check if the config is sane */
6891da177e4SLinus Torvalds 
6901da177e4SLinus Torvalds 	if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) {
6911da177e4SLinus Torvalds 		MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
6921da177e4SLinus Torvalds 			     (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints );
6931da177e4SLinus Torvalds 		return -ENODEV;
6941da177e4SLinus Torvalds 	}
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds 	for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) {
6971da177e4SLinus Torvalds 		if ((altsetting->endpoint[i].desc.bmAttributes &
6981da177e4SLinus Torvalds 		     USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
6991da177e4SLinus Torvalds 
7001da177e4SLinus Torvalds 			MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
7011da177e4SLinus Torvalds 			     (int)altsetting->endpoint[i].desc.bEndpointAddress );
7021da177e4SLinus Torvalds 		} else {
7031da177e4SLinus Torvalds 			if (altsetting->endpoint[i].desc.bEndpointAddress &
7041da177e4SLinus Torvalds 			    USB_DIR_IN)
7051da177e4SLinus Torvalds 				*ep_in_current++
7061da177e4SLinus Torvalds 					= altsetting->endpoint[i].desc.bEndpointAddress &
7071da177e4SLinus Torvalds 					USB_ENDPOINT_NUMBER_MASK;
7081da177e4SLinus Torvalds 			else {
7091da177e4SLinus Torvalds 				if ( ep_out != -1 ) {
7101da177e4SLinus Torvalds 					MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
7111da177e4SLinus Torvalds 					return -ENODEV;
7121da177e4SLinus Torvalds 				}
7131da177e4SLinus Torvalds 
7141da177e4SLinus Torvalds 				ep_out = altsetting->endpoint[i].desc.bEndpointAddress &
7151da177e4SLinus Torvalds 					USB_ENDPOINT_NUMBER_MASK;
7161da177e4SLinus Torvalds 			}
7171da177e4SLinus Torvalds 		}
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds 	}
7201da177e4SLinus Torvalds 
721177238c3SJohan Hovold 	if (ep_in_current != &ep_in_set[2]) {
722177238c3SJohan Hovold 		MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n");
723177238c3SJohan Hovold 		return -ENODEV;
724177238c3SJohan Hovold 	}
7251da177e4SLinus Torvalds 
7261da177e4SLinus Torvalds 	if ( ep_out == -1 ) {
7271da177e4SLinus Torvalds 		MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
7281da177e4SLinus Torvalds 		return -ENODEV;
7291da177e4SLinus Torvalds 	}
7301da177e4SLinus Torvalds 
7311da177e4SLinus Torvalds 
73261eded6eSOliver Neukum 	new_desc = kzalloc(sizeof(struct mts_desc), GFP_KERNEL);
7331da177e4SLinus Torvalds 	if (!new_desc)
7341da177e4SLinus Torvalds 		goto out;
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds 	new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
7371da177e4SLinus Torvalds 	if (!new_desc->urb)
7381da177e4SLinus Torvalds 		goto out_kfree;
7391da177e4SLinus Torvalds 
7409142d59aSOliver Neukum 	new_desc->context.scsi_status = kmalloc(1, GFP_KERNEL);
7419142d59aSOliver Neukum 	if (!new_desc->context.scsi_status)
742a3b1f50cSMariusz Kozlowski 		goto out_free_urb;
7439142d59aSOliver Neukum 
7441da177e4SLinus Torvalds 	new_desc->usb_dev = dev;
7451da177e4SLinus Torvalds 	new_desc->usb_intf = intf;
7461da177e4SLinus Torvalds 
7471da177e4SLinus Torvalds 	/* endpoints */
7481da177e4SLinus Torvalds 	new_desc->ep_out = ep_out;
7491da177e4SLinus Torvalds 	new_desc->ep_response = ep_in_set[0];
7501da177e4SLinus Torvalds 	new_desc->ep_image = ep_in_set[1];
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	if ( new_desc->ep_out != MTS_EP_OUT )
7531da177e4SLinus Torvalds 		MTS_WARNING( "will this work? Command EP is not usually %d\n",
7541da177e4SLinus Torvalds 			     (int)new_desc->ep_out );
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 	if ( new_desc->ep_response != MTS_EP_RESPONSE )
7571da177e4SLinus Torvalds 		MTS_WARNING( "will this work? Response EP is not usually %d\n",
7581da177e4SLinus Torvalds 			     (int)new_desc->ep_response );
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds 	if ( new_desc->ep_image != MTS_EP_IMAGE )
7611da177e4SLinus Torvalds 		MTS_WARNING( "will this work? Image data EP is not usually %d\n",
7621da177e4SLinus Torvalds 			     (int)new_desc->ep_image );
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 	new_desc->host = scsi_host_alloc(&mts_scsi_host_template,
7651da177e4SLinus Torvalds 			sizeof(new_desc));
7661da177e4SLinus Torvalds 	if (!new_desc->host)
767a3b1f50cSMariusz Kozlowski 		goto out_kfree2;
7681da177e4SLinus Torvalds 
7691da177e4SLinus Torvalds 	new_desc->host->hostdata[0] = (unsigned long)new_desc;
7705cf1973aSOliver Neukum 	if (scsi_add_host(new_desc->host, &dev->dev)) {
7711da177e4SLinus Torvalds 		err_retval = -EIO;
772a3b1f50cSMariusz Kozlowski 		goto out_host_put;
7731da177e4SLinus Torvalds 	}
7741da177e4SLinus Torvalds 	scsi_scan_host(new_desc->host);
7751da177e4SLinus Torvalds 
7761da177e4SLinus Torvalds 	usb_set_intfdata(intf, new_desc);
7771da177e4SLinus Torvalds 	return 0;
7781da177e4SLinus Torvalds 
779a3b1f50cSMariusz Kozlowski  out_host_put:
780a3b1f50cSMariusz Kozlowski 	scsi_host_put(new_desc->host);
7819142d59aSOliver Neukum  out_kfree2:
7829142d59aSOliver Neukum 	kfree(new_desc->context.scsi_status);
7831da177e4SLinus Torvalds  out_free_urb:
7841da177e4SLinus Torvalds 	usb_free_urb(new_desc->urb);
7851da177e4SLinus Torvalds  out_kfree:
7861da177e4SLinus Torvalds 	kfree(new_desc);
7871da177e4SLinus Torvalds  out:
7881da177e4SLinus Torvalds 	return err_retval;
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds 
mts_usb_disconnect(struct usb_interface * intf)7911da177e4SLinus Torvalds static void mts_usb_disconnect (struct usb_interface *intf)
7921da177e4SLinus Torvalds {
7931da177e4SLinus Torvalds 	struct mts_desc *desc = usb_get_intfdata(intf);
7941da177e4SLinus Torvalds 
7951da177e4SLinus Torvalds 	usb_set_intfdata(intf, NULL);
7961da177e4SLinus Torvalds 
7971da177e4SLinus Torvalds 	usb_kill_urb(desc->urb);
7981da177e4SLinus Torvalds 	scsi_remove_host(desc->host);
7991da177e4SLinus Torvalds 
8001da177e4SLinus Torvalds 	scsi_host_put(desc->host);
8011da177e4SLinus Torvalds 	usb_free_urb(desc->urb);
8029142d59aSOliver Neukum 	kfree(desc->context.scsi_status);
8031da177e4SLinus Torvalds 	kfree(desc);
8041da177e4SLinus Torvalds }
8051da177e4SLinus Torvalds 
80665db4305SGreg Kroah-Hartman module_usb_driver(mts_usb_driver);
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds MODULE_AUTHOR( DRIVER_AUTHOR );
8091da177e4SLinus Torvalds MODULE_DESCRIPTION( DRIVER_DESC );
8101da177e4SLinus Torvalds MODULE_LICENSE("GPL");
811