xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 9e603ca0)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
31da177e4SLinus Torvalds  *  Copyright (C) 1992  Eric Youngdale
41da177e4SLinus Torvalds  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
51da177e4SLinus Torvalds  *  to make sure that we are not getting blocks mixed up, and PANIC if
61da177e4SLinus Torvalds  *  anything out of the ordinary is seen.
71da177e4SLinus Torvalds  * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This version is more generic, simulating a variable number of disk
1023183910SDouglas Gilbert  *  (or disk like devices) sharing a common amount of RAM. To be more
1123183910SDouglas Gilbert  *  realistic, the simulated devices have the transport attributes of
1223183910SDouglas Gilbert  *  SAS disks.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *  For documentation see http://www.torque.net/sg/sdebug26.html
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
181da177e4SLinus Torvalds  *   dpg: work for devfs large number of disks [20010809]
191da177e4SLinus Torvalds  *        forked for lk 2.5 series [20011216, 20020101]
201da177e4SLinus Torvalds  *        use vmalloc() more inquiry+mode_sense [20020302]
211da177e4SLinus Torvalds  *        add timers for delayed responses [20020721]
221da177e4SLinus Torvalds  *   Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
231da177e4SLinus Torvalds  *   Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
241da177e4SLinus Torvalds  *   dpg: change style of boot options to "scsi_debug.num_tgts=2" and
251da177e4SLinus Torvalds  *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <linux/kernel.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321da177e4SLinus Torvalds #include <linux/timer.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/genhd.h>
361da177e4SLinus Torvalds #include <linux/fs.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/proc_fs.h>
391da177e4SLinus Torvalds #include <linux/vmalloc.h>
401da177e4SLinus Torvalds #include <linux/moduleparam.h>
41852e034dSJens Axboe #include <linux/scatterlist.h>
421da177e4SLinus Torvalds #include <linux/blkdev.h>
439ff26eefSFUJITA Tomonori 
449ff26eefSFUJITA Tomonori #include <scsi/scsi.h>
459ff26eefSFUJITA Tomonori #include <scsi/scsi_cmnd.h>
469ff26eefSFUJITA Tomonori #include <scsi/scsi_device.h>
471da177e4SLinus Torvalds #include <scsi/scsi_host.h>
481da177e4SLinus Torvalds #include <scsi/scsicam.h>
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include <linux/stat.h>
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds #include "scsi_logging.h"
531da177e4SLinus Torvalds 
546f3cbf55SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.81"
556f3cbf55SDouglas Gilbert static const char * scsi_debug_version_date = "20070104";
561da177e4SLinus Torvalds 
576f3cbf55SDouglas Gilbert /* Additional Sense Code (ASC) */
58c65b1445SDouglas Gilbert #define NO_ADDITIONAL_SENSE 0x0
59c65b1445SDouglas Gilbert #define LOGICAL_UNIT_NOT_READY 0x4
601da177e4SLinus Torvalds #define UNRECOVERED_READ_ERR 0x11
61c65b1445SDouglas Gilbert #define PARAMETER_LIST_LENGTH_ERR 0x1a
621da177e4SLinus Torvalds #define INVALID_OPCODE 0x20
631da177e4SLinus Torvalds #define ADDR_OUT_OF_RANGE 0x21
641da177e4SLinus Torvalds #define INVALID_FIELD_IN_CDB 0x24
65c65b1445SDouglas Gilbert #define INVALID_FIELD_IN_PARAM_LIST 0x26
661da177e4SLinus Torvalds #define POWERON_RESET 0x29
671da177e4SLinus Torvalds #define SAVING_PARAMS_UNSUP 0x39
686f3cbf55SDouglas Gilbert #define TRANSPORT_PROBLEM 0x4b
69c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
70c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
711da177e4SLinus Torvalds 
726f3cbf55SDouglas Gilbert /* Additional Sense Code Qualifier (ASCQ) */
736f3cbf55SDouglas Gilbert #define ACK_NAK_TO 0x3
746f3cbf55SDouglas Gilbert 
751da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds /* Default values for driver parameters */
781da177e4SLinus Torvalds #define DEF_NUM_HOST   1
791da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
801da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
811da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
821da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
831da177e4SLinus Torvalds  */
841da177e4SLinus Torvalds #define DEF_DELAY   1
851da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
861da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
871da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
881da177e4SLinus Torvalds #define DEF_OPTS   0
891da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
901da177e4SLinus Torvalds #define DEF_PTYPE   0
911da177e4SLinus Torvalds #define DEF_D_SENSE   0
92c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
93c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
9423183910SDouglas Gilbert #define DEF_FAKE_RW	0
9523183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
981da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
991da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
1001da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
1011da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1026f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
1031da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1041da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1051da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1061da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1076f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1086f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1091da177e4SLinus Torvalds  *
1101da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1111da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1121da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1131da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1146f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1156f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1161da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1171da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1181da177e4SLinus Torvalds  */
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1211da177e4SLinus Torvalds  * sector on read commands: */
1221da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1251da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1261da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
127c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1301da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1311da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1321da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1331da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1341da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1351da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1361da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1371da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1381da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1391da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
140c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
141c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
14223183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
14323183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1481da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1491da177e4SLinus Torvalds 
150c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
151c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1521da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1551da177e4SLinus Torvalds    may still need them */
1561da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1571da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1581da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1611da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1621da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1631da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1681da177e4SLinus Torvalds 
1699e603ca0SFUJITA Tomonori #define SCSI_DEBUG_CANQUEUE  255
1709e603ca0SFUJITA Tomonori #define SCSI_DEBUG_MAX_CMD_LEN 16
1719e603ca0SFUJITA Tomonori 
1721da177e4SLinus Torvalds struct sdebug_dev_info {
1731da177e4SLinus Torvalds 	struct list_head dev_list;
1741da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1751da177e4SLinus Torvalds 	unsigned int channel;
1761da177e4SLinus Torvalds 	unsigned int target;
1771da177e4SLinus Torvalds 	unsigned int lun;
1781da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
179c65b1445SDouglas Gilbert 	unsigned int wlun;
1801da177e4SLinus Torvalds 	char reset;
181c65b1445SDouglas Gilbert 	char stopped;
1821da177e4SLinus Torvalds 	char used;
1831da177e4SLinus Torvalds };
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds struct sdebug_host_info {
1861da177e4SLinus Torvalds 	struct list_head host_list;
1871da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1881da177e4SLinus Torvalds 	struct device dev;
1891da177e4SLinus Torvalds 	struct list_head dev_info_list;
1901da177e4SLinus Torvalds };
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1931da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1961da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds struct sdebug_queued_cmd {
2011da177e4SLinus Torvalds 	int in_use;
2021da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
2031da177e4SLinus Torvalds 	done_funct_t done_funct;
2041da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2051da177e4SLinus Torvalds 	int scsi_result;
2061da177e4SLinus Torvalds };
2071da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds static int num_aborts = 0;
2121da177e4SLinus Torvalds static int num_dev_resets = 0;
2131da177e4SLinus Torvalds static int num_bus_resets = 0;
2141da177e4SLinus Torvalds static int num_host_resets = 0;
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2171da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2221da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2231da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2261da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2271da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2281da177e4SLinus Torvalds };
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static const int check_condition_result =
2311da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2321da177e4SLinus Torvalds 
233c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
234c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
235c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
236c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
237c65b1445SDouglas Gilbert 
2381da177e4SLinus Torvalds /* function declarations */
2391da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2401da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2411da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2421da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
243c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
244c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2455a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
2465a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip);
2471da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2481da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
249c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2501da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
251c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
252c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
253c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
254c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
255c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
256c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
257c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
258c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
259c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
260c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2611da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2621da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
263c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
264c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip);
2651da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2661da177e4SLinus Torvalds                                 int arr_len);
2671da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2681da177e4SLinus Torvalds                                int max_arr_len);
2691da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2701da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2711da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2721da177e4SLinus Torvalds 			    int asc, int asq);
273c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2741da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2751da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2761da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2771da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2781da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2791da177e4SLinus Torvalds static void __init init_all_queued(void);
2801da177e4SLinus Torvalds static void stop_all_queued(void);
2811da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
2825a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
2835a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
2845a09e398SHannes Reinecke 			   const char * dev_id_str, int dev_id_str_len);
285c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
2866ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
2871da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2901da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2911da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds static struct device pseudo_primary;
2941da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2951da177e4SLinus Torvalds 
2963de9f944SFUJITA Tomonori static void get_data_transfer_info(unsigned char *cmd,
2973de9f944SFUJITA Tomonori 				   unsigned long long *lba, unsigned int *num)
2983de9f944SFUJITA Tomonori {
2993de9f944SFUJITA Tomonori 	int i;
3003de9f944SFUJITA Tomonori 
3013de9f944SFUJITA Tomonori 	switch (*cmd) {
3023de9f944SFUJITA Tomonori 	case WRITE_16:
3033de9f944SFUJITA Tomonori 	case READ_16:
3043de9f944SFUJITA Tomonori 		for (*lba = 0, i = 0; i < 8; ++i) {
3053de9f944SFUJITA Tomonori 			if (i > 0)
3063de9f944SFUJITA Tomonori 				*lba <<= 8;
3073de9f944SFUJITA Tomonori 			*lba += cmd[2 + i];
3083de9f944SFUJITA Tomonori 		}
3093de9f944SFUJITA Tomonori 		*num = cmd[13] + (cmd[12] << 8) +
3103de9f944SFUJITA Tomonori 			(cmd[11] << 16) + (cmd[10] << 24);
3113de9f944SFUJITA Tomonori 		break;
3123de9f944SFUJITA Tomonori 	case WRITE_12:
3133de9f944SFUJITA Tomonori 	case READ_12:
3143de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3153de9f944SFUJITA Tomonori 		*num = cmd[9] + (cmd[8] << 8) +	(cmd[7] << 16) + (cmd[6] << 24);
3163de9f944SFUJITA Tomonori 		break;
3173de9f944SFUJITA Tomonori 	case WRITE_10:
3183de9f944SFUJITA Tomonori 	case READ_10:
319c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
3203de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3213de9f944SFUJITA Tomonori 		*num = cmd[8] + (cmd[7] << 8);
3223de9f944SFUJITA Tomonori 		break;
3233de9f944SFUJITA Tomonori 	case WRITE_6:
3243de9f944SFUJITA Tomonori 	case READ_6:
3253de9f944SFUJITA Tomonori 		*lba = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
3263de9f944SFUJITA Tomonori 		*num = (0 == cmd[4]) ? 256 : cmd[4];
3273de9f944SFUJITA Tomonori 		break;
3283de9f944SFUJITA Tomonori 	default:
3293de9f944SFUJITA Tomonori 		break;
3303de9f944SFUJITA Tomonori 	}
3313de9f944SFUJITA Tomonori }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds static
3341da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
3373de9f944SFUJITA Tomonori 	int len, k;
338c65b1445SDouglas Gilbert 	unsigned int num;
339c65b1445SDouglas Gilbert 	unsigned long long lba;
3401da177e4SLinus Torvalds 	int errsts = 0;
341c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3421da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3431da177e4SLinus Torvalds 	int inj_recovered = 0;
3446f3cbf55SDouglas Gilbert 	int inj_transport = 0;
345c65b1445SDouglas Gilbert 	int delay_override = 0;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	if (done == NULL)
3481da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3491da177e4SLinus Torvalds 
350c73961e5SBoaz Harrosh 	scsi_set_resid(SCpnt, 0);
3511da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3521da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
353c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3541da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3551da177e4SLinus Torvalds 		printk("\n");
3561da177e4SLinus Torvalds 	}
3579e603ca0SFUJITA Tomonori 
3589e603ca0SFUJITA Tomonori 	if (target == SCpnt->device->host->hostt->this_id) {
3591da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3601da177e4SLinus Torvalds 		       "target!\n");
3611da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3621da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3631da177e4SLinus Torvalds         }
3641da177e4SLinus Torvalds 
365c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
366c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3671da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3681da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3691da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3701da177e4SLinus Torvalds 	if (NULL == devip)
3711da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3721da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3751da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3761da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3771da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3781da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3791da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3801da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3811da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3821da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3836f3cbf55SDouglas Gilbert 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
3846f3cbf55SDouglas Gilbert 			inj_transport = 1; /* to reads and writes below */
3851da177e4SLinus Torvalds         }
3861da177e4SLinus Torvalds 
387c65b1445SDouglas Gilbert 	if (devip->wlun) {
388c65b1445SDouglas Gilbert 		switch (*cmd) {
389c65b1445SDouglas Gilbert 		case INQUIRY:
390c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
391c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
392c65b1445SDouglas Gilbert 		case REPORT_LUNS:
393c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
394c65b1445SDouglas Gilbert 		default:
395c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
396c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
397c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
398c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
399c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
400c65b1445SDouglas Gilbert 			errsts = check_condition_result;
401c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
402c65b1445SDouglas Gilbert 					     0);
403c65b1445SDouglas Gilbert 		}
404c65b1445SDouglas Gilbert 	}
405c65b1445SDouglas Gilbert 
4061da177e4SLinus Torvalds 	switch (*cmd) {
4071da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
408c65b1445SDouglas Gilbert 		delay_override = 1;
4091da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
4101da177e4SLinus Torvalds 		break;
4111da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
412c65b1445SDouglas Gilbert 		delay_override = 1;
4131da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
4141da177e4SLinus Torvalds 		break;
4151da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
4161da177e4SLinus Torvalds 	case START_STOP:
417c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
4181da177e4SLinus Torvalds 		break;
4191da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
420c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
4211da177e4SLinus Torvalds 			break;
4221da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
4231da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
4241da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
4251da177e4SLinus Torvalds 		break;
4261da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
427c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4281da177e4SLinus Torvalds 		break;
4291da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
430c65b1445SDouglas Gilbert 		delay_override = 1;
431c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4321da177e4SLinus Torvalds 		break;
4331da177e4SLinus Torvalds         case RESERVE:
434c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4351da177e4SLinus Torvalds                 break;
4361da177e4SLinus Torvalds         case RESERVE_10:
437c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4381da177e4SLinus Torvalds                 break;
4391da177e4SLinus Torvalds         case RELEASE:
440c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4411da177e4SLinus Torvalds                 break;
4421da177e4SLinus Torvalds         case RELEASE_10:
443c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4441da177e4SLinus Torvalds                 break;
4451da177e4SLinus Torvalds 	case READ_CAPACITY:
4461da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4471da177e4SLinus Torvalds 		break;
448c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
449c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
450c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
451c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
452c65b1445SDouglas Gilbert 			errsts = check_condition_result;
453c65b1445SDouglas Gilbert 			break;
454c65b1445SDouglas Gilbert 		}
455c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
456c65b1445SDouglas Gilbert 		break;
4575a09e398SHannes Reinecke 	case MAINTENANCE_IN:
4585a09e398SHannes Reinecke 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
4595a09e398SHannes Reinecke 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
4605a09e398SHannes Reinecke 					INVALID_OPCODE, 0);
4615a09e398SHannes Reinecke 			errsts = check_condition_result;
4625a09e398SHannes Reinecke 			break;
4635a09e398SHannes Reinecke 		}
4645a09e398SHannes Reinecke 		errsts = resp_report_tgtpgs(SCpnt, devip);
4655a09e398SHannes Reinecke 		break;
4661da177e4SLinus Torvalds 	case READ_16:
4671da177e4SLinus Torvalds 	case READ_12:
4681da177e4SLinus Torvalds 	case READ_10:
4691da177e4SLinus Torvalds 	case READ_6:
470c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4711da177e4SLinus Torvalds 			break;
47223183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
47323183910SDouglas Gilbert 			break;
4743de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
475c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4761da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4771da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
478c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4791da177e4SLinus Torvalds 			errsts = check_condition_result;
4806f3cbf55SDouglas Gilbert 		} else if (inj_transport && (0 == errsts)) {
4816f3cbf55SDouglas Gilbert                         mk_sense_buffer(devip, ABORTED_COMMAND,
4826f3cbf55SDouglas Gilbert                                         TRANSPORT_PROBLEM, ACK_NAK_TO);
4836f3cbf55SDouglas Gilbert                         errsts = check_condition_result;
4841da177e4SLinus Torvalds                 }
4851da177e4SLinus Torvalds 		break;
4861da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
487c65b1445SDouglas Gilbert 		delay_override = 1;
4881da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4891da177e4SLinus Torvalds 		break;
4901da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
491c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4921da177e4SLinus Torvalds 		break;
4931da177e4SLinus Torvalds 	case WRITE_16:
4941da177e4SLinus Torvalds 	case WRITE_12:
4951da177e4SLinus Torvalds 	case WRITE_10:
4961da177e4SLinus Torvalds 	case WRITE_6:
497c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4981da177e4SLinus Torvalds 			break;
49923183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
50023183910SDouglas Gilbert 			break;
5013de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
502c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5031da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5041da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
505c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5061da177e4SLinus Torvalds 			errsts = check_condition_result;
5071da177e4SLinus Torvalds 		}
5081da177e4SLinus Torvalds 		break;
5091da177e4SLinus Torvalds 	case MODE_SENSE:
5101da177e4SLinus Torvalds 	case MODE_SENSE_10:
5111da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5121da177e4SLinus Torvalds 		break;
513c65b1445SDouglas Gilbert 	case MODE_SELECT:
514c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
515c65b1445SDouglas Gilbert 		break;
516c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
517c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
518c65b1445SDouglas Gilbert 		break;
519c65b1445SDouglas Gilbert 	case LOG_SENSE:
520c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
521c65b1445SDouglas Gilbert 		break;
5221da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
523c65b1445SDouglas Gilbert 		delay_override = 1;
524c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5251da177e4SLinus Torvalds 		break;
5266f3cbf55SDouglas Gilbert 	case WRITE_BUFFER:
5276f3cbf55SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
5286f3cbf55SDouglas Gilbert 		break;
529c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
530c639d14eSFUJITA Tomonori 		if (!scsi_bidi_cmnd(SCpnt)) {
531c639d14eSFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
532c639d14eSFUJITA Tomonori 					INVALID_FIELD_IN_CDB, 0);
533c639d14eSFUJITA Tomonori 			errsts = check_condition_result;
534c639d14eSFUJITA Tomonori 			break;
535c639d14eSFUJITA Tomonori 		}
536c639d14eSFUJITA Tomonori 
537c639d14eSFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
538c639d14eSFUJITA Tomonori 		if (errsts)
539c639d14eSFUJITA Tomonori 			break;
540c639d14eSFUJITA Tomonori 		if (scsi_debug_fake_rw)
541c639d14eSFUJITA Tomonori 			break;
542c639d14eSFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
543c639d14eSFUJITA Tomonori 		errsts = resp_read(SCpnt, lba, num, devip);
544c639d14eSFUJITA Tomonori 		if (errsts)
545c639d14eSFUJITA Tomonori 			break;
546c639d14eSFUJITA Tomonori 		errsts = resp_write(SCpnt, lba, num, devip);
547c639d14eSFUJITA Tomonori 		if (errsts)
548c639d14eSFUJITA Tomonori 			break;
549c639d14eSFUJITA Tomonori 		errsts = resp_xdwriteread(SCpnt, lba, num, devip);
550c639d14eSFUJITA Tomonori 		break;
5511da177e4SLinus Torvalds 	default:
5521da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5531da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5541da177e4SLinus Torvalds 			       "supported\n", *cmd);
555c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5561da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5571da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5581da177e4SLinus Torvalds 		errsts = check_condition_result;
5591da177e4SLinus Torvalds 		break;
5601da177e4SLinus Torvalds 	}
561c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
562c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5661da177e4SLinus Torvalds {
5671da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5681da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 	return -EINVAL;
5711da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds 
574c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
575c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	if (devip->reset) {
5781da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5791da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5801da177e4SLinus Torvalds 			       "attention: power on reset\n");
5811da177e4SLinus Torvalds 		devip->reset = 0;
5821da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5831da177e4SLinus Torvalds 		return check_condition_result;
5841da177e4SLinus Torvalds 	}
585c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
586c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
587c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
588c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
589c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
590c65b1445SDouglas Gilbert 				0x2);
591c65b1445SDouglas Gilbert 		return check_condition_result;
592c65b1445SDouglas Gilbert 	}
5931da177e4SLinus Torvalds 	return 0;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5971da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5981da177e4SLinus Torvalds 				int arr_len)
5991da177e4SLinus Torvalds {
6001da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
6011da177e4SLinus Torvalds 	void * kaddr;
6021da177e4SLinus Torvalds 	void * kaddr_off;
603852e034dSJens Axboe 	struct scatterlist *sg;
604072d0bb3SFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
6051da177e4SLinus Torvalds 
606072d0bb3SFUJITA Tomonori 	if (!sdb->length)
6071da177e4SLinus Torvalds 		return 0;
608072d0bb3SFUJITA Tomonori 	if (!sdb->table.sgl)
6091da177e4SLinus Torvalds 		return (DID_ERROR << 16);
610072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
6111da177e4SLinus Torvalds 		return (DID_ERROR << 16);
6121da177e4SLinus Torvalds 	active = 1;
613852e034dSJens Axboe 	req_len = act_len = 0;
614072d0bb3SFUJITA Tomonori 	for_each_sg(sdb->table.sgl, sg, sdb->table.nents, k) {
6151da177e4SLinus Torvalds 		if (active) {
6161da177e4SLinus Torvalds 			kaddr = (unsigned char *)
61745711f1aSJens Axboe 				kmap_atomic(sg_page(sg), KM_USER0);
6181da177e4SLinus Torvalds 			if (NULL == kaddr)
6191da177e4SLinus Torvalds 				return (DID_ERROR << 16);
620852e034dSJens Axboe 			kaddr_off = (unsigned char *)kaddr + sg->offset;
621852e034dSJens Axboe 			len = sg->length;
6221da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
6231da177e4SLinus Torvalds 				active = 0;
6241da177e4SLinus Torvalds 				len = arr_len - req_len;
6251da177e4SLinus Torvalds 			}
6261da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6271da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6281da177e4SLinus Torvalds 			act_len += len;
6291da177e4SLinus Torvalds 		}
630852e034dSJens Axboe 		req_len += sg->length;
6311da177e4SLinus Torvalds 	}
632072d0bb3SFUJITA Tomonori 	if (sdb->resid)
633072d0bb3SFUJITA Tomonori 		sdb->resid -= act_len;
634c65b1445SDouglas Gilbert 	else
635072d0bb3SFUJITA Tomonori 		sdb->resid = req_len - act_len;
6361da177e4SLinus Torvalds 	return 0;
6371da177e4SLinus Torvalds }
6381da177e4SLinus Torvalds 
6391da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6401da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6411da177e4SLinus Torvalds 			       int max_arr_len)
6421da177e4SLinus Torvalds {
6431da177e4SLinus Torvalds 	int k, req_len, len, fin;
6441da177e4SLinus Torvalds 	void * kaddr;
6451da177e4SLinus Torvalds 	void * kaddr_off;
646852e034dSJens Axboe 	struct scatterlist * sg;
6471da177e4SLinus Torvalds 
648c73961e5SBoaz Harrosh 	if (0 == scsi_bufflen(scp))
6491da177e4SLinus Torvalds 		return 0;
650c73961e5SBoaz Harrosh 	if (NULL == scsi_sglist(scp))
6511da177e4SLinus Torvalds 		return -1;
652072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE))
6531da177e4SLinus Torvalds 		return -1;
654852e034dSJens Axboe 	req_len = fin = 0;
655c73961e5SBoaz Harrosh 	scsi_for_each_sg(scp, sg, scsi_sg_count(scp), k) {
65645711f1aSJens Axboe 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
6571da177e4SLinus Torvalds 		if (NULL == kaddr)
6581da177e4SLinus Torvalds 			return -1;
659852e034dSJens Axboe 		kaddr_off = (unsigned char *)kaddr + sg->offset;
660852e034dSJens Axboe 		len = sg->length;
6611da177e4SLinus Torvalds 		if ((req_len + len) > max_arr_len) {
6621da177e4SLinus Torvalds 			len = max_arr_len - req_len;
6631da177e4SLinus Torvalds 			fin = 1;
6641da177e4SLinus Torvalds 		}
6651da177e4SLinus Torvalds 		memcpy(arr + req_len, kaddr_off, len);
6661da177e4SLinus Torvalds 		kunmap_atomic(kaddr, KM_USER0);
6671da177e4SLinus Torvalds 		if (fin)
6681da177e4SLinus Torvalds 			return req_len + len;
669852e034dSJens Axboe 		req_len += sg->length;
6701da177e4SLinus Torvalds 	}
6711da177e4SLinus Torvalds 	return req_len;
6721da177e4SLinus Torvalds }
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 
6751da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6761da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6771da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6781da177e4SLinus Torvalds 
6795a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
6805a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
6815a09e398SHannes Reinecke 			   const char * dev_id_str,
682c65b1445SDouglas Gilbert 			   int dev_id_str_len)
6831da177e4SLinus Torvalds {
684c65b1445SDouglas Gilbert 	int num, port_a;
685c65b1445SDouglas Gilbert 	char b[32];
6861da177e4SLinus Torvalds 
687c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
6881da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
6891da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
6901da177e4SLinus Torvalds 	arr[1] = 0x1;
6911da177e4SLinus Torvalds 	arr[2] = 0x0;
6921da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
6931da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
6941da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
6951da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
6961da177e4SLinus Torvalds 	arr[3] = num;
6971da177e4SLinus Torvalds 	num += 4;
698c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
699c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
700c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
701c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
702c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
703c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
704c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
705c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
706c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
707c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
708c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
709c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
710c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
711c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
712c65b1445SDouglas Gilbert 		/* Target relative port number */
713c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
714c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
715c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
716c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
717c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
718c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
719c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
720c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
721c65b1445SDouglas Gilbert 	}
722c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
723c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
724c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
725c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
726c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
727c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
728c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
729c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
730c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
731c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
732c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
733c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
734c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
7355a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
7365a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
7375a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
7385a09e398SHannes Reinecke 	arr[num++] = 0x0;
7395a09e398SHannes Reinecke 	arr[num++] = 0x4;
7405a09e398SHannes Reinecke 	arr[num++] = 0;
7415a09e398SHannes Reinecke 	arr[num++] = 0;
7425a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
7435a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
744c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
745c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
746c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
747c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
748c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
749c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
750c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
751c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
752c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
753c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
754c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
755c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
756c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
757c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
758c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
759c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
760c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
761c65b1445SDouglas Gilbert 	arr[num++] = 24;
762c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
763c65b1445SDouglas Gilbert 	num += 12;
764c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
765c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
766c65b1445SDouglas Gilbert 	num += 8;
767c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
768c65b1445SDouglas Gilbert 	num += 4;
769c65b1445SDouglas Gilbert 	return num;
770c65b1445SDouglas Gilbert }
771c65b1445SDouglas Gilbert 
772c65b1445SDouglas Gilbert 
773c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
774c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
775c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
776c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
777c65b1445SDouglas Gilbert };
778c65b1445SDouglas Gilbert 
779c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
780c65b1445SDouglas Gilbert {
781c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
782c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
783c65b1445SDouglas Gilbert }
784c65b1445SDouglas Gilbert 
785c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
786c65b1445SDouglas Gilbert {
787c65b1445SDouglas Gilbert 	int num = 0;
788c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
789c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
790c65b1445SDouglas Gilbert 	int plen, olen;
791c65b1445SDouglas Gilbert 
792c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
793c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
794c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
795c65b1445SDouglas Gilbert 	olen = strlen(na1);
796c65b1445SDouglas Gilbert 	plen = olen + 1;
797c65b1445SDouglas Gilbert 	if (plen % 4)
798c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
799c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
800c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
801c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
802c65b1445SDouglas Gilbert 	num += plen;
803c65b1445SDouglas Gilbert 
804c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
805c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
806c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
807c65b1445SDouglas Gilbert 	olen = strlen(na2);
808c65b1445SDouglas Gilbert 	plen = olen + 1;
809c65b1445SDouglas Gilbert 	if (plen % 4)
810c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
811c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
812c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
813c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
814c65b1445SDouglas Gilbert 	num += plen;
815c65b1445SDouglas Gilbert 
816c65b1445SDouglas Gilbert 	return num;
817c65b1445SDouglas Gilbert }
818c65b1445SDouglas Gilbert 
819c65b1445SDouglas Gilbert /* SCSI ports VPD page */
820c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
821c65b1445SDouglas Gilbert {
822c65b1445SDouglas Gilbert 	int num = 0;
823c65b1445SDouglas Gilbert 	int port_a, port_b;
824c65b1445SDouglas Gilbert 
825c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
826c65b1445SDouglas Gilbert 	port_b = port_a + 1;
827c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
828c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
829c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
830c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
831c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
832c65b1445SDouglas Gilbert 	num += 6;
833c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
834c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
835c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
836c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
837c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
838c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
839c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
840c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
841c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
842c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
843c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
844c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
845c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
846c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
847c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
848c65b1445SDouglas Gilbert 
849c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
850c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
851c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
852c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
853c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
854c65b1445SDouglas Gilbert 	num += 6;
855c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
856c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
857c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
858c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
859c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
860c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
861c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
862c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
863c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
864c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
865c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
866c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
867c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
868c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
869c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
870c65b1445SDouglas Gilbert 
871c65b1445SDouglas Gilbert 	return num;
872c65b1445SDouglas Gilbert }
873c65b1445SDouglas Gilbert 
874c65b1445SDouglas Gilbert 
875c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
876c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
877c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
878c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
879c65b1445SDouglas Gilbert '1','2','3','4',
880c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
881c65b1445SDouglas Gilbert 0xec,0,0,0,
882c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
883c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
884c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
885c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
886c65b1445SDouglas Gilbert 0x53,0x41,
887c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
888c65b1445SDouglas Gilbert 0x20,0x20,
889c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
890c65b1445SDouglas Gilbert 0x10,0x80,
891c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
892c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
893c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
894c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
895c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
896c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
897c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
898c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
899c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
900c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
901c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
902c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
903c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
904c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
905c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
906c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
907c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
908c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
909c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
910c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
911c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
912c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
913c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
914c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
915c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
916c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
917c65b1445SDouglas Gilbert };
918c65b1445SDouglas Gilbert 
919c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
920c65b1445SDouglas Gilbert {
921c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
922c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
923c65b1445SDouglas Gilbert }
924c65b1445SDouglas Gilbert 
925c65b1445SDouglas Gilbert 
926c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
927c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
928c65b1445SDouglas Gilbert 	0,0,0x4,0,
929c65b1445SDouglas Gilbert 	0,0,0,64,
930c65b1445SDouglas Gilbert };
931c65b1445SDouglas Gilbert 
932c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
933c65b1445SDouglas Gilbert {
934c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
935c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
936c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
937c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
938c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
939c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
940c65b1445SDouglas Gilbert 	}
941c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
9421da177e4SLinus Torvalds }
9431da177e4SLinus Torvalds 
9441da177e4SLinus Torvalds 
9451da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
946c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9471da177e4SLinus Torvalds 
9481da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9491da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	unsigned char pq_pdt;
9525a09e398SHannes Reinecke 	unsigned char * arr;
9531da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
9545a09e398SHannes Reinecke 	int alloc_len, n, ret;
9551da177e4SLinus Torvalds 
9561da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9576f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
9586f3cbf55SDouglas Gilbert 	if (! arr)
9596f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
960c65b1445SDouglas Gilbert 	if (devip->wlun)
961c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
962c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
963c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
964c65b1445SDouglas Gilbert 	else
9651da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9661da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9671da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9681da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9691da177e4SLinus Torvalds 			       	0);
9705a09e398SHannes Reinecke 		kfree(arr);
9711da177e4SLinus Torvalds 		return check_condition_result;
9721da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
9735a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
974c65b1445SDouglas Gilbert 		char lu_id_str[6];
975c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9761da177e4SLinus Torvalds 
9775a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
9785a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
97923183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
98023183910SDouglas Gilbert 			host_no = 0;
981c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
982c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
983c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
984c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
985c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9861da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
987c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
988c65b1445SDouglas Gilbert 			n = 4;
989c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
990c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
991c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
992c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
993c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
994c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
995c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
996c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
997c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
998c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
999c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
10001da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
1001c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10021da177e4SLinus Torvalds 			arr[3] = len;
1003c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
10041da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
1005c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10065a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
10075a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
10085a09e398SHannes Reinecke 						 lu_id_str, len);
1009c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
1010c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1011c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
1012c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
1013c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1014c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
1015c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
1016c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1017c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
1018c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
1019c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
1020c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
1021c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1022c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
1023c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
1024c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
1025c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
1026c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
1027c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
1028c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1029c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
1030c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
1031c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1032c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
1033c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
1034c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
1035c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
1036c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1037c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10381da177e4SLinus Torvalds 		} else {
10391da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10401da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10411da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10425a09e398SHannes Reinecke 			kfree(arr);
10431da177e4SLinus Torvalds 			return check_condition_result;
10441da177e4SLinus Torvalds 		}
1045c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10465a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
1047c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10485a09e398SHannes Reinecke 		kfree(arr);
10495a09e398SHannes Reinecke 		return ret;
10501da177e4SLinus Torvalds 	}
10511da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10521da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10531da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10541da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10551da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
10565a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
10575a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
1058c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10591da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1060c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10611da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10621da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10631da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10641da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1065c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1066c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1067c65b1445SDouglas Gilbert 	n = 62;
10681da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1069c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10701da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1071c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10721da177e4SLinus Torvalds 	}
1073c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10745a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
10751da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10765a09e398SHannes Reinecke 	kfree(arr);
10775a09e398SHannes Reinecke 	return ret;
10781da177e4SLinus Torvalds }
10791da177e4SLinus Torvalds 
10801da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10811da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10821da177e4SLinus Torvalds {
10831da177e4SLinus Torvalds 	unsigned char * sbuff;
10841da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10851da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1086c65b1445SDouglas Gilbert 	int want_dsense;
10871da177e4SLinus Torvalds 	int len = 18;
10881da177e4SLinus Torvalds 
1089c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10901da177e4SLinus Torvalds 	if (devip->reset == 1)
1091c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1092c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10931da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1094c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1095c65b1445SDouglas Gilbert 		if (want_dsense) {
1096c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1097c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1098c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1099c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1100c65b1445SDouglas Gilbert 		} else {
1101c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1102c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1103c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1104c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1105c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1106c65b1445SDouglas Gilbert 		}
1107c65b1445SDouglas Gilbert 	} else {
1108c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
11091da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
11101da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1111c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
11121da177e4SLinus Torvalds 			arr[0] = 0x72;
11131da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
11141da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
11151da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
11161da177e4SLinus Torvalds 			len = 8;
1117c65b1445SDouglas Gilbert 		}
1118c65b1445SDouglas Gilbert 	}
1119c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
11201da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
11211da177e4SLinus Torvalds }
11221da177e4SLinus Torvalds 
1123c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1124c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1125c65b1445SDouglas Gilbert {
1126c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1127c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1128c65b1445SDouglas Gilbert 
1129c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1130c65b1445SDouglas Gilbert 		return errsts;
1131c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1132c65b1445SDouglas Gilbert 	if (power_cond) {
1133c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1134c65b1445SDouglas Gilbert 			       	0);
1135c65b1445SDouglas Gilbert 		return check_condition_result;
1136c65b1445SDouglas Gilbert 	}
1137c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1138c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1139c65b1445SDouglas Gilbert 		devip->stopped = !start;
1140c65b1445SDouglas Gilbert 	return 0;
1141c65b1445SDouglas Gilbert }
1142c65b1445SDouglas Gilbert 
11431da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11441da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11451da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11461da177e4SLinus Torvalds {
11471da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1148c65b1445SDouglas Gilbert 	unsigned int capac;
11491da177e4SLinus Torvalds 	int errsts;
11501da177e4SLinus Torvalds 
1151c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11521da177e4SLinus Torvalds 		return errsts;
1153c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1154c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1155c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1156c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1157c65b1445SDouglas Gilbert 	} else
1158c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11591da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1160c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1161c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11621da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11631da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11641da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11651da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1166c65b1445SDouglas Gilbert 	} else {
1167c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1168c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1169c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1170c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1171c65b1445SDouglas Gilbert 	}
11721da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11731da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11741da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11751da177e4SLinus Torvalds }
11761da177e4SLinus Torvalds 
1177c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1178c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1179c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1180c65b1445SDouglas Gilbert {
1181c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1182c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1183c65b1445SDouglas Gilbert 	unsigned long long capac;
1184c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1185c65b1445SDouglas Gilbert 
1186c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1187c65b1445SDouglas Gilbert 		return errsts;
1188c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1189c65b1445SDouglas Gilbert 		     + cmd[13]);
1190c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1191c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1192c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1193c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1194c65b1445SDouglas Gilbert 	} else
1195c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1196c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1197c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1198c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1199c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1200c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1201c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1202c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1203c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1204c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1205c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1206c65b1445SDouglas Gilbert }
1207c65b1445SDouglas Gilbert 
12085a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
12095a09e398SHannes Reinecke 
12105a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
12115a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
12125a09e398SHannes Reinecke {
12135a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
12145a09e398SHannes Reinecke 	unsigned char * arr;
12155a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
12165a09e398SHannes Reinecke 	int n, ret, alen, rlen;
12175a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
12185a09e398SHannes Reinecke 
12195a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
12205a09e398SHannes Reinecke 		+ cmd[9]);
12215a09e398SHannes Reinecke 
12226f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
12236f3cbf55SDouglas Gilbert 	if (! arr)
12246f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
12255a09e398SHannes Reinecke 	/*
12265a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
12275a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
12285a09e398SHannes Reinecke 	 * So we create two port groups with one port each
12295a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
12305a09e398SHannes Reinecke 	 */
12315a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
12325a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
12335a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
12345a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
12355a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
12365a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
12375a09e398SHannes Reinecke 
12385a09e398SHannes Reinecke 	/*
12395a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
12405a09e398SHannes Reinecke 	 */
12415a09e398SHannes Reinecke 	n = 4;
12425a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
12435a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
12445a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
12455a09e398SHannes Reinecke 	} else {
12465a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
12475a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
12485a09e398SHannes Reinecke 	}
12495a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
12505a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
12515a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12525a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12535a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12545a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12555a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12565a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12575a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
12585a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
12595a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
12605a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
12615a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
12625a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
12635a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12645a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12655a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12665a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12675a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12685a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12695a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
12705a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
12715a09e398SHannes Reinecke 
12725a09e398SHannes Reinecke 	rlen = n - 4;
12735a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
12745a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
12755a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
12765a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
12775a09e398SHannes Reinecke 
12785a09e398SHannes Reinecke 	/*
12795a09e398SHannes Reinecke 	 * Return the smallest value of either
12805a09e398SHannes Reinecke 	 * - The allocated length
12815a09e398SHannes Reinecke 	 * - The constructed command length
12825a09e398SHannes Reinecke 	 * - The maximum array size
12835a09e398SHannes Reinecke 	 */
12845a09e398SHannes Reinecke 	rlen = min(alen,n);
12855a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
12865a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
12875a09e398SHannes Reinecke 	kfree(arr);
12885a09e398SHannes Reinecke 	return ret;
12895a09e398SHannes Reinecke }
12905a09e398SHannes Reinecke 
12911da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
12941da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
12951da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
12961da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
12971da177e4SLinus Torvalds 
12981da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
12991da177e4SLinus Torvalds 	if (1 == pcontrol)
13001da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
13011da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
13051da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
13061da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
13071da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
13081da177e4SLinus Torvalds 
13091da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
13101da177e4SLinus Torvalds 	if (1 == pcontrol)
13111da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
13121da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
13131da177e4SLinus Torvalds }
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
13161da177e4SLinus Torvalds {       /* Format device page for mode_sense */
13171da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
13181da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
13191da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
13221da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
13231da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
13241da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
13251da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
13261da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
13271da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
13281da177e4SLinus Torvalds         if (1 == pcontrol)
13291da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
13301da177e4SLinus Torvalds         return sizeof(format_pg);
13311da177e4SLinus Torvalds }
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
13341da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
13351da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
13361da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
13391da177e4SLinus Torvalds 	if (1 == pcontrol)
13401da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
13411da177e4SLinus Torvalds 	return sizeof(caching_pg);
13421da177e4SLinus Torvalds }
13431da177e4SLinus Torvalds 
13441da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
13451da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1346c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1347c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1348c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
13491da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
13501da177e4SLinus Torvalds 
13511da177e4SLinus Torvalds 	if (scsi_debug_dsense)
13521da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1353c65b1445SDouglas Gilbert 	else
1354c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
13551da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
13561da177e4SLinus Torvalds 	if (1 == pcontrol)
1357c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1358c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1359c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
13601da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
13611da177e4SLinus Torvalds }
13621da177e4SLinus Torvalds 
1363c65b1445SDouglas Gilbert 
13641da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
13651da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1366c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
13671da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1368c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1369c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1370c65b1445SDouglas Gilbert 
13711da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
13721da177e4SLinus Torvalds 	if (1 == pcontrol)
1373c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1374c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1375c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
13761da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
13771da177e4SLinus Torvalds }
13781da177e4SLinus Torvalds 
1379c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1380c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1381c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1382c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1383c65b1445SDouglas Gilbert 
1384c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1385c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1386c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1387c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1388c65b1445SDouglas Gilbert }
1389c65b1445SDouglas Gilbert 
1390c65b1445SDouglas Gilbert 
1391c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1392c65b1445SDouglas Gilbert 			      int target_dev_id)
1393c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1394c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1395c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1396c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1397c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1398c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1399c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1400c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1401c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1402c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1403c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1404c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1405c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1406c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1407c65b1445SDouglas Gilbert 		};
1408c65b1445SDouglas Gilbert 	int port_a, port_b;
1409c65b1445SDouglas Gilbert 
1410c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1411c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1412c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1413c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1414c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1415c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1416c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1417c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1418c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1419c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1420c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1421c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1422c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1423c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1424c65b1445SDouglas Gilbert }
1425c65b1445SDouglas Gilbert 
1426c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1427c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1428c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1429c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1430c65b1445SDouglas Gilbert 		};
1431c65b1445SDouglas Gilbert 
1432c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1433c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1434c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1435c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1436c65b1445SDouglas Gilbert }
1437c65b1445SDouglas Gilbert 
14381da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
14391da177e4SLinus Torvalds 
14401da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
14411da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
14421da177e4SLinus Torvalds {
144323183910SDouglas Gilbert 	unsigned char dbd, llbaa;
144423183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
14451da177e4SLinus Torvalds 	unsigned char dev_spec;
144623183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
14471da177e4SLinus Torvalds 	unsigned char * ap;
14481da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
14491da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
14501da177e4SLinus Torvalds 
1451c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
14521da177e4SLinus Torvalds 		return errsts;
145323183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
14541da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
14551da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
14561da177e4SLinus Torvalds 	subpcode = cmd[3];
14571da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
145823183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
145923183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
146023183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
146123183910SDouglas Gilbert 	else
146223183910SDouglas Gilbert 		bd_len = 0;
14631da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
14641da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
14651da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
14661da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
14671da177e4SLinus Torvalds 			       	0);
14681da177e4SLinus Torvalds 		return check_condition_result;
14691da177e4SLinus Torvalds 	}
1470c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1471c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
147223183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
147323183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
147423183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
147523183910SDouglas Gilbert 	else
147623183910SDouglas Gilbert 		dev_spec = 0x0;
14771da177e4SLinus Torvalds 	if (msense_6) {
14781da177e4SLinus Torvalds 		arr[2] = dev_spec;
147923183910SDouglas Gilbert 		arr[3] = bd_len;
14801da177e4SLinus Torvalds 		offset = 4;
14811da177e4SLinus Torvalds 	} else {
14821da177e4SLinus Torvalds 		arr[3] = dev_spec;
148323183910SDouglas Gilbert 		if (16 == bd_len)
148423183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
148523183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
14861da177e4SLinus Torvalds 		offset = 8;
14871da177e4SLinus Torvalds 	}
14881da177e4SLinus Torvalds 	ap = arr + offset;
148923183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
149023183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
149123183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
149223183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
149323183910SDouglas Gilbert 		} else
149423183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
149523183910SDouglas Gilbert 	}
149623183910SDouglas Gilbert 	if (8 == bd_len) {
149723183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
149823183910SDouglas Gilbert 			ap[0] = 0xff;
149923183910SDouglas Gilbert 			ap[1] = 0xff;
150023183910SDouglas Gilbert 			ap[2] = 0xff;
150123183910SDouglas Gilbert 			ap[3] = 0xff;
150223183910SDouglas Gilbert 		} else {
150323183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
150423183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
150523183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
150623183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
150723183910SDouglas Gilbert 		}
150823183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
150923183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
151023183910SDouglas Gilbert 		offset += bd_len;
151123183910SDouglas Gilbert 		ap = arr + offset;
151223183910SDouglas Gilbert 	} else if (16 == bd_len) {
151323183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
151423183910SDouglas Gilbert 
151523183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
151623183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
151723183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
151823183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
151923183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
152023183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
152123183910SDouglas Gilbert 		offset += bd_len;
152223183910SDouglas Gilbert 		ap = arr + offset;
152323183910SDouglas Gilbert 	}
15241da177e4SLinus Torvalds 
1525c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1526c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
15271da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15281da177e4SLinus Torvalds 			       	0);
15291da177e4SLinus Torvalds 		return check_condition_result;
15301da177e4SLinus Torvalds 	}
15311da177e4SLinus Torvalds 	switch (pcode) {
15321da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
15331da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
15341da177e4SLinus Torvalds 		offset += len;
15351da177e4SLinus Torvalds 		break;
15361da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
15371da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
15381da177e4SLinus Torvalds 		offset += len;
15391da177e4SLinus Torvalds 		break;
15401da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
15411da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
15421da177e4SLinus Torvalds                 offset += len;
15431da177e4SLinus Torvalds                 break;
15441da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
15451da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
15461da177e4SLinus Torvalds 		offset += len;
15471da177e4SLinus Torvalds 		break;
15481da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
15491da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
15501da177e4SLinus Torvalds 		offset += len;
15511da177e4SLinus Torvalds 		break;
1552c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1553c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1554c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1555c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1556c65b1445SDouglas Gilbert 			return check_condition_result;
1557c65b1445SDouglas Gilbert 	        }
1558c65b1445SDouglas Gilbert 		len = 0;
1559c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1560c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1561c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1562c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1563c65b1445SDouglas Gilbert 						  target_dev_id);
1564c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1565c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1566c65b1445SDouglas Gilbert 		offset += len;
1567c65b1445SDouglas Gilbert 		break;
15681da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
15691da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
15701da177e4SLinus Torvalds 		offset += len;
15711da177e4SLinus Torvalds 		break;
15721da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1573c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
15741da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
15751da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
15761da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
15771da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
15781da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1579c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1580c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1581c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1582c65b1445SDouglas Gilbert 						  target, target_dev_id);
1583c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1584c65b1445SDouglas Gilbert 			}
15851da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1586c65b1445SDouglas Gilbert 		} else {
1587c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1588c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1589c65b1445SDouglas Gilbert 			return check_condition_result;
1590c65b1445SDouglas Gilbert                 }
15911da177e4SLinus Torvalds 		offset += len;
15921da177e4SLinus Torvalds 		break;
15931da177e4SLinus Torvalds 	default:
15941da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15951da177e4SLinus Torvalds 			       	0);
15961da177e4SLinus Torvalds 		return check_condition_result;
15971da177e4SLinus Torvalds 	}
15981da177e4SLinus Torvalds 	if (msense_6)
15991da177e4SLinus Torvalds 		arr[0] = offset - 1;
16001da177e4SLinus Torvalds 	else {
16011da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
16021da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
16031da177e4SLinus Torvalds 	}
16041da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
16051da177e4SLinus Torvalds }
16061da177e4SLinus Torvalds 
1607c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1608c65b1445SDouglas Gilbert 
1609c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1610c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1611c65b1445SDouglas Gilbert {
1612c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1613c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1614c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1615c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1616c65b1445SDouglas Gilbert 
1617c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1618c65b1445SDouglas Gilbert 		return errsts;
1619c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1620c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1621c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1622c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1623c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1624c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1625c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1626c65b1445SDouglas Gilbert 		return check_condition_result;
1627c65b1445SDouglas Gilbert 	}
1628c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1629c65b1445SDouglas Gilbert         if (-1 == res)
1630c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1631c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1632c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1633c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1634c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1635c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1636c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
163723183910SDouglas Gilbert 	if (md_len > 2) {
1638c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1639c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1640c65b1445SDouglas Gilbert 		return check_condition_result;
1641c65b1445SDouglas Gilbert 	}
1642c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1643c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1644c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1645c65b1445SDouglas Gilbert 	if (ps) {
1646c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1647c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1648c65b1445SDouglas Gilbert 		return check_condition_result;
1649c65b1445SDouglas Gilbert 	}
1650c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1651c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1652c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1653c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1654c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1655c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1656c65b1445SDouglas Gilbert 		return check_condition_result;
1657c65b1445SDouglas Gilbert 	}
1658c65b1445SDouglas Gilbert 	switch (mpage) {
1659c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1660c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1661c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1662c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1663c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1664c65b1445SDouglas Gilbert 			return 0;
1665c65b1445SDouglas Gilbert 		}
1666c65b1445SDouglas Gilbert 		break;
1667c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1668c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1669c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1670c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1671c65b1445SDouglas Gilbert 			return 0;
1672c65b1445SDouglas Gilbert 		}
1673c65b1445SDouglas Gilbert 		break;
1674c65b1445SDouglas Gilbert 	default:
1675c65b1445SDouglas Gilbert 		break;
1676c65b1445SDouglas Gilbert 	}
1677c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1678c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1679c65b1445SDouglas Gilbert 	return check_condition_result;
1680c65b1445SDouglas Gilbert }
1681c65b1445SDouglas Gilbert 
1682c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1683c65b1445SDouglas Gilbert {
1684c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1685c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1686c65b1445SDouglas Gilbert 		};
1687c65b1445SDouglas Gilbert 
1688c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1689c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1690c65b1445SDouglas Gilbert }
1691c65b1445SDouglas Gilbert 
1692c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1693c65b1445SDouglas Gilbert {
1694c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1695c65b1445SDouglas Gilbert 		};
1696c65b1445SDouglas Gilbert 
1697c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1698c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1699c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1700c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1701c65b1445SDouglas Gilbert 	}
1702c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1703c65b1445SDouglas Gilbert }
1704c65b1445SDouglas Gilbert 
1705c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1706c65b1445SDouglas Gilbert 
1707c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1708c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1709c65b1445SDouglas Gilbert {
171023183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1711c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1712c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1713c65b1445SDouglas Gilbert 
1714c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1715c65b1445SDouglas Gilbert 		return errsts;
1716c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1717c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1718c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1719c65b1445SDouglas Gilbert 	if (ppc || sp) {
1720c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1721c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1722c65b1445SDouglas Gilbert 		return check_condition_result;
1723c65b1445SDouglas Gilbert 	}
1724c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1725c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
172623183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1727c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1728c65b1445SDouglas Gilbert 	arr[0] = pcode;
172923183910SDouglas Gilbert 	if (0 == subpcode) {
1730c65b1445SDouglas Gilbert 		switch (pcode) {
1731c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1732c65b1445SDouglas Gilbert 			n = 4;
1733c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1734c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1735c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1736c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1737c65b1445SDouglas Gilbert 			break;
1738c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1739c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1740c65b1445SDouglas Gilbert 			break;
1741c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1742c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1743c65b1445SDouglas Gilbert 			break;
1744c65b1445SDouglas Gilbert 		default:
1745c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1746c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1747c65b1445SDouglas Gilbert 			return check_condition_result;
1748c65b1445SDouglas Gilbert 		}
174923183910SDouglas Gilbert 	} else if (0xff == subpcode) {
175023183910SDouglas Gilbert 		arr[0] |= 0x40;
175123183910SDouglas Gilbert 		arr[1] = subpcode;
175223183910SDouglas Gilbert 		switch (pcode) {
175323183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
175423183910SDouglas Gilbert 			n = 4;
175523183910SDouglas Gilbert 			arr[n++] = 0x0;
175623183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
175723183910SDouglas Gilbert 			arr[n++] = 0x0;
175823183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
175923183910SDouglas Gilbert 			arr[n++] = 0xd;
176023183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
176123183910SDouglas Gilbert 			arr[n++] = 0x2f;
176223183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
176323183910SDouglas Gilbert 			arr[3] = n - 4;
176423183910SDouglas Gilbert 			break;
176523183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
176623183910SDouglas Gilbert 			n = 4;
176723183910SDouglas Gilbert 			arr[n++] = 0xd;
176823183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
176923183910SDouglas Gilbert 			arr[3] = n - 4;
177023183910SDouglas Gilbert 			break;
177123183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
177223183910SDouglas Gilbert 			n = 4;
177323183910SDouglas Gilbert 			arr[n++] = 0x2f;
177423183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
177523183910SDouglas Gilbert 			arr[3] = n - 4;
177623183910SDouglas Gilbert 			break;
177723183910SDouglas Gilbert 		default:
177823183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
177923183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
178023183910SDouglas Gilbert 			return check_condition_result;
178123183910SDouglas Gilbert 		}
178223183910SDouglas Gilbert 	} else {
178323183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
178423183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
178523183910SDouglas Gilbert 		return check_condition_result;
178623183910SDouglas Gilbert 	}
1787c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1788c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1789c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1790c65b1445SDouglas Gilbert }
1791c65b1445SDouglas Gilbert 
1792c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1793c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
17941da177e4SLinus Torvalds {
17951da177e4SLinus Torvalds 	unsigned long iflags;
1796c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1797c65b1445SDouglas Gilbert 	unsigned long long u;
17981da177e4SLinus Torvalds 	int ret;
17991da177e4SLinus Torvalds 
1800c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18011da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18021da177e4SLinus Torvalds 				0);
18031da177e4SLinus Torvalds 		return check_condition_result;
18041da177e4SLinus Torvalds 	}
1805c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1806c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1807c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1808c65b1445SDouglas Gilbert 				0);
1809c65b1445SDouglas Gilbert 		return check_condition_result;
1810c65b1445SDouglas Gilbert 	}
18111da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1812c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1813c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1814c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
18151da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
18161da177e4SLinus Torvalds 				0);
1817c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1818c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1819c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1820c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1821c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1822c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1823c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1824c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1825c65b1445SDouglas Gilbert 		}
18261da177e4SLinus Torvalds 		return check_condition_result;
18271da177e4SLinus Torvalds 	}
18281da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1829c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1830c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1831c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
18321da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1833c65b1445SDouglas Gilbert 	else {
1834c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1835c65b1445SDouglas Gilbert 		u = lba;
1836c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1837c65b1445SDouglas Gilbert 		from_bottom = 0;
1838c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1839c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1840c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1841c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1842c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1843c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1844c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1845c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1846c65b1445SDouglas Gilbert 	}
18471da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18481da177e4SLinus Torvalds 	return ret;
18491da177e4SLinus Torvalds }
18501da177e4SLinus Torvalds 
1851c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1852c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
18531da177e4SLinus Torvalds {
18541da177e4SLinus Torvalds 	unsigned long iflags;
1855c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1856c65b1445SDouglas Gilbert 	unsigned long long u;
18571da177e4SLinus Torvalds 	int res;
18581da177e4SLinus Torvalds 
1859c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18601da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18611da177e4SLinus Torvalds 			       	0);
18621da177e4SLinus Torvalds 		return check_condition_result;
18631da177e4SLinus Torvalds 	}
1864c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1865c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1866c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1867c65b1445SDouglas Gilbert 				0);
1868c65b1445SDouglas Gilbert 		return check_condition_result;
1869c65b1445SDouglas Gilbert 	}
18701da177e4SLinus Torvalds 
18711da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1872c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1873c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1874c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
18751da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1876c65b1445SDouglas Gilbert 	else {
1877c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1878c65b1445SDouglas Gilbert 		u = lba;
1879c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1880c65b1445SDouglas Gilbert 		to_bottom = 0;
1881c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1882c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1883c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1884c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1885c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1886c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1887c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1888c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1889c65b1445SDouglas Gilbert 	}
18901da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
18911da177e4SLinus Torvalds 	if (-1 == res)
18921da177e4SLinus Torvalds 		return (DID_ERROR << 16);
18931da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
18941da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1895c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
18961da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
18971da177e4SLinus Torvalds 	return 0;
18981da177e4SLinus Torvalds }
18991da177e4SLinus Torvalds 
1900c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
19011da177e4SLinus Torvalds 
19021da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
19031da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
19041da177e4SLinus Torvalds {
19051da177e4SLinus Torvalds 	unsigned int alloc_len;
1906c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
19071da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
19081da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
19091da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
19101da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1911c65b1445SDouglas Gilbert 	unsigned char * max_addr;
19121da177e4SLinus Torvalds 
19131da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1914c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
19151da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
19161da177e4SLinus Torvalds 			       	0);
19171da177e4SLinus Torvalds 		return check_condition_result;
19181da177e4SLinus Torvalds 	}
19191da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
19201da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
19211da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1922c65b1445SDouglas Gilbert 	if (1 == select_report)
1923c65b1445SDouglas Gilbert 		lun_cnt = 0;
1924c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1925c65b1445SDouglas Gilbert 		--lun_cnt;
1926c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1927c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1928c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1929c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1930c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1931c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1932c65b1445SDouglas Gilbert 	if (n < num) {
1933c65b1445SDouglas Gilbert 		wlun = 0;
1934c65b1445SDouglas Gilbert 		lun_cnt = n;
1935c65b1445SDouglas Gilbert 	}
19361da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1937c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1938c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1939c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1940c65b1445SDouglas Gilbert 	     i++, lun++) {
1941c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
19421da177e4SLinus Torvalds 		if (upper)
19431da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
19441da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1945c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
19461da177e4SLinus Torvalds 	}
1947c65b1445SDouglas Gilbert 	if (wlun) {
1948c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1949c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1950c65b1445SDouglas Gilbert 		i++;
1951c65b1445SDouglas Gilbert 	}
1952c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
19531da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
19541da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
19551da177e4SLinus Torvalds }
19561da177e4SLinus Torvalds 
1957c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
1958c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip)
1959c639d14eSFUJITA Tomonori {
1960c639d14eSFUJITA Tomonori 	int i, j, ret = -1;
1961c639d14eSFUJITA Tomonori 	unsigned char *kaddr, *buf;
1962c639d14eSFUJITA Tomonori 	unsigned int offset;
1963c639d14eSFUJITA Tomonori 	struct scatterlist *sg;
1964c639d14eSFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
1965c639d14eSFUJITA Tomonori 
1966c639d14eSFUJITA Tomonori 	/* better not to use temporary buffer. */
1967c639d14eSFUJITA Tomonori 	buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
1968c639d14eSFUJITA Tomonori 	if (!buf)
1969c639d14eSFUJITA Tomonori 		return ret;
1970c639d14eSFUJITA Tomonori 
1971c639d14eSFUJITA Tomonori 	offset = 0;
1972c639d14eSFUJITA Tomonori 	scsi_for_each_sg(scp, sg, scsi_sg_count(scp), i) {
1973c639d14eSFUJITA Tomonori 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
1974c639d14eSFUJITA Tomonori 		if (!kaddr)
1975c639d14eSFUJITA Tomonori 			goto out;
1976c639d14eSFUJITA Tomonori 
1977c639d14eSFUJITA Tomonori 		memcpy(buf + offset, kaddr + sg->offset, sg->length);
1978c639d14eSFUJITA Tomonori 		offset += sg->length;
1979c639d14eSFUJITA Tomonori 		kunmap_atomic(kaddr, KM_USER0);
1980c639d14eSFUJITA Tomonori 	}
1981c639d14eSFUJITA Tomonori 
1982c639d14eSFUJITA Tomonori 	offset = 0;
1983c639d14eSFUJITA Tomonori 	for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
1984c639d14eSFUJITA Tomonori 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
1985c639d14eSFUJITA Tomonori 		if (!kaddr)
1986c639d14eSFUJITA Tomonori 			goto out;
1987c639d14eSFUJITA Tomonori 
1988c639d14eSFUJITA Tomonori 		for (j = 0; j < sg->length; j++)
1989c639d14eSFUJITA Tomonori 			*(kaddr + sg->offset + j) ^= *(buf + offset + j);
1990c639d14eSFUJITA Tomonori 
1991c639d14eSFUJITA Tomonori 		offset += sg->length;
1992c639d14eSFUJITA Tomonori 		kunmap_atomic(kaddr, KM_USER0);
1993c639d14eSFUJITA Tomonori 	}
1994c639d14eSFUJITA Tomonori 	ret = 0;
1995c639d14eSFUJITA Tomonori out:
1996c639d14eSFUJITA Tomonori 	kfree(buf);
1997c639d14eSFUJITA Tomonori 
1998c639d14eSFUJITA Tomonori 	return ret;
1999c639d14eSFUJITA Tomonori }
2000c639d14eSFUJITA Tomonori 
20011da177e4SLinus Torvalds /* When timer goes off this function is called. */
20021da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
20031da177e4SLinus Torvalds {
20041da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
20051da177e4SLinus Torvalds 	unsigned long iflags;
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
20081da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
20091da177e4SLinus Torvalds 		       "large\n");
20101da177e4SLinus Torvalds 		return;
20111da177e4SLinus Torvalds 	}
20121da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
20131da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
20141da177e4SLinus Torvalds 	if (! sqcp->in_use) {
20151da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
20161da177e4SLinus Torvalds 		       "interrupt\n");
20171da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
20181da177e4SLinus Torvalds 		return;
20191da177e4SLinus Torvalds 	}
20201da177e4SLinus Torvalds 	sqcp->in_use = 0;
20211da177e4SLinus Torvalds 	if (sqcp->done_funct) {
20221da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
20231da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
20241da177e4SLinus Torvalds 	}
20251da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
20261da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20271da177e4SLinus Torvalds }
20281da177e4SLinus Torvalds 
20291da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
20301da177e4SLinus Torvalds {
20311da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2032c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
2033c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
2034c639d14eSFUJITA Tomonori 	set_bit(QUEUE_FLAG_BIDI, &sdp->request_queue->queue_flags);
20351da177e4SLinus Torvalds 	return 0;
20361da177e4SLinus Torvalds }
20371da177e4SLinus Torvalds 
20381da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
20391da177e4SLinus Torvalds {
20401da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2043c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
2044c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20451da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
20461da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
20471da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
20486f3cbf55SDouglas Gilbert 	if (NULL == devip)
20496f3cbf55SDouglas Gilbert 		return 1;	/* no resources, will be marked offline */
20501da177e4SLinus Torvalds 	sdp->hostdata = devip;
20511da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
20521da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
20531da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
2054c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
20551da177e4SLinus Torvalds 	return 0;
20561da177e4SLinus Torvalds }
20571da177e4SLinus Torvalds 
20581da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
20591da177e4SLinus Torvalds {
20601da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20611da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
20621da177e4SLinus Torvalds 
20631da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2064c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
2065c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20661da177e4SLinus Torvalds 	if (devip) {
20671da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
20681da177e4SLinus Torvalds 		devip->used = 0;
20691da177e4SLinus Torvalds 		sdp->hostdata = NULL;
20701da177e4SLinus Torvalds 	}
20711da177e4SLinus Torvalds }
20721da177e4SLinus Torvalds 
20731da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
20741da177e4SLinus Torvalds {
20751da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20761da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
20771da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20781da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
20791da177e4SLinus Torvalds 
20801da177e4SLinus Torvalds 	if (devip)
20811da177e4SLinus Torvalds 		return devip;
20821da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
20831da177e4SLinus Torvalds         if(! sdbg_host) {
20841da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
20851da177e4SLinus Torvalds 		return NULL;
20861da177e4SLinus Torvalds         }
20871da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
20881da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
20891da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
20901da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
20911da177e4SLinus Torvalds                         return devip;
20921da177e4SLinus Torvalds 		else {
20931da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
20941da177e4SLinus Torvalds 				open_devip = devip;
20951da177e4SLinus Torvalds 		}
20961da177e4SLinus Torvalds 	}
20971da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
20986f3cbf55SDouglas Gilbert 		open_devip = kzalloc(sizeof(*open_devip),GFP_ATOMIC);
20991da177e4SLinus Torvalds 		if (NULL == open_devip) {
21001da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
21011da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
21021da177e4SLinus Torvalds 			return NULL;
21031da177e4SLinus Torvalds 		}
21041da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
21051da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
21061da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
21071da177e4SLinus Torvalds 	}
21081da177e4SLinus Torvalds         if (open_devip) {
21091da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
21101da177e4SLinus Torvalds 		open_devip->target = sdev->id;
21111da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
21121da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
21131da177e4SLinus Torvalds 		open_devip->reset = 1;
21141da177e4SLinus Torvalds 		open_devip->used = 1;
21151da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
21161da177e4SLinus Torvalds 		if (scsi_debug_dsense)
21171da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
21181da177e4SLinus Torvalds 		else {
21191da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
21201da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
21211da177e4SLinus Torvalds 		}
2122c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2123c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
21241da177e4SLinus Torvalds 		return open_devip;
21251da177e4SLinus Torvalds         }
21261da177e4SLinus Torvalds         return NULL;
21271da177e4SLinus Torvalds }
21281da177e4SLinus Torvalds 
21291da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
21301da177e4SLinus Torvalds 			    int asc, int asq)
21311da177e4SLinus Torvalds {
21321da177e4SLinus Torvalds 	unsigned char * sbuff;
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
21351da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
21361da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
21371da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
21381da177e4SLinus Torvalds 		sbuff[1] = key;
21391da177e4SLinus Torvalds 		sbuff[2] = asc;
21401da177e4SLinus Torvalds 		sbuff[3] = asq;
21411da177e4SLinus Torvalds 	} else {
21421da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
21431da177e4SLinus Torvalds 		sbuff[2] = key;
21441da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
21451da177e4SLinus Torvalds 		sbuff[12] = asc;
21461da177e4SLinus Torvalds 		sbuff[13] = asq;
21471da177e4SLinus Torvalds 	}
21481da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21491da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
21501da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
21511da177e4SLinus Torvalds }
21521da177e4SLinus Torvalds 
21531da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
21541da177e4SLinus Torvalds {
21551da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21561da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
21571da177e4SLinus Torvalds 	++num_aborts;
21581da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
21591da177e4SLinus Torvalds 	return SUCCESS;
21601da177e4SLinus Torvalds }
21611da177e4SLinus Torvalds 
21621da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
21631da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
21641da177e4SLinus Torvalds {
21651da177e4SLinus Torvalds 	int res;
21661da177e4SLinus Torvalds 	unsigned char *buf;
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21691da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
21701da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
21711da177e4SLinus Torvalds 	if (buf) {
21721da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
21731da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
21741da177e4SLinus Torvalds 		kfree(buf);
21751da177e4SLinus Torvalds 		if (! res)
21761da177e4SLinus Torvalds 			return res;
21771da177e4SLinus Torvalds 	}
21781da177e4SLinus Torvalds 	info[0] = sdebug_heads;
21791da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
21801da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
21811da177e4SLinus Torvalds 	return 0;
21821da177e4SLinus Torvalds }
21831da177e4SLinus Torvalds 
21841da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
21851da177e4SLinus Torvalds {
21861da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
21871da177e4SLinus Torvalds 
21881da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21891da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
21901da177e4SLinus Torvalds 	++num_dev_resets;
21911da177e4SLinus Torvalds 	if (SCpnt) {
21921da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
21931da177e4SLinus Torvalds 		if (devip)
21941da177e4SLinus Torvalds 			devip->reset = 1;
21951da177e4SLinus Torvalds 	}
21961da177e4SLinus Torvalds 	return SUCCESS;
21971da177e4SLinus Torvalds }
21981da177e4SLinus Torvalds 
21991da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
22001da177e4SLinus Torvalds {
22011da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
22021da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
22031da177e4SLinus Torvalds         struct scsi_device * sdp;
22041da177e4SLinus Torvalds         struct Scsi_Host * hp;
22051da177e4SLinus Torvalds 
22061da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
22071da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
22081da177e4SLinus Torvalds 	++num_bus_resets;
22091da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
22101da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
22111da177e4SLinus Torvalds 		if (sdbg_host) {
22121da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
22131da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
22141da177e4SLinus Torvalds                                             dev_list)
22151da177e4SLinus Torvalds 				dev_info->reset = 1;
22161da177e4SLinus Torvalds 		}
22171da177e4SLinus Torvalds 	}
22181da177e4SLinus Torvalds 	return SUCCESS;
22191da177e4SLinus Torvalds }
22201da177e4SLinus Torvalds 
22211da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
22221da177e4SLinus Torvalds {
22231da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
22241da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
22251da177e4SLinus Torvalds 
22261da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
22271da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
22281da177e4SLinus Torvalds 	++num_host_resets;
22291da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
22301da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
22311da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
22321da177e4SLinus Torvalds                                     dev_list)
22331da177e4SLinus Torvalds                         dev_info->reset = 1;
22341da177e4SLinus Torvalds         }
22351da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
22361da177e4SLinus Torvalds 	stop_all_queued();
22371da177e4SLinus Torvalds 	return SUCCESS;
22381da177e4SLinus Torvalds }
22391da177e4SLinus Torvalds 
22401da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
22411da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
22421da177e4SLinus Torvalds {
22431da177e4SLinus Torvalds 	unsigned long iflags;
22441da177e4SLinus Torvalds 	int k;
22451da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22461da177e4SLinus Torvalds 
22471da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22481da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22491da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22501da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
22511da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22521da177e4SLinus Torvalds 			sqcp->in_use = 0;
22531da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22541da177e4SLinus Torvalds 			break;
22551da177e4SLinus Torvalds 		}
22561da177e4SLinus Torvalds 	}
22571da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22581da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
22591da177e4SLinus Torvalds }
22601da177e4SLinus Torvalds 
22611da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
22621da177e4SLinus Torvalds static void stop_all_queued(void)
22631da177e4SLinus Torvalds {
22641da177e4SLinus Torvalds 	unsigned long iflags;
22651da177e4SLinus Torvalds 	int k;
22661da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22671da177e4SLinus Torvalds 
22681da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22691da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22701da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22711da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
22721da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22731da177e4SLinus Torvalds 			sqcp->in_use = 0;
22741da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22751da177e4SLinus Torvalds 		}
22761da177e4SLinus Torvalds 	}
22771da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22781da177e4SLinus Torvalds }
22791da177e4SLinus Torvalds 
22801da177e4SLinus Torvalds /* Initializes timers in queued array */
22811da177e4SLinus Torvalds static void __init init_all_queued(void)
22821da177e4SLinus Torvalds {
22831da177e4SLinus Torvalds 	unsigned long iflags;
22841da177e4SLinus Torvalds 	int k;
22851da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22861da177e4SLinus Torvalds 
22871da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22881da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22891da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22901da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
22911da177e4SLinus Torvalds 		sqcp->in_use = 0;
22921da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
22931da177e4SLinus Torvalds 	}
22941da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22951da177e4SLinus Torvalds }
22961da177e4SLinus Torvalds 
22971da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
22981da177e4SLinus Torvalds {
22991da177e4SLinus Torvalds 	struct partition * pp;
23001da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
23011da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
23021da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
23031da177e4SLinus Torvalds 
23041da177e4SLinus Torvalds 	/* assume partition table already zeroed */
23051da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
23061da177e4SLinus Torvalds 		return;
23071da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
23081da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
23091da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
23101da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
23111da177e4SLinus Torvalds 	}
2312c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
23131da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
23141da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
23151da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
23161da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
23171da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
23181da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
23191da177e4SLinus Torvalds 			    * heads_by_sects;
23201da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
23211da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
23221da177e4SLinus Torvalds 
23231da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
23241da177e4SLinus Torvalds 	ramp[511] = 0xAA;
23251da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
23261da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
23271da177e4SLinus Torvalds 		start_sec = starts[k];
23281da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
23291da177e4SLinus Torvalds 		pp->boot_ind = 0;
23301da177e4SLinus Torvalds 
23311da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
23321da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
23331da177e4SLinus Torvalds 			   / sdebug_sectors_per;
23341da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
23351da177e4SLinus Torvalds 
23361da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
23371da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
23381da177e4SLinus Torvalds 			       / sdebug_sectors_per;
23391da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
23401da177e4SLinus Torvalds 
23411da177e4SLinus Torvalds 		pp->start_sect = start_sec;
23421da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
23431da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
23441da177e4SLinus Torvalds 	}
23451da177e4SLinus Torvalds }
23461da177e4SLinus Torvalds 
23471da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
23481da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
23491da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
23501da177e4SLinus Torvalds {
23511da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
23521da177e4SLinus Torvalds 		if (scsi_result) {
23531da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
23541da177e4SLinus Torvalds 
2355c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2356c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2357c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
23581da177e4SLinus Torvalds 		}
23591da177e4SLinus Torvalds 	}
23601da177e4SLinus Torvalds 	if (cmnd && devip) {
23611da177e4SLinus Torvalds 		/* simulate autosense by this driver */
23621da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
23631da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
23641da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
23651da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
23661da177e4SLinus Torvalds 	}
23671da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
23681da177e4SLinus Torvalds 		if (cmnd)
23691da177e4SLinus Torvalds 			cmnd->result = scsi_result;
23701da177e4SLinus Torvalds 		if (done)
23711da177e4SLinus Torvalds 			done(cmnd);
23721da177e4SLinus Torvalds 		return 0;
23731da177e4SLinus Torvalds 	} else {
23741da177e4SLinus Torvalds 		unsigned long iflags;
23751da177e4SLinus Torvalds 		int k;
23761da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
23771da177e4SLinus Torvalds 
23781da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
23791da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
23801da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
23811da177e4SLinus Torvalds 			if (! sqcp->in_use)
23821da177e4SLinus Torvalds 				break;
23831da177e4SLinus Torvalds 		}
23841da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
23851da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
23861da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
23871da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
23881da177e4SLinus Torvalds 		}
23891da177e4SLinus Torvalds 		sqcp->in_use = 1;
23901da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
23911da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
23921da177e4SLinus Torvalds 		sqcp->done_funct = done;
23931da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
23941da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
23951da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
23961da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
23971da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23981da177e4SLinus Torvalds 		if (cmnd)
23991da177e4SLinus Torvalds 			cmnd->result = 0;
24001da177e4SLinus Torvalds 		return 0;
24011da177e4SLinus Torvalds 	}
24021da177e4SLinus Torvalds }
24031da177e4SLinus Torvalds 
240423183910SDouglas Gilbert /* Note: The following macros create attribute files in the
240523183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
240623183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
240723183910SDouglas Gilbert    as it can when the corresponding attribute in the
240823183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
240923183910SDouglas Gilbert  */
2410c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2411c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2412c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2413c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2414c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
241523183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2416c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2417c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2418c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2419c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2420c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2421c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2422c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2423c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
242423183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
242523183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
24261da177e4SLinus Torvalds 
24271da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
24281da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
24291da177e4SLinus Torvalds MODULE_LICENSE("GPL");
24301da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
24331da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2434c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2435c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2436beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
243723183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2438c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2439c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
24401da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2441c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
24426f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
24431da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
24441da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2445c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
244623183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
24471da177e4SLinus Torvalds 
24481da177e4SLinus Torvalds 
24491da177e4SLinus Torvalds static char sdebug_info[256];
24501da177e4SLinus Torvalds 
24511da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
24521da177e4SLinus Torvalds {
24531da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
24541da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
24551da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
24561da177e4SLinus Torvalds 		scsi_debug_opts);
24571da177e4SLinus Torvalds 	return sdebug_info;
24581da177e4SLinus Torvalds }
24591da177e4SLinus Torvalds 
24601da177e4SLinus Torvalds /* scsi_debug_proc_info
24611da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
24621da177e4SLinus Torvalds  */
24631da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
24641da177e4SLinus Torvalds 				int length, int inout)
24651da177e4SLinus Torvalds {
24661da177e4SLinus Torvalds 	int len, pos, begin;
24671da177e4SLinus Torvalds 	int orig_length;
24681da177e4SLinus Torvalds 
24691da177e4SLinus Torvalds 	orig_length = length;
24701da177e4SLinus Torvalds 
24711da177e4SLinus Torvalds 	if (inout == 1) {
24721da177e4SLinus Torvalds 		char arr[16];
24731da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
24741da177e4SLinus Torvalds 
24751da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
24761da177e4SLinus Torvalds 			return -EACCES;
24771da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
24781da177e4SLinus Torvalds 		arr[minLen] = '\0';
24791da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
24801da177e4SLinus Torvalds 			return -EINVAL;
24811da177e4SLinus Torvalds 		scsi_debug_opts = pos;
24821da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
24831da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
24841da177e4SLinus Torvalds 		return length;
24851da177e4SLinus Torvalds 	}
24861da177e4SLinus Torvalds 	begin = 0;
24871da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
24881da177e4SLinus Torvalds 	    "%s [%s]\n"
24891da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
24901da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
24911da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
24921da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
24931da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
24941da177e4SLinus Torvalds 	    "host_resets=%d\n",
24951da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
24961da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
24971da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
24981da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
24991da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
25001da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
25011da177e4SLinus Torvalds 	if (pos < offset) {
25021da177e4SLinus Torvalds 		len = 0;
25031da177e4SLinus Torvalds 		begin = pos;
25041da177e4SLinus Torvalds 	}
25051da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
25061da177e4SLinus Torvalds 	len -= (offset - begin);
25071da177e4SLinus Torvalds 	if (len > length)
25081da177e4SLinus Torvalds 		len = length;
25091da177e4SLinus Torvalds 	return len;
25101da177e4SLinus Torvalds }
25111da177e4SLinus Torvalds 
25121da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
25131da177e4SLinus Torvalds {
25141da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
25151da177e4SLinus Torvalds }
25161da177e4SLinus Torvalds 
25171da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
25181da177e4SLinus Torvalds 				  const char * buf, size_t count)
25191da177e4SLinus Torvalds {
25201da177e4SLinus Torvalds         int delay;
25211da177e4SLinus Torvalds 	char work[20];
25221da177e4SLinus Torvalds 
25231da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
25241da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
25251da177e4SLinus Torvalds 			scsi_debug_delay = delay;
25261da177e4SLinus Torvalds 			return count;
25271da177e4SLinus Torvalds 		}
25281da177e4SLinus Torvalds 	}
25291da177e4SLinus Torvalds 	return -EINVAL;
25301da177e4SLinus Torvalds }
25311da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
25321da177e4SLinus Torvalds 	    sdebug_delay_store);
25331da177e4SLinus Torvalds 
25341da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
25351da177e4SLinus Torvalds {
25361da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
25371da177e4SLinus Torvalds }
25381da177e4SLinus Torvalds 
25391da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
25401da177e4SLinus Torvalds 				 const char * buf, size_t count)
25411da177e4SLinus Torvalds {
25421da177e4SLinus Torvalds         int opts;
25431da177e4SLinus Torvalds 	char work[20];
25441da177e4SLinus Torvalds 
25451da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
25461da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
25471da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
25481da177e4SLinus Torvalds 				goto opts_done;
25491da177e4SLinus Torvalds 		} else {
25501da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
25511da177e4SLinus Torvalds 				goto opts_done;
25521da177e4SLinus Torvalds 		}
25531da177e4SLinus Torvalds 	}
25541da177e4SLinus Torvalds 	return -EINVAL;
25551da177e4SLinus Torvalds opts_done:
25561da177e4SLinus Torvalds 	scsi_debug_opts = opts;
25571da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
25581da177e4SLinus Torvalds 	return count;
25591da177e4SLinus Torvalds }
25601da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
25611da177e4SLinus Torvalds 	    sdebug_opts_store);
25621da177e4SLinus Torvalds 
25631da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
25641da177e4SLinus Torvalds {
25651da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
25661da177e4SLinus Torvalds }
25671da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
25681da177e4SLinus Torvalds 				  const char * buf, size_t count)
25691da177e4SLinus Torvalds {
25701da177e4SLinus Torvalds         int n;
25711da177e4SLinus Torvalds 
25721da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25731da177e4SLinus Torvalds 		scsi_debug_ptype = n;
25741da177e4SLinus Torvalds 		return count;
25751da177e4SLinus Torvalds 	}
25761da177e4SLinus Torvalds 	return -EINVAL;
25771da177e4SLinus Torvalds }
25781da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
25791da177e4SLinus Torvalds 
25801da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
25811da177e4SLinus Torvalds {
25821da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
25831da177e4SLinus Torvalds }
25841da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
25851da177e4SLinus Torvalds 				  const char * buf, size_t count)
25861da177e4SLinus Torvalds {
25871da177e4SLinus Torvalds         int n;
25881da177e4SLinus Torvalds 
25891da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25901da177e4SLinus Torvalds 		scsi_debug_dsense = n;
25911da177e4SLinus Torvalds 		return count;
25921da177e4SLinus Torvalds 	}
25931da177e4SLinus Torvalds 	return -EINVAL;
25941da177e4SLinus Torvalds }
25951da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
25961da177e4SLinus Torvalds 	    sdebug_dsense_store);
25971da177e4SLinus Torvalds 
259823183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
259923183910SDouglas Gilbert {
260023183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
260123183910SDouglas Gilbert }
260223183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
260323183910SDouglas Gilbert 				    const char * buf, size_t count)
260423183910SDouglas Gilbert {
260523183910SDouglas Gilbert         int n;
260623183910SDouglas Gilbert 
260723183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
260823183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
260923183910SDouglas Gilbert 		return count;
261023183910SDouglas Gilbert 	}
261123183910SDouglas Gilbert 	return -EINVAL;
261223183910SDouglas Gilbert }
261323183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
261423183910SDouglas Gilbert 	    sdebug_fake_rw_store);
261523183910SDouglas Gilbert 
2616c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2617c65b1445SDouglas Gilbert {
2618c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2619c65b1445SDouglas Gilbert }
2620c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2621c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2622c65b1445SDouglas Gilbert {
2623c65b1445SDouglas Gilbert         int n;
2624c65b1445SDouglas Gilbert 
2625c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2626c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2627c65b1445SDouglas Gilbert 		return count;
2628c65b1445SDouglas Gilbert 	}
2629c65b1445SDouglas Gilbert 	return -EINVAL;
2630c65b1445SDouglas Gilbert }
2631c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2632c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2633c65b1445SDouglas Gilbert 
26341da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
26351da177e4SLinus Torvalds {
26361da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
26371da177e4SLinus Torvalds }
26381da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
26391da177e4SLinus Torvalds 				     const char * buf, size_t count)
26401da177e4SLinus Torvalds {
26411da177e4SLinus Torvalds         int n;
26421da177e4SLinus Torvalds 
26431da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26441da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
26451da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26461da177e4SLinus Torvalds 		return count;
26471da177e4SLinus Torvalds 	}
26481da177e4SLinus Torvalds 	return -EINVAL;
26491da177e4SLinus Torvalds }
26501da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
26511da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
26521da177e4SLinus Torvalds 
26531da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
26541da177e4SLinus Torvalds {
26551da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
26561da177e4SLinus Torvalds }
26571da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
26581da177e4SLinus Torvalds 
26591da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
26601da177e4SLinus Torvalds {
26611da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
26621da177e4SLinus Torvalds }
26631da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
26641da177e4SLinus Torvalds 
26651da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
26661da177e4SLinus Torvalds {
26671da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
26681da177e4SLinus Torvalds }
26691da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
26701da177e4SLinus Torvalds 				      const char * buf, size_t count)
26711da177e4SLinus Torvalds {
26721da177e4SLinus Torvalds         int nth;
26731da177e4SLinus Torvalds 
26741da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
26751da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
26761da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
26771da177e4SLinus Torvalds 		return count;
26781da177e4SLinus Torvalds 	}
26791da177e4SLinus Torvalds 	return -EINVAL;
26801da177e4SLinus Torvalds }
26811da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
26821da177e4SLinus Torvalds 	    sdebug_every_nth_store);
26831da177e4SLinus Torvalds 
26841da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
26851da177e4SLinus Torvalds {
26861da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
26871da177e4SLinus Torvalds }
26881da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
26891da177e4SLinus Torvalds 				     const char * buf, size_t count)
26901da177e4SLinus Torvalds {
26911da177e4SLinus Torvalds         int n;
26921da177e4SLinus Torvalds 
26931da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26941da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
26951da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26961da177e4SLinus Torvalds 		return count;
26971da177e4SLinus Torvalds 	}
26981da177e4SLinus Torvalds 	return -EINVAL;
26991da177e4SLinus Torvalds }
27001da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
27011da177e4SLinus Torvalds 	    sdebug_max_luns_store);
27021da177e4SLinus Torvalds 
27031da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
27041da177e4SLinus Torvalds {
27051da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
27061da177e4SLinus Torvalds }
27071da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
27081da177e4SLinus Torvalds 
2709c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2710c65b1445SDouglas Gilbert {
2711c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2712c65b1445SDouglas Gilbert }
2713c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2714c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2715c65b1445SDouglas Gilbert {
2716c65b1445SDouglas Gilbert         int n;
2717c65b1445SDouglas Gilbert 
2718c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2719c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2720c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2721c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2722c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2723c65b1445SDouglas Gilbert 		} else
2724c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2725c65b1445SDouglas Gilbert 		return count;
2726c65b1445SDouglas Gilbert 	}
2727c65b1445SDouglas Gilbert 	return -EINVAL;
2728c65b1445SDouglas Gilbert }
2729c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2730c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2731c65b1445SDouglas Gilbert 
27321da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
27331da177e4SLinus Torvalds {
27341da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
27351da177e4SLinus Torvalds }
27361da177e4SLinus Torvalds 
27371da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
27381da177e4SLinus Torvalds 				     const char * buf, size_t count)
27391da177e4SLinus Torvalds {
27401da177e4SLinus Torvalds         int delta_hosts;
27411da177e4SLinus Torvalds 	char work[20];
27421da177e4SLinus Torvalds 
27431da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
27441da177e4SLinus Torvalds 		return -EINVAL;
27451da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
27461da177e4SLinus Torvalds 		int neg = 0;
27471da177e4SLinus Torvalds 
27481da177e4SLinus Torvalds 		if ('-' == *work)
27491da177e4SLinus Torvalds 			neg = 1;
27501da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
27511da177e4SLinus Torvalds 			return -EINVAL;
27521da177e4SLinus Torvalds 		if (neg)
27531da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
27541da177e4SLinus Torvalds 	}
27551da177e4SLinus Torvalds 	if (delta_hosts > 0) {
27561da177e4SLinus Torvalds 		do {
27571da177e4SLinus Torvalds 			sdebug_add_adapter();
27581da177e4SLinus Torvalds 		} while (--delta_hosts);
27591da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
27601da177e4SLinus Torvalds 		do {
27611da177e4SLinus Torvalds 			sdebug_remove_adapter();
27621da177e4SLinus Torvalds 		} while (++delta_hosts);
27631da177e4SLinus Torvalds 	}
27641da177e4SLinus Torvalds 	return count;
27651da177e4SLinus Torvalds }
27661da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
27671da177e4SLinus Torvalds 	    sdebug_add_host_store);
27681da177e4SLinus Torvalds 
276923183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
277023183910SDouglas Gilbert 					  char * buf)
277123183910SDouglas Gilbert {
277223183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
277323183910SDouglas Gilbert }
277423183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
277523183910SDouglas Gilbert 					   const char * buf, size_t count)
277623183910SDouglas Gilbert {
277723183910SDouglas Gilbert 	int n;
277823183910SDouglas Gilbert 
277923183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
278023183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
278123183910SDouglas Gilbert 		return count;
278223183910SDouglas Gilbert 	}
278323183910SDouglas Gilbert 	return -EINVAL;
278423183910SDouglas Gilbert }
278523183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
278623183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
278723183910SDouglas Gilbert 
278823183910SDouglas Gilbert /* Note: The following function creates attribute files in the
278923183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
279023183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
279123183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
279223183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
279323183910SDouglas Gilbert  */
27946ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
27951da177e4SLinus Torvalds {
27966ecaff7fSRandy Dunlap 	int ret;
27976ecaff7fSRandy Dunlap 
27986ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27996ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
28006ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
28016ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
28026ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
280323183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
28046ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
280523183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
28066ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
280723183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
28086ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
28096ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
28106ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
281123183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
281223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
28136ecaff7fSRandy Dunlap 	return ret;
28141da177e4SLinus Torvalds }
28151da177e4SLinus Torvalds 
28161da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
28171da177e4SLinus Torvalds {
281823183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
281923183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
28201da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
28211da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
28221da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
28231da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
282423183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
282523183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
28261da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
282723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
28281da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
28291da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
28301da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
28311da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
28321da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
28331da177e4SLinus Torvalds }
28341da177e4SLinus Torvalds 
28351da177e4SLinus Torvalds static int __init scsi_debug_init(void)
28361da177e4SLinus Torvalds {
2837c65b1445SDouglas Gilbert 	unsigned int sz;
28381da177e4SLinus Torvalds 	int host_to_add;
28391da177e4SLinus Torvalds 	int k;
28406ecaff7fSRandy Dunlap 	int ret;
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
28431da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2844c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2845c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2846c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2847c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2848c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2849c65b1445SDouglas Gilbert 	} else
2850c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
28511da177e4SLinus Torvalds 
28521da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
28531da177e4SLinus Torvalds 	sdebug_heads = 8;
28541da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
28551da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
28561da177e4SLinus Torvalds 		sdebug_heads = 32;
28571da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
28581da177e4SLinus Torvalds 		sdebug_heads = 64;
28591da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28601da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28611da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
28621da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
28631da177e4SLinus Torvalds 		sdebug_heads = 255;
28641da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
28651da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28661da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28671da177e4SLinus Torvalds 	}
28681da177e4SLinus Torvalds 
28691da177e4SLinus Torvalds 	sz = sdebug_store_size;
28701da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
28711da177e4SLinus Torvalds 	if (NULL == fake_storep) {
28721da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
28731da177e4SLinus Torvalds 		return -ENOMEM;
28741da177e4SLinus Torvalds 	}
28751da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
28761da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
28771da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
28781da177e4SLinus Torvalds 
28796ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
28806ecaff7fSRandy Dunlap 	if (ret < 0) {
28816ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
28826ecaff7fSRandy Dunlap 			ret);
28836ecaff7fSRandy Dunlap 		goto free_vm;
28846ecaff7fSRandy Dunlap 	}
28856ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
28866ecaff7fSRandy Dunlap 	if (ret < 0) {
28876ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
28886ecaff7fSRandy Dunlap 			ret);
28896ecaff7fSRandy Dunlap 		goto dev_unreg;
28906ecaff7fSRandy Dunlap 	}
28916ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
28926ecaff7fSRandy Dunlap 	if (ret < 0) {
28936ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
28946ecaff7fSRandy Dunlap 			ret);
28956ecaff7fSRandy Dunlap 		goto bus_unreg;
28966ecaff7fSRandy Dunlap 	}
28976ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
28986ecaff7fSRandy Dunlap 	if (ret < 0) {
28996ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
29006ecaff7fSRandy Dunlap 			ret);
29016ecaff7fSRandy Dunlap 		goto del_files;
29026ecaff7fSRandy Dunlap 	}
29031da177e4SLinus Torvalds 
29046ecaff7fSRandy Dunlap 	init_all_queued();
29051da177e4SLinus Torvalds 
29061da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
29071da177e4SLinus Torvalds         scsi_debug_add_host = 0;
29081da177e4SLinus Torvalds 
29091da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
29101da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
29111da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
29121da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
29131da177e4SLinus Torvalds                         break;
29141da177e4SLinus Torvalds                 }
29151da177e4SLinus Torvalds         }
29161da177e4SLinus Torvalds 
29171da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
29181da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
29191da177e4SLinus Torvalds 		       scsi_debug_add_host);
29201da177e4SLinus Torvalds 	}
29211da177e4SLinus Torvalds 	return 0;
29226ecaff7fSRandy Dunlap 
29236ecaff7fSRandy Dunlap del_files:
29246ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
29256ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
29266ecaff7fSRandy Dunlap bus_unreg:
29276ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
29286ecaff7fSRandy Dunlap dev_unreg:
29296ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
29306ecaff7fSRandy Dunlap free_vm:
29316ecaff7fSRandy Dunlap 	vfree(fake_storep);
29326ecaff7fSRandy Dunlap 
29336ecaff7fSRandy Dunlap 	return ret;
29341da177e4SLinus Torvalds }
29351da177e4SLinus Torvalds 
29361da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
29371da177e4SLinus Torvalds {
29381da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
29391da177e4SLinus Torvalds 
29401da177e4SLinus Torvalds 	stop_all_queued();
29411da177e4SLinus Torvalds 	for (; k; k--)
29421da177e4SLinus Torvalds 		sdebug_remove_adapter();
29431da177e4SLinus Torvalds 	do_remove_driverfs_files();
29441da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
29451da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
29461da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
29471da177e4SLinus Torvalds 
29481da177e4SLinus Torvalds 	vfree(fake_storep);
29491da177e4SLinus Torvalds }
29501da177e4SLinus Torvalds 
29511da177e4SLinus Torvalds device_initcall(scsi_debug_init);
29521da177e4SLinus Torvalds module_exit(scsi_debug_exit);
29531da177e4SLinus Torvalds 
295452c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
29551da177e4SLinus Torvalds {
29561da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
29571da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
29581da177e4SLinus Torvalds }
29591da177e4SLinus Torvalds 
29601da177e4SLinus Torvalds static struct device pseudo_primary = {
29611da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
29621da177e4SLinus Torvalds 	.release	= pseudo_0_release,
29631da177e4SLinus Torvalds };
29641da177e4SLinus Torvalds 
29651da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
29661da177e4SLinus Torvalds                           struct device_driver *dev_driver)
29671da177e4SLinus Torvalds {
29681da177e4SLinus Torvalds         return 1;
29691da177e4SLinus Torvalds }
29701da177e4SLinus Torvalds 
29711da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
29721da177e4SLinus Torvalds         .name = "pseudo",
29731da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2974bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2975bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
29761da177e4SLinus Torvalds };
29771da177e4SLinus Torvalds 
29781da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
29791da177e4SLinus Torvalds {
29801da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29811da177e4SLinus Torvalds 
29821da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29831da177e4SLinus Torvalds         kfree(sdbg_host);
29841da177e4SLinus Torvalds }
29851da177e4SLinus Torvalds 
29861da177e4SLinus Torvalds static int sdebug_add_adapter(void)
29871da177e4SLinus Torvalds {
29881da177e4SLinus Torvalds 	int k, devs_per_host;
29891da177e4SLinus Torvalds         int error = 0;
29901da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29911da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29921da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29931da177e4SLinus Torvalds 
299424669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
29951da177e4SLinus Torvalds         if (NULL == sdbg_host) {
29961da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
29971da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
29981da177e4SLinus Torvalds                 return -ENOMEM;
29991da177e4SLinus Torvalds         }
30001da177e4SLinus Torvalds 
30011da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
30021da177e4SLinus Torvalds 
30031da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
30041da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
300524669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
30061da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
30071da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
30081da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
30091da177e4SLinus Torvalds                         error = -ENOMEM;
30101da177e4SLinus Torvalds 			goto clean;
30111da177e4SLinus Torvalds                 }
30121da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
30131da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
30141da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
30151da177e4SLinus Torvalds         }
30161da177e4SLinus Torvalds 
30171da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
30181da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
30191da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
30201da177e4SLinus Torvalds 
30211da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
30221da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
30231da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
30241da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
30251da177e4SLinus Torvalds 
30261da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
30271da177e4SLinus Torvalds 
30281da177e4SLinus Torvalds         if (error)
30291da177e4SLinus Torvalds 		goto clean;
30301da177e4SLinus Torvalds 
30311da177e4SLinus Torvalds 	++scsi_debug_add_host;
30321da177e4SLinus Torvalds         return error;
30331da177e4SLinus Torvalds 
30341da177e4SLinus Torvalds clean:
30351da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30361da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30371da177e4SLinus Torvalds 					  dev_list);
30381da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
30391da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
30401da177e4SLinus Torvalds 	}
30411da177e4SLinus Torvalds 
30421da177e4SLinus Torvalds 	kfree(sdbg_host);
30431da177e4SLinus Torvalds         return error;
30441da177e4SLinus Torvalds }
30451da177e4SLinus Torvalds 
30461da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
30471da177e4SLinus Torvalds {
30481da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
30491da177e4SLinus Torvalds 
30501da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
30511da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
30521da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
30531da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
30541da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
30551da177e4SLinus Torvalds 	}
30561da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
30571da177e4SLinus Torvalds 
30581da177e4SLinus Torvalds 	if (!sdbg_host)
30591da177e4SLinus Torvalds 		return;
30601da177e4SLinus Torvalds 
30611da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
30621da177e4SLinus Torvalds         --scsi_debug_add_host;
30631da177e4SLinus Torvalds }
30641da177e4SLinus Torvalds 
30659e603ca0SFUJITA Tomonori static struct scsi_host_template sdebug_driver_template = {
30669e603ca0SFUJITA Tomonori 	.proc_info =		scsi_debug_proc_info,
30679e603ca0SFUJITA Tomonori 	.proc_name =		sdebug_proc_name,
30689e603ca0SFUJITA Tomonori 	.name =			"SCSI DEBUG",
30699e603ca0SFUJITA Tomonori 	.info =			scsi_debug_info,
30709e603ca0SFUJITA Tomonori 	.slave_alloc =		scsi_debug_slave_alloc,
30719e603ca0SFUJITA Tomonori 	.slave_configure =	scsi_debug_slave_configure,
30729e603ca0SFUJITA Tomonori 	.slave_destroy =	scsi_debug_slave_destroy,
30739e603ca0SFUJITA Tomonori 	.ioctl =		scsi_debug_ioctl,
30749e603ca0SFUJITA Tomonori 	.queuecommand =		scsi_debug_queuecommand,
30759e603ca0SFUJITA Tomonori 	.eh_abort_handler =	scsi_debug_abort,
30769e603ca0SFUJITA Tomonori 	.eh_bus_reset_handler = scsi_debug_bus_reset,
30779e603ca0SFUJITA Tomonori 	.eh_device_reset_handler = scsi_debug_device_reset,
30789e603ca0SFUJITA Tomonori 	.eh_host_reset_handler = scsi_debug_host_reset,
30799e603ca0SFUJITA Tomonori 	.bios_param =		scsi_debug_biosparam,
30809e603ca0SFUJITA Tomonori 	.can_queue =		SCSI_DEBUG_CANQUEUE,
30819e603ca0SFUJITA Tomonori 	.this_id =		7,
30829e603ca0SFUJITA Tomonori 	.sg_tablesize =		256,
30839e603ca0SFUJITA Tomonori 	.cmd_per_lun =		16,
30849e603ca0SFUJITA Tomonori 	.max_sectors =		0xffff,
30859e603ca0SFUJITA Tomonori 	.use_clustering = 	DISABLE_CLUSTERING,
30869e603ca0SFUJITA Tomonori 	.module =		THIS_MODULE,
30879e603ca0SFUJITA Tomonori };
30889e603ca0SFUJITA Tomonori 
30891da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
30901da177e4SLinus Torvalds {
30911da177e4SLinus Torvalds         int error = 0;
30921da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30931da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
30941da177e4SLinus Torvalds 
30951da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30961da177e4SLinus Torvalds 
30971da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
30981da177e4SLinus Torvalds         if (NULL == hpnt) {
30991da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
31001da177e4SLinus Torvalds                 error = -ENODEV;
31011da177e4SLinus Torvalds 		return error;
31021da177e4SLinus Torvalds         }
31031da177e4SLinus Torvalds 
31041da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
31051da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
31061da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
31071da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
31081da177e4SLinus Torvalds 	else
31091da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3110c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
31111da177e4SLinus Torvalds 
31121da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
31131da177e4SLinus Torvalds         if (error) {
31141da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
31151da177e4SLinus Torvalds                 error = -ENODEV;
31161da177e4SLinus Torvalds 		scsi_host_put(hpnt);
31171da177e4SLinus Torvalds         } else
31181da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
31191da177e4SLinus Torvalds 
31201da177e4SLinus Torvalds 
31211da177e4SLinus Torvalds         return error;
31221da177e4SLinus Torvalds }
31231da177e4SLinus Torvalds 
31241da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
31251da177e4SLinus Torvalds {
31261da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
31271da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
31281da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
31291da177e4SLinus Torvalds 
31301da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
31311da177e4SLinus Torvalds 
31321da177e4SLinus Torvalds 	if (!sdbg_host) {
31331da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
31341da177e4SLinus Torvalds 		       __FUNCTION__);
31351da177e4SLinus Torvalds 		return -ENODEV;
31361da177e4SLinus Torvalds 	}
31371da177e4SLinus Torvalds 
31381da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
31391da177e4SLinus Torvalds 
31401da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
31411da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
31421da177e4SLinus Torvalds                                           dev_list);
31431da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
31441da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
31451da177e4SLinus Torvalds         }
31461da177e4SLinus Torvalds 
31471da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
31481da177e4SLinus Torvalds         return 0;
31491da177e4SLinus Torvalds }
31501da177e4SLinus Torvalds 
31511da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
31521da177e4SLinus Torvalds {
31531da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
31541da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
31551da177e4SLinus Torvalds 
31561da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
31571da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
31581da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
31591da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
31601da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
31611da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
31621da177e4SLinus Torvalds 		else
31631da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
3164c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
31651da177e4SLinus Torvalds 	}
31661da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
31671da177e4SLinus Torvalds }
3168