xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 5a09e398)
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/sched.h>
321da177e4SLinus Torvalds #include <linux/errno.h>
331da177e4SLinus Torvalds #include <linux/timer.h>
341da177e4SLinus Torvalds #include <linux/types.h>
351da177e4SLinus Torvalds #include <linux/string.h>
361da177e4SLinus Torvalds #include <linux/genhd.h>
371da177e4SLinus Torvalds #include <linux/fs.h>
381da177e4SLinus Torvalds #include <linux/init.h>
391da177e4SLinus Torvalds #include <linux/proc_fs.h>
401da177e4SLinus Torvalds #include <linux/smp_lock.h>
411da177e4SLinus Torvalds #include <linux/vmalloc.h>
421da177e4SLinus Torvalds #include <linux/moduleparam.h>
431da177e4SLinus Torvalds 
441da177e4SLinus Torvalds #include <linux/blkdev.h>
451da177e4SLinus Torvalds #include "scsi.h"
461da177e4SLinus Torvalds #include <scsi/scsi_host.h>
471da177e4SLinus Torvalds #include <scsi/scsicam.h>
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds #include <linux/stat.h>
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #include "scsi_logging.h"
521da177e4SLinus Torvalds #include "scsi_debug.h"
531da177e4SLinus Torvalds 
5423183910SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.80"
555a09e398SHannes Reinecke static const char * scsi_debug_version_date = "20061018";
561da177e4SLinus Torvalds 
571da177e4SLinus Torvalds /* Additional Sense Code (ASC) used */
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
68c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
69c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
721da177e4SLinus Torvalds 
731da177e4SLinus Torvalds /* Default values for driver parameters */
741da177e4SLinus Torvalds #define DEF_NUM_HOST   1
751da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
761da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
771da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
781da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
791da177e4SLinus Torvalds  */
801da177e4SLinus Torvalds #define DEF_DELAY   1
811da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
821da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
831da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
841da177e4SLinus Torvalds #define DEF_OPTS   0
851da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
861da177e4SLinus Torvalds #define DEF_PTYPE   0
871da177e4SLinus Torvalds #define DEF_D_SENSE   0
88c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
89c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
9023183910SDouglas Gilbert #define DEF_FAKE_RW	0
9123183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
941da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
951da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
961da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
971da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
981da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
991da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1001da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1011da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1021da177e4SLinus Torvalds  *
1031da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- 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.
1071da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1081da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1091da177e4SLinus Torvalds  */
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1121da177e4SLinus Torvalds  * sector on read commands: */
1131da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1161da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1171da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
118c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1211da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1221da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1231da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1241da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1251da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1261da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1271da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1281da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1291da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1301da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
131c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
132c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
13323183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
13423183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1391da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1401da177e4SLinus Torvalds 
141c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
142c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1431da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1461da177e4SLinus Torvalds    may still need them */
1471da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1481da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1491da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1521da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1531da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1541da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds struct sdebug_dev_info {
1611da177e4SLinus Torvalds 	struct list_head dev_list;
1621da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1631da177e4SLinus Torvalds 	unsigned int channel;
1641da177e4SLinus Torvalds 	unsigned int target;
1651da177e4SLinus Torvalds 	unsigned int lun;
1661da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
167c65b1445SDouglas Gilbert 	unsigned int wlun;
1681da177e4SLinus Torvalds 	char reset;
169c65b1445SDouglas Gilbert 	char stopped;
1701da177e4SLinus Torvalds 	char used;
1711da177e4SLinus Torvalds };
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds struct sdebug_host_info {
1741da177e4SLinus Torvalds 	struct list_head host_list;
1751da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1761da177e4SLinus Torvalds 	struct device dev;
1771da177e4SLinus Torvalds 	struct list_head dev_info_list;
1781da177e4SLinus Torvalds };
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1811da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1841da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1851da177e4SLinus Torvalds 
1861da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds struct sdebug_queued_cmd {
1891da177e4SLinus Torvalds 	int in_use;
1901da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
1911da177e4SLinus Torvalds 	done_funct_t done_funct;
1921da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
1931da177e4SLinus Torvalds 	int scsi_result;
1941da177e4SLinus Torvalds };
1951da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
1961da177e4SLinus Torvalds 
197d0be4a7dSChristoph Hellwig static struct scsi_host_template sdebug_driver_template = {
1981da177e4SLinus Torvalds 	.proc_info =		scsi_debug_proc_info,
1991da177e4SLinus Torvalds 	.name =			"SCSI DEBUG",
2001da177e4SLinus Torvalds 	.info =			scsi_debug_info,
2011da177e4SLinus Torvalds 	.slave_alloc =		scsi_debug_slave_alloc,
2021da177e4SLinus Torvalds 	.slave_configure =	scsi_debug_slave_configure,
2031da177e4SLinus Torvalds 	.slave_destroy =	scsi_debug_slave_destroy,
2041da177e4SLinus Torvalds 	.ioctl =		scsi_debug_ioctl,
2051da177e4SLinus Torvalds 	.queuecommand =		scsi_debug_queuecommand,
2061da177e4SLinus Torvalds 	.eh_abort_handler =	scsi_debug_abort,
2071da177e4SLinus Torvalds 	.eh_bus_reset_handler = scsi_debug_bus_reset,
2081da177e4SLinus Torvalds 	.eh_device_reset_handler = scsi_debug_device_reset,
2091da177e4SLinus Torvalds 	.eh_host_reset_handler = scsi_debug_host_reset,
2101da177e4SLinus Torvalds 	.bios_param =		scsi_debug_biosparam,
2111da177e4SLinus Torvalds 	.can_queue =		SCSI_DEBUG_CANQUEUE,
2121da177e4SLinus Torvalds 	.this_id =		7,
213c65b1445SDouglas Gilbert 	.sg_tablesize =		256,
214c65b1445SDouglas Gilbert 	.cmd_per_lun =		16,
215c65b1445SDouglas Gilbert 	.max_sectors =		0xffff,
2161da177e4SLinus Torvalds 	.unchecked_isa_dma = 	0,
217c65b1445SDouglas Gilbert 	.use_clustering = 	ENABLE_CLUSTERING,
2181da177e4SLinus Torvalds 	.module =		THIS_MODULE,
2191da177e4SLinus Torvalds };
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds static int num_aborts = 0;
2241da177e4SLinus Torvalds static int num_dev_resets = 0;
2251da177e4SLinus Torvalds static int num_bus_resets = 0;
2261da177e4SLinus Torvalds static int num_host_resets = 0;
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2291da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2341da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2351da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2381da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2391da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2401da177e4SLinus Torvalds };
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds static const int check_condition_result =
2431da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2441da177e4SLinus Torvalds 
245c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
246c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
247c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
248c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
249c65b1445SDouglas Gilbert 
2501da177e4SLinus Torvalds /* function declarations */
2511da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2521da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2531da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2541da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
255c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
256c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2575a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
2585a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip);
2591da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2601da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
261c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2621da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
263c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
264c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
265c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
266c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
267c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
268c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
269c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
270c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
271c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
272c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2731da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2741da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
2751da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2761da177e4SLinus Torvalds                                 int arr_len);
2771da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2781da177e4SLinus Torvalds                                int max_arr_len);
2791da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2801da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2811da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2821da177e4SLinus Torvalds 			    int asc, int asq);
283c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2841da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2851da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2861da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2871da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2881da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2891da177e4SLinus Torvalds static void __init init_all_queued(void);
2901da177e4SLinus Torvalds static void stop_all_queued(void);
2911da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
2925a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
2935a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
2945a09e398SHannes Reinecke 			   const char * dev_id_str, int dev_id_str_len);
295c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
2966ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
2971da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds static int sdebug_add_adapter(void);
3001da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
3011da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds static struct device pseudo_primary;
3041da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds static
3081da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
311c65b1445SDouglas Gilbert 	int len, k, j;
312c65b1445SDouglas Gilbert 	unsigned int num;
313c65b1445SDouglas Gilbert 	unsigned long long lba;
3141da177e4SLinus Torvalds 	int errsts = 0;
315c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3161da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3171da177e4SLinus Torvalds 	int inj_recovered = 0;
318c65b1445SDouglas Gilbert 	int delay_override = 0;
3191da177e4SLinus Torvalds 
3201da177e4SLinus Torvalds 	if (done == NULL)
3211da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3221da177e4SLinus Torvalds 
323c65b1445SDouglas Gilbert 	SCpnt->resid = 0;
3241da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3251da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
326c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3271da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3281da177e4SLinus Torvalds 		printk("\n");
3291da177e4SLinus Torvalds 	}
3301da177e4SLinus Torvalds         if(target == sdebug_driver_template.this_id) {
3311da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3321da177e4SLinus Torvalds 		       "target!\n");
3331da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3341da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3351da177e4SLinus Torvalds         }
3361da177e4SLinus Torvalds 
337c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
338c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3391da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3401da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3411da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3421da177e4SLinus Torvalds 	if (NULL == devip)
3431da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3441da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3471da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3481da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3491da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3501da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3511da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3521da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3531da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3541da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3551da177e4SLinus Torvalds         }
3561da177e4SLinus Torvalds 
357c65b1445SDouglas Gilbert 	if (devip->wlun) {
358c65b1445SDouglas Gilbert 		switch (*cmd) {
359c65b1445SDouglas Gilbert 		case INQUIRY:
360c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
361c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
362c65b1445SDouglas Gilbert 		case REPORT_LUNS:
363c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
364c65b1445SDouglas Gilbert 		default:
365c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
366c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
367c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
368c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
369c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
370c65b1445SDouglas Gilbert 			errsts = check_condition_result;
371c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
372c65b1445SDouglas Gilbert 					     0);
373c65b1445SDouglas Gilbert 		}
374c65b1445SDouglas Gilbert 	}
375c65b1445SDouglas Gilbert 
3761da177e4SLinus Torvalds 	switch (*cmd) {
3771da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
378c65b1445SDouglas Gilbert 		delay_override = 1;
3791da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
3801da177e4SLinus Torvalds 		break;
3811da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
382c65b1445SDouglas Gilbert 		delay_override = 1;
3831da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
3841da177e4SLinus Torvalds 		break;
3851da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3861da177e4SLinus Torvalds 	case START_STOP:
387c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
3881da177e4SLinus Torvalds 		break;
3891da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
390c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
3911da177e4SLinus Torvalds 			break;
3921da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3931da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
3941da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
3951da177e4SLinus Torvalds 		break;
3961da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
397c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
3981da177e4SLinus Torvalds 		break;
3991da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
400c65b1445SDouglas Gilbert 		delay_override = 1;
401c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4021da177e4SLinus Torvalds 		break;
4031da177e4SLinus Torvalds         case RESERVE:
404c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4051da177e4SLinus Torvalds                 break;
4061da177e4SLinus Torvalds         case RESERVE_10:
407c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4081da177e4SLinus Torvalds                 break;
4091da177e4SLinus Torvalds         case RELEASE:
410c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4111da177e4SLinus Torvalds                 break;
4121da177e4SLinus Torvalds         case RELEASE_10:
413c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4141da177e4SLinus Torvalds                 break;
4151da177e4SLinus Torvalds 	case READ_CAPACITY:
4161da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4171da177e4SLinus Torvalds 		break;
418c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
419c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
420c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
421c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
422c65b1445SDouglas Gilbert 			errsts = check_condition_result;
423c65b1445SDouglas Gilbert 			break;
424c65b1445SDouglas Gilbert 		}
425c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
426c65b1445SDouglas Gilbert 		break;
4275a09e398SHannes Reinecke 	case MAINTENANCE_IN:
4285a09e398SHannes Reinecke 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
4295a09e398SHannes Reinecke 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
4305a09e398SHannes Reinecke 					INVALID_OPCODE, 0);
4315a09e398SHannes Reinecke 			errsts = check_condition_result;
4325a09e398SHannes Reinecke 			break;
4335a09e398SHannes Reinecke 		}
4345a09e398SHannes Reinecke 		errsts = resp_report_tgtpgs(SCpnt, devip);
4355a09e398SHannes Reinecke 		break;
4361da177e4SLinus Torvalds 	case READ_16:
4371da177e4SLinus Torvalds 	case READ_12:
4381da177e4SLinus Torvalds 	case READ_10:
4391da177e4SLinus Torvalds 	case READ_6:
440c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4411da177e4SLinus Torvalds 			break;
44223183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
44323183910SDouglas Gilbert 			break;
4441da177e4SLinus Torvalds 		if ((*cmd) == READ_16) {
445c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
446c65b1445SDouglas Gilbert 				if (j > 0)
447c65b1445SDouglas Gilbert 					lba <<= 8;
448c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
449c65b1445SDouglas Gilbert 			}
4501da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4511da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4521da177e4SLinus Torvalds 		} else if ((*cmd) == READ_12) {
453c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4541da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4551da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4561da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4571da177e4SLinus Torvalds 		} else if ((*cmd) == READ_10) {
458c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4591da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4601da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
461c65b1445SDouglas Gilbert 		} else {	/* READ (6) */
462c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4631da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
464c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4651da177e4SLinus Torvalds 		}
466c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4671da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4681da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
469c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4701da177e4SLinus Torvalds 			errsts = check_condition_result;
4711da177e4SLinus Torvalds 		}
4721da177e4SLinus Torvalds 		break;
4731da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
474c65b1445SDouglas Gilbert 		delay_override = 1;
4751da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4761da177e4SLinus Torvalds 		break;
4771da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
478c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4791da177e4SLinus Torvalds 		break;
4801da177e4SLinus Torvalds 	case WRITE_16:
4811da177e4SLinus Torvalds 	case WRITE_12:
4821da177e4SLinus Torvalds 	case WRITE_10:
4831da177e4SLinus Torvalds 	case WRITE_6:
484c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4851da177e4SLinus Torvalds 			break;
48623183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
48723183910SDouglas Gilbert 			break;
4881da177e4SLinus Torvalds 		if ((*cmd) == WRITE_16) {
489c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
490c65b1445SDouglas Gilbert 				if (j > 0)
491c65b1445SDouglas Gilbert 					lba <<= 8;
492c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
493c65b1445SDouglas Gilbert 			}
4941da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4951da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4961da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_12) {
497c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4981da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4991da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
5001da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
5011da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_10) {
502c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
5031da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
5041da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
505c65b1445SDouglas Gilbert 		} else {	/* WRITE (6) */
506c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
5071da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
508c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
5091da177e4SLinus Torvalds 		}
510c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5111da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5121da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
513c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5141da177e4SLinus Torvalds 			errsts = check_condition_result;
5151da177e4SLinus Torvalds 		}
5161da177e4SLinus Torvalds 		break;
5171da177e4SLinus Torvalds 	case MODE_SENSE:
5181da177e4SLinus Torvalds 	case MODE_SENSE_10:
5191da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5201da177e4SLinus Torvalds 		break;
521c65b1445SDouglas Gilbert 	case MODE_SELECT:
522c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
523c65b1445SDouglas Gilbert 		break;
524c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
525c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
526c65b1445SDouglas Gilbert 		break;
527c65b1445SDouglas Gilbert 	case LOG_SENSE:
528c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
529c65b1445SDouglas Gilbert 		break;
5301da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
531c65b1445SDouglas Gilbert 		delay_override = 1;
532c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5331da177e4SLinus Torvalds 		break;
5341da177e4SLinus Torvalds 	default:
5351da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5361da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5371da177e4SLinus Torvalds 			       "supported\n", *cmd);
538c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5391da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5401da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5411da177e4SLinus Torvalds 		errsts = check_condition_result;
5421da177e4SLinus Torvalds 		break;
5431da177e4SLinus Torvalds 	}
544c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
545c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5461da177e4SLinus Torvalds }
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5491da177e4SLinus Torvalds {
5501da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5511da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5521da177e4SLinus Torvalds 	}
5531da177e4SLinus Torvalds 	return -EINVAL;
5541da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5551da177e4SLinus Torvalds }
5561da177e4SLinus Torvalds 
557c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
558c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5591da177e4SLinus Torvalds {
5601da177e4SLinus Torvalds 	if (devip->reset) {
5611da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5621da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5631da177e4SLinus Torvalds 			       "attention: power on reset\n");
5641da177e4SLinus Torvalds 		devip->reset = 0;
5651da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5661da177e4SLinus Torvalds 		return check_condition_result;
5671da177e4SLinus Torvalds 	}
568c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
569c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
570c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
571c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
572c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
573c65b1445SDouglas Gilbert 				0x2);
574c65b1445SDouglas Gilbert 		return check_condition_result;
575c65b1445SDouglas Gilbert 	}
5761da177e4SLinus Torvalds 	return 0;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5801da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5811da177e4SLinus Torvalds 				int arr_len)
5821da177e4SLinus Torvalds {
5831da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
5841da177e4SLinus Torvalds 	void * kaddr;
5851da177e4SLinus Torvalds 	void * kaddr_off;
5861da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
5871da177e4SLinus Torvalds 
5881da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
5891da177e4SLinus Torvalds 		return 0;
5901da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
5911da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5921da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
5931da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
5941da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5951da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
5961da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
5971da177e4SLinus Torvalds 		act_len = (req_len < arr_len) ? req_len : arr_len;
5981da177e4SLinus Torvalds 		memcpy(scp->request_buffer, arr, act_len);
599c65b1445SDouglas Gilbert 		if (scp->resid)
600c65b1445SDouglas Gilbert 			scp->resid -= act_len;
601c65b1445SDouglas Gilbert 		else
6021da177e4SLinus Torvalds 			scp->resid = req_len - act_len;
6031da177e4SLinus Torvalds 		return 0;
6041da177e4SLinus Torvalds 	}
6051da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6061da177e4SLinus Torvalds 	active = 1;
6071da177e4SLinus Torvalds 	for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
6081da177e4SLinus Torvalds 		if (active) {
6091da177e4SLinus Torvalds 			kaddr = (unsigned char *)
6101da177e4SLinus Torvalds 				kmap_atomic(sgpnt->page, KM_USER0);
6111da177e4SLinus Torvalds 			if (NULL == kaddr)
6121da177e4SLinus Torvalds 				return (DID_ERROR << 16);
6131da177e4SLinus Torvalds 			kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6141da177e4SLinus Torvalds 			len = sgpnt->length;
6151da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
6161da177e4SLinus Torvalds 				active = 0;
6171da177e4SLinus Torvalds 				len = arr_len - req_len;
6181da177e4SLinus Torvalds 			}
6191da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6201da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6211da177e4SLinus Torvalds 			act_len += len;
6221da177e4SLinus Torvalds 		}
6231da177e4SLinus Torvalds 		req_len += sgpnt->length;
6241da177e4SLinus Torvalds 	}
625c65b1445SDouglas Gilbert 	if (scp->resid)
626c65b1445SDouglas Gilbert 		scp->resid -= act_len;
627c65b1445SDouglas Gilbert 	else
6281da177e4SLinus Torvalds 		scp->resid = req_len - act_len;
6291da177e4SLinus Torvalds 	return 0;
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds 
6321da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6331da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6341da177e4SLinus Torvalds 			       int max_arr_len)
6351da177e4SLinus Torvalds {
6361da177e4SLinus Torvalds 	int k, req_len, len, fin;
6371da177e4SLinus Torvalds 	void * kaddr;
6381da177e4SLinus Torvalds 	void * kaddr_off;
6391da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
6421da177e4SLinus Torvalds 		return 0;
6431da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
6441da177e4SLinus Torvalds 		return -1;
6451da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6461da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_TO_DEVICE)))
6471da177e4SLinus Torvalds 		return -1;
6481da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
6491da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
6501da177e4SLinus Torvalds 		len = (req_len < max_arr_len) ? req_len : max_arr_len;
6511da177e4SLinus Torvalds 		memcpy(arr, scp->request_buffer, len);
6521da177e4SLinus Torvalds 		return len;
6531da177e4SLinus Torvalds 	}
6541da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6551da177e4SLinus Torvalds 	for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
6561da177e4SLinus Torvalds 		kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
6571da177e4SLinus Torvalds 		if (NULL == kaddr)
6581da177e4SLinus Torvalds 			return -1;
6591da177e4SLinus Torvalds 		kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6601da177e4SLinus Torvalds 		len = sgpnt->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;
6691da177e4SLinus Torvalds 		req_len += sgpnt->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];
9575a09e398SHannes Reinecke 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_KERNEL);
958c65b1445SDouglas Gilbert 	if (devip->wlun)
959c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
960c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
961c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
962c65b1445SDouglas Gilbert 	else
9631da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9641da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9651da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9661da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9671da177e4SLinus Torvalds 			       	0);
9685a09e398SHannes Reinecke 		kfree(arr);
9691da177e4SLinus Torvalds 		return check_condition_result;
9701da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
9715a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
972c65b1445SDouglas Gilbert 		char lu_id_str[6];
973c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9741da177e4SLinus Torvalds 
9755a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
9765a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
97723183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
97823183910SDouglas Gilbert 			host_no = 0;
979c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
980c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
981c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
982c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
983c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9841da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
985c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
986c65b1445SDouglas Gilbert 			n = 4;
987c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
988c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
989c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
990c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
991c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
992c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
993c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
994c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
995c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
996c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
997c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
9981da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
999c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10001da177e4SLinus Torvalds 			arr[3] = len;
1001c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
10021da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
1003c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10045a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
10055a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
10065a09e398SHannes Reinecke 						 lu_id_str, len);
1007c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
1008c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1009c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
1010c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
1011c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1012c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
1013c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
1014c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1015c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
1016c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
1017c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
1018c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
1019c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1020c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
1021c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
1022c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
1023c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
1024c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
1025c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
1026c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1027c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
1028c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
1029c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1030c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
1031c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
1032c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
1033c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
1034c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1035c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10361da177e4SLinus Torvalds 		} else {
10371da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10381da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10391da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10405a09e398SHannes Reinecke 			kfree(arr);
10411da177e4SLinus Torvalds 			return check_condition_result;
10421da177e4SLinus Torvalds 		}
1043c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10445a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
1045c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10465a09e398SHannes Reinecke 		kfree(arr);
10475a09e398SHannes Reinecke 		return ret;
10481da177e4SLinus Torvalds 	}
10491da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10501da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10511da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10521da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10531da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
10545a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
10555a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
1056c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10571da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1058c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10591da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10601da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10611da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10621da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1063c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1064c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1065c65b1445SDouglas Gilbert 	n = 62;
10661da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1067c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10681da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1069c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10701da177e4SLinus Torvalds 	}
1071c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10725a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
10731da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10745a09e398SHannes Reinecke 	kfree(arr);
10755a09e398SHannes Reinecke 	return ret;
10761da177e4SLinus Torvalds }
10771da177e4SLinus Torvalds 
10781da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10791da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10801da177e4SLinus Torvalds {
10811da177e4SLinus Torvalds 	unsigned char * sbuff;
10821da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10831da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1084c65b1445SDouglas Gilbert 	int want_dsense;
10851da177e4SLinus Torvalds 	int len = 18;
10861da177e4SLinus Torvalds 
1087c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10881da177e4SLinus Torvalds 	if (devip->reset == 1)
1089c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1090c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10911da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1092c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1093c65b1445SDouglas Gilbert 		if (want_dsense) {
1094c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1095c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1096c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1097c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1098c65b1445SDouglas Gilbert 		} else {
1099c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1100c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1101c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1102c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1103c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1104c65b1445SDouglas Gilbert 		}
1105c65b1445SDouglas Gilbert 	} else {
1106c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
11071da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
11081da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1109c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
11101da177e4SLinus Torvalds 			arr[0] = 0x72;
11111da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
11121da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
11131da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
11141da177e4SLinus Torvalds 			len = 8;
1115c65b1445SDouglas Gilbert 		}
1116c65b1445SDouglas Gilbert 	}
1117c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
11181da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
11191da177e4SLinus Torvalds }
11201da177e4SLinus Torvalds 
1121c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1122c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1123c65b1445SDouglas Gilbert {
1124c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1125c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1126c65b1445SDouglas Gilbert 
1127c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1128c65b1445SDouglas Gilbert 		return errsts;
1129c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1130c65b1445SDouglas Gilbert 	if (power_cond) {
1131c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1132c65b1445SDouglas Gilbert 			       	0);
1133c65b1445SDouglas Gilbert 		return check_condition_result;
1134c65b1445SDouglas Gilbert 	}
1135c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1136c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1137c65b1445SDouglas Gilbert 		devip->stopped = !start;
1138c65b1445SDouglas Gilbert 	return 0;
1139c65b1445SDouglas Gilbert }
1140c65b1445SDouglas Gilbert 
11411da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11421da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11431da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11441da177e4SLinus Torvalds {
11451da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1146c65b1445SDouglas Gilbert 	unsigned int capac;
11471da177e4SLinus Torvalds 	int errsts;
11481da177e4SLinus Torvalds 
1149c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11501da177e4SLinus Torvalds 		return errsts;
1151c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1152c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1153c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1154c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1155c65b1445SDouglas Gilbert 	} else
1156c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11571da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1158c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1159c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11601da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11611da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11621da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11631da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1164c65b1445SDouglas Gilbert 	} else {
1165c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1166c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1167c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1168c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1169c65b1445SDouglas Gilbert 	}
11701da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11711da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11721da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11731da177e4SLinus Torvalds }
11741da177e4SLinus Torvalds 
1175c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1176c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1177c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1178c65b1445SDouglas Gilbert {
1179c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1180c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1181c65b1445SDouglas Gilbert 	unsigned long long capac;
1182c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1183c65b1445SDouglas Gilbert 
1184c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1185c65b1445SDouglas Gilbert 		return errsts;
1186c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1187c65b1445SDouglas Gilbert 		     + cmd[13]);
1188c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1189c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1190c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1191c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1192c65b1445SDouglas Gilbert 	} else
1193c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1194c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1195c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1196c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1197c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1198c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1199c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1200c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1201c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1202c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1203c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1204c65b1445SDouglas Gilbert }
1205c65b1445SDouglas Gilbert 
12065a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
12075a09e398SHannes Reinecke 
12085a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
12095a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
12105a09e398SHannes Reinecke {
12115a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
12125a09e398SHannes Reinecke 	unsigned char * arr;
12135a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
12145a09e398SHannes Reinecke 	int n, ret, alen, rlen;
12155a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
12165a09e398SHannes Reinecke 
12175a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
12185a09e398SHannes Reinecke 		+ cmd[9]);
12195a09e398SHannes Reinecke 
12205a09e398SHannes Reinecke 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_KERNEL);
12215a09e398SHannes Reinecke 	/*
12225a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
12235a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
12245a09e398SHannes Reinecke 	 * So we create two port groups with one port each
12255a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
12265a09e398SHannes Reinecke 	 */
12275a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
12285a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
12295a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
12305a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
12315a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
12325a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
12335a09e398SHannes Reinecke 
12345a09e398SHannes Reinecke 	/*
12355a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
12365a09e398SHannes Reinecke 	 */
12375a09e398SHannes Reinecke 	n = 4;
12385a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
12395a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
12405a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
12415a09e398SHannes Reinecke 	} else {
12425a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
12435a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
12445a09e398SHannes Reinecke 	}
12455a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
12465a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
12475a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12485a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12495a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12505a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12515a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12525a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12535a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
12545a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
12555a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
12565a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
12575a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
12585a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
12595a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12605a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12615a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12625a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12635a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12645a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12655a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
12665a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
12675a09e398SHannes Reinecke 
12685a09e398SHannes Reinecke 	rlen = n - 4;
12695a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
12705a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
12715a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
12725a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
12735a09e398SHannes Reinecke 
12745a09e398SHannes Reinecke 	/*
12755a09e398SHannes Reinecke 	 * Return the smallest value of either
12765a09e398SHannes Reinecke 	 * - The allocated length
12775a09e398SHannes Reinecke 	 * - The constructed command length
12785a09e398SHannes Reinecke 	 * - The maximum array size
12795a09e398SHannes Reinecke 	 */
12805a09e398SHannes Reinecke 	rlen = min(alen,n);
12815a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
12825a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
12835a09e398SHannes Reinecke 	kfree(arr);
12845a09e398SHannes Reinecke 	return ret;
12855a09e398SHannes Reinecke }
12865a09e398SHannes Reinecke 
12871da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
12881da177e4SLinus Torvalds 
12891da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
12901da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
12911da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
12921da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
12931da177e4SLinus Torvalds 
12941da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
12951da177e4SLinus Torvalds 	if (1 == pcontrol)
12961da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
12971da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
12981da177e4SLinus Torvalds }
12991da177e4SLinus Torvalds 
13001da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
13011da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
13021da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
13031da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
13041da177e4SLinus Torvalds 
13051da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
13061da177e4SLinus Torvalds 	if (1 == pcontrol)
13071da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
13081da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
13091da177e4SLinus Torvalds }
13101da177e4SLinus Torvalds 
13111da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
13121da177e4SLinus Torvalds {       /* Format device page for mode_sense */
13131da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
13141da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
13151da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
13161da177e4SLinus Torvalds 
13171da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
13181da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
13191da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
13201da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
13211da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
13221da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
13231da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
13241da177e4SLinus Torvalds         if (1 == pcontrol)
13251da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
13261da177e4SLinus Torvalds         return sizeof(format_pg);
13271da177e4SLinus Torvalds }
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
13301da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
13311da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
13321da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
13331da177e4SLinus Torvalds 
13341da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
13351da177e4SLinus Torvalds 	if (1 == pcontrol)
13361da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
13371da177e4SLinus Torvalds 	return sizeof(caching_pg);
13381da177e4SLinus Torvalds }
13391da177e4SLinus Torvalds 
13401da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
13411da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1342c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1343c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1344c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
13451da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
13461da177e4SLinus Torvalds 
13471da177e4SLinus Torvalds 	if (scsi_debug_dsense)
13481da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1349c65b1445SDouglas Gilbert 	else
1350c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
13511da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
13521da177e4SLinus Torvalds 	if (1 == pcontrol)
1353c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1354c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1355c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
13561da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
13571da177e4SLinus Torvalds }
13581da177e4SLinus Torvalds 
1359c65b1445SDouglas Gilbert 
13601da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
13611da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1362c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
13631da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1364c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1365c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1366c65b1445SDouglas Gilbert 
13671da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
13681da177e4SLinus Torvalds 	if (1 == pcontrol)
1369c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1370c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1371c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
13721da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
13731da177e4SLinus Torvalds }
13741da177e4SLinus Torvalds 
1375c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1376c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1377c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1378c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1379c65b1445SDouglas Gilbert 
1380c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1381c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1382c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1383c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1384c65b1445SDouglas Gilbert }
1385c65b1445SDouglas Gilbert 
1386c65b1445SDouglas Gilbert 
1387c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1388c65b1445SDouglas Gilbert 			      int target_dev_id)
1389c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1390c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1391c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1392c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1393c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1394c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1395c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1396c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1397c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1398c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1399c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1400c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1401c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1402c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1403c65b1445SDouglas Gilbert 		};
1404c65b1445SDouglas Gilbert 	int port_a, port_b;
1405c65b1445SDouglas Gilbert 
1406c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1407c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1408c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1409c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1410c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1411c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1412c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1413c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1414c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1415c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1416c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1417c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1418c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1419c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1420c65b1445SDouglas Gilbert }
1421c65b1445SDouglas Gilbert 
1422c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1423c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1424c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1425c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1426c65b1445SDouglas Gilbert 		};
1427c65b1445SDouglas Gilbert 
1428c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1429c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1430c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1431c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1432c65b1445SDouglas Gilbert }
1433c65b1445SDouglas Gilbert 
14341da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
14351da177e4SLinus Torvalds 
14361da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
14371da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
14381da177e4SLinus Torvalds {
143923183910SDouglas Gilbert 	unsigned char dbd, llbaa;
144023183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
14411da177e4SLinus Torvalds 	unsigned char dev_spec;
144223183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
14431da177e4SLinus Torvalds 	unsigned char * ap;
14441da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
14451da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
14461da177e4SLinus Torvalds 
1447c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
14481da177e4SLinus Torvalds 		return errsts;
144923183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
14501da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
14511da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
14521da177e4SLinus Torvalds 	subpcode = cmd[3];
14531da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
145423183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
145523183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
145623183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
145723183910SDouglas Gilbert 	else
145823183910SDouglas Gilbert 		bd_len = 0;
14591da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
14601da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
14611da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
14621da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
14631da177e4SLinus Torvalds 			       	0);
14641da177e4SLinus Torvalds 		return check_condition_result;
14651da177e4SLinus Torvalds 	}
1466c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1467c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
146823183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
146923183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
147023183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
147123183910SDouglas Gilbert 	else
147223183910SDouglas Gilbert 		dev_spec = 0x0;
14731da177e4SLinus Torvalds 	if (msense_6) {
14741da177e4SLinus Torvalds 		arr[2] = dev_spec;
147523183910SDouglas Gilbert 		arr[3] = bd_len;
14761da177e4SLinus Torvalds 		offset = 4;
14771da177e4SLinus Torvalds 	} else {
14781da177e4SLinus Torvalds 		arr[3] = dev_spec;
147923183910SDouglas Gilbert 		if (16 == bd_len)
148023183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
148123183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
14821da177e4SLinus Torvalds 		offset = 8;
14831da177e4SLinus Torvalds 	}
14841da177e4SLinus Torvalds 	ap = arr + offset;
148523183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
148623183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
148723183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
148823183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
148923183910SDouglas Gilbert 		} else
149023183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
149123183910SDouglas Gilbert 	}
149223183910SDouglas Gilbert 	if (8 == bd_len) {
149323183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
149423183910SDouglas Gilbert 			ap[0] = 0xff;
149523183910SDouglas Gilbert 			ap[1] = 0xff;
149623183910SDouglas Gilbert 			ap[2] = 0xff;
149723183910SDouglas Gilbert 			ap[3] = 0xff;
149823183910SDouglas Gilbert 		} else {
149923183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
150023183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
150123183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
150223183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
150323183910SDouglas Gilbert 		}
150423183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
150523183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
150623183910SDouglas Gilbert 		offset += bd_len;
150723183910SDouglas Gilbert 		ap = arr + offset;
150823183910SDouglas Gilbert 	} else if (16 == bd_len) {
150923183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
151023183910SDouglas Gilbert 
151123183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
151223183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
151323183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
151423183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
151523183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
151623183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
151723183910SDouglas Gilbert 		offset += bd_len;
151823183910SDouglas Gilbert 		ap = arr + offset;
151923183910SDouglas Gilbert 	}
15201da177e4SLinus Torvalds 
1521c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1522c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
15231da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15241da177e4SLinus Torvalds 			       	0);
15251da177e4SLinus Torvalds 		return check_condition_result;
15261da177e4SLinus Torvalds 	}
15271da177e4SLinus Torvalds 	switch (pcode) {
15281da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
15291da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
15301da177e4SLinus Torvalds 		offset += len;
15311da177e4SLinus Torvalds 		break;
15321da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
15331da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
15341da177e4SLinus Torvalds 		offset += len;
15351da177e4SLinus Torvalds 		break;
15361da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
15371da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
15381da177e4SLinus Torvalds                 offset += len;
15391da177e4SLinus Torvalds                 break;
15401da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
15411da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
15421da177e4SLinus Torvalds 		offset += len;
15431da177e4SLinus Torvalds 		break;
15441da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
15451da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
15461da177e4SLinus Torvalds 		offset += len;
15471da177e4SLinus Torvalds 		break;
1548c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1549c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1550c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1551c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1552c65b1445SDouglas Gilbert 			return check_condition_result;
1553c65b1445SDouglas Gilbert 	        }
1554c65b1445SDouglas Gilbert 		len = 0;
1555c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1556c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1557c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1558c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1559c65b1445SDouglas Gilbert 						  target_dev_id);
1560c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1561c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1562c65b1445SDouglas Gilbert 		offset += len;
1563c65b1445SDouglas Gilbert 		break;
15641da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
15651da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
15661da177e4SLinus Torvalds 		offset += len;
15671da177e4SLinus Torvalds 		break;
15681da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1569c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
15701da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
15711da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
15721da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
15731da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
15741da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1575c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1576c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1577c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1578c65b1445SDouglas Gilbert 						  target, target_dev_id);
1579c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1580c65b1445SDouglas Gilbert 			}
15811da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1582c65b1445SDouglas Gilbert 		} else {
1583c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1584c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1585c65b1445SDouglas Gilbert 			return check_condition_result;
1586c65b1445SDouglas Gilbert                 }
15871da177e4SLinus Torvalds 		offset += len;
15881da177e4SLinus Torvalds 		break;
15891da177e4SLinus Torvalds 	default:
15901da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15911da177e4SLinus Torvalds 			       	0);
15921da177e4SLinus Torvalds 		return check_condition_result;
15931da177e4SLinus Torvalds 	}
15941da177e4SLinus Torvalds 	if (msense_6)
15951da177e4SLinus Torvalds 		arr[0] = offset - 1;
15961da177e4SLinus Torvalds 	else {
15971da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
15981da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
15991da177e4SLinus Torvalds 	}
16001da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
16011da177e4SLinus Torvalds }
16021da177e4SLinus Torvalds 
1603c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1604c65b1445SDouglas Gilbert 
1605c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1606c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1607c65b1445SDouglas Gilbert {
1608c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1609c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1610c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1611c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1612c65b1445SDouglas Gilbert 
1613c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1614c65b1445SDouglas Gilbert 		return errsts;
1615c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1616c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1617c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1618c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1619c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1620c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1621c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1622c65b1445SDouglas Gilbert 		return check_condition_result;
1623c65b1445SDouglas Gilbert 	}
1624c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1625c65b1445SDouglas Gilbert         if (-1 == res)
1626c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1627c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1628c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1629c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1630c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1631c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1632c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
163323183910SDouglas Gilbert 	if (md_len > 2) {
1634c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1635c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1636c65b1445SDouglas Gilbert 		return check_condition_result;
1637c65b1445SDouglas Gilbert 	}
1638c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1639c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1640c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1641c65b1445SDouglas Gilbert 	if (ps) {
1642c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1643c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1644c65b1445SDouglas Gilbert 		return check_condition_result;
1645c65b1445SDouglas Gilbert 	}
1646c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1647c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1648c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1649c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1650c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1651c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1652c65b1445SDouglas Gilbert 		return check_condition_result;
1653c65b1445SDouglas Gilbert 	}
1654c65b1445SDouglas Gilbert 	switch (mpage) {
1655c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1656c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1657c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1658c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1659c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1660c65b1445SDouglas Gilbert 			return 0;
1661c65b1445SDouglas Gilbert 		}
1662c65b1445SDouglas Gilbert 		break;
1663c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1664c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1665c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1666c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1667c65b1445SDouglas Gilbert 			return 0;
1668c65b1445SDouglas Gilbert 		}
1669c65b1445SDouglas Gilbert 		break;
1670c65b1445SDouglas Gilbert 	default:
1671c65b1445SDouglas Gilbert 		break;
1672c65b1445SDouglas Gilbert 	}
1673c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1674c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1675c65b1445SDouglas Gilbert 	return check_condition_result;
1676c65b1445SDouglas Gilbert }
1677c65b1445SDouglas Gilbert 
1678c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1679c65b1445SDouglas Gilbert {
1680c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1681c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1682c65b1445SDouglas Gilbert 		};
1683c65b1445SDouglas Gilbert 
1684c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1685c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1686c65b1445SDouglas Gilbert }
1687c65b1445SDouglas Gilbert 
1688c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1689c65b1445SDouglas Gilbert {
1690c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1691c65b1445SDouglas Gilbert 		};
1692c65b1445SDouglas Gilbert 
1693c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1694c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1695c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1696c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1697c65b1445SDouglas Gilbert 	}
1698c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1699c65b1445SDouglas Gilbert }
1700c65b1445SDouglas Gilbert 
1701c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1702c65b1445SDouglas Gilbert 
1703c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1704c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1705c65b1445SDouglas Gilbert {
170623183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1707c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1708c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1709c65b1445SDouglas Gilbert 
1710c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1711c65b1445SDouglas Gilbert 		return errsts;
1712c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1713c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1714c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1715c65b1445SDouglas Gilbert 	if (ppc || sp) {
1716c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1717c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1718c65b1445SDouglas Gilbert 		return check_condition_result;
1719c65b1445SDouglas Gilbert 	}
1720c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1721c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
172223183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1723c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1724c65b1445SDouglas Gilbert 	arr[0] = pcode;
172523183910SDouglas Gilbert 	if (0 == subpcode) {
1726c65b1445SDouglas Gilbert 		switch (pcode) {
1727c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1728c65b1445SDouglas Gilbert 			n = 4;
1729c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1730c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1731c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1732c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1733c65b1445SDouglas Gilbert 			break;
1734c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1735c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1736c65b1445SDouglas Gilbert 			break;
1737c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1738c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1739c65b1445SDouglas Gilbert 			break;
1740c65b1445SDouglas Gilbert 		default:
1741c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1742c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1743c65b1445SDouglas Gilbert 			return check_condition_result;
1744c65b1445SDouglas Gilbert 		}
174523183910SDouglas Gilbert 	} else if (0xff == subpcode) {
174623183910SDouglas Gilbert 		arr[0] |= 0x40;
174723183910SDouglas Gilbert 		arr[1] = subpcode;
174823183910SDouglas Gilbert 		switch (pcode) {
174923183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
175023183910SDouglas Gilbert 			n = 4;
175123183910SDouglas Gilbert 			arr[n++] = 0x0;
175223183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
175323183910SDouglas Gilbert 			arr[n++] = 0x0;
175423183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
175523183910SDouglas Gilbert 			arr[n++] = 0xd;
175623183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
175723183910SDouglas Gilbert 			arr[n++] = 0x2f;
175823183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
175923183910SDouglas Gilbert 			arr[3] = n - 4;
176023183910SDouglas Gilbert 			break;
176123183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
176223183910SDouglas Gilbert 			n = 4;
176323183910SDouglas Gilbert 			arr[n++] = 0xd;
176423183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
176523183910SDouglas Gilbert 			arr[3] = n - 4;
176623183910SDouglas Gilbert 			break;
176723183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
176823183910SDouglas Gilbert 			n = 4;
176923183910SDouglas Gilbert 			arr[n++] = 0x2f;
177023183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
177123183910SDouglas Gilbert 			arr[3] = n - 4;
177223183910SDouglas Gilbert 			break;
177323183910SDouglas Gilbert 		default:
177423183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
177523183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
177623183910SDouglas Gilbert 			return check_condition_result;
177723183910SDouglas Gilbert 		}
177823183910SDouglas Gilbert 	} else {
177923183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
178023183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
178123183910SDouglas Gilbert 		return check_condition_result;
178223183910SDouglas Gilbert 	}
1783c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1784c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1785c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1786c65b1445SDouglas Gilbert }
1787c65b1445SDouglas Gilbert 
1788c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1789c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
17901da177e4SLinus Torvalds {
17911da177e4SLinus Torvalds 	unsigned long iflags;
1792c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1793c65b1445SDouglas Gilbert 	unsigned long long u;
17941da177e4SLinus Torvalds 	int ret;
17951da177e4SLinus Torvalds 
1796c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
17971da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
17981da177e4SLinus Torvalds 				0);
17991da177e4SLinus Torvalds 		return check_condition_result;
18001da177e4SLinus Torvalds 	}
1801c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1802c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1803c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1804c65b1445SDouglas Gilbert 				0);
1805c65b1445SDouglas Gilbert 		return check_condition_result;
1806c65b1445SDouglas Gilbert 	}
18071da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1808c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1809c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1810c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
18111da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
18121da177e4SLinus Torvalds 				0);
1813c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1814c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1815c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1816c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1817c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1818c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1819c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1820c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1821c65b1445SDouglas Gilbert 		}
18221da177e4SLinus Torvalds 		return check_condition_result;
18231da177e4SLinus Torvalds 	}
18241da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1825c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1826c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1827c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
18281da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1829c65b1445SDouglas Gilbert 	else {
1830c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1831c65b1445SDouglas Gilbert 		u = lba;
1832c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1833c65b1445SDouglas Gilbert 		from_bottom = 0;
1834c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1835c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1836c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1837c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1838c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1839c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1840c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1841c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1842c65b1445SDouglas Gilbert 	}
18431da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18441da177e4SLinus Torvalds 	return ret;
18451da177e4SLinus Torvalds }
18461da177e4SLinus Torvalds 
1847c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1848c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
18491da177e4SLinus Torvalds {
18501da177e4SLinus Torvalds 	unsigned long iflags;
1851c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1852c65b1445SDouglas Gilbert 	unsigned long long u;
18531da177e4SLinus Torvalds 	int res;
18541da177e4SLinus Torvalds 
1855c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18561da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18571da177e4SLinus Torvalds 			       	0);
18581da177e4SLinus Torvalds 		return check_condition_result;
18591da177e4SLinus Torvalds 	}
1860c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1861c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1862c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1863c65b1445SDouglas Gilbert 				0);
1864c65b1445SDouglas Gilbert 		return check_condition_result;
1865c65b1445SDouglas Gilbert 	}
18661da177e4SLinus Torvalds 
18671da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1868c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1869c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1870c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
18711da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1872c65b1445SDouglas Gilbert 	else {
1873c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1874c65b1445SDouglas Gilbert 		u = lba;
1875c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1876c65b1445SDouglas Gilbert 		to_bottom = 0;
1877c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1878c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1879c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1880c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1881c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1882c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1883c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1884c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1885c65b1445SDouglas Gilbert 	}
18861da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
18871da177e4SLinus Torvalds 	if (-1 == res)
18881da177e4SLinus Torvalds 		return (DID_ERROR << 16);
18891da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
18901da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1891c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
18921da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
18931da177e4SLinus Torvalds 	return 0;
18941da177e4SLinus Torvalds }
18951da177e4SLinus Torvalds 
1896c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
18971da177e4SLinus Torvalds 
18981da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
18991da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
19001da177e4SLinus Torvalds {
19011da177e4SLinus Torvalds 	unsigned int alloc_len;
1902c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
19031da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
19041da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
19051da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
19061da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1907c65b1445SDouglas Gilbert 	unsigned char * max_addr;
19081da177e4SLinus Torvalds 
19091da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1910c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
19111da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
19121da177e4SLinus Torvalds 			       	0);
19131da177e4SLinus Torvalds 		return check_condition_result;
19141da177e4SLinus Torvalds 	}
19151da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
19161da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
19171da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1918c65b1445SDouglas Gilbert 	if (1 == select_report)
1919c65b1445SDouglas Gilbert 		lun_cnt = 0;
1920c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1921c65b1445SDouglas Gilbert 		--lun_cnt;
1922c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1923c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1924c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1925c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1926c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1927c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1928c65b1445SDouglas Gilbert 	if (n < num) {
1929c65b1445SDouglas Gilbert 		wlun = 0;
1930c65b1445SDouglas Gilbert 		lun_cnt = n;
1931c65b1445SDouglas Gilbert 	}
19321da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1933c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1934c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1935c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1936c65b1445SDouglas Gilbert 	     i++, lun++) {
1937c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
19381da177e4SLinus Torvalds 		if (upper)
19391da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
19401da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1941c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
19421da177e4SLinus Torvalds 	}
1943c65b1445SDouglas Gilbert 	if (wlun) {
1944c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1945c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1946c65b1445SDouglas Gilbert 		i++;
1947c65b1445SDouglas Gilbert 	}
1948c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
19491da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
19501da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
19511da177e4SLinus Torvalds }
19521da177e4SLinus Torvalds 
19531da177e4SLinus Torvalds /* When timer goes off this function is called. */
19541da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
19551da177e4SLinus Torvalds {
19561da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
19571da177e4SLinus Torvalds 	unsigned long iflags;
19581da177e4SLinus Torvalds 
19591da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
19601da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
19611da177e4SLinus Torvalds 		       "large\n");
19621da177e4SLinus Torvalds 		return;
19631da177e4SLinus Torvalds 	}
19641da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
19651da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
19661da177e4SLinus Torvalds 	if (! sqcp->in_use) {
19671da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
19681da177e4SLinus Torvalds 		       "interrupt\n");
19691da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
19701da177e4SLinus Torvalds 		return;
19711da177e4SLinus Torvalds 	}
19721da177e4SLinus Torvalds 	sqcp->in_use = 0;
19731da177e4SLinus Torvalds 	if (sqcp->done_funct) {
19741da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
19751da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
19761da177e4SLinus Torvalds 	}
19771da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
19781da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
19791da177e4SLinus Torvalds }
19801da177e4SLinus Torvalds 
19811da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
19821da177e4SLinus Torvalds {
19831da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1984c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
1985c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19861da177e4SLinus Torvalds 	return 0;
19871da177e4SLinus Torvalds }
19881da177e4SLinus Torvalds 
19891da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
19901da177e4SLinus Torvalds {
19911da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
19921da177e4SLinus Torvalds 
19931da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1994c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1995c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19961da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
19971da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
19981da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
19991da177e4SLinus Torvalds 	sdp->hostdata = devip;
20001da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
20011da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
20021da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
2003c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
20041da177e4SLinus Torvalds 	return 0;
20051da177e4SLinus Torvalds }
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
20081da177e4SLinus Torvalds {
20091da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20101da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
20111da177e4SLinus Torvalds 
20121da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2013c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
2014c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20151da177e4SLinus Torvalds 	if (devip) {
20161da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
20171da177e4SLinus Torvalds 		devip->used = 0;
20181da177e4SLinus Torvalds 		sdp->hostdata = NULL;
20191da177e4SLinus Torvalds 	}
20201da177e4SLinus Torvalds }
20211da177e4SLinus Torvalds 
20221da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
20231da177e4SLinus Torvalds {
20241da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20251da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
20261da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20271da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
20281da177e4SLinus Torvalds 
20291da177e4SLinus Torvalds 	if (devip)
20301da177e4SLinus Torvalds 		return devip;
20311da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
20321da177e4SLinus Torvalds         if(! sdbg_host) {
20331da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
20341da177e4SLinus Torvalds 		return NULL;
20351da177e4SLinus Torvalds         }
20361da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
20371da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
20381da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
20391da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
20401da177e4SLinus Torvalds                         return devip;
20411da177e4SLinus Torvalds 		else {
20421da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
20431da177e4SLinus Torvalds 				open_devip = devip;
20441da177e4SLinus Torvalds 		}
20451da177e4SLinus Torvalds 	}
20461da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
204724669f75SJes Sorensen 		open_devip = kzalloc(sizeof(*open_devip),GFP_KERNEL);
20481da177e4SLinus Torvalds 		if (NULL == open_devip) {
20491da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
20501da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
20511da177e4SLinus Torvalds 			return NULL;
20521da177e4SLinus Torvalds 		}
20531da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20541da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
20551da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
20561da177e4SLinus Torvalds 	}
20571da177e4SLinus Torvalds         if (open_devip) {
20581da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
20591da177e4SLinus Torvalds 		open_devip->target = sdev->id;
20601da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
20611da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20621da177e4SLinus Torvalds 		open_devip->reset = 1;
20631da177e4SLinus Torvalds 		open_devip->used = 1;
20641da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
20651da177e4SLinus Torvalds 		if (scsi_debug_dsense)
20661da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
20671da177e4SLinus Torvalds 		else {
20681da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
20691da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
20701da177e4SLinus Torvalds 		}
2071c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2072c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
20731da177e4SLinus Torvalds 		return open_devip;
20741da177e4SLinus Torvalds         }
20751da177e4SLinus Torvalds         return NULL;
20761da177e4SLinus Torvalds }
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
20791da177e4SLinus Torvalds 			    int asc, int asq)
20801da177e4SLinus Torvalds {
20811da177e4SLinus Torvalds 	unsigned char * sbuff;
20821da177e4SLinus Torvalds 
20831da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
20841da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
20851da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
20861da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
20871da177e4SLinus Torvalds 		sbuff[1] = key;
20881da177e4SLinus Torvalds 		sbuff[2] = asc;
20891da177e4SLinus Torvalds 		sbuff[3] = asq;
20901da177e4SLinus Torvalds 	} else {
20911da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
20921da177e4SLinus Torvalds 		sbuff[2] = key;
20931da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
20941da177e4SLinus Torvalds 		sbuff[12] = asc;
20951da177e4SLinus Torvalds 		sbuff[13] = asq;
20961da177e4SLinus Torvalds 	}
20971da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20981da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
20991da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
21001da177e4SLinus Torvalds }
21011da177e4SLinus Torvalds 
21021da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
21031da177e4SLinus Torvalds {
21041da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21051da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
21061da177e4SLinus Torvalds 	++num_aborts;
21071da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
21081da177e4SLinus Torvalds 	return SUCCESS;
21091da177e4SLinus Torvalds }
21101da177e4SLinus Torvalds 
21111da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
21121da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
21131da177e4SLinus Torvalds {
21141da177e4SLinus Torvalds 	int res;
21151da177e4SLinus Torvalds 	unsigned char *buf;
21161da177e4SLinus Torvalds 
21171da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21181da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
21191da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
21201da177e4SLinus Torvalds 	if (buf) {
21211da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
21221da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
21231da177e4SLinus Torvalds 		kfree(buf);
21241da177e4SLinus Torvalds 		if (! res)
21251da177e4SLinus Torvalds 			return res;
21261da177e4SLinus Torvalds 	}
21271da177e4SLinus Torvalds 	info[0] = sdebug_heads;
21281da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
21291da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
21301da177e4SLinus Torvalds 	return 0;
21311da177e4SLinus Torvalds }
21321da177e4SLinus Torvalds 
21331da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
21341da177e4SLinus Torvalds {
21351da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
21361da177e4SLinus Torvalds 
21371da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21381da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
21391da177e4SLinus Torvalds 	++num_dev_resets;
21401da177e4SLinus Torvalds 	if (SCpnt) {
21411da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
21421da177e4SLinus Torvalds 		if (devip)
21431da177e4SLinus Torvalds 			devip->reset = 1;
21441da177e4SLinus Torvalds 	}
21451da177e4SLinus Torvalds 	return SUCCESS;
21461da177e4SLinus Torvalds }
21471da177e4SLinus Torvalds 
21481da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
21491da177e4SLinus Torvalds {
21501da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
21511da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21521da177e4SLinus Torvalds         struct scsi_device * sdp;
21531da177e4SLinus Torvalds         struct Scsi_Host * hp;
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21561da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
21571da177e4SLinus Torvalds 	++num_bus_resets;
21581da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
21591da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
21601da177e4SLinus Torvalds 		if (sdbg_host) {
21611da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
21621da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
21631da177e4SLinus Torvalds                                             dev_list)
21641da177e4SLinus Torvalds 				dev_info->reset = 1;
21651da177e4SLinus Torvalds 		}
21661da177e4SLinus Torvalds 	}
21671da177e4SLinus Torvalds 	return SUCCESS;
21681da177e4SLinus Torvalds }
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
21711da177e4SLinus Torvalds {
21721da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
21731da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21741da177e4SLinus Torvalds 
21751da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21761da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
21771da177e4SLinus Torvalds 	++num_host_resets;
21781da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
21791da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
21801da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
21811da177e4SLinus Torvalds                                     dev_list)
21821da177e4SLinus Torvalds                         dev_info->reset = 1;
21831da177e4SLinus Torvalds         }
21841da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
21851da177e4SLinus Torvalds 	stop_all_queued();
21861da177e4SLinus Torvalds 	return SUCCESS;
21871da177e4SLinus Torvalds }
21881da177e4SLinus Torvalds 
21891da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
21901da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
21911da177e4SLinus Torvalds {
21921da177e4SLinus Torvalds 	unsigned long iflags;
21931da177e4SLinus Torvalds 	int k;
21941da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
21951da177e4SLinus Torvalds 
21961da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
21971da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21981da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
21991da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
22001da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22011da177e4SLinus Torvalds 			sqcp->in_use = 0;
22021da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22031da177e4SLinus Torvalds 			break;
22041da177e4SLinus Torvalds 		}
22051da177e4SLinus Torvalds 	}
22061da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22071da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
22081da177e4SLinus Torvalds }
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
22111da177e4SLinus Torvalds static void stop_all_queued(void)
22121da177e4SLinus Torvalds {
22131da177e4SLinus Torvalds 	unsigned long iflags;
22141da177e4SLinus Torvalds 	int k;
22151da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22161da177e4SLinus Torvalds 
22171da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22181da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22191da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22201da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
22211da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22221da177e4SLinus Torvalds 			sqcp->in_use = 0;
22231da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22241da177e4SLinus Torvalds 		}
22251da177e4SLinus Torvalds 	}
22261da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22271da177e4SLinus Torvalds }
22281da177e4SLinus Torvalds 
22291da177e4SLinus Torvalds /* Initializes timers in queued array */
22301da177e4SLinus Torvalds static void __init init_all_queued(void)
22311da177e4SLinus Torvalds {
22321da177e4SLinus Torvalds 	unsigned long iflags;
22331da177e4SLinus Torvalds 	int k;
22341da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22351da177e4SLinus Torvalds 
22361da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22371da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22381da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22391da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
22401da177e4SLinus Torvalds 		sqcp->in_use = 0;
22411da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
22421da177e4SLinus Torvalds 	}
22431da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22441da177e4SLinus Torvalds }
22451da177e4SLinus Torvalds 
22461da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
22471da177e4SLinus Torvalds {
22481da177e4SLinus Torvalds 	struct partition * pp;
22491da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
22501da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
22511da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
22521da177e4SLinus Torvalds 
22531da177e4SLinus Torvalds 	/* assume partition table already zeroed */
22541da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
22551da177e4SLinus Torvalds 		return;
22561da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
22571da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
22581da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
22591da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
22601da177e4SLinus Torvalds 	}
2261c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
22621da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
22631da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
22641da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
22651da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
22661da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
22671da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
22681da177e4SLinus Torvalds 			    * heads_by_sects;
22691da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
22701da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
22711da177e4SLinus Torvalds 
22721da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
22731da177e4SLinus Torvalds 	ramp[511] = 0xAA;
22741da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
22751da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
22761da177e4SLinus Torvalds 		start_sec = starts[k];
22771da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
22781da177e4SLinus Torvalds 		pp->boot_ind = 0;
22791da177e4SLinus Torvalds 
22801da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
22811da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
22821da177e4SLinus Torvalds 			   / sdebug_sectors_per;
22831da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
22841da177e4SLinus Torvalds 
22851da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
22861da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
22871da177e4SLinus Torvalds 			       / sdebug_sectors_per;
22881da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
22891da177e4SLinus Torvalds 
22901da177e4SLinus Torvalds 		pp->start_sect = start_sec;
22911da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
22921da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
22931da177e4SLinus Torvalds 	}
22941da177e4SLinus Torvalds }
22951da177e4SLinus Torvalds 
22961da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
22971da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
22981da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
22991da177e4SLinus Torvalds {
23001da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
23011da177e4SLinus Torvalds 		if (scsi_result) {
23021da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
23031da177e4SLinus Torvalds 
2304c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2305c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2306c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
23071da177e4SLinus Torvalds 		}
23081da177e4SLinus Torvalds 	}
23091da177e4SLinus Torvalds 	if (cmnd && devip) {
23101da177e4SLinus Torvalds 		/* simulate autosense by this driver */
23111da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
23121da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
23131da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
23141da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
23151da177e4SLinus Torvalds 	}
23161da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
23171da177e4SLinus Torvalds 		if (cmnd)
23181da177e4SLinus Torvalds 			cmnd->result = scsi_result;
23191da177e4SLinus Torvalds 		if (done)
23201da177e4SLinus Torvalds 			done(cmnd);
23211da177e4SLinus Torvalds 		return 0;
23221da177e4SLinus Torvalds 	} else {
23231da177e4SLinus Torvalds 		unsigned long iflags;
23241da177e4SLinus Torvalds 		int k;
23251da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
23261da177e4SLinus Torvalds 
23271da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
23281da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
23291da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
23301da177e4SLinus Torvalds 			if (! sqcp->in_use)
23311da177e4SLinus Torvalds 				break;
23321da177e4SLinus Torvalds 		}
23331da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
23341da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
23351da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
23361da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
23371da177e4SLinus Torvalds 		}
23381da177e4SLinus Torvalds 		sqcp->in_use = 1;
23391da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
23401da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
23411da177e4SLinus Torvalds 		sqcp->done_funct = done;
23421da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
23431da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
23441da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
23451da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
23461da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23471da177e4SLinus Torvalds 		if (cmnd)
23481da177e4SLinus Torvalds 			cmnd->result = 0;
23491da177e4SLinus Torvalds 		return 0;
23501da177e4SLinus Torvalds 	}
23511da177e4SLinus Torvalds }
23521da177e4SLinus Torvalds 
235323183910SDouglas Gilbert /* Note: The following macros create attribute files in the
235423183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
235523183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
235623183910SDouglas Gilbert    as it can when the corresponding attribute in the
235723183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
235823183910SDouglas Gilbert  */
2359c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2360c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2361c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2362c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2363c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
236423183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2365c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2366c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2367c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2368c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2369c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2370c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2371c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2372c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
237323183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
237423183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
23751da177e4SLinus Torvalds 
23761da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
23771da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
23781da177e4SLinus Torvalds MODULE_LICENSE("GPL");
23791da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
23801da177e4SLinus Torvalds 
23811da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
23821da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2383c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2384c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
23851da177e4SLinus Torvalds MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
238623183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2387c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2388c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
23891da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2390c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
2391c65b1445SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->... (def=0)");
23921da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
23931da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2394c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
239523183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
23961da177e4SLinus Torvalds 
23971da177e4SLinus Torvalds 
23981da177e4SLinus Torvalds static char sdebug_info[256];
23991da177e4SLinus Torvalds 
24001da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
24011da177e4SLinus Torvalds {
24021da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
24031da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
24041da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
24051da177e4SLinus Torvalds 		scsi_debug_opts);
24061da177e4SLinus Torvalds 	return sdebug_info;
24071da177e4SLinus Torvalds }
24081da177e4SLinus Torvalds 
24091da177e4SLinus Torvalds /* scsi_debug_proc_info
24101da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
24111da177e4SLinus Torvalds  */
24121da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
24131da177e4SLinus Torvalds 				int length, int inout)
24141da177e4SLinus Torvalds {
24151da177e4SLinus Torvalds 	int len, pos, begin;
24161da177e4SLinus Torvalds 	int orig_length;
24171da177e4SLinus Torvalds 
24181da177e4SLinus Torvalds 	orig_length = length;
24191da177e4SLinus Torvalds 
24201da177e4SLinus Torvalds 	if (inout == 1) {
24211da177e4SLinus Torvalds 		char arr[16];
24221da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
24231da177e4SLinus Torvalds 
24241da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
24251da177e4SLinus Torvalds 			return -EACCES;
24261da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
24271da177e4SLinus Torvalds 		arr[minLen] = '\0';
24281da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
24291da177e4SLinus Torvalds 			return -EINVAL;
24301da177e4SLinus Torvalds 		scsi_debug_opts = pos;
24311da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
24321da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
24331da177e4SLinus Torvalds 		return length;
24341da177e4SLinus Torvalds 	}
24351da177e4SLinus Torvalds 	begin = 0;
24361da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
24371da177e4SLinus Torvalds 	    "%s [%s]\n"
24381da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
24391da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
24401da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
24411da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
24421da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
24431da177e4SLinus Torvalds 	    "host_resets=%d\n",
24441da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
24451da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
24461da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
24471da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
24481da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
24491da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
24501da177e4SLinus Torvalds 	if (pos < offset) {
24511da177e4SLinus Torvalds 		len = 0;
24521da177e4SLinus Torvalds 		begin = pos;
24531da177e4SLinus Torvalds 	}
24541da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
24551da177e4SLinus Torvalds 	len -= (offset - begin);
24561da177e4SLinus Torvalds 	if (len > length)
24571da177e4SLinus Torvalds 		len = length;
24581da177e4SLinus Torvalds 	return len;
24591da177e4SLinus Torvalds }
24601da177e4SLinus Torvalds 
24611da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
24621da177e4SLinus Torvalds {
24631da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
24641da177e4SLinus Torvalds }
24651da177e4SLinus Torvalds 
24661da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
24671da177e4SLinus Torvalds 				  const char * buf, size_t count)
24681da177e4SLinus Torvalds {
24691da177e4SLinus Torvalds         int delay;
24701da177e4SLinus Torvalds 	char work[20];
24711da177e4SLinus Torvalds 
24721da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24731da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
24741da177e4SLinus Torvalds 			scsi_debug_delay = delay;
24751da177e4SLinus Torvalds 			return count;
24761da177e4SLinus Torvalds 		}
24771da177e4SLinus Torvalds 	}
24781da177e4SLinus Torvalds 	return -EINVAL;
24791da177e4SLinus Torvalds }
24801da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
24811da177e4SLinus Torvalds 	    sdebug_delay_store);
24821da177e4SLinus Torvalds 
24831da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
24841da177e4SLinus Torvalds {
24851da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
24861da177e4SLinus Torvalds }
24871da177e4SLinus Torvalds 
24881da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
24891da177e4SLinus Torvalds 				 const char * buf, size_t count)
24901da177e4SLinus Torvalds {
24911da177e4SLinus Torvalds         int opts;
24921da177e4SLinus Torvalds 	char work[20];
24931da177e4SLinus Torvalds 
24941da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24951da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
24961da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
24971da177e4SLinus Torvalds 				goto opts_done;
24981da177e4SLinus Torvalds 		} else {
24991da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
25001da177e4SLinus Torvalds 				goto opts_done;
25011da177e4SLinus Torvalds 		}
25021da177e4SLinus Torvalds 	}
25031da177e4SLinus Torvalds 	return -EINVAL;
25041da177e4SLinus Torvalds opts_done:
25051da177e4SLinus Torvalds 	scsi_debug_opts = opts;
25061da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
25071da177e4SLinus Torvalds 	return count;
25081da177e4SLinus Torvalds }
25091da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
25101da177e4SLinus Torvalds 	    sdebug_opts_store);
25111da177e4SLinus Torvalds 
25121da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
25131da177e4SLinus Torvalds {
25141da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
25151da177e4SLinus Torvalds }
25161da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
25171da177e4SLinus Torvalds 				  const char * buf, size_t count)
25181da177e4SLinus Torvalds {
25191da177e4SLinus Torvalds         int n;
25201da177e4SLinus Torvalds 
25211da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25221da177e4SLinus Torvalds 		scsi_debug_ptype = n;
25231da177e4SLinus Torvalds 		return count;
25241da177e4SLinus Torvalds 	}
25251da177e4SLinus Torvalds 	return -EINVAL;
25261da177e4SLinus Torvalds }
25271da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
25281da177e4SLinus Torvalds 
25291da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
25301da177e4SLinus Torvalds {
25311da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
25321da177e4SLinus Torvalds }
25331da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
25341da177e4SLinus Torvalds 				  const char * buf, size_t count)
25351da177e4SLinus Torvalds {
25361da177e4SLinus Torvalds         int n;
25371da177e4SLinus Torvalds 
25381da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25391da177e4SLinus Torvalds 		scsi_debug_dsense = n;
25401da177e4SLinus Torvalds 		return count;
25411da177e4SLinus Torvalds 	}
25421da177e4SLinus Torvalds 	return -EINVAL;
25431da177e4SLinus Torvalds }
25441da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
25451da177e4SLinus Torvalds 	    sdebug_dsense_store);
25461da177e4SLinus Torvalds 
254723183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
254823183910SDouglas Gilbert {
254923183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
255023183910SDouglas Gilbert }
255123183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
255223183910SDouglas Gilbert 				    const char * buf, size_t count)
255323183910SDouglas Gilbert {
255423183910SDouglas Gilbert         int n;
255523183910SDouglas Gilbert 
255623183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
255723183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
255823183910SDouglas Gilbert 		return count;
255923183910SDouglas Gilbert 	}
256023183910SDouglas Gilbert 	return -EINVAL;
256123183910SDouglas Gilbert }
256223183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
256323183910SDouglas Gilbert 	    sdebug_fake_rw_store);
256423183910SDouglas Gilbert 
2565c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2566c65b1445SDouglas Gilbert {
2567c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2568c65b1445SDouglas Gilbert }
2569c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2570c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2571c65b1445SDouglas Gilbert {
2572c65b1445SDouglas Gilbert         int n;
2573c65b1445SDouglas Gilbert 
2574c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2575c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2576c65b1445SDouglas Gilbert 		return count;
2577c65b1445SDouglas Gilbert 	}
2578c65b1445SDouglas Gilbert 	return -EINVAL;
2579c65b1445SDouglas Gilbert }
2580c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2581c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2582c65b1445SDouglas Gilbert 
25831da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
25841da177e4SLinus Torvalds {
25851da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
25861da177e4SLinus Torvalds }
25871da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
25881da177e4SLinus Torvalds 				     const char * buf, size_t count)
25891da177e4SLinus Torvalds {
25901da177e4SLinus Torvalds         int n;
25911da177e4SLinus Torvalds 
25921da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25931da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
25941da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
25951da177e4SLinus Torvalds 		return count;
25961da177e4SLinus Torvalds 	}
25971da177e4SLinus Torvalds 	return -EINVAL;
25981da177e4SLinus Torvalds }
25991da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
26001da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
26011da177e4SLinus Torvalds 
26021da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
26031da177e4SLinus Torvalds {
26041da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
26051da177e4SLinus Torvalds }
26061da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
26071da177e4SLinus Torvalds 
26081da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
26091da177e4SLinus Torvalds {
26101da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
26111da177e4SLinus Torvalds }
26121da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
26131da177e4SLinus Torvalds 
26141da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
26151da177e4SLinus Torvalds {
26161da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
26171da177e4SLinus Torvalds }
26181da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
26191da177e4SLinus Torvalds 				      const char * buf, size_t count)
26201da177e4SLinus Torvalds {
26211da177e4SLinus Torvalds         int nth;
26221da177e4SLinus Torvalds 
26231da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
26241da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
26251da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
26261da177e4SLinus Torvalds 		return count;
26271da177e4SLinus Torvalds 	}
26281da177e4SLinus Torvalds 	return -EINVAL;
26291da177e4SLinus Torvalds }
26301da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
26311da177e4SLinus Torvalds 	    sdebug_every_nth_store);
26321da177e4SLinus Torvalds 
26331da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
26341da177e4SLinus Torvalds {
26351da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
26361da177e4SLinus Torvalds }
26371da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
26381da177e4SLinus Torvalds 				     const char * buf, size_t count)
26391da177e4SLinus Torvalds {
26401da177e4SLinus Torvalds         int n;
26411da177e4SLinus Torvalds 
26421da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26431da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
26441da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26451da177e4SLinus Torvalds 		return count;
26461da177e4SLinus Torvalds 	}
26471da177e4SLinus Torvalds 	return -EINVAL;
26481da177e4SLinus Torvalds }
26491da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
26501da177e4SLinus Torvalds 	    sdebug_max_luns_store);
26511da177e4SLinus Torvalds 
26521da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
26531da177e4SLinus Torvalds {
26541da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
26551da177e4SLinus Torvalds }
26561da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
26571da177e4SLinus Torvalds 
2658c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2659c65b1445SDouglas Gilbert {
2660c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2661c65b1445SDouglas Gilbert }
2662c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2663c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2664c65b1445SDouglas Gilbert {
2665c65b1445SDouglas Gilbert         int n;
2666c65b1445SDouglas Gilbert 
2667c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2668c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2669c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2670c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2671c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2672c65b1445SDouglas Gilbert 		} else
2673c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2674c65b1445SDouglas Gilbert 		return count;
2675c65b1445SDouglas Gilbert 	}
2676c65b1445SDouglas Gilbert 	return -EINVAL;
2677c65b1445SDouglas Gilbert }
2678c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2679c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2680c65b1445SDouglas Gilbert 
26811da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
26821da177e4SLinus Torvalds {
26831da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
26841da177e4SLinus Torvalds }
26851da177e4SLinus Torvalds 
26861da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
26871da177e4SLinus Torvalds 				     const char * buf, size_t count)
26881da177e4SLinus Torvalds {
26891da177e4SLinus Torvalds         int delta_hosts;
26901da177e4SLinus Torvalds 	char work[20];
26911da177e4SLinus Torvalds 
26921da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
26931da177e4SLinus Torvalds 		return -EINVAL;
26941da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
26951da177e4SLinus Torvalds 		int neg = 0;
26961da177e4SLinus Torvalds 
26971da177e4SLinus Torvalds 		if ('-' == *work)
26981da177e4SLinus Torvalds 			neg = 1;
26991da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
27001da177e4SLinus Torvalds 			return -EINVAL;
27011da177e4SLinus Torvalds 		if (neg)
27021da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
27031da177e4SLinus Torvalds 	}
27041da177e4SLinus Torvalds 	if (delta_hosts > 0) {
27051da177e4SLinus Torvalds 		do {
27061da177e4SLinus Torvalds 			sdebug_add_adapter();
27071da177e4SLinus Torvalds 		} while (--delta_hosts);
27081da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
27091da177e4SLinus Torvalds 		do {
27101da177e4SLinus Torvalds 			sdebug_remove_adapter();
27111da177e4SLinus Torvalds 		} while (++delta_hosts);
27121da177e4SLinus Torvalds 	}
27131da177e4SLinus Torvalds 	return count;
27141da177e4SLinus Torvalds }
27151da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
27161da177e4SLinus Torvalds 	    sdebug_add_host_store);
27171da177e4SLinus Torvalds 
271823183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
271923183910SDouglas Gilbert 					  char * buf)
272023183910SDouglas Gilbert {
272123183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
272223183910SDouglas Gilbert }
272323183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
272423183910SDouglas Gilbert 					   const char * buf, size_t count)
272523183910SDouglas Gilbert {
272623183910SDouglas Gilbert 	int n;
272723183910SDouglas Gilbert 
272823183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
272923183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
273023183910SDouglas Gilbert 		return count;
273123183910SDouglas Gilbert 	}
273223183910SDouglas Gilbert 	return -EINVAL;
273323183910SDouglas Gilbert }
273423183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
273523183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
273623183910SDouglas Gilbert 
273723183910SDouglas Gilbert /* Note: The following function creates attribute files in the
273823183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
273923183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
274023183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
274123183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
274223183910SDouglas Gilbert  */
27436ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
27441da177e4SLinus Torvalds {
27456ecaff7fSRandy Dunlap 	int ret;
27466ecaff7fSRandy Dunlap 
27476ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27486ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
27496ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27506ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27516ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
275223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27536ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
275423183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27556ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
275623183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
27576ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27586ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
27596ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
276023183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
276123183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
27626ecaff7fSRandy Dunlap 	return ret;
27631da177e4SLinus Torvalds }
27641da177e4SLinus Torvalds 
27651da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
27661da177e4SLinus Torvalds {
276723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
276823183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
27691da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
27701da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
27711da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27721da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
277323183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
277423183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27751da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
277623183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27771da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
27781da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27791da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27801da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
27811da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27821da177e4SLinus Torvalds }
27831da177e4SLinus Torvalds 
27841da177e4SLinus Torvalds static int __init scsi_debug_init(void)
27851da177e4SLinus Torvalds {
2786c65b1445SDouglas Gilbert 	unsigned int sz;
27871da177e4SLinus Torvalds 	int host_to_add;
27881da177e4SLinus Torvalds 	int k;
27896ecaff7fSRandy Dunlap 	int ret;
27901da177e4SLinus Torvalds 
27911da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
27921da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2793c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2794c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2795c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2796c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2797c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2798c65b1445SDouglas Gilbert 	} else
2799c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
28001da177e4SLinus Torvalds 
28011da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
28021da177e4SLinus Torvalds 	sdebug_heads = 8;
28031da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
28041da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
28051da177e4SLinus Torvalds 		sdebug_heads = 32;
28061da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
28071da177e4SLinus Torvalds 		sdebug_heads = 64;
28081da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28091da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28101da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
28111da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
28121da177e4SLinus Torvalds 		sdebug_heads = 255;
28131da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
28141da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28151da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28161da177e4SLinus Torvalds 	}
28171da177e4SLinus Torvalds 
28181da177e4SLinus Torvalds 	sz = sdebug_store_size;
28191da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
28201da177e4SLinus Torvalds 	if (NULL == fake_storep) {
28211da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
28221da177e4SLinus Torvalds 		return -ENOMEM;
28231da177e4SLinus Torvalds 	}
28241da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
28251da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
28261da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
28271da177e4SLinus Torvalds 
28286ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
28296ecaff7fSRandy Dunlap 	if (ret < 0) {
28306ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
28316ecaff7fSRandy Dunlap 			ret);
28326ecaff7fSRandy Dunlap 		goto free_vm;
28336ecaff7fSRandy Dunlap 	}
28346ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
28356ecaff7fSRandy Dunlap 	if (ret < 0) {
28366ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
28376ecaff7fSRandy Dunlap 			ret);
28386ecaff7fSRandy Dunlap 		goto dev_unreg;
28396ecaff7fSRandy Dunlap 	}
28406ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
28416ecaff7fSRandy Dunlap 	if (ret < 0) {
28426ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
28436ecaff7fSRandy Dunlap 			ret);
28446ecaff7fSRandy Dunlap 		goto bus_unreg;
28456ecaff7fSRandy Dunlap 	}
28466ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
28476ecaff7fSRandy Dunlap 	if (ret < 0) {
28486ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
28496ecaff7fSRandy Dunlap 			ret);
28506ecaff7fSRandy Dunlap 		goto del_files;
28516ecaff7fSRandy Dunlap 	}
28521da177e4SLinus Torvalds 
28536ecaff7fSRandy Dunlap 	init_all_queued();
28541da177e4SLinus Torvalds 
28551da177e4SLinus Torvalds 	sdebug_driver_template.proc_name = (char *)sdebug_proc_name;
28561da177e4SLinus Torvalds 
28571da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
28581da177e4SLinus Torvalds         scsi_debug_add_host = 0;
28591da177e4SLinus Torvalds 
28601da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
28611da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
28621da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
28631da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
28641da177e4SLinus Torvalds                         break;
28651da177e4SLinus Torvalds                 }
28661da177e4SLinus Torvalds         }
28671da177e4SLinus Torvalds 
28681da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
28691da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
28701da177e4SLinus Torvalds 		       scsi_debug_add_host);
28711da177e4SLinus Torvalds 	}
28721da177e4SLinus Torvalds 	return 0;
28736ecaff7fSRandy Dunlap 
28746ecaff7fSRandy Dunlap del_files:
28756ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
28766ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
28776ecaff7fSRandy Dunlap bus_unreg:
28786ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
28796ecaff7fSRandy Dunlap dev_unreg:
28806ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
28816ecaff7fSRandy Dunlap free_vm:
28826ecaff7fSRandy Dunlap 	vfree(fake_storep);
28836ecaff7fSRandy Dunlap 
28846ecaff7fSRandy Dunlap 	return ret;
28851da177e4SLinus Torvalds }
28861da177e4SLinus Torvalds 
28871da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
28881da177e4SLinus Torvalds {
28891da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
28901da177e4SLinus Torvalds 
28911da177e4SLinus Torvalds 	stop_all_queued();
28921da177e4SLinus Torvalds 	for (; k; k--)
28931da177e4SLinus Torvalds 		sdebug_remove_adapter();
28941da177e4SLinus Torvalds 	do_remove_driverfs_files();
28951da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
28961da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
28971da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
28981da177e4SLinus Torvalds 
28991da177e4SLinus Torvalds 	vfree(fake_storep);
29001da177e4SLinus Torvalds }
29011da177e4SLinus Torvalds 
29021da177e4SLinus Torvalds device_initcall(scsi_debug_init);
29031da177e4SLinus Torvalds module_exit(scsi_debug_exit);
29041da177e4SLinus Torvalds 
290552c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
29061da177e4SLinus Torvalds {
29071da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
29081da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
29091da177e4SLinus Torvalds }
29101da177e4SLinus Torvalds 
29111da177e4SLinus Torvalds static struct device pseudo_primary = {
29121da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
29131da177e4SLinus Torvalds 	.release	= pseudo_0_release,
29141da177e4SLinus Torvalds };
29151da177e4SLinus Torvalds 
29161da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
29171da177e4SLinus Torvalds                           struct device_driver *dev_driver)
29181da177e4SLinus Torvalds {
29191da177e4SLinus Torvalds         return 1;
29201da177e4SLinus Torvalds }
29211da177e4SLinus Torvalds 
29221da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
29231da177e4SLinus Torvalds         .name = "pseudo",
29241da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2925bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2926bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
29271da177e4SLinus Torvalds };
29281da177e4SLinus Torvalds 
29291da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
29301da177e4SLinus Torvalds {
29311da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29321da177e4SLinus Torvalds 
29331da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29341da177e4SLinus Torvalds         kfree(sdbg_host);
29351da177e4SLinus Torvalds }
29361da177e4SLinus Torvalds 
29371da177e4SLinus Torvalds static int sdebug_add_adapter(void)
29381da177e4SLinus Torvalds {
29391da177e4SLinus Torvalds 	int k, devs_per_host;
29401da177e4SLinus Torvalds         int error = 0;
29411da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29421da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29431da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29441da177e4SLinus Torvalds 
294524669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
29461da177e4SLinus Torvalds 
29471da177e4SLinus Torvalds         if (NULL == sdbg_host) {
29481da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
29491da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
29501da177e4SLinus Torvalds                 return -ENOMEM;
29511da177e4SLinus Torvalds         }
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
29541da177e4SLinus Torvalds 
29551da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
29561da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
295724669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
29581da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
29591da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
29601da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
29611da177e4SLinus Torvalds                         error = -ENOMEM;
29621da177e4SLinus Torvalds 			goto clean;
29631da177e4SLinus Torvalds                 }
29641da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
29651da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
29661da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
29671da177e4SLinus Torvalds         }
29681da177e4SLinus Torvalds 
29691da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
29701da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
29711da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
29741da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
29751da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
29761da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
29771da177e4SLinus Torvalds 
29781da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
29791da177e4SLinus Torvalds 
29801da177e4SLinus Torvalds         if (error)
29811da177e4SLinus Torvalds 		goto clean;
29821da177e4SLinus Torvalds 
29831da177e4SLinus Torvalds 	++scsi_debug_add_host;
29841da177e4SLinus Torvalds         return error;
29851da177e4SLinus Torvalds 
29861da177e4SLinus Torvalds clean:
29871da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
29881da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
29891da177e4SLinus Torvalds 					  dev_list);
29901da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
29911da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
29921da177e4SLinus Torvalds 	}
29931da177e4SLinus Torvalds 
29941da177e4SLinus Torvalds 	kfree(sdbg_host);
29951da177e4SLinus Torvalds         return error;
29961da177e4SLinus Torvalds }
29971da177e4SLinus Torvalds 
29981da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
29991da177e4SLinus Torvalds {
30001da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
30011da177e4SLinus Torvalds 
30021da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
30031da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
30041da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
30051da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
30061da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
30071da177e4SLinus Torvalds 	}
30081da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
30091da177e4SLinus Torvalds 
30101da177e4SLinus Torvalds 	if (!sdbg_host)
30111da177e4SLinus Torvalds 		return;
30121da177e4SLinus Torvalds 
30131da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
30141da177e4SLinus Torvalds         --scsi_debug_add_host;
30151da177e4SLinus Torvalds }
30161da177e4SLinus Torvalds 
30171da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
30181da177e4SLinus Torvalds {
30191da177e4SLinus Torvalds         int error = 0;
30201da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30211da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
30221da177e4SLinus Torvalds 
30231da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30241da177e4SLinus Torvalds 
30251da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
30261da177e4SLinus Torvalds         if (NULL == hpnt) {
30271da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
30281da177e4SLinus Torvalds                 error = -ENODEV;
30291da177e4SLinus Torvalds 		return error;
30301da177e4SLinus Torvalds         }
30311da177e4SLinus Torvalds 
30321da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
30331da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
30341da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
30351da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
30361da177e4SLinus Torvalds 	else
30371da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3038c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
30391da177e4SLinus Torvalds 
30401da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
30411da177e4SLinus Torvalds         if (error) {
30421da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
30431da177e4SLinus Torvalds                 error = -ENODEV;
30441da177e4SLinus Torvalds 		scsi_host_put(hpnt);
30451da177e4SLinus Torvalds         } else
30461da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
30471da177e4SLinus Torvalds 
30481da177e4SLinus Torvalds 
30491da177e4SLinus Torvalds         return error;
30501da177e4SLinus Torvalds }
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
30531da177e4SLinus Torvalds {
30541da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
30551da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30561da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
30571da177e4SLinus Torvalds 
30581da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30591da177e4SLinus Torvalds 
30601da177e4SLinus Torvalds 	if (!sdbg_host) {
30611da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
30621da177e4SLinus Torvalds 		       __FUNCTION__);
30631da177e4SLinus Torvalds 		return -ENODEV;
30641da177e4SLinus Torvalds 	}
30651da177e4SLinus Torvalds 
30661da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
30671da177e4SLinus Torvalds 
30681da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30691da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30701da177e4SLinus Torvalds                                           dev_list);
30711da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
30721da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
30731da177e4SLinus Torvalds         }
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
30761da177e4SLinus Torvalds         return 0;
30771da177e4SLinus Torvalds }
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
30801da177e4SLinus Torvalds {
30811da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
30821da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
30831da177e4SLinus Torvalds 
30841da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
30851da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
30861da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
30871da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
30881da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
30891da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
30901da177e4SLinus Torvalds 		else
30911da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
3092c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
30931da177e4SLinus Torvalds 	}
30941da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
30951da177e4SLinus Torvalds }
3096