18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * (C) Copyright 2000 John Fremlin <vii@penguinpowered.com>
58c2ecf20Sopenharmony_ci * (C) Copyright 2000 Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Parts shamelessly stolen from usb-storage and copyright by their
88c2ecf20Sopenharmony_ci * authors. Thanks to Matt Dharm for giving us permission!
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * This driver implements a SCSI host controller driver and a USB
118c2ecf20Sopenharmony_ci * device driver. To avoid confusion, all the USB related stuff is
128c2ecf20Sopenharmony_ci * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * Microtek (www.microtek.com) did not release the specifications for
158c2ecf20Sopenharmony_ci * their USB protocol to us, so we had to reverse engineer them. We
168c2ecf20Sopenharmony_ci * don't know for which models they are valid.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * The X6 USB has three bulk endpoints, one output (0x1) down which
198c2ecf20Sopenharmony_ci * commands and outgoing data are sent, and two input: 0x82 from which
208c2ecf20Sopenharmony_ci * normal data is read from the scanner (in packets of maximum 32
218c2ecf20Sopenharmony_ci * bytes) and from which the status byte is read, and 0x83 from which
228c2ecf20Sopenharmony_ci * the results of a scan (or preview) are read in up to 64 * 1024 byte
238c2ecf20Sopenharmony_ci * chunks by the Windows driver. We don't know how much it is possible
248c2ecf20Sopenharmony_ci * to read at a time from 0x83.
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * It seems possible to read (with URB transfers) everything from 0x82
278c2ecf20Sopenharmony_ci * in one go, without bothering to read in 32 byte chunks.
288c2ecf20Sopenharmony_ci *
298c2ecf20Sopenharmony_ci * There seems to be an optimisation of a further READ implicit if
308c2ecf20Sopenharmony_ci * you simply read from 0x83.
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Guessed protocol:
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci *	Send raw SCSI command to EP 0x1
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci *	If there is data to receive:
378c2ecf20Sopenharmony_ci *		If the command was READ datatype=image:
388c2ecf20Sopenharmony_ci *			Read a lot of data from EP 0x83
398c2ecf20Sopenharmony_ci *		Else:
408c2ecf20Sopenharmony_ci *			Read data from EP 0x82
418c2ecf20Sopenharmony_ci *	Else:
428c2ecf20Sopenharmony_ci *		If there is data to transmit:
438c2ecf20Sopenharmony_ci *			Write it to EP 0x1
448c2ecf20Sopenharmony_ci *
458c2ecf20Sopenharmony_ci *	Read status byte from EP 0x82
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * References:
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * The SCSI command set for the scanner is available from
508c2ecf20Sopenharmony_ci *	ftp://ftp.microtek.com/microtek/devpack/
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * Microtek NV sent us a more up to date version of the document. If
538c2ecf20Sopenharmony_ci * you want it, just send mail.
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * Status:
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci *	Untested with multiple scanners.
588c2ecf20Sopenharmony_ci *	Untested on SMP.
598c2ecf20Sopenharmony_ci *	Untested on a bigendian machine.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * History:
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci *	20000417 starting history
648c2ecf20Sopenharmony_ci *	20000417 fixed load oops
658c2ecf20Sopenharmony_ci *	20000417 fixed unload oops
668c2ecf20Sopenharmony_ci *	20000419 fixed READ IMAGE detection
678c2ecf20Sopenharmony_ci *	20000424 started conversion to use URBs
688c2ecf20Sopenharmony_ci *	20000502 handled short transfers as errors
698c2ecf20Sopenharmony_ci *	20000513 rename and organisation of functions (john)
708c2ecf20Sopenharmony_ci *	20000513 added IDs for all products supported by Windows driver (john)
718c2ecf20Sopenharmony_ci *	20000514 Rewrote mts_scsi_queuecommand to use URBs (john)
728c2ecf20Sopenharmony_ci *	20000514 Version 0.0.8j
738c2ecf20Sopenharmony_ci *      20000514 Fix reporting of non-existent devices to SCSI layer (john)
748c2ecf20Sopenharmony_ci *	20000514 Added MTS_DEBUG_INT (john)
758c2ecf20Sopenharmony_ci *	20000514 Changed "usb-microtek" to "microtek" for consistency (john)
768c2ecf20Sopenharmony_ci *	20000514 Stupid bug fixes (john)
778c2ecf20Sopenharmony_ci *	20000514 Version 0.0.9j
788c2ecf20Sopenharmony_ci *	20000515 Put transfer context and URB in mts_desc (john)
798c2ecf20Sopenharmony_ci *	20000515 Added prelim turn off debugging support (john)
808c2ecf20Sopenharmony_ci *	20000515 Version 0.0.10j
818c2ecf20Sopenharmony_ci *      20000515 Fixed up URB allocation (clear URB on alloc) (john)
828c2ecf20Sopenharmony_ci *      20000515 Version 0.0.11j
838c2ecf20Sopenharmony_ci *	20000516 Removed unnecessary spinlock in mts_transfer_context (john)
848c2ecf20Sopenharmony_ci *	20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john)
858c2ecf20Sopenharmony_ci *	20000516 Implemented (badly) scsi_abort (john)
868c2ecf20Sopenharmony_ci *	20000516 Version 0.0.12j
878c2ecf20Sopenharmony_ci *      20000517 Hopefully removed mts_remove_nolock quasideadlock (john)
888c2ecf20Sopenharmony_ci *      20000517 Added mts_debug_dump to print ll USB info (john)
898c2ecf20Sopenharmony_ci *	20000518 Tweaks and documentation updates (john)
908c2ecf20Sopenharmony_ci *	20000518 Version 0.0.13j
918c2ecf20Sopenharmony_ci *	20000518 Cleaned up abort handling (john)
928c2ecf20Sopenharmony_ci *	20000523 Removed scsi_command and various scsi_..._resets (john)
938c2ecf20Sopenharmony_ci *	20000523 Added unlink URB on scsi_abort, now OHCI supports it (john)
948c2ecf20Sopenharmony_ci *	20000523 Fixed last tiresome compile warning (john)
958c2ecf20Sopenharmony_ci *	20000523 Version 0.0.14j (though version 0.1 has come out?)
968c2ecf20Sopenharmony_ci *	20000602 Added primitive reset
978c2ecf20Sopenharmony_ci *	20000602 Version 0.2.0
988c2ecf20Sopenharmony_ci *	20000603 various cosmetic changes
998c2ecf20Sopenharmony_ci *	20000603 Version 0.2.1
1008c2ecf20Sopenharmony_ci *	20000620 minor cosmetic changes
1018c2ecf20Sopenharmony_ci *	20000620 Version 0.2.2
1028c2ecf20Sopenharmony_ci *	20000822 Hopefully fixed deadlock in mts_remove_nolock()
1038c2ecf20Sopenharmony_ci *	20000822 Fixed minor race in mts_transfer_cleanup()
1048c2ecf20Sopenharmony_ci *	20000822 Fixed deadlock on submission error in queuecommand
1058c2ecf20Sopenharmony_ci *	20000822 Version 0.2.3
1068c2ecf20Sopenharmony_ci *	20000913 Reduced module size if debugging is off
1078c2ecf20Sopenharmony_ci *	20000913 Version 0.2.4
1088c2ecf20Sopenharmony_ci *      20010210 New abort logic
1098c2ecf20Sopenharmony_ci *      20010210 Version 0.3.0
1108c2ecf20Sopenharmony_ci *	20010217 Merged scatter/gather
1118c2ecf20Sopenharmony_ci *	20010218 Version 0.4.0
1128c2ecf20Sopenharmony_ci *	20010218 Cosmetic fixes
1138c2ecf20Sopenharmony_ci *	20010218 Version 0.4.1
1148c2ecf20Sopenharmony_ci *      20010306 Abort while using scatter/gather
1158c2ecf20Sopenharmony_ci *      20010306 Version 0.4.2
1168c2ecf20Sopenharmony_ci *      20010311 Remove all timeouts and tidy up generally (john)
1178c2ecf20Sopenharmony_ci *	20010320 check return value of scsi_register()
1188c2ecf20Sopenharmony_ci *	20010320 Version 0.4.3
1198c2ecf20Sopenharmony_ci *	20010408 Identify version on module load.
1208c2ecf20Sopenharmony_ci *	20011003 Fix multiple requests
1218c2ecf20Sopenharmony_ci */
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#include <linux/module.h>
1248c2ecf20Sopenharmony_ci#include <linux/kernel.h>
1258c2ecf20Sopenharmony_ci#include <linux/signal.h>
1268c2ecf20Sopenharmony_ci#include <linux/errno.h>
1278c2ecf20Sopenharmony_ci#include <linux/random.h>
1288c2ecf20Sopenharmony_ci#include <linux/poll.h>
1298c2ecf20Sopenharmony_ci#include <linux/slab.h>
1308c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
1318c2ecf20Sopenharmony_ci#include <linux/usb.h>
1328c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci#include <linux/atomic.h>
1358c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
1368c2ecf20Sopenharmony_ci#include "../../scsi/scsi.h"
1378c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#include "microtek.h"
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "John Fremlin <vii@penguinpowered.com>, Oliver Neukum <Oliver.Neukum@lrz.uni-muenchen.de>"
1428c2ecf20Sopenharmony_ci#define DRIVER_DESC "Microtek Scanmaker X6 USB scanner driver"
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/* Should we do debugging? */
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci//#define MTS_DO_DEBUG
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* USB layer driver interface */
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int mts_usb_probe(struct usb_interface *intf,
1518c2ecf20Sopenharmony_ci			 const struct usb_device_id *id);
1528c2ecf20Sopenharmony_cistatic void mts_usb_disconnect(struct usb_interface *intf);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic const struct usb_device_id mts_usb_ids[];
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic struct usb_driver mts_usb_driver = {
1578c2ecf20Sopenharmony_ci	.name =		"microtekX6",
1588c2ecf20Sopenharmony_ci	.probe =	mts_usb_probe,
1598c2ecf20Sopenharmony_ci	.disconnect =	mts_usb_disconnect,
1608c2ecf20Sopenharmony_ci	.id_table =	mts_usb_ids,
1618c2ecf20Sopenharmony_ci};
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/* Internal driver stuff */
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci#define MTS_VERSION	"0.4.3"
1678c2ecf20Sopenharmony_ci#define MTS_NAME	"microtek usb (rev " MTS_VERSION "): "
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci#define MTS_WARNING(x...) \
1708c2ecf20Sopenharmony_ci	printk( KERN_WARNING MTS_NAME x )
1718c2ecf20Sopenharmony_ci#define MTS_ERROR(x...) \
1728c2ecf20Sopenharmony_ci	printk( KERN_ERR MTS_NAME x )
1738c2ecf20Sopenharmony_ci#define MTS_INT_ERROR(x...) \
1748c2ecf20Sopenharmony_ci	MTS_ERROR(x)
1758c2ecf20Sopenharmony_ci#define MTS_MESSAGE(x...) \
1768c2ecf20Sopenharmony_ci	printk( KERN_INFO MTS_NAME x )
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci#if defined MTS_DO_DEBUG
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci#define MTS_DEBUG(x...) \
1818c2ecf20Sopenharmony_ci	printk( KERN_DEBUG MTS_NAME x )
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci#define MTS_DEBUG_GOT_HERE() \
1848c2ecf20Sopenharmony_ci	MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __func__ )
1858c2ecf20Sopenharmony_ci#define MTS_DEBUG_INT() \
1868c2ecf20Sopenharmony_ci	do { MTS_DEBUG_GOT_HERE(); \
1878c2ecf20Sopenharmony_ci	     MTS_DEBUG("transfer = 0x%x context = 0x%x\n",(int)transfer,(int)context ); \
1888c2ecf20Sopenharmony_ci	     MTS_DEBUG("status = 0x%x data-length = 0x%x sent = 0x%x\n",transfer->status,(int)context->data_length, (int)transfer->actual_length ); \
1898c2ecf20Sopenharmony_ci             mts_debug_dump(context->instance);\
1908c2ecf20Sopenharmony_ci	   } while(0)
1918c2ecf20Sopenharmony_ci#else
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci#define MTS_NUL_STATEMENT do { } while(0)
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci#define MTS_DEBUG(x...)	MTS_NUL_STATEMENT
1968c2ecf20Sopenharmony_ci#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT
1978c2ecf20Sopenharmony_ci#define MTS_DEBUG_INT() MTS_NUL_STATEMENT
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#endif
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci#define MTS_INT_INIT()\
2048c2ecf20Sopenharmony_ci	struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; \
2058c2ecf20Sopenharmony_ci	MTS_DEBUG_INT();\
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci#ifdef MTS_DO_DEBUG
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic inline void mts_debug_dump(struct mts_desc* desc) {
2108c2ecf20Sopenharmony_ci	MTS_DEBUG("desc at 0x%x: toggle = %02x%02x\n",
2118c2ecf20Sopenharmony_ci		  (int)desc,
2128c2ecf20Sopenharmony_ci		  (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0]
2138c2ecf20Sopenharmony_ci		);
2148c2ecf20Sopenharmony_ci	MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n",
2158c2ecf20Sopenharmony_ci		  usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
2168c2ecf20Sopenharmony_ci		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_response),
2178c2ecf20Sopenharmony_ci		  usb_rcvbulkpipe(desc->usb_dev,desc->ep_image)
2188c2ecf20Sopenharmony_ci		);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic inline void mts_show_command(struct scsi_cmnd *srb)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	char *what = NULL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	switch (srb->cmnd[0]) {
2278c2ecf20Sopenharmony_ci	case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break;
2288c2ecf20Sopenharmony_ci	case REZERO_UNIT: what = "REZERO_UNIT"; break;
2298c2ecf20Sopenharmony_ci	case REQUEST_SENSE: what = "REQUEST_SENSE"; break;
2308c2ecf20Sopenharmony_ci	case FORMAT_UNIT: what = "FORMAT_UNIT"; break;
2318c2ecf20Sopenharmony_ci	case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break;
2328c2ecf20Sopenharmony_ci	case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break;
2338c2ecf20Sopenharmony_ci	case READ_6: what = "READ_6"; break;
2348c2ecf20Sopenharmony_ci	case WRITE_6: what = "WRITE_6"; break;
2358c2ecf20Sopenharmony_ci	case SEEK_6: what = "SEEK_6"; break;
2368c2ecf20Sopenharmony_ci	case READ_REVERSE: what = "READ_REVERSE"; break;
2378c2ecf20Sopenharmony_ci	case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break;
2388c2ecf20Sopenharmony_ci	case SPACE: what = "SPACE"; break;
2398c2ecf20Sopenharmony_ci	case INQUIRY: what = "INQUIRY"; break;
2408c2ecf20Sopenharmony_ci	case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break;
2418c2ecf20Sopenharmony_ci	case MODE_SELECT: what = "MODE_SELECT"; break;
2428c2ecf20Sopenharmony_ci	case RESERVE: what = "RESERVE"; break;
2438c2ecf20Sopenharmony_ci	case RELEASE: what = "RELEASE"; break;
2448c2ecf20Sopenharmony_ci	case COPY: what = "COPY"; break;
2458c2ecf20Sopenharmony_ci	case ERASE: what = "ERASE"; break;
2468c2ecf20Sopenharmony_ci	case MODE_SENSE: what = "MODE_SENSE"; break;
2478c2ecf20Sopenharmony_ci	case START_STOP: what = "START_STOP"; break;
2488c2ecf20Sopenharmony_ci	case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break;
2498c2ecf20Sopenharmony_ci	case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break;
2508c2ecf20Sopenharmony_ci	case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break;
2518c2ecf20Sopenharmony_ci	case SET_WINDOW: what = "SET_WINDOW"; break;
2528c2ecf20Sopenharmony_ci	case READ_CAPACITY: what = "READ_CAPACITY"; break;
2538c2ecf20Sopenharmony_ci	case READ_10: what = "READ_10"; break;
2548c2ecf20Sopenharmony_ci	case WRITE_10: what = "WRITE_10"; break;
2558c2ecf20Sopenharmony_ci	case SEEK_10: what = "SEEK_10"; break;
2568c2ecf20Sopenharmony_ci	case WRITE_VERIFY: what = "WRITE_VERIFY"; break;
2578c2ecf20Sopenharmony_ci	case VERIFY: what = "VERIFY"; break;
2588c2ecf20Sopenharmony_ci	case SEARCH_HIGH: what = "SEARCH_HIGH"; break;
2598c2ecf20Sopenharmony_ci	case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break;
2608c2ecf20Sopenharmony_ci	case SEARCH_LOW: what = "SEARCH_LOW"; break;
2618c2ecf20Sopenharmony_ci	case SET_LIMITS: what = "SET_LIMITS"; break;
2628c2ecf20Sopenharmony_ci	case READ_POSITION: what = "READ_POSITION"; break;
2638c2ecf20Sopenharmony_ci	case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break;
2648c2ecf20Sopenharmony_ci	case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break;
2658c2ecf20Sopenharmony_ci	case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break;
2668c2ecf20Sopenharmony_ci	case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break;
2678c2ecf20Sopenharmony_ci	case COMPARE: what = "COMPARE"; break;
2688c2ecf20Sopenharmony_ci	case COPY_VERIFY: what = "COPY_VERIFY"; break;
2698c2ecf20Sopenharmony_ci	case WRITE_BUFFER: what = "WRITE_BUFFER"; break;
2708c2ecf20Sopenharmony_ci	case READ_BUFFER: what = "READ_BUFFER"; break;
2718c2ecf20Sopenharmony_ci	case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break;
2728c2ecf20Sopenharmony_ci	case READ_LONG: what = "READ_LONG"; break;
2738c2ecf20Sopenharmony_ci	case WRITE_LONG: what = "WRITE_LONG"; break;
2748c2ecf20Sopenharmony_ci	case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break;
2758c2ecf20Sopenharmony_ci	case WRITE_SAME: what = "WRITE_SAME"; break;
2768c2ecf20Sopenharmony_ci	case READ_TOC: what = "READ_TOC"; break;
2778c2ecf20Sopenharmony_ci	case LOG_SELECT: what = "LOG_SELECT"; break;
2788c2ecf20Sopenharmony_ci	case LOG_SENSE: what = "LOG_SENSE"; break;
2798c2ecf20Sopenharmony_ci	case MODE_SELECT_10: what = "MODE_SELECT_10"; break;
2808c2ecf20Sopenharmony_ci	case MODE_SENSE_10: what = "MODE_SENSE_10"; break;
2818c2ecf20Sopenharmony_ci	case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break;
2828c2ecf20Sopenharmony_ci	case READ_12: what = "READ_12"; break;
2838c2ecf20Sopenharmony_ci	case WRITE_12: what = "WRITE_12"; break;
2848c2ecf20Sopenharmony_ci	case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break;
2858c2ecf20Sopenharmony_ci	case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break;
2868c2ecf20Sopenharmony_ci	case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break;
2878c2ecf20Sopenharmony_ci	case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break;
2888c2ecf20Sopenharmony_ci	case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break;
2898c2ecf20Sopenharmony_ci	case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break;
2908c2ecf20Sopenharmony_ci	case WRITE_LONG_2: what = "WRITE_LONG_2"; break;
2918c2ecf20Sopenharmony_ci	default:
2928c2ecf20Sopenharmony_ci		MTS_DEBUG("can't decode command\n");
2938c2ecf20Sopenharmony_ci		goto out;
2948c2ecf20Sopenharmony_ci		break;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len);
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci out:
2998c2ecf20Sopenharmony_ci	MTS_DEBUG( "  %10ph\n", srb->cmnd);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci#else
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic inline void mts_show_command(struct scsi_cmnd * dummy)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic inline void mts_debug_dump(struct mts_desc* dummy)
3098c2ecf20Sopenharmony_ci{
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci#endif
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic inline void mts_urb_abort(struct mts_desc* desc) {
3158c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
3168c2ecf20Sopenharmony_ci	mts_debug_dump(desc);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	usb_kill_urb( desc->urb );
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic int mts_slave_alloc (struct scsi_device *s)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	s->inquiry_len = 0x24;
3248c2ecf20Sopenharmony_ci	return 0;
3258c2ecf20Sopenharmony_ci}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int mts_slave_configure (struct scsi_device *s)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	blk_queue_dma_alignment(s->request_queue, (512 - 1));
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int mts_scsi_abort(struct scsi_cmnd *srb)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	mts_urb_abort(desc);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return FAILED;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int mts_scsi_host_reset(struct scsi_cmnd *srb)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
3478c2ecf20Sopenharmony_ci	int result;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
3508c2ecf20Sopenharmony_ci	mts_debug_dump(desc);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	result = usb_lock_device_for_reset(desc->usb_dev, desc->usb_intf);
3538c2ecf20Sopenharmony_ci	if (result == 0) {
3548c2ecf20Sopenharmony_ci		result = usb_reset_device(desc->usb_dev);
3558c2ecf20Sopenharmony_ci		usb_unlock_device(desc->usb_dev);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	return result ? FAILED : SUCCESS;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic int
3618c2ecf20Sopenharmony_cimts_scsi_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *srb);
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic void mts_transfer_cleanup( struct urb *transfer );
3648c2ecf20Sopenharmony_cistatic void mts_do_sg(struct urb * transfer);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic inline
3678c2ecf20Sopenharmony_civoid mts_int_submit_urb (struct urb* transfer,
3688c2ecf20Sopenharmony_ci			int pipe,
3698c2ecf20Sopenharmony_ci			void* data,
3708c2ecf20Sopenharmony_ci			unsigned length,
3718c2ecf20Sopenharmony_ci			usb_complete_t callback )
3728c2ecf20Sopenharmony_ci/* Interrupt context! */
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci/* Holding transfer->context->lock! */
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	int res;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	MTS_INT_INIT();
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(transfer,
3818c2ecf20Sopenharmony_ci		      context->instance->usb_dev,
3828c2ecf20Sopenharmony_ci		      pipe,
3838c2ecf20Sopenharmony_ci		      data,
3848c2ecf20Sopenharmony_ci		      length,
3858c2ecf20Sopenharmony_ci		      callback,
3868c2ecf20Sopenharmony_ci		      context
3878c2ecf20Sopenharmony_ci		);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	res = usb_submit_urb( transfer, GFP_ATOMIC );
3908c2ecf20Sopenharmony_ci	if ( unlikely(res) ) {
3918c2ecf20Sopenharmony_ci		MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res );
3928c2ecf20Sopenharmony_ci		set_host_byte(context->srb, DID_ERROR);
3938c2ecf20Sopenharmony_ci		mts_transfer_cleanup(transfer);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_cistatic void mts_transfer_cleanup( struct urb *transfer )
3998c2ecf20Sopenharmony_ci/* Interrupt context! */
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	if ( likely(context->final_callback != NULL) )
4048c2ecf20Sopenharmony_ci		context->final_callback(context->srb);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void mts_transfer_done( struct urb *transfer )
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	context->srb->result &= MTS_SCSI_ERR_MASK;
4128c2ecf20Sopenharmony_ci	context->srb->result |= (unsigned)(*context->scsi_status)<<1;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	mts_transfer_cleanup(transfer);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cistatic void mts_get_status( struct urb *transfer )
4198c2ecf20Sopenharmony_ci/* Interrupt context! */
4208c2ecf20Sopenharmony_ci{
4218c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	mts_int_submit_urb(transfer,
4248c2ecf20Sopenharmony_ci			   usb_rcvbulkpipe(context->instance->usb_dev,
4258c2ecf20Sopenharmony_ci					   context->instance->ep_response),
4268c2ecf20Sopenharmony_ci			   context->scsi_status,
4278c2ecf20Sopenharmony_ci			   1,
4288c2ecf20Sopenharmony_ci			   mts_transfer_done );
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic void mts_data_done( struct urb* transfer )
4328c2ecf20Sopenharmony_ci/* Interrupt context! */
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	int status = transfer->status;
4358c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	if ( context->data_length != transfer->actual_length ) {
4388c2ecf20Sopenharmony_ci		scsi_set_resid(context->srb, context->data_length -
4398c2ecf20Sopenharmony_ci			       transfer->actual_length);
4408c2ecf20Sopenharmony_ci	} else if ( unlikely(status) ) {
4418c2ecf20Sopenharmony_ci		set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	mts_get_status(transfer);
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void mts_command_done( struct urb *transfer )
4498c2ecf20Sopenharmony_ci/* Interrupt context! */
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	int status = transfer->status;
4528c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if ( unlikely(status) ) {
4558c2ecf20Sopenharmony_ci	        if (status == -ENOENT) {
4568c2ecf20Sopenharmony_ci		        /* We are being killed */
4578c2ecf20Sopenharmony_ci			MTS_DEBUG_GOT_HERE();
4588c2ecf20Sopenharmony_ci			set_host_byte(context->srb, DID_ABORT);
4598c2ecf20Sopenharmony_ci                } else {
4608c2ecf20Sopenharmony_ci		        /* A genuine error has occurred */
4618c2ecf20Sopenharmony_ci			MTS_DEBUG_GOT_HERE();
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		        set_host_byte(context->srb, DID_ERROR);
4648c2ecf20Sopenharmony_ci                }
4658c2ecf20Sopenharmony_ci		mts_transfer_cleanup(transfer);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci		return;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (context->srb->cmnd[0] == REQUEST_SENSE) {
4718c2ecf20Sopenharmony_ci		mts_int_submit_urb(transfer,
4728c2ecf20Sopenharmony_ci				   context->data_pipe,
4738c2ecf20Sopenharmony_ci				   context->srb->sense_buffer,
4748c2ecf20Sopenharmony_ci				   context->data_length,
4758c2ecf20Sopenharmony_ci				   mts_data_done);
4768c2ecf20Sopenharmony_ci	} else { if ( context->data ) {
4778c2ecf20Sopenharmony_ci			mts_int_submit_urb(transfer,
4788c2ecf20Sopenharmony_ci					   context->data_pipe,
4798c2ecf20Sopenharmony_ci					   context->data,
4808c2ecf20Sopenharmony_ci					   context->data_length,
4818c2ecf20Sopenharmony_ci					   scsi_sg_count(context->srb) > 1 ?
4828c2ecf20Sopenharmony_ci					           mts_do_sg : mts_data_done);
4838c2ecf20Sopenharmony_ci		} else {
4848c2ecf20Sopenharmony_ci			mts_get_status(transfer);
4858c2ecf20Sopenharmony_ci		}
4868c2ecf20Sopenharmony_ci	}
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic void mts_do_sg (struct urb* transfer)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	int status = transfer->status;
4928c2ecf20Sopenharmony_ci	MTS_INT_INIT();
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	MTS_DEBUG("Processing fragment %d of %d\n", context->fragment,
4958c2ecf20Sopenharmony_ci	                                          scsi_sg_count(context->srb));
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (unlikely(status)) {
4988c2ecf20Sopenharmony_ci                set_host_byte(context->srb, (status == -ENOENT ? DID_ABORT : DID_ERROR));
4998c2ecf20Sopenharmony_ci		mts_transfer_cleanup(transfer);
5008c2ecf20Sopenharmony_ci        }
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	context->curr_sg = sg_next(context->curr_sg);
5038c2ecf20Sopenharmony_ci	mts_int_submit_urb(transfer,
5048c2ecf20Sopenharmony_ci			   context->data_pipe,
5058c2ecf20Sopenharmony_ci			   sg_virt(context->curr_sg),
5068c2ecf20Sopenharmony_ci			   context->curr_sg->length,
5078c2ecf20Sopenharmony_ci			   sg_is_last(context->curr_sg) ?
5088c2ecf20Sopenharmony_ci			   mts_data_done : mts_do_sg);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_cistatic const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 };
5128c2ecf20Sopenharmony_cistatic const u8 mts_read_image_sig_len = 4;
5138c2ecf20Sopenharmony_cistatic const unsigned char mts_direction[256/8] = {
5148c2ecf20Sopenharmony_ci	0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
5158c2ecf20Sopenharmony_ci	0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
5168c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,
5178c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
5188c2ecf20Sopenharmony_ci};
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_cistatic void
5248c2ecf20Sopenharmony_cimts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	int pipe;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	desc->context.instance = desc;
5318c2ecf20Sopenharmony_ci	desc->context.srb = srb;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (!scsi_bufflen(srb)) {
5348c2ecf20Sopenharmony_ci		desc->context.data = NULL;
5358c2ecf20Sopenharmony_ci		desc->context.data_length = 0;
5368c2ecf20Sopenharmony_ci		return;
5378c2ecf20Sopenharmony_ci	} else {
5388c2ecf20Sopenharmony_ci		desc->context.curr_sg = scsi_sglist(srb);
5398c2ecf20Sopenharmony_ci		desc->context.data = sg_virt(desc->context.curr_sg);
5408c2ecf20Sopenharmony_ci		desc->context.data_length = desc->context.curr_sg->length;
5418c2ecf20Sopenharmony_ci	}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* can't rely on srb->sc_data_direction */
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	/* Brutally ripped from usb-storage */
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len )
5498c2ecf20Sopenharmony_ci) { 		pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image);
5508c2ecf20Sopenharmony_ci		MTS_DEBUG( "transferring from desc->ep_image == %d\n",
5518c2ecf20Sopenharmony_ci			   (int)desc->ep_image );
5528c2ecf20Sopenharmony_ci	} else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) {
5538c2ecf20Sopenharmony_ci			pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response);
5548c2ecf20Sopenharmony_ci			MTS_DEBUG( "transferring from desc->ep_response == %d\n",
5558c2ecf20Sopenharmony_ci				   (int)desc->ep_response);
5568c2ecf20Sopenharmony_ci	} else {
5578c2ecf20Sopenharmony_ci		MTS_DEBUG("transferring to desc->ep_out == %d\n",
5588c2ecf20Sopenharmony_ci			  (int)desc->ep_out);
5598c2ecf20Sopenharmony_ci		pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out);
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	desc->context.data_pipe = pipe;
5628c2ecf20Sopenharmony_ci}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int
5668c2ecf20Sopenharmony_cimts_scsi_queuecommand_lck(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
5698c2ecf20Sopenharmony_ci	int res;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
5728c2ecf20Sopenharmony_ci	mts_show_command(srb);
5738c2ecf20Sopenharmony_ci	mts_debug_dump(desc);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if ( srb->device->lun || srb->device->id || srb->device->channel ) {
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci		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 );
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		MTS_DEBUG("this device doesn't exist\n");
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		set_host_byte(srb, DID_BAD_TARGET);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		if(likely(callback != NULL))
5848c2ecf20Sopenharmony_ci			callback(srb);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		goto out;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	usb_fill_bulk_urb(desc->urb,
5918c2ecf20Sopenharmony_ci		      desc->usb_dev,
5928c2ecf20Sopenharmony_ci		      usb_sndbulkpipe(desc->usb_dev,desc->ep_out),
5938c2ecf20Sopenharmony_ci		      srb->cmnd,
5948c2ecf20Sopenharmony_ci		      srb->cmd_len,
5958c2ecf20Sopenharmony_ci		      mts_command_done,
5968c2ecf20Sopenharmony_ci		      &desc->context
5978c2ecf20Sopenharmony_ci		      );
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	mts_build_transfer_context( srb, desc );
6018c2ecf20Sopenharmony_ci	desc->context.final_callback = callback;
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* here we need ATOMIC as we are called with the iolock */
6048c2ecf20Sopenharmony_ci	res=usb_submit_urb(desc->urb, GFP_ATOMIC);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	if(unlikely(res)){
6078c2ecf20Sopenharmony_ci		MTS_ERROR("error %d submitting URB\n",(int)res);
6088c2ecf20Sopenharmony_ci		set_host_byte(srb, DID_ERROR);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		if(likely(callback != NULL))
6118c2ecf20Sopenharmony_ci			callback(srb);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ciout:
6158c2ecf20Sopenharmony_ci	return 0;
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(mts_scsi_queuecommand)
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic struct scsi_host_template mts_scsi_host_template = {
6218c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
6228c2ecf20Sopenharmony_ci	.name			= "microtekX6",
6238c2ecf20Sopenharmony_ci	.proc_name		= "microtekX6",
6248c2ecf20Sopenharmony_ci	.queuecommand		= mts_scsi_queuecommand,
6258c2ecf20Sopenharmony_ci	.eh_abort_handler	= mts_scsi_abort,
6268c2ecf20Sopenharmony_ci	.eh_host_reset_handler	= mts_scsi_host_reset,
6278c2ecf20Sopenharmony_ci	.sg_tablesize =		SG_ALL,
6288c2ecf20Sopenharmony_ci	.can_queue =		1,
6298c2ecf20Sopenharmony_ci	.this_id =		-1,
6308c2ecf20Sopenharmony_ci	.emulated =		1,
6318c2ecf20Sopenharmony_ci	.slave_alloc =		mts_slave_alloc,
6328c2ecf20Sopenharmony_ci	.slave_configure =	mts_slave_configure,
6338c2ecf20Sopenharmony_ci	.max_sectors=		256, /* 128 K */
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci/* The entries of microtek_table must correspond, line-by-line to
6378c2ecf20Sopenharmony_ci   the entries of mts_supported_products[]. */
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_cistatic const struct usb_device_id mts_usb_ids[] =
6408c2ecf20Sopenharmony_ci{
6418c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x4ce, 0x0300) },
6428c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x0094) },
6438c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x0099) },
6448c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x009a) },
6458c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x00a0) },
6468c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x00a3) },
6478c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x80a3) },
6488c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x80ac) },
6498c2ecf20Sopenharmony_ci	{ USB_DEVICE(0x5da, 0x00b6) },
6508c2ecf20Sopenharmony_ci	{ }						/* Terminating entry */
6518c2ecf20Sopenharmony_ci};
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (usb, mts_usb_ids);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_cistatic int mts_usb_probe(struct usb_interface *intf,
6578c2ecf20Sopenharmony_ci			 const struct usb_device_id *id)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	int i;
6608c2ecf20Sopenharmony_ci	int ep_out = -1;
6618c2ecf20Sopenharmony_ci	int ep_in_set[3]; /* this will break if we have more than three endpoints
6628c2ecf20Sopenharmony_ci			   which is why we check */
6638c2ecf20Sopenharmony_ci	int *ep_in_current = ep_in_set;
6648c2ecf20Sopenharmony_ci	int err_retval = -ENOMEM;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	struct mts_desc * new_desc;
6678c2ecf20Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev (intf);
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ci	/* the current altsetting on the interface we're probing */
6708c2ecf20Sopenharmony_ci	struct usb_host_interface *altsetting;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
6738c2ecf20Sopenharmony_ci	MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev );
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n",
6768c2ecf20Sopenharmony_ci		   le16_to_cpu(dev->descriptor.idProduct),
6778c2ecf20Sopenharmony_ci		   le16_to_cpu(dev->descriptor.idVendor) );
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	MTS_DEBUG_GOT_HERE();
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	/* the current altsetting on the interface we're probing */
6828c2ecf20Sopenharmony_ci	altsetting = intf->cur_altsetting;
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Check if the config is sane */
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	if ( altsetting->desc.bNumEndpoints != MTS_EP_TOTAL ) {
6888c2ecf20Sopenharmony_ci		MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n",
6898c2ecf20Sopenharmony_ci			     (int)MTS_EP_TOTAL, (int)altsetting->desc.bNumEndpoints );
6908c2ecf20Sopenharmony_ci		return -ENODEV;
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	for( i = 0; i < altsetting->desc.bNumEndpoints; i++ ) {
6948c2ecf20Sopenharmony_ci		if ((altsetting->endpoint[i].desc.bmAttributes &
6958c2ecf20Sopenharmony_ci		     USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci			MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n",
6988c2ecf20Sopenharmony_ci			     (int)altsetting->endpoint[i].desc.bEndpointAddress );
6998c2ecf20Sopenharmony_ci		} else {
7008c2ecf20Sopenharmony_ci			if (altsetting->endpoint[i].desc.bEndpointAddress &
7018c2ecf20Sopenharmony_ci			    USB_DIR_IN)
7028c2ecf20Sopenharmony_ci				*ep_in_current++
7038c2ecf20Sopenharmony_ci					= altsetting->endpoint[i].desc.bEndpointAddress &
7048c2ecf20Sopenharmony_ci					USB_ENDPOINT_NUMBER_MASK;
7058c2ecf20Sopenharmony_ci			else {
7068c2ecf20Sopenharmony_ci				if ( ep_out != -1 ) {
7078c2ecf20Sopenharmony_ci					MTS_WARNING( "can only deal with one output endpoints. Bailing out." );
7088c2ecf20Sopenharmony_ci					return -ENODEV;
7098c2ecf20Sopenharmony_ci				}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci				ep_out = altsetting->endpoint[i].desc.bEndpointAddress &
7128c2ecf20Sopenharmony_ci					USB_ENDPOINT_NUMBER_MASK;
7138c2ecf20Sopenharmony_ci			}
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (ep_in_current != &ep_in_set[2]) {
7198c2ecf20Sopenharmony_ci		MTS_WARNING("couldn't find two input bulk endpoints. Bailing out.\n");
7208c2ecf20Sopenharmony_ci		return -ENODEV;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	if ( ep_out == -1 ) {
7248c2ecf20Sopenharmony_ci		MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" );
7258c2ecf20Sopenharmony_ci		return -ENODEV;
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	new_desc = kzalloc(sizeof(struct mts_desc), GFP_KERNEL);
7308c2ecf20Sopenharmony_ci	if (!new_desc)
7318c2ecf20Sopenharmony_ci		goto out;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	new_desc->urb = usb_alloc_urb(0, GFP_KERNEL);
7348c2ecf20Sopenharmony_ci	if (!new_desc->urb)
7358c2ecf20Sopenharmony_ci		goto out_kfree;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	new_desc->context.scsi_status = kmalloc(1, GFP_KERNEL);
7388c2ecf20Sopenharmony_ci	if (!new_desc->context.scsi_status)
7398c2ecf20Sopenharmony_ci		goto out_free_urb;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	new_desc->usb_dev = dev;
7428c2ecf20Sopenharmony_ci	new_desc->usb_intf = intf;
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* endpoints */
7458c2ecf20Sopenharmony_ci	new_desc->ep_out = ep_out;
7468c2ecf20Sopenharmony_ci	new_desc->ep_response = ep_in_set[0];
7478c2ecf20Sopenharmony_ci	new_desc->ep_image = ep_in_set[1];
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	if ( new_desc->ep_out != MTS_EP_OUT )
7508c2ecf20Sopenharmony_ci		MTS_WARNING( "will this work? Command EP is not usually %d\n",
7518c2ecf20Sopenharmony_ci			     (int)new_desc->ep_out );
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if ( new_desc->ep_response != MTS_EP_RESPONSE )
7548c2ecf20Sopenharmony_ci		MTS_WARNING( "will this work? Response EP is not usually %d\n",
7558c2ecf20Sopenharmony_ci			     (int)new_desc->ep_response );
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if ( new_desc->ep_image != MTS_EP_IMAGE )
7588c2ecf20Sopenharmony_ci		MTS_WARNING( "will this work? Image data EP is not usually %d\n",
7598c2ecf20Sopenharmony_ci			     (int)new_desc->ep_image );
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	new_desc->host = scsi_host_alloc(&mts_scsi_host_template,
7628c2ecf20Sopenharmony_ci			sizeof(new_desc));
7638c2ecf20Sopenharmony_ci	if (!new_desc->host)
7648c2ecf20Sopenharmony_ci		goto out_kfree2;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	new_desc->host->hostdata[0] = (unsigned long)new_desc;
7678c2ecf20Sopenharmony_ci	if (scsi_add_host(new_desc->host, &dev->dev)) {
7688c2ecf20Sopenharmony_ci		err_retval = -EIO;
7698c2ecf20Sopenharmony_ci		goto out_host_put;
7708c2ecf20Sopenharmony_ci	}
7718c2ecf20Sopenharmony_ci	scsi_scan_host(new_desc->host);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, new_desc);
7748c2ecf20Sopenharmony_ci	return 0;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci out_host_put:
7778c2ecf20Sopenharmony_ci	scsi_host_put(new_desc->host);
7788c2ecf20Sopenharmony_ci out_kfree2:
7798c2ecf20Sopenharmony_ci	kfree(new_desc->context.scsi_status);
7808c2ecf20Sopenharmony_ci out_free_urb:
7818c2ecf20Sopenharmony_ci	usb_free_urb(new_desc->urb);
7828c2ecf20Sopenharmony_ci out_kfree:
7838c2ecf20Sopenharmony_ci	kfree(new_desc);
7848c2ecf20Sopenharmony_ci out:
7858c2ecf20Sopenharmony_ci	return err_retval;
7868c2ecf20Sopenharmony_ci}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_cistatic void mts_usb_disconnect (struct usb_interface *intf)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	struct mts_desc *desc = usb_get_intfdata(intf);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	usb_kill_urb(desc->urb);
7958c2ecf20Sopenharmony_ci	scsi_remove_host(desc->host);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	scsi_host_put(desc->host);
7988c2ecf20Sopenharmony_ci	usb_free_urb(desc->urb);
7998c2ecf20Sopenharmony_ci	kfree(desc->context.scsi_status);
8008c2ecf20Sopenharmony_ci	kfree(desc);
8018c2ecf20Sopenharmony_ci}
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_cimodule_usb_driver(mts_usb_driver);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ciMODULE_AUTHOR( DRIVER_AUTHOR );
8068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION( DRIVER_DESC );
8078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
808