xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 23183910)
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"
5523183910SDouglas Gilbert static const char * scsi_debug_version_date = "20060914";
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);
2571da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2581da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
259c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2601da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
261c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
262c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
263c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
264c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
265c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
266c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
267c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
268c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
269c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
270c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2711da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2721da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
2731da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2741da177e4SLinus Torvalds                                 int arr_len);
2751da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2761da177e4SLinus Torvalds                                int max_arr_len);
2771da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2781da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2791da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2801da177e4SLinus Torvalds 			    int asc, int asq);
281c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2821da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2831da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2841da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2851da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2861da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2871da177e4SLinus Torvalds static void __init init_all_queued(void);
2881da177e4SLinus Torvalds static void stop_all_queued(void);
2891da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
290c65b1445SDouglas Gilbert static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
291c65b1445SDouglas Gilbert 			   int dev_id_num, const char * dev_id_str,
292c65b1445SDouglas Gilbert 			   int dev_id_str_len);
293c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
2946ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
2951da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2981da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2991da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
3001da177e4SLinus Torvalds 
3011da177e4SLinus Torvalds static struct device pseudo_primary;
3021da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
3031da177e4SLinus Torvalds 
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds static
3061da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
309c65b1445SDouglas Gilbert 	int len, k, j;
310c65b1445SDouglas Gilbert 	unsigned int num;
311c65b1445SDouglas Gilbert 	unsigned long long lba;
3121da177e4SLinus Torvalds 	int errsts = 0;
313c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3141da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3151da177e4SLinus Torvalds 	int inj_recovered = 0;
316c65b1445SDouglas Gilbert 	int delay_override = 0;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	if (done == NULL)
3191da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3201da177e4SLinus Torvalds 
321c65b1445SDouglas Gilbert 	SCpnt->resid = 0;
3221da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3231da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
324c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3251da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3261da177e4SLinus Torvalds 		printk("\n");
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds         if(target == sdebug_driver_template.this_id) {
3291da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3301da177e4SLinus Torvalds 		       "target!\n");
3311da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3321da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3331da177e4SLinus Torvalds         }
3341da177e4SLinus Torvalds 
335c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
336c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3371da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3381da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3391da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3401da177e4SLinus Torvalds 	if (NULL == devip)
3411da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3421da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3451da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3461da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3471da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3481da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3491da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3501da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3511da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3521da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3531da177e4SLinus Torvalds         }
3541da177e4SLinus Torvalds 
355c65b1445SDouglas Gilbert 	if (devip->wlun) {
356c65b1445SDouglas Gilbert 		switch (*cmd) {
357c65b1445SDouglas Gilbert 		case INQUIRY:
358c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
359c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
360c65b1445SDouglas Gilbert 		case REPORT_LUNS:
361c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
362c65b1445SDouglas Gilbert 		default:
363c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
364c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
365c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
366c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
367c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
368c65b1445SDouglas Gilbert 			errsts = check_condition_result;
369c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
370c65b1445SDouglas Gilbert 					     0);
371c65b1445SDouglas Gilbert 		}
372c65b1445SDouglas Gilbert 	}
373c65b1445SDouglas Gilbert 
3741da177e4SLinus Torvalds 	switch (*cmd) {
3751da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
376c65b1445SDouglas Gilbert 		delay_override = 1;
3771da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
3781da177e4SLinus Torvalds 		break;
3791da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
380c65b1445SDouglas Gilbert 		delay_override = 1;
3811da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
3821da177e4SLinus Torvalds 		break;
3831da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3841da177e4SLinus Torvalds 	case START_STOP:
385c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
3861da177e4SLinus Torvalds 		break;
3871da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
388c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
3891da177e4SLinus Torvalds 			break;
3901da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3911da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
3921da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
3931da177e4SLinus Torvalds 		break;
3941da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
395c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
3961da177e4SLinus Torvalds 		break;
3971da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
398c65b1445SDouglas Gilbert 		delay_override = 1;
399c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4001da177e4SLinus Torvalds 		break;
4011da177e4SLinus Torvalds         case RESERVE:
402c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4031da177e4SLinus Torvalds                 break;
4041da177e4SLinus Torvalds         case RESERVE_10:
405c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4061da177e4SLinus Torvalds                 break;
4071da177e4SLinus Torvalds         case RELEASE:
408c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4091da177e4SLinus Torvalds                 break;
4101da177e4SLinus Torvalds         case RELEASE_10:
411c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4121da177e4SLinus Torvalds                 break;
4131da177e4SLinus Torvalds 	case READ_CAPACITY:
4141da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4151da177e4SLinus Torvalds 		break;
416c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
417c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
418c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
419c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
420c65b1445SDouglas Gilbert 			errsts = check_condition_result;
421c65b1445SDouglas Gilbert 			break;
422c65b1445SDouglas Gilbert 		}
423c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
424c65b1445SDouglas Gilbert 		break;
4251da177e4SLinus Torvalds 	case READ_16:
4261da177e4SLinus Torvalds 	case READ_12:
4271da177e4SLinus Torvalds 	case READ_10:
4281da177e4SLinus Torvalds 	case READ_6:
429c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4301da177e4SLinus Torvalds 			break;
43123183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
43223183910SDouglas Gilbert 			break;
4331da177e4SLinus Torvalds 		if ((*cmd) == READ_16) {
434c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
435c65b1445SDouglas Gilbert 				if (j > 0)
436c65b1445SDouglas Gilbert 					lba <<= 8;
437c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
438c65b1445SDouglas Gilbert 			}
4391da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4401da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4411da177e4SLinus Torvalds 		} else if ((*cmd) == READ_12) {
442c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4431da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4441da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4451da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4461da177e4SLinus Torvalds 		} else if ((*cmd) == READ_10) {
447c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4481da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4491da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
450c65b1445SDouglas Gilbert 		} else {	/* READ (6) */
451c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4521da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
453c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4541da177e4SLinus Torvalds 		}
455c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4561da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4571da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
458c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4591da177e4SLinus Torvalds 			errsts = check_condition_result;
4601da177e4SLinus Torvalds 		}
4611da177e4SLinus Torvalds 		break;
4621da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
463c65b1445SDouglas Gilbert 		delay_override = 1;
4641da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4651da177e4SLinus Torvalds 		break;
4661da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
467c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4681da177e4SLinus Torvalds 		break;
4691da177e4SLinus Torvalds 	case WRITE_16:
4701da177e4SLinus Torvalds 	case WRITE_12:
4711da177e4SLinus Torvalds 	case WRITE_10:
4721da177e4SLinus Torvalds 	case WRITE_6:
473c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4741da177e4SLinus Torvalds 			break;
47523183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
47623183910SDouglas Gilbert 			break;
4771da177e4SLinus Torvalds 		if ((*cmd) == WRITE_16) {
478c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
479c65b1445SDouglas Gilbert 				if (j > 0)
480c65b1445SDouglas Gilbert 					lba <<= 8;
481c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
482c65b1445SDouglas Gilbert 			}
4831da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4841da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4851da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_12) {
486c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4871da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4881da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4891da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4901da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_10) {
491c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4921da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4931da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
494c65b1445SDouglas Gilbert 		} else {	/* WRITE (6) */
495c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4961da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
497c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4981da177e4SLinus Torvalds 		}
499c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5001da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5011da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
502c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5031da177e4SLinus Torvalds 			errsts = check_condition_result;
5041da177e4SLinus Torvalds 		}
5051da177e4SLinus Torvalds 		break;
5061da177e4SLinus Torvalds 	case MODE_SENSE:
5071da177e4SLinus Torvalds 	case MODE_SENSE_10:
5081da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5091da177e4SLinus Torvalds 		break;
510c65b1445SDouglas Gilbert 	case MODE_SELECT:
511c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
512c65b1445SDouglas Gilbert 		break;
513c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
514c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
515c65b1445SDouglas Gilbert 		break;
516c65b1445SDouglas Gilbert 	case LOG_SENSE:
517c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
518c65b1445SDouglas Gilbert 		break;
5191da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
520c65b1445SDouglas Gilbert 		delay_override = 1;
521c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5221da177e4SLinus Torvalds 		break;
5231da177e4SLinus Torvalds 	default:
5241da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5251da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5261da177e4SLinus Torvalds 			       "supported\n", *cmd);
527c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5281da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5291da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5301da177e4SLinus Torvalds 		errsts = check_condition_result;
5311da177e4SLinus Torvalds 		break;
5321da177e4SLinus Torvalds 	}
533c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
534c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5381da177e4SLinus Torvalds {
5391da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5401da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5411da177e4SLinus Torvalds 	}
5421da177e4SLinus Torvalds 	return -EINVAL;
5431da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
546c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
547c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5481da177e4SLinus Torvalds {
5491da177e4SLinus Torvalds 	if (devip->reset) {
5501da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5511da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5521da177e4SLinus Torvalds 			       "attention: power on reset\n");
5531da177e4SLinus Torvalds 		devip->reset = 0;
5541da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5551da177e4SLinus Torvalds 		return check_condition_result;
5561da177e4SLinus Torvalds 	}
557c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
558c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
559c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
560c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
561c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
562c65b1445SDouglas Gilbert 				0x2);
563c65b1445SDouglas Gilbert 		return check_condition_result;
564c65b1445SDouglas Gilbert 	}
5651da177e4SLinus Torvalds 	return 0;
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5691da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5701da177e4SLinus Torvalds 				int arr_len)
5711da177e4SLinus Torvalds {
5721da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
5731da177e4SLinus Torvalds 	void * kaddr;
5741da177e4SLinus Torvalds 	void * kaddr_off;
5751da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
5761da177e4SLinus Torvalds 
5771da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
5781da177e4SLinus Torvalds 		return 0;
5791da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
5801da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5811da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
5821da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
5831da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5841da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
5851da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
5861da177e4SLinus Torvalds 		act_len = (req_len < arr_len) ? req_len : arr_len;
5871da177e4SLinus Torvalds 		memcpy(scp->request_buffer, arr, act_len);
588c65b1445SDouglas Gilbert 		if (scp->resid)
589c65b1445SDouglas Gilbert 			scp->resid -= act_len;
590c65b1445SDouglas Gilbert 		else
5911da177e4SLinus Torvalds 			scp->resid = req_len - act_len;
5921da177e4SLinus Torvalds 		return 0;
5931da177e4SLinus Torvalds 	}
5941da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
5951da177e4SLinus Torvalds 	active = 1;
5961da177e4SLinus Torvalds 	for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
5971da177e4SLinus Torvalds 		if (active) {
5981da177e4SLinus Torvalds 			kaddr = (unsigned char *)
5991da177e4SLinus Torvalds 				kmap_atomic(sgpnt->page, KM_USER0);
6001da177e4SLinus Torvalds 			if (NULL == kaddr)
6011da177e4SLinus Torvalds 				return (DID_ERROR << 16);
6021da177e4SLinus Torvalds 			kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6031da177e4SLinus Torvalds 			len = sgpnt->length;
6041da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
6051da177e4SLinus Torvalds 				active = 0;
6061da177e4SLinus Torvalds 				len = arr_len - req_len;
6071da177e4SLinus Torvalds 			}
6081da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6091da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6101da177e4SLinus Torvalds 			act_len += len;
6111da177e4SLinus Torvalds 		}
6121da177e4SLinus Torvalds 		req_len += sgpnt->length;
6131da177e4SLinus Torvalds 	}
614c65b1445SDouglas Gilbert 	if (scp->resid)
615c65b1445SDouglas Gilbert 		scp->resid -= act_len;
616c65b1445SDouglas Gilbert 	else
6171da177e4SLinus Torvalds 		scp->resid = req_len - act_len;
6181da177e4SLinus Torvalds 	return 0;
6191da177e4SLinus Torvalds }
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6221da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6231da177e4SLinus Torvalds 			       int max_arr_len)
6241da177e4SLinus Torvalds {
6251da177e4SLinus Torvalds 	int k, req_len, len, fin;
6261da177e4SLinus Torvalds 	void * kaddr;
6271da177e4SLinus Torvalds 	void * kaddr_off;
6281da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
6291da177e4SLinus Torvalds 
6301da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
6311da177e4SLinus Torvalds 		return 0;
6321da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
6331da177e4SLinus Torvalds 		return -1;
6341da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6351da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_TO_DEVICE)))
6361da177e4SLinus Torvalds 		return -1;
6371da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
6381da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
6391da177e4SLinus Torvalds 		len = (req_len < max_arr_len) ? req_len : max_arr_len;
6401da177e4SLinus Torvalds 		memcpy(arr, scp->request_buffer, len);
6411da177e4SLinus Torvalds 		return len;
6421da177e4SLinus Torvalds 	}
6431da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6441da177e4SLinus Torvalds 	for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
6451da177e4SLinus Torvalds 		kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
6461da177e4SLinus Torvalds 		if (NULL == kaddr)
6471da177e4SLinus Torvalds 			return -1;
6481da177e4SLinus Torvalds 		kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6491da177e4SLinus Torvalds 		len = sgpnt->length;
6501da177e4SLinus Torvalds 		if ((req_len + len) > max_arr_len) {
6511da177e4SLinus Torvalds 			len = max_arr_len - req_len;
6521da177e4SLinus Torvalds 			fin = 1;
6531da177e4SLinus Torvalds 		}
6541da177e4SLinus Torvalds 		memcpy(arr + req_len, kaddr_off, len);
6551da177e4SLinus Torvalds 		kunmap_atomic(kaddr, KM_USER0);
6561da177e4SLinus Torvalds 		if (fin)
6571da177e4SLinus Torvalds 			return req_len + len;
6581da177e4SLinus Torvalds 		req_len += sgpnt->length;
6591da177e4SLinus Torvalds 	}
6601da177e4SLinus Torvalds 	return req_len;
6611da177e4SLinus Torvalds }
6621da177e4SLinus Torvalds 
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6651da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6661da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6671da177e4SLinus Torvalds 
668c65b1445SDouglas Gilbert static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
669c65b1445SDouglas Gilbert 			   int dev_id_num, const char * dev_id_str,
670c65b1445SDouglas Gilbert 			   int dev_id_str_len)
6711da177e4SLinus Torvalds {
672c65b1445SDouglas Gilbert 	int num, port_a;
673c65b1445SDouglas Gilbert 	char b[32];
6741da177e4SLinus Torvalds 
675c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
6761da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
6771da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
6781da177e4SLinus Torvalds 	arr[1] = 0x1;
6791da177e4SLinus Torvalds 	arr[2] = 0x0;
6801da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
6811da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
6821da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
6831da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
6841da177e4SLinus Torvalds 	arr[3] = num;
6851da177e4SLinus Torvalds 	num += 4;
686c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
687c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
688c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
689c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
690c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
691c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
692c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
693c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
694c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
695c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
696c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
697c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
698c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
699c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
700c65b1445SDouglas Gilbert 		/* Target relative port number */
701c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
702c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
703c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
704c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
705c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
706c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
707c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
708c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
709c65b1445SDouglas Gilbert 	}
710c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
711c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
712c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
713c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
714c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
715c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
716c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
717c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
718c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
719c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
720c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
721c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
722c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
723c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
724c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
725c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
726c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
727c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
728c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
729c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
730c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
731c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
732c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
733c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
734c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
735c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
736c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
737c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
738c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
739c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
740c65b1445SDouglas Gilbert 	arr[num++] = 24;
741c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
742c65b1445SDouglas Gilbert 	num += 12;
743c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
744c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
745c65b1445SDouglas Gilbert 	num += 8;
746c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
747c65b1445SDouglas Gilbert 	num += 4;
748c65b1445SDouglas Gilbert 	return num;
749c65b1445SDouglas Gilbert }
750c65b1445SDouglas Gilbert 
751c65b1445SDouglas Gilbert 
752c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
753c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
754c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
755c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
756c65b1445SDouglas Gilbert };
757c65b1445SDouglas Gilbert 
758c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
759c65b1445SDouglas Gilbert {
760c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
761c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
762c65b1445SDouglas Gilbert }
763c65b1445SDouglas Gilbert 
764c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
765c65b1445SDouglas Gilbert {
766c65b1445SDouglas Gilbert 	int num = 0;
767c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
768c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
769c65b1445SDouglas Gilbert 	int plen, olen;
770c65b1445SDouglas Gilbert 
771c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
772c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
773c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
774c65b1445SDouglas Gilbert 	olen = strlen(na1);
775c65b1445SDouglas Gilbert 	plen = olen + 1;
776c65b1445SDouglas Gilbert 	if (plen % 4)
777c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
778c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
779c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
780c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
781c65b1445SDouglas Gilbert 	num += plen;
782c65b1445SDouglas Gilbert 
783c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
784c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
785c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
786c65b1445SDouglas Gilbert 	olen = strlen(na2);
787c65b1445SDouglas Gilbert 	plen = olen + 1;
788c65b1445SDouglas Gilbert 	if (plen % 4)
789c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
790c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
791c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
792c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
793c65b1445SDouglas Gilbert 	num += plen;
794c65b1445SDouglas Gilbert 
795c65b1445SDouglas Gilbert 	return num;
796c65b1445SDouglas Gilbert }
797c65b1445SDouglas Gilbert 
798c65b1445SDouglas Gilbert /* SCSI ports VPD page */
799c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
800c65b1445SDouglas Gilbert {
801c65b1445SDouglas Gilbert 	int num = 0;
802c65b1445SDouglas Gilbert 	int port_a, port_b;
803c65b1445SDouglas Gilbert 
804c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
805c65b1445SDouglas Gilbert 	port_b = port_a + 1;
806c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
807c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
808c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
809c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
810c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
811c65b1445SDouglas Gilbert 	num += 6;
812c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
813c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
814c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
815c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
816c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
817c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
818c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
819c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
820c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
821c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
822c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
823c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
824c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
825c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
826c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
827c65b1445SDouglas Gilbert 
828c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
829c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
830c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
831c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
832c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
833c65b1445SDouglas Gilbert 	num += 6;
834c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
835c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
836c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
837c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
838c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
839c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
840c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
841c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
842c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
843c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
844c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
845c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
846c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
847c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
848c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
849c65b1445SDouglas Gilbert 
850c65b1445SDouglas Gilbert 	return num;
851c65b1445SDouglas Gilbert }
852c65b1445SDouglas Gilbert 
853c65b1445SDouglas Gilbert 
854c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
855c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
856c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
857c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
858c65b1445SDouglas Gilbert '1','2','3','4',
859c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
860c65b1445SDouglas Gilbert 0xec,0,0,0,
861c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
862c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
863c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
864c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
865c65b1445SDouglas Gilbert 0x53,0x41,
866c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
867c65b1445SDouglas Gilbert 0x20,0x20,
868c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
869c65b1445SDouglas Gilbert 0x10,0x80,
870c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
871c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
872c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
873c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
874c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
875c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
876c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
877c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
878c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
879c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
880c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
881c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
882c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
883c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
884c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
885c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
886c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
887c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
888c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
889c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
890c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
891c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
892c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
893c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
894c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
895c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
896c65b1445SDouglas Gilbert };
897c65b1445SDouglas Gilbert 
898c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
899c65b1445SDouglas Gilbert {
900c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
901c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
902c65b1445SDouglas Gilbert }
903c65b1445SDouglas Gilbert 
904c65b1445SDouglas Gilbert 
905c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
906c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
907c65b1445SDouglas Gilbert 	0,0,0x4,0,
908c65b1445SDouglas Gilbert 	0,0,0,64,
909c65b1445SDouglas Gilbert };
910c65b1445SDouglas Gilbert 
911c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
912c65b1445SDouglas Gilbert {
913c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
914c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
915c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
916c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
917c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
918c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
919c65b1445SDouglas Gilbert 	}
920c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
9211da177e4SLinus Torvalds }
9221da177e4SLinus Torvalds 
9231da177e4SLinus Torvalds 
9241da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
925c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9261da177e4SLinus Torvalds 
9271da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9281da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9291da177e4SLinus Torvalds {
9301da177e4SLinus Torvalds 	unsigned char pq_pdt;
9311da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
9321da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
933c65b1445SDouglas Gilbert 	int alloc_len, n;
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9361da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
937c65b1445SDouglas Gilbert 	if (devip->wlun)
938c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
939c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
940c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
941c65b1445SDouglas Gilbert 	else
9421da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9431da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9441da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9451da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9461da177e4SLinus Torvalds 			       	0);
9471da177e4SLinus Torvalds 		return check_condition_result;
9481da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
949c65b1445SDouglas Gilbert 		int lu_id_num, target_dev_id, len;
950c65b1445SDouglas Gilbert 		char lu_id_str[6];
951c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9521da177e4SLinus Torvalds 
95323183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
95423183910SDouglas Gilbert 			host_no = 0;
955c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
956c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
957c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
958c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
959c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9601da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
961c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
962c65b1445SDouglas Gilbert 			n = 4;
963c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
964c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
965c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
966c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
967c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
968c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
969c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
970c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
971c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
972c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
973c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
9741da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
975c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
9761da177e4SLinus Torvalds 			arr[3] = len;
977c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
9781da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
979c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
980c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_83(&arr[4], target_dev_id,
981c65b1445SDouglas Gilbert 						 lu_id_num, lu_id_str, len);
982c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
983c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
984c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
985c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
986c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
987c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
988c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
989c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
990c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
991c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
992c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
993c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
994c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
995c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
996c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
997c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
998c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
999c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
1000c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
1001c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1002c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
1003c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
1004c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1005c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
1006c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
1007c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
1008c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
1009c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1010c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10111da177e4SLinus Torvalds 		} else {
10121da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10131da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10141da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10151da177e4SLinus Torvalds 			return check_condition_result;
10161da177e4SLinus Torvalds 		}
1017c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10181da177e4SLinus Torvalds 		return fill_from_dev_buffer(scp, arr,
1019c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10201da177e4SLinus Torvalds 	}
10211da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10221da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10231da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10241da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10251da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
1026c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10271da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1028c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10291da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10301da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10311da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10321da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1033c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1034c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1035c65b1445SDouglas Gilbert 	n = 62;
10361da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1037c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10381da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1039c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10401da177e4SLinus Torvalds 	}
1041c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10421da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
10431da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10441da177e4SLinus Torvalds }
10451da177e4SLinus Torvalds 
10461da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10471da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10481da177e4SLinus Torvalds {
10491da177e4SLinus Torvalds 	unsigned char * sbuff;
10501da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10511da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1052c65b1445SDouglas Gilbert 	int want_dsense;
10531da177e4SLinus Torvalds 	int len = 18;
10541da177e4SLinus Torvalds 
1055c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10561da177e4SLinus Torvalds 	if (devip->reset == 1)
1057c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1058c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10591da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1060c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1061c65b1445SDouglas Gilbert 		if (want_dsense) {
1062c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1063c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1064c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1065c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1066c65b1445SDouglas Gilbert 		} else {
1067c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1068c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1069c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1070c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1071c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1072c65b1445SDouglas Gilbert 		}
1073c65b1445SDouglas Gilbert 	} else {
1074c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
10751da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
10761da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1077c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
10781da177e4SLinus Torvalds 			arr[0] = 0x72;
10791da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
10801da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
10811da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
10821da177e4SLinus Torvalds 			len = 8;
1083c65b1445SDouglas Gilbert 		}
1084c65b1445SDouglas Gilbert 	}
1085c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
10861da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
10871da177e4SLinus Torvalds }
10881da177e4SLinus Torvalds 
1089c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1090c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1091c65b1445SDouglas Gilbert {
1092c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1093c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1094c65b1445SDouglas Gilbert 
1095c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1096c65b1445SDouglas Gilbert 		return errsts;
1097c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1098c65b1445SDouglas Gilbert 	if (power_cond) {
1099c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1100c65b1445SDouglas Gilbert 			       	0);
1101c65b1445SDouglas Gilbert 		return check_condition_result;
1102c65b1445SDouglas Gilbert 	}
1103c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1104c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1105c65b1445SDouglas Gilbert 		devip->stopped = !start;
1106c65b1445SDouglas Gilbert 	return 0;
1107c65b1445SDouglas Gilbert }
1108c65b1445SDouglas Gilbert 
11091da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11101da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11111da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11121da177e4SLinus Torvalds {
11131da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1114c65b1445SDouglas Gilbert 	unsigned int capac;
11151da177e4SLinus Torvalds 	int errsts;
11161da177e4SLinus Torvalds 
1117c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11181da177e4SLinus Torvalds 		return errsts;
1119c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1120c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1121c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1122c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1123c65b1445SDouglas Gilbert 	} else
1124c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11251da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1126c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1127c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11281da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11291da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11301da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11311da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1132c65b1445SDouglas Gilbert 	} else {
1133c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1134c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1135c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1136c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1137c65b1445SDouglas Gilbert 	}
11381da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11391da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11401da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11411da177e4SLinus Torvalds }
11421da177e4SLinus Torvalds 
1143c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1144c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1145c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1146c65b1445SDouglas Gilbert {
1147c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1148c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1149c65b1445SDouglas Gilbert 	unsigned long long capac;
1150c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1151c65b1445SDouglas Gilbert 
1152c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1153c65b1445SDouglas Gilbert 		return errsts;
1154c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1155c65b1445SDouglas Gilbert 		     + cmd[13]);
1156c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1157c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1158c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1159c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1160c65b1445SDouglas Gilbert 	} else
1161c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1162c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1163c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1164c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1165c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1166c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1167c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1168c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1169c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1170c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1171c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1172c65b1445SDouglas Gilbert }
1173c65b1445SDouglas Gilbert 
11741da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
11751da177e4SLinus Torvalds 
11761da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
11771da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
11781da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
11791da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
11801da177e4SLinus Torvalds 
11811da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
11821da177e4SLinus Torvalds 	if (1 == pcontrol)
11831da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
11841da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
11851da177e4SLinus Torvalds }
11861da177e4SLinus Torvalds 
11871da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
11881da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
11891da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
11901da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
11911da177e4SLinus Torvalds 
11921da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
11931da177e4SLinus Torvalds 	if (1 == pcontrol)
11941da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
11951da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
11961da177e4SLinus Torvalds }
11971da177e4SLinus Torvalds 
11981da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
11991da177e4SLinus Torvalds {       /* Format device page for mode_sense */
12001da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
12011da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
12021da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
12031da177e4SLinus Torvalds 
12041da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
12051da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
12061da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
12071da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
12081da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
12091da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
12101da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
12111da177e4SLinus Torvalds         if (1 == pcontrol)
12121da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
12131da177e4SLinus Torvalds         return sizeof(format_pg);
12141da177e4SLinus Torvalds }
12151da177e4SLinus Torvalds 
12161da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
12171da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
12181da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
12191da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
12201da177e4SLinus Torvalds 
12211da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
12221da177e4SLinus Torvalds 	if (1 == pcontrol)
12231da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
12241da177e4SLinus Torvalds 	return sizeof(caching_pg);
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds 
12271da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
12281da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1229c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1230c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1231c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
12321da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
12331da177e4SLinus Torvalds 
12341da177e4SLinus Torvalds 	if (scsi_debug_dsense)
12351da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1236c65b1445SDouglas Gilbert 	else
1237c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
12381da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
12391da177e4SLinus Torvalds 	if (1 == pcontrol)
1240c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1241c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1242c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
12431da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
1246c65b1445SDouglas Gilbert 
12471da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
12481da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1249c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
12501da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1251c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1252c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1253c65b1445SDouglas Gilbert 
12541da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
12551da177e4SLinus Torvalds 	if (1 == pcontrol)
1256c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1257c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1258c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
12591da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
12601da177e4SLinus Torvalds }
12611da177e4SLinus Torvalds 
1262c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1263c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1264c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1265c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1266c65b1445SDouglas Gilbert 
1267c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1268c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1269c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1270c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1271c65b1445SDouglas Gilbert }
1272c65b1445SDouglas Gilbert 
1273c65b1445SDouglas Gilbert 
1274c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1275c65b1445SDouglas Gilbert 			      int target_dev_id)
1276c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1277c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1278c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1279c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1280c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1281c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1282c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1283c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1284c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1285c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1286c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1287c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1288c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1289c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1290c65b1445SDouglas Gilbert 		};
1291c65b1445SDouglas Gilbert 	int port_a, port_b;
1292c65b1445SDouglas Gilbert 
1293c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1294c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1295c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1296c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1297c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1298c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1299c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1300c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1301c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1302c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1303c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1304c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1305c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1306c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1307c65b1445SDouglas Gilbert }
1308c65b1445SDouglas Gilbert 
1309c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1310c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1311c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1312c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1313c65b1445SDouglas Gilbert 		};
1314c65b1445SDouglas Gilbert 
1315c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1316c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1317c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1318c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1319c65b1445SDouglas Gilbert }
1320c65b1445SDouglas Gilbert 
13211da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
13221da177e4SLinus Torvalds 
13231da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
13241da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
13251da177e4SLinus Torvalds {
132623183910SDouglas Gilbert 	unsigned char dbd, llbaa;
132723183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
13281da177e4SLinus Torvalds 	unsigned char dev_spec;
132923183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
13301da177e4SLinus Torvalds 	unsigned char * ap;
13311da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
13321da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
13331da177e4SLinus Torvalds 
1334c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
13351da177e4SLinus Torvalds 		return errsts;
133623183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
13371da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
13381da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
13391da177e4SLinus Torvalds 	subpcode = cmd[3];
13401da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
134123183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
134223183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
134323183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
134423183910SDouglas Gilbert 	else
134523183910SDouglas Gilbert 		bd_len = 0;
13461da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
13471da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
13481da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
13491da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
13501da177e4SLinus Torvalds 			       	0);
13511da177e4SLinus Torvalds 		return check_condition_result;
13521da177e4SLinus Torvalds 	}
1353c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1354c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
135523183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
135623183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
135723183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
135823183910SDouglas Gilbert 	else
135923183910SDouglas Gilbert 		dev_spec = 0x0;
13601da177e4SLinus Torvalds 	if (msense_6) {
13611da177e4SLinus Torvalds 		arr[2] = dev_spec;
136223183910SDouglas Gilbert 		arr[3] = bd_len;
13631da177e4SLinus Torvalds 		offset = 4;
13641da177e4SLinus Torvalds 	} else {
13651da177e4SLinus Torvalds 		arr[3] = dev_spec;
136623183910SDouglas Gilbert 		if (16 == bd_len)
136723183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
136823183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
13691da177e4SLinus Torvalds 		offset = 8;
13701da177e4SLinus Torvalds 	}
13711da177e4SLinus Torvalds 	ap = arr + offset;
137223183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
137323183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
137423183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
137523183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
137623183910SDouglas Gilbert 		} else
137723183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
137823183910SDouglas Gilbert 	}
137923183910SDouglas Gilbert 	if (8 == bd_len) {
138023183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
138123183910SDouglas Gilbert 			ap[0] = 0xff;
138223183910SDouglas Gilbert 			ap[1] = 0xff;
138323183910SDouglas Gilbert 			ap[2] = 0xff;
138423183910SDouglas Gilbert 			ap[3] = 0xff;
138523183910SDouglas Gilbert 		} else {
138623183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
138723183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
138823183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
138923183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
139023183910SDouglas Gilbert 		}
139123183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
139223183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
139323183910SDouglas Gilbert 		offset += bd_len;
139423183910SDouglas Gilbert 		ap = arr + offset;
139523183910SDouglas Gilbert 	} else if (16 == bd_len) {
139623183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
139723183910SDouglas Gilbert 
139823183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
139923183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
140023183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
140123183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
140223183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
140323183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
140423183910SDouglas Gilbert 		offset += bd_len;
140523183910SDouglas Gilbert 		ap = arr + offset;
140623183910SDouglas Gilbert 	}
14071da177e4SLinus Torvalds 
1408c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1409c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
14101da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14111da177e4SLinus Torvalds 			       	0);
14121da177e4SLinus Torvalds 		return check_condition_result;
14131da177e4SLinus Torvalds 	}
14141da177e4SLinus Torvalds 	switch (pcode) {
14151da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
14161da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
14171da177e4SLinus Torvalds 		offset += len;
14181da177e4SLinus Torvalds 		break;
14191da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
14201da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
14211da177e4SLinus Torvalds 		offset += len;
14221da177e4SLinus Torvalds 		break;
14231da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
14241da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
14251da177e4SLinus Torvalds                 offset += len;
14261da177e4SLinus Torvalds                 break;
14271da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
14281da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
14291da177e4SLinus Torvalds 		offset += len;
14301da177e4SLinus Torvalds 		break;
14311da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
14321da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
14331da177e4SLinus Torvalds 		offset += len;
14341da177e4SLinus Torvalds 		break;
1435c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1436c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1437c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1438c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1439c65b1445SDouglas Gilbert 			return check_condition_result;
1440c65b1445SDouglas Gilbert 	        }
1441c65b1445SDouglas Gilbert 		len = 0;
1442c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1443c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1444c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1445c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1446c65b1445SDouglas Gilbert 						  target_dev_id);
1447c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1448c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1449c65b1445SDouglas Gilbert 		offset += len;
1450c65b1445SDouglas Gilbert 		break;
14511da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
14521da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
14531da177e4SLinus Torvalds 		offset += len;
14541da177e4SLinus Torvalds 		break;
14551da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1456c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
14571da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
14581da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
14591da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
14601da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
14611da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1462c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1463c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1464c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1465c65b1445SDouglas Gilbert 						  target, target_dev_id);
1466c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1467c65b1445SDouglas Gilbert 			}
14681da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1469c65b1445SDouglas Gilbert 		} else {
1470c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1471c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1472c65b1445SDouglas Gilbert 			return check_condition_result;
1473c65b1445SDouglas Gilbert                 }
14741da177e4SLinus Torvalds 		offset += len;
14751da177e4SLinus Torvalds 		break;
14761da177e4SLinus Torvalds 	default:
14771da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14781da177e4SLinus Torvalds 			       	0);
14791da177e4SLinus Torvalds 		return check_condition_result;
14801da177e4SLinus Torvalds 	}
14811da177e4SLinus Torvalds 	if (msense_6)
14821da177e4SLinus Torvalds 		arr[0] = offset - 1;
14831da177e4SLinus Torvalds 	else {
14841da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
14851da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
14861da177e4SLinus Torvalds 	}
14871da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
14881da177e4SLinus Torvalds }
14891da177e4SLinus Torvalds 
1490c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1491c65b1445SDouglas Gilbert 
1492c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1493c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1494c65b1445SDouglas Gilbert {
1495c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1496c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1497c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1498c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1499c65b1445SDouglas Gilbert 
1500c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1501c65b1445SDouglas Gilbert 		return errsts;
1502c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1503c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1504c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1505c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1506c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1507c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1508c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1509c65b1445SDouglas Gilbert 		return check_condition_result;
1510c65b1445SDouglas Gilbert 	}
1511c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1512c65b1445SDouglas Gilbert         if (-1 == res)
1513c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1514c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1515c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1516c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1517c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1518c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1519c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
152023183910SDouglas Gilbert 	if (md_len > 2) {
1521c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1522c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1523c65b1445SDouglas Gilbert 		return check_condition_result;
1524c65b1445SDouglas Gilbert 	}
1525c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1526c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1527c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1528c65b1445SDouglas Gilbert 	if (ps) {
1529c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1530c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1531c65b1445SDouglas Gilbert 		return check_condition_result;
1532c65b1445SDouglas Gilbert 	}
1533c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1534c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1535c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1536c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1537c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1538c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1539c65b1445SDouglas Gilbert 		return check_condition_result;
1540c65b1445SDouglas Gilbert 	}
1541c65b1445SDouglas Gilbert 	switch (mpage) {
1542c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1543c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1544c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1545c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1546c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1547c65b1445SDouglas Gilbert 			return 0;
1548c65b1445SDouglas Gilbert 		}
1549c65b1445SDouglas Gilbert 		break;
1550c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1551c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1552c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1553c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1554c65b1445SDouglas Gilbert 			return 0;
1555c65b1445SDouglas Gilbert 		}
1556c65b1445SDouglas Gilbert 		break;
1557c65b1445SDouglas Gilbert 	default:
1558c65b1445SDouglas Gilbert 		break;
1559c65b1445SDouglas Gilbert 	}
1560c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1561c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1562c65b1445SDouglas Gilbert 	return check_condition_result;
1563c65b1445SDouglas Gilbert }
1564c65b1445SDouglas Gilbert 
1565c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1566c65b1445SDouglas Gilbert {
1567c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1568c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1569c65b1445SDouglas Gilbert 		};
1570c65b1445SDouglas Gilbert 
1571c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1572c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1573c65b1445SDouglas Gilbert }
1574c65b1445SDouglas Gilbert 
1575c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1576c65b1445SDouglas Gilbert {
1577c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1578c65b1445SDouglas Gilbert 		};
1579c65b1445SDouglas Gilbert 
1580c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1581c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1582c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1583c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1584c65b1445SDouglas Gilbert 	}
1585c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1586c65b1445SDouglas Gilbert }
1587c65b1445SDouglas Gilbert 
1588c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1589c65b1445SDouglas Gilbert 
1590c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1591c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1592c65b1445SDouglas Gilbert {
159323183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1594c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1595c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1596c65b1445SDouglas Gilbert 
1597c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1598c65b1445SDouglas Gilbert 		return errsts;
1599c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1600c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1601c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1602c65b1445SDouglas Gilbert 	if (ppc || sp) {
1603c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1604c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1605c65b1445SDouglas Gilbert 		return check_condition_result;
1606c65b1445SDouglas Gilbert 	}
1607c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1608c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
160923183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1610c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1611c65b1445SDouglas Gilbert 	arr[0] = pcode;
161223183910SDouglas Gilbert 	if (0 == subpcode) {
1613c65b1445SDouglas Gilbert 		switch (pcode) {
1614c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1615c65b1445SDouglas Gilbert 			n = 4;
1616c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1617c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1618c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1619c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1620c65b1445SDouglas Gilbert 			break;
1621c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1622c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1623c65b1445SDouglas Gilbert 			break;
1624c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1625c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1626c65b1445SDouglas Gilbert 			break;
1627c65b1445SDouglas Gilbert 		default:
1628c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1629c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1630c65b1445SDouglas Gilbert 			return check_condition_result;
1631c65b1445SDouglas Gilbert 		}
163223183910SDouglas Gilbert 	} else if (0xff == subpcode) {
163323183910SDouglas Gilbert 		arr[0] |= 0x40;
163423183910SDouglas Gilbert 		arr[1] = subpcode;
163523183910SDouglas Gilbert 		switch (pcode) {
163623183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
163723183910SDouglas Gilbert 			n = 4;
163823183910SDouglas Gilbert 			arr[n++] = 0x0;
163923183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
164023183910SDouglas Gilbert 			arr[n++] = 0x0;
164123183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
164223183910SDouglas Gilbert 			arr[n++] = 0xd;
164323183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
164423183910SDouglas Gilbert 			arr[n++] = 0x2f;
164523183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
164623183910SDouglas Gilbert 			arr[3] = n - 4;
164723183910SDouglas Gilbert 			break;
164823183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
164923183910SDouglas Gilbert 			n = 4;
165023183910SDouglas Gilbert 			arr[n++] = 0xd;
165123183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
165223183910SDouglas Gilbert 			arr[3] = n - 4;
165323183910SDouglas Gilbert 			break;
165423183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
165523183910SDouglas Gilbert 			n = 4;
165623183910SDouglas Gilbert 			arr[n++] = 0x2f;
165723183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
165823183910SDouglas Gilbert 			arr[3] = n - 4;
165923183910SDouglas Gilbert 			break;
166023183910SDouglas Gilbert 		default:
166123183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
166223183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
166323183910SDouglas Gilbert 			return check_condition_result;
166423183910SDouglas Gilbert 		}
166523183910SDouglas Gilbert 	} else {
166623183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
166723183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
166823183910SDouglas Gilbert 		return check_condition_result;
166923183910SDouglas Gilbert 	}
1670c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1671c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1672c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1673c65b1445SDouglas Gilbert }
1674c65b1445SDouglas Gilbert 
1675c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1676c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
16771da177e4SLinus Torvalds {
16781da177e4SLinus Torvalds 	unsigned long iflags;
1679c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1680c65b1445SDouglas Gilbert 	unsigned long long u;
16811da177e4SLinus Torvalds 	int ret;
16821da177e4SLinus Torvalds 
1683c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
16841da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
16851da177e4SLinus Torvalds 				0);
16861da177e4SLinus Torvalds 		return check_condition_result;
16871da177e4SLinus Torvalds 	}
1688c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1689c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1690c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1691c65b1445SDouglas Gilbert 				0);
1692c65b1445SDouglas Gilbert 		return check_condition_result;
1693c65b1445SDouglas Gilbert 	}
16941da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1695c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1696c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1697c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
16981da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
16991da177e4SLinus Torvalds 				0);
1700c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1701c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1702c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1703c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1704c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1705c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1706c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1707c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1708c65b1445SDouglas Gilbert 		}
17091da177e4SLinus Torvalds 		return check_condition_result;
17101da177e4SLinus Torvalds 	}
17111da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1712c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1713c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1714c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
17151da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1716c65b1445SDouglas Gilbert 	else {
1717c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1718c65b1445SDouglas Gilbert 		u = lba;
1719c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1720c65b1445SDouglas Gilbert 		from_bottom = 0;
1721c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1722c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1723c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1724c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1725c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1726c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1727c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1728c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1729c65b1445SDouglas Gilbert 	}
17301da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
17311da177e4SLinus Torvalds 	return ret;
17321da177e4SLinus Torvalds }
17331da177e4SLinus Torvalds 
1734c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1735c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
17361da177e4SLinus Torvalds {
17371da177e4SLinus Torvalds 	unsigned long iflags;
1738c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1739c65b1445SDouglas Gilbert 	unsigned long long u;
17401da177e4SLinus Torvalds 	int res;
17411da177e4SLinus Torvalds 
1742c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
17431da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
17441da177e4SLinus Torvalds 			       	0);
17451da177e4SLinus Torvalds 		return check_condition_result;
17461da177e4SLinus Torvalds 	}
1747c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1748c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1749c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1750c65b1445SDouglas Gilbert 				0);
1751c65b1445SDouglas Gilbert 		return check_condition_result;
1752c65b1445SDouglas Gilbert 	}
17531da177e4SLinus Torvalds 
17541da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1755c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1756c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1757c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
17581da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1759c65b1445SDouglas Gilbert 	else {
1760c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1761c65b1445SDouglas Gilbert 		u = lba;
1762c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1763c65b1445SDouglas Gilbert 		to_bottom = 0;
1764c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1765c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1766c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1767c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1768c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1769c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1770c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1771c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1772c65b1445SDouglas Gilbert 	}
17731da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
17741da177e4SLinus Torvalds 	if (-1 == res)
17751da177e4SLinus Torvalds 		return (DID_ERROR << 16);
17761da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
17771da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1778c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
17791da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
17801da177e4SLinus Torvalds 	return 0;
17811da177e4SLinus Torvalds }
17821da177e4SLinus Torvalds 
1783c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
17841da177e4SLinus Torvalds 
17851da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
17861da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
17871da177e4SLinus Torvalds {
17881da177e4SLinus Torvalds 	unsigned int alloc_len;
1789c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
17901da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
17911da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
17921da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
17931da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1794c65b1445SDouglas Gilbert 	unsigned char * max_addr;
17951da177e4SLinus Torvalds 
17961da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1797c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
17981da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
17991da177e4SLinus Torvalds 			       	0);
18001da177e4SLinus Torvalds 		return check_condition_result;
18011da177e4SLinus Torvalds 	}
18021da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
18031da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
18041da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1805c65b1445SDouglas Gilbert 	if (1 == select_report)
1806c65b1445SDouglas Gilbert 		lun_cnt = 0;
1807c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1808c65b1445SDouglas Gilbert 		--lun_cnt;
1809c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1810c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1811c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1812c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1813c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1814c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1815c65b1445SDouglas Gilbert 	if (n < num) {
1816c65b1445SDouglas Gilbert 		wlun = 0;
1817c65b1445SDouglas Gilbert 		lun_cnt = n;
1818c65b1445SDouglas Gilbert 	}
18191da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1820c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1821c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1822c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1823c65b1445SDouglas Gilbert 	     i++, lun++) {
1824c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
18251da177e4SLinus Torvalds 		if (upper)
18261da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
18271da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1828c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
18291da177e4SLinus Torvalds 	}
1830c65b1445SDouglas Gilbert 	if (wlun) {
1831c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1832c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1833c65b1445SDouglas Gilbert 		i++;
1834c65b1445SDouglas Gilbert 	}
1835c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
18361da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
18371da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
18381da177e4SLinus Torvalds }
18391da177e4SLinus Torvalds 
18401da177e4SLinus Torvalds /* When timer goes off this function is called. */
18411da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
18421da177e4SLinus Torvalds {
18431da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
18441da177e4SLinus Torvalds 	unsigned long iflags;
18451da177e4SLinus Torvalds 
18461da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
18471da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
18481da177e4SLinus Torvalds 		       "large\n");
18491da177e4SLinus Torvalds 		return;
18501da177e4SLinus Torvalds 	}
18511da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
18521da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
18531da177e4SLinus Torvalds 	if (! sqcp->in_use) {
18541da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
18551da177e4SLinus Torvalds 		       "interrupt\n");
18561da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
18571da177e4SLinus Torvalds 		return;
18581da177e4SLinus Torvalds 	}
18591da177e4SLinus Torvalds 	sqcp->in_use = 0;
18601da177e4SLinus Torvalds 	if (sqcp->done_funct) {
18611da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
18621da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
18631da177e4SLinus Torvalds 	}
18641da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
18651da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
18661da177e4SLinus Torvalds }
18671da177e4SLinus Torvalds 
18681da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
18691da177e4SLinus Torvalds {
18701da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1871c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
1872c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
18731da177e4SLinus Torvalds 	return 0;
18741da177e4SLinus Torvalds }
18751da177e4SLinus Torvalds 
18761da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
18771da177e4SLinus Torvalds {
18781da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
18791da177e4SLinus Torvalds 
18801da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1881c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1882c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
18831da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
18841da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
18851da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
18861da177e4SLinus Torvalds 	sdp->hostdata = devip;
18871da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
18881da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
18891da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
1890c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
18911da177e4SLinus Torvalds 	return 0;
18921da177e4SLinus Torvalds }
18931da177e4SLinus Torvalds 
18941da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
18951da177e4SLinus Torvalds {
18961da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
18971da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
18981da177e4SLinus Torvalds 
18991da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1900c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
1901c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19021da177e4SLinus Torvalds 	if (devip) {
19031da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
19041da177e4SLinus Torvalds 		devip->used = 0;
19051da177e4SLinus Torvalds 		sdp->hostdata = NULL;
19061da177e4SLinus Torvalds 	}
19071da177e4SLinus Torvalds }
19081da177e4SLinus Torvalds 
19091da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
19101da177e4SLinus Torvalds {
19111da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
19121da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
19131da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
19141da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
19151da177e4SLinus Torvalds 
19161da177e4SLinus Torvalds 	if (devip)
19171da177e4SLinus Torvalds 		return devip;
19181da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
19191da177e4SLinus Torvalds         if(! sdbg_host) {
19201da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
19211da177e4SLinus Torvalds 		return NULL;
19221da177e4SLinus Torvalds         }
19231da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
19241da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
19251da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
19261da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
19271da177e4SLinus Torvalds                         return devip;
19281da177e4SLinus Torvalds 		else {
19291da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
19301da177e4SLinus Torvalds 				open_devip = devip;
19311da177e4SLinus Torvalds 		}
19321da177e4SLinus Torvalds 	}
19331da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
193424669f75SJes Sorensen 		open_devip = kzalloc(sizeof(*open_devip),GFP_KERNEL);
19351da177e4SLinus Torvalds 		if (NULL == open_devip) {
19361da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
19371da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
19381da177e4SLinus Torvalds 			return NULL;
19391da177e4SLinus Torvalds 		}
19401da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
19411da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
19421da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
19431da177e4SLinus Torvalds 	}
19441da177e4SLinus Torvalds         if (open_devip) {
19451da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
19461da177e4SLinus Torvalds 		open_devip->target = sdev->id;
19471da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
19481da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
19491da177e4SLinus Torvalds 		open_devip->reset = 1;
19501da177e4SLinus Torvalds 		open_devip->used = 1;
19511da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
19521da177e4SLinus Torvalds 		if (scsi_debug_dsense)
19531da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
19541da177e4SLinus Torvalds 		else {
19551da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
19561da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
19571da177e4SLinus Torvalds 		}
1958c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
1959c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
19601da177e4SLinus Torvalds 		return open_devip;
19611da177e4SLinus Torvalds         }
19621da177e4SLinus Torvalds         return NULL;
19631da177e4SLinus Torvalds }
19641da177e4SLinus Torvalds 
19651da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
19661da177e4SLinus Torvalds 			    int asc, int asq)
19671da177e4SLinus Torvalds {
19681da177e4SLinus Torvalds 	unsigned char * sbuff;
19691da177e4SLinus Torvalds 
19701da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
19711da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
19721da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
19731da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
19741da177e4SLinus Torvalds 		sbuff[1] = key;
19751da177e4SLinus Torvalds 		sbuff[2] = asc;
19761da177e4SLinus Torvalds 		sbuff[3] = asq;
19771da177e4SLinus Torvalds 	} else {
19781da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
19791da177e4SLinus Torvalds 		sbuff[2] = key;
19801da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
19811da177e4SLinus Torvalds 		sbuff[12] = asc;
19821da177e4SLinus Torvalds 		sbuff[13] = asq;
19831da177e4SLinus Torvalds 	}
19841da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19851da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
19861da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
19871da177e4SLinus Torvalds }
19881da177e4SLinus Torvalds 
19891da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
19901da177e4SLinus Torvalds {
19911da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19921da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
19931da177e4SLinus Torvalds 	++num_aborts;
19941da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
19951da177e4SLinus Torvalds 	return SUCCESS;
19961da177e4SLinus Torvalds }
19971da177e4SLinus Torvalds 
19981da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
19991da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
20001da177e4SLinus Torvalds {
20011da177e4SLinus Torvalds 	int res;
20021da177e4SLinus Torvalds 	unsigned char *buf;
20031da177e4SLinus Torvalds 
20041da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20051da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
20061da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
20071da177e4SLinus Torvalds 	if (buf) {
20081da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
20091da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
20101da177e4SLinus Torvalds 		kfree(buf);
20111da177e4SLinus Torvalds 		if (! res)
20121da177e4SLinus Torvalds 			return res;
20131da177e4SLinus Torvalds 	}
20141da177e4SLinus Torvalds 	info[0] = sdebug_heads;
20151da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
20161da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
20171da177e4SLinus Torvalds 	return 0;
20181da177e4SLinus Torvalds }
20191da177e4SLinus Torvalds 
20201da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
20211da177e4SLinus Torvalds {
20221da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20251da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
20261da177e4SLinus Torvalds 	++num_dev_resets;
20271da177e4SLinus Torvalds 	if (SCpnt) {
20281da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
20291da177e4SLinus Torvalds 		if (devip)
20301da177e4SLinus Torvalds 			devip->reset = 1;
20311da177e4SLinus Torvalds 	}
20321da177e4SLinus Torvalds 	return SUCCESS;
20331da177e4SLinus Torvalds }
20341da177e4SLinus Torvalds 
20351da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
20361da177e4SLinus Torvalds {
20371da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
20381da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
20391da177e4SLinus Torvalds         struct scsi_device * sdp;
20401da177e4SLinus Torvalds         struct Scsi_Host * hp;
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20431da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
20441da177e4SLinus Torvalds 	++num_bus_resets;
20451da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
20461da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
20471da177e4SLinus Torvalds 		if (sdbg_host) {
20481da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
20491da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
20501da177e4SLinus Torvalds                                             dev_list)
20511da177e4SLinus Torvalds 				dev_info->reset = 1;
20521da177e4SLinus Torvalds 		}
20531da177e4SLinus Torvalds 	}
20541da177e4SLinus Torvalds 	return SUCCESS;
20551da177e4SLinus Torvalds }
20561da177e4SLinus Torvalds 
20571da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
20581da177e4SLinus Torvalds {
20591da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20601da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
20611da177e4SLinus Torvalds 
20621da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20631da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
20641da177e4SLinus Torvalds 	++num_host_resets;
20651da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
20661da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
20671da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
20681da177e4SLinus Torvalds                                     dev_list)
20691da177e4SLinus Torvalds                         dev_info->reset = 1;
20701da177e4SLinus Torvalds         }
20711da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
20721da177e4SLinus Torvalds 	stop_all_queued();
20731da177e4SLinus Torvalds 	return SUCCESS;
20741da177e4SLinus Torvalds }
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
20771da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
20781da177e4SLinus Torvalds {
20791da177e4SLinus Torvalds 	unsigned long iflags;
20801da177e4SLinus Torvalds 	int k;
20811da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
20821da177e4SLinus Torvalds 
20831da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
20841da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
20851da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
20861da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
20871da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
20881da177e4SLinus Torvalds 			sqcp->in_use = 0;
20891da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
20901da177e4SLinus Torvalds 			break;
20911da177e4SLinus Torvalds 		}
20921da177e4SLinus Torvalds 	}
20931da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20941da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
20951da177e4SLinus Torvalds }
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
20981da177e4SLinus Torvalds static void stop_all_queued(void)
20991da177e4SLinus Torvalds {
21001da177e4SLinus Torvalds 	unsigned long iflags;
21011da177e4SLinus Torvalds 	int k;
21021da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
21031da177e4SLinus Torvalds 
21041da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
21051da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21061da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
21071da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
21081da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
21091da177e4SLinus Torvalds 			sqcp->in_use = 0;
21101da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
21111da177e4SLinus Torvalds 		}
21121da177e4SLinus Torvalds 	}
21131da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
21141da177e4SLinus Torvalds }
21151da177e4SLinus Torvalds 
21161da177e4SLinus Torvalds /* Initializes timers in queued array */
21171da177e4SLinus Torvalds static void __init init_all_queued(void)
21181da177e4SLinus Torvalds {
21191da177e4SLinus Torvalds 	unsigned long iflags;
21201da177e4SLinus Torvalds 	int k;
21211da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
21221da177e4SLinus Torvalds 
21231da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
21241da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21251da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
21261da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
21271da177e4SLinus Torvalds 		sqcp->in_use = 0;
21281da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
21291da177e4SLinus Torvalds 	}
21301da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
21311da177e4SLinus Torvalds }
21321da177e4SLinus Torvalds 
21331da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
21341da177e4SLinus Torvalds {
21351da177e4SLinus Torvalds 	struct partition * pp;
21361da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
21371da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
21381da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
21391da177e4SLinus Torvalds 
21401da177e4SLinus Torvalds 	/* assume partition table already zeroed */
21411da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
21421da177e4SLinus Torvalds 		return;
21431da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
21441da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
21451da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
21461da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
21471da177e4SLinus Torvalds 	}
2148c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
21491da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
21501da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
21511da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
21521da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
21531da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
21541da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
21551da177e4SLinus Torvalds 			    * heads_by_sects;
21561da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
21571da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
21581da177e4SLinus Torvalds 
21591da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
21601da177e4SLinus Torvalds 	ramp[511] = 0xAA;
21611da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
21621da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
21631da177e4SLinus Torvalds 		start_sec = starts[k];
21641da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
21651da177e4SLinus Torvalds 		pp->boot_ind = 0;
21661da177e4SLinus Torvalds 
21671da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
21681da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
21691da177e4SLinus Torvalds 			   / sdebug_sectors_per;
21701da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
21711da177e4SLinus Torvalds 
21721da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
21731da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
21741da177e4SLinus Torvalds 			       / sdebug_sectors_per;
21751da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
21761da177e4SLinus Torvalds 
21771da177e4SLinus Torvalds 		pp->start_sect = start_sec;
21781da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
21791da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
21801da177e4SLinus Torvalds 	}
21811da177e4SLinus Torvalds }
21821da177e4SLinus Torvalds 
21831da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
21841da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
21851da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
21861da177e4SLinus Torvalds {
21871da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
21881da177e4SLinus Torvalds 		if (scsi_result) {
21891da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
21901da177e4SLinus Torvalds 
2191c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2192c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2193c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
21941da177e4SLinus Torvalds 		}
21951da177e4SLinus Torvalds 	}
21961da177e4SLinus Torvalds 	if (cmnd && devip) {
21971da177e4SLinus Torvalds 		/* simulate autosense by this driver */
21981da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
21991da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
22001da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
22011da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
22021da177e4SLinus Torvalds 	}
22031da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
22041da177e4SLinus Torvalds 		if (cmnd)
22051da177e4SLinus Torvalds 			cmnd->result = scsi_result;
22061da177e4SLinus Torvalds 		if (done)
22071da177e4SLinus Torvalds 			done(cmnd);
22081da177e4SLinus Torvalds 		return 0;
22091da177e4SLinus Torvalds 	} else {
22101da177e4SLinus Torvalds 		unsigned long iflags;
22111da177e4SLinus Torvalds 		int k;
22121da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
22131da177e4SLinus Torvalds 
22141da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
22151da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22161da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
22171da177e4SLinus Torvalds 			if (! sqcp->in_use)
22181da177e4SLinus Torvalds 				break;
22191da177e4SLinus Torvalds 		}
22201da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
22211da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
22221da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
22231da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
22241da177e4SLinus Torvalds 		}
22251da177e4SLinus Torvalds 		sqcp->in_use = 1;
22261da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
22271da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
22281da177e4SLinus Torvalds 		sqcp->done_funct = done;
22291da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
22301da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
22311da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
22321da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
22331da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
22341da177e4SLinus Torvalds 		if (cmnd)
22351da177e4SLinus Torvalds 			cmnd->result = 0;
22361da177e4SLinus Torvalds 		return 0;
22371da177e4SLinus Torvalds 	}
22381da177e4SLinus Torvalds }
22391da177e4SLinus Torvalds 
224023183910SDouglas Gilbert /* Note: The following macros create attribute files in the
224123183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
224223183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
224323183910SDouglas Gilbert    as it can when the corresponding attribute in the
224423183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
224523183910SDouglas Gilbert  */
2246c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2247c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2248c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2249c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2250c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
225123183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2252c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2253c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2254c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2255c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2256c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2257c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2258c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2259c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
226023183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
226123183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
22621da177e4SLinus Torvalds 
22631da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
22641da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
22651da177e4SLinus Torvalds MODULE_LICENSE("GPL");
22661da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
22671da177e4SLinus Torvalds 
22681da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
22691da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2270c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2271c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
22721da177e4SLinus Torvalds MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
227323183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2274c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2275c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
22761da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2277c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
2278c65b1445SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->... (def=0)");
22791da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
22801da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2281c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
228223183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
22831da177e4SLinus Torvalds 
22841da177e4SLinus Torvalds 
22851da177e4SLinus Torvalds static char sdebug_info[256];
22861da177e4SLinus Torvalds 
22871da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
22881da177e4SLinus Torvalds {
22891da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
22901da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
22911da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
22921da177e4SLinus Torvalds 		scsi_debug_opts);
22931da177e4SLinus Torvalds 	return sdebug_info;
22941da177e4SLinus Torvalds }
22951da177e4SLinus Torvalds 
22961da177e4SLinus Torvalds /* scsi_debug_proc_info
22971da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
22981da177e4SLinus Torvalds  */
22991da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
23001da177e4SLinus Torvalds 				int length, int inout)
23011da177e4SLinus Torvalds {
23021da177e4SLinus Torvalds 	int len, pos, begin;
23031da177e4SLinus Torvalds 	int orig_length;
23041da177e4SLinus Torvalds 
23051da177e4SLinus Torvalds 	orig_length = length;
23061da177e4SLinus Torvalds 
23071da177e4SLinus Torvalds 	if (inout == 1) {
23081da177e4SLinus Torvalds 		char arr[16];
23091da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
23101da177e4SLinus Torvalds 
23111da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
23121da177e4SLinus Torvalds 			return -EACCES;
23131da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
23141da177e4SLinus Torvalds 		arr[minLen] = '\0';
23151da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
23161da177e4SLinus Torvalds 			return -EINVAL;
23171da177e4SLinus Torvalds 		scsi_debug_opts = pos;
23181da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
23191da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
23201da177e4SLinus Torvalds 		return length;
23211da177e4SLinus Torvalds 	}
23221da177e4SLinus Torvalds 	begin = 0;
23231da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
23241da177e4SLinus Torvalds 	    "%s [%s]\n"
23251da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
23261da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
23271da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
23281da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
23291da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
23301da177e4SLinus Torvalds 	    "host_resets=%d\n",
23311da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
23321da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
23331da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
23341da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
23351da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
23361da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
23371da177e4SLinus Torvalds 	if (pos < offset) {
23381da177e4SLinus Torvalds 		len = 0;
23391da177e4SLinus Torvalds 		begin = pos;
23401da177e4SLinus Torvalds 	}
23411da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
23421da177e4SLinus Torvalds 	len -= (offset - begin);
23431da177e4SLinus Torvalds 	if (len > length)
23441da177e4SLinus Torvalds 		len = length;
23451da177e4SLinus Torvalds 	return len;
23461da177e4SLinus Torvalds }
23471da177e4SLinus Torvalds 
23481da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
23491da177e4SLinus Torvalds {
23501da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
23511da177e4SLinus Torvalds }
23521da177e4SLinus Torvalds 
23531da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
23541da177e4SLinus Torvalds 				  const char * buf, size_t count)
23551da177e4SLinus Torvalds {
23561da177e4SLinus Torvalds         int delay;
23571da177e4SLinus Torvalds 	char work[20];
23581da177e4SLinus Torvalds 
23591da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
23601da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
23611da177e4SLinus Torvalds 			scsi_debug_delay = delay;
23621da177e4SLinus Torvalds 			return count;
23631da177e4SLinus Torvalds 		}
23641da177e4SLinus Torvalds 	}
23651da177e4SLinus Torvalds 	return -EINVAL;
23661da177e4SLinus Torvalds }
23671da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
23681da177e4SLinus Torvalds 	    sdebug_delay_store);
23691da177e4SLinus Torvalds 
23701da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
23711da177e4SLinus Torvalds {
23721da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
23731da177e4SLinus Torvalds }
23741da177e4SLinus Torvalds 
23751da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
23761da177e4SLinus Torvalds 				 const char * buf, size_t count)
23771da177e4SLinus Torvalds {
23781da177e4SLinus Torvalds         int opts;
23791da177e4SLinus Torvalds 	char work[20];
23801da177e4SLinus Torvalds 
23811da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
23821da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
23831da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
23841da177e4SLinus Torvalds 				goto opts_done;
23851da177e4SLinus Torvalds 		} else {
23861da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
23871da177e4SLinus Torvalds 				goto opts_done;
23881da177e4SLinus Torvalds 		}
23891da177e4SLinus Torvalds 	}
23901da177e4SLinus Torvalds 	return -EINVAL;
23911da177e4SLinus Torvalds opts_done:
23921da177e4SLinus Torvalds 	scsi_debug_opts = opts;
23931da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
23941da177e4SLinus Torvalds 	return count;
23951da177e4SLinus Torvalds }
23961da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
23971da177e4SLinus Torvalds 	    sdebug_opts_store);
23981da177e4SLinus Torvalds 
23991da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
24001da177e4SLinus Torvalds {
24011da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
24021da177e4SLinus Torvalds }
24031da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
24041da177e4SLinus Torvalds 				  const char * buf, size_t count)
24051da177e4SLinus Torvalds {
24061da177e4SLinus Torvalds         int n;
24071da177e4SLinus Torvalds 
24081da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
24091da177e4SLinus Torvalds 		scsi_debug_ptype = n;
24101da177e4SLinus Torvalds 		return count;
24111da177e4SLinus Torvalds 	}
24121da177e4SLinus Torvalds 	return -EINVAL;
24131da177e4SLinus Torvalds }
24141da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
24151da177e4SLinus Torvalds 
24161da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
24171da177e4SLinus Torvalds {
24181da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
24191da177e4SLinus Torvalds }
24201da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
24211da177e4SLinus Torvalds 				  const char * buf, size_t count)
24221da177e4SLinus Torvalds {
24231da177e4SLinus Torvalds         int n;
24241da177e4SLinus Torvalds 
24251da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
24261da177e4SLinus Torvalds 		scsi_debug_dsense = n;
24271da177e4SLinus Torvalds 		return count;
24281da177e4SLinus Torvalds 	}
24291da177e4SLinus Torvalds 	return -EINVAL;
24301da177e4SLinus Torvalds }
24311da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
24321da177e4SLinus Torvalds 	    sdebug_dsense_store);
24331da177e4SLinus Torvalds 
243423183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
243523183910SDouglas Gilbert {
243623183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
243723183910SDouglas Gilbert }
243823183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
243923183910SDouglas Gilbert 				    const char * buf, size_t count)
244023183910SDouglas Gilbert {
244123183910SDouglas Gilbert         int n;
244223183910SDouglas Gilbert 
244323183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
244423183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
244523183910SDouglas Gilbert 		return count;
244623183910SDouglas Gilbert 	}
244723183910SDouglas Gilbert 	return -EINVAL;
244823183910SDouglas Gilbert }
244923183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
245023183910SDouglas Gilbert 	    sdebug_fake_rw_store);
245123183910SDouglas Gilbert 
2452c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2453c65b1445SDouglas Gilbert {
2454c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2455c65b1445SDouglas Gilbert }
2456c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2457c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2458c65b1445SDouglas Gilbert {
2459c65b1445SDouglas Gilbert         int n;
2460c65b1445SDouglas Gilbert 
2461c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2462c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2463c65b1445SDouglas Gilbert 		return count;
2464c65b1445SDouglas Gilbert 	}
2465c65b1445SDouglas Gilbert 	return -EINVAL;
2466c65b1445SDouglas Gilbert }
2467c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2468c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2469c65b1445SDouglas Gilbert 
24701da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
24711da177e4SLinus Torvalds {
24721da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
24731da177e4SLinus Torvalds }
24741da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
24751da177e4SLinus Torvalds 				     const char * buf, size_t count)
24761da177e4SLinus Torvalds {
24771da177e4SLinus Torvalds         int n;
24781da177e4SLinus Torvalds 
24791da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
24801da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
24811da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
24821da177e4SLinus Torvalds 		return count;
24831da177e4SLinus Torvalds 	}
24841da177e4SLinus Torvalds 	return -EINVAL;
24851da177e4SLinus Torvalds }
24861da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
24871da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
24881da177e4SLinus Torvalds 
24891da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
24901da177e4SLinus Torvalds {
24911da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
24921da177e4SLinus Torvalds }
24931da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
24941da177e4SLinus Torvalds 
24951da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
24961da177e4SLinus Torvalds {
24971da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
24981da177e4SLinus Torvalds }
24991da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
25021da177e4SLinus Torvalds {
25031da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
25041da177e4SLinus Torvalds }
25051da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
25061da177e4SLinus Torvalds 				      const char * buf, size_t count)
25071da177e4SLinus Torvalds {
25081da177e4SLinus Torvalds         int nth;
25091da177e4SLinus Torvalds 
25101da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
25111da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
25121da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
25131da177e4SLinus Torvalds 		return count;
25141da177e4SLinus Torvalds 	}
25151da177e4SLinus Torvalds 	return -EINVAL;
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
25181da177e4SLinus Torvalds 	    sdebug_every_nth_store);
25191da177e4SLinus Torvalds 
25201da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
25211da177e4SLinus Torvalds {
25221da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
25231da177e4SLinus Torvalds }
25241da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
25251da177e4SLinus Torvalds 				     const char * buf, size_t count)
25261da177e4SLinus Torvalds {
25271da177e4SLinus Torvalds         int n;
25281da177e4SLinus Torvalds 
25291da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25301da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
25311da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
25321da177e4SLinus Torvalds 		return count;
25331da177e4SLinus Torvalds 	}
25341da177e4SLinus Torvalds 	return -EINVAL;
25351da177e4SLinus Torvalds }
25361da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
25371da177e4SLinus Torvalds 	    sdebug_max_luns_store);
25381da177e4SLinus Torvalds 
25391da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
25401da177e4SLinus Torvalds {
25411da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
25421da177e4SLinus Torvalds }
25431da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
25441da177e4SLinus Torvalds 
2545c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2546c65b1445SDouglas Gilbert {
2547c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2548c65b1445SDouglas Gilbert }
2549c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2550c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2551c65b1445SDouglas Gilbert {
2552c65b1445SDouglas Gilbert         int n;
2553c65b1445SDouglas Gilbert 
2554c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2555c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2556c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2557c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2558c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2559c65b1445SDouglas Gilbert 		} else
2560c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2561c65b1445SDouglas Gilbert 		return count;
2562c65b1445SDouglas Gilbert 	}
2563c65b1445SDouglas Gilbert 	return -EINVAL;
2564c65b1445SDouglas Gilbert }
2565c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2566c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2567c65b1445SDouglas Gilbert 
25681da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
25691da177e4SLinus Torvalds {
25701da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
25711da177e4SLinus Torvalds }
25721da177e4SLinus Torvalds 
25731da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
25741da177e4SLinus Torvalds 				     const char * buf, size_t count)
25751da177e4SLinus Torvalds {
25761da177e4SLinus Torvalds         int delta_hosts;
25771da177e4SLinus Torvalds 	char work[20];
25781da177e4SLinus Torvalds 
25791da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
25801da177e4SLinus Torvalds 		return -EINVAL;
25811da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
25821da177e4SLinus Torvalds 		int neg = 0;
25831da177e4SLinus Torvalds 
25841da177e4SLinus Torvalds 		if ('-' == *work)
25851da177e4SLinus Torvalds 			neg = 1;
25861da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
25871da177e4SLinus Torvalds 			return -EINVAL;
25881da177e4SLinus Torvalds 		if (neg)
25891da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
25901da177e4SLinus Torvalds 	}
25911da177e4SLinus Torvalds 	if (delta_hosts > 0) {
25921da177e4SLinus Torvalds 		do {
25931da177e4SLinus Torvalds 			sdebug_add_adapter();
25941da177e4SLinus Torvalds 		} while (--delta_hosts);
25951da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
25961da177e4SLinus Torvalds 		do {
25971da177e4SLinus Torvalds 			sdebug_remove_adapter();
25981da177e4SLinus Torvalds 		} while (++delta_hosts);
25991da177e4SLinus Torvalds 	}
26001da177e4SLinus Torvalds 	return count;
26011da177e4SLinus Torvalds }
26021da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
26031da177e4SLinus Torvalds 	    sdebug_add_host_store);
26041da177e4SLinus Torvalds 
260523183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
260623183910SDouglas Gilbert 					  char * buf)
260723183910SDouglas Gilbert {
260823183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
260923183910SDouglas Gilbert }
261023183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
261123183910SDouglas Gilbert 					   const char * buf, size_t count)
261223183910SDouglas Gilbert {
261323183910SDouglas Gilbert 	int n;
261423183910SDouglas Gilbert 
261523183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
261623183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
261723183910SDouglas Gilbert 		return count;
261823183910SDouglas Gilbert 	}
261923183910SDouglas Gilbert 	return -EINVAL;
262023183910SDouglas Gilbert }
262123183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
262223183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
262323183910SDouglas Gilbert 
262423183910SDouglas Gilbert /* Note: The following function creates attribute files in the
262523183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
262623183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
262723183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
262823183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
262923183910SDouglas Gilbert  */
26306ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
26311da177e4SLinus Torvalds {
26326ecaff7fSRandy Dunlap 	int ret;
26336ecaff7fSRandy Dunlap 
26346ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
26356ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
26366ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
26376ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
26386ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
263923183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
26406ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
264123183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
26426ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
264323183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
26446ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
26456ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
26466ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
264723183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
264823183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
26496ecaff7fSRandy Dunlap 	return ret;
26501da177e4SLinus Torvalds }
26511da177e4SLinus Torvalds 
26521da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
26531da177e4SLinus Torvalds {
265423183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
265523183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
26561da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
26571da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
26581da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
26591da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
266023183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
266123183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
26621da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
266323183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
26641da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
26651da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
26661da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
26671da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
26681da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
26691da177e4SLinus Torvalds }
26701da177e4SLinus Torvalds 
26711da177e4SLinus Torvalds static int __init scsi_debug_init(void)
26721da177e4SLinus Torvalds {
2673c65b1445SDouglas Gilbert 	unsigned int sz;
26741da177e4SLinus Torvalds 	int host_to_add;
26751da177e4SLinus Torvalds 	int k;
26766ecaff7fSRandy Dunlap 	int ret;
26771da177e4SLinus Torvalds 
26781da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
26791da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2680c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2681c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2682c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2683c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2684c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2685c65b1445SDouglas Gilbert 	} else
2686c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
26871da177e4SLinus Torvalds 
26881da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
26891da177e4SLinus Torvalds 	sdebug_heads = 8;
26901da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
26911da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
26921da177e4SLinus Torvalds 		sdebug_heads = 32;
26931da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
26941da177e4SLinus Torvalds 		sdebug_heads = 64;
26951da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
26961da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
26971da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
26981da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
26991da177e4SLinus Torvalds 		sdebug_heads = 255;
27001da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
27011da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
27021da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
27031da177e4SLinus Torvalds 	}
27041da177e4SLinus Torvalds 
27051da177e4SLinus Torvalds 	sz = sdebug_store_size;
27061da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
27071da177e4SLinus Torvalds 	if (NULL == fake_storep) {
27081da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
27091da177e4SLinus Torvalds 		return -ENOMEM;
27101da177e4SLinus Torvalds 	}
27111da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
27121da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
27131da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
27141da177e4SLinus Torvalds 
27156ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
27166ecaff7fSRandy Dunlap 	if (ret < 0) {
27176ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
27186ecaff7fSRandy Dunlap 			ret);
27196ecaff7fSRandy Dunlap 		goto free_vm;
27206ecaff7fSRandy Dunlap 	}
27216ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
27226ecaff7fSRandy Dunlap 	if (ret < 0) {
27236ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
27246ecaff7fSRandy Dunlap 			ret);
27256ecaff7fSRandy Dunlap 		goto dev_unreg;
27266ecaff7fSRandy Dunlap 	}
27276ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
27286ecaff7fSRandy Dunlap 	if (ret < 0) {
27296ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
27306ecaff7fSRandy Dunlap 			ret);
27316ecaff7fSRandy Dunlap 		goto bus_unreg;
27326ecaff7fSRandy Dunlap 	}
27336ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
27346ecaff7fSRandy Dunlap 	if (ret < 0) {
27356ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
27366ecaff7fSRandy Dunlap 			ret);
27376ecaff7fSRandy Dunlap 		goto del_files;
27386ecaff7fSRandy Dunlap 	}
27391da177e4SLinus Torvalds 
27406ecaff7fSRandy Dunlap 	init_all_queued();
27411da177e4SLinus Torvalds 
27421da177e4SLinus Torvalds 	sdebug_driver_template.proc_name = (char *)sdebug_proc_name;
27431da177e4SLinus Torvalds 
27441da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
27451da177e4SLinus Torvalds         scsi_debug_add_host = 0;
27461da177e4SLinus Torvalds 
27471da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
27481da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
27491da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
27501da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
27511da177e4SLinus Torvalds                         break;
27521da177e4SLinus Torvalds                 }
27531da177e4SLinus Torvalds         }
27541da177e4SLinus Torvalds 
27551da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
27561da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
27571da177e4SLinus Torvalds 		       scsi_debug_add_host);
27581da177e4SLinus Torvalds 	}
27591da177e4SLinus Torvalds 	return 0;
27606ecaff7fSRandy Dunlap 
27616ecaff7fSRandy Dunlap del_files:
27626ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
27636ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
27646ecaff7fSRandy Dunlap bus_unreg:
27656ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
27666ecaff7fSRandy Dunlap dev_unreg:
27676ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
27686ecaff7fSRandy Dunlap free_vm:
27696ecaff7fSRandy Dunlap 	vfree(fake_storep);
27706ecaff7fSRandy Dunlap 
27716ecaff7fSRandy Dunlap 	return ret;
27721da177e4SLinus Torvalds }
27731da177e4SLinus Torvalds 
27741da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
27751da177e4SLinus Torvalds {
27761da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
27771da177e4SLinus Torvalds 
27781da177e4SLinus Torvalds 	stop_all_queued();
27791da177e4SLinus Torvalds 	for (; k; k--)
27801da177e4SLinus Torvalds 		sdebug_remove_adapter();
27811da177e4SLinus Torvalds 	do_remove_driverfs_files();
27821da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
27831da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
27841da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
27851da177e4SLinus Torvalds 
27861da177e4SLinus Torvalds 	vfree(fake_storep);
27871da177e4SLinus Torvalds }
27881da177e4SLinus Torvalds 
27891da177e4SLinus Torvalds device_initcall(scsi_debug_init);
27901da177e4SLinus Torvalds module_exit(scsi_debug_exit);
27911da177e4SLinus Torvalds 
279252c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
27931da177e4SLinus Torvalds {
27941da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
27951da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
27961da177e4SLinus Torvalds }
27971da177e4SLinus Torvalds 
27981da177e4SLinus Torvalds static struct device pseudo_primary = {
27991da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
28001da177e4SLinus Torvalds 	.release	= pseudo_0_release,
28011da177e4SLinus Torvalds };
28021da177e4SLinus Torvalds 
28031da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
28041da177e4SLinus Torvalds                           struct device_driver *dev_driver)
28051da177e4SLinus Torvalds {
28061da177e4SLinus Torvalds         return 1;
28071da177e4SLinus Torvalds }
28081da177e4SLinus Torvalds 
28091da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
28101da177e4SLinus Torvalds         .name = "pseudo",
28111da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2812bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2813bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
28141da177e4SLinus Torvalds };
28151da177e4SLinus Torvalds 
28161da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
28171da177e4SLinus Torvalds {
28181da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
28191da177e4SLinus Torvalds 
28201da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
28211da177e4SLinus Torvalds         kfree(sdbg_host);
28221da177e4SLinus Torvalds }
28231da177e4SLinus Torvalds 
28241da177e4SLinus Torvalds static int sdebug_add_adapter(void)
28251da177e4SLinus Torvalds {
28261da177e4SLinus Torvalds 	int k, devs_per_host;
28271da177e4SLinus Torvalds         int error = 0;
28281da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
28291da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
28301da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
28311da177e4SLinus Torvalds 
283224669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
28331da177e4SLinus Torvalds 
28341da177e4SLinus Torvalds         if (NULL == sdbg_host) {
28351da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
28361da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
28371da177e4SLinus Torvalds                 return -ENOMEM;
28381da177e4SLinus Torvalds         }
28391da177e4SLinus Torvalds 
28401da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
28431da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
284424669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
28451da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
28461da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
28471da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
28481da177e4SLinus Torvalds                         error = -ENOMEM;
28491da177e4SLinus Torvalds 			goto clean;
28501da177e4SLinus Torvalds                 }
28511da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
28521da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
28531da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
28541da177e4SLinus Torvalds         }
28551da177e4SLinus Torvalds 
28561da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
28571da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
28581da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
28591da177e4SLinus Torvalds 
28601da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
28611da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
28621da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
28631da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
28641da177e4SLinus Torvalds 
28651da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
28661da177e4SLinus Torvalds 
28671da177e4SLinus Torvalds         if (error)
28681da177e4SLinus Torvalds 		goto clean;
28691da177e4SLinus Torvalds 
28701da177e4SLinus Torvalds 	++scsi_debug_add_host;
28711da177e4SLinus Torvalds         return error;
28721da177e4SLinus Torvalds 
28731da177e4SLinus Torvalds clean:
28741da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
28751da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
28761da177e4SLinus Torvalds 					  dev_list);
28771da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
28781da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
28791da177e4SLinus Torvalds 	}
28801da177e4SLinus Torvalds 
28811da177e4SLinus Torvalds 	kfree(sdbg_host);
28821da177e4SLinus Torvalds         return error;
28831da177e4SLinus Torvalds }
28841da177e4SLinus Torvalds 
28851da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
28861da177e4SLinus Torvalds {
28871da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
28881da177e4SLinus Torvalds 
28891da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
28901da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
28911da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
28921da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
28931da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
28941da177e4SLinus Torvalds 	}
28951da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
28961da177e4SLinus Torvalds 
28971da177e4SLinus Torvalds 	if (!sdbg_host)
28981da177e4SLinus Torvalds 		return;
28991da177e4SLinus Torvalds 
29001da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
29011da177e4SLinus Torvalds         --scsi_debug_add_host;
29021da177e4SLinus Torvalds }
29031da177e4SLinus Torvalds 
29041da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
29051da177e4SLinus Torvalds {
29061da177e4SLinus Torvalds         int error = 0;
29071da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29081da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
29091da177e4SLinus Torvalds 
29101da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29111da177e4SLinus Torvalds 
29121da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
29131da177e4SLinus Torvalds         if (NULL == hpnt) {
29141da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
29151da177e4SLinus Torvalds                 error = -ENODEV;
29161da177e4SLinus Torvalds 		return error;
29171da177e4SLinus Torvalds         }
29181da177e4SLinus Torvalds 
29191da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
29201da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
29211da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
29221da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
29231da177e4SLinus Torvalds 	else
29241da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
2925c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
29261da177e4SLinus Torvalds 
29271da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
29281da177e4SLinus Torvalds         if (error) {
29291da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
29301da177e4SLinus Torvalds                 error = -ENODEV;
29311da177e4SLinus Torvalds 		scsi_host_put(hpnt);
29321da177e4SLinus Torvalds         } else
29331da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
29341da177e4SLinus Torvalds 
29351da177e4SLinus Torvalds 
29361da177e4SLinus Torvalds         return error;
29371da177e4SLinus Torvalds }
29381da177e4SLinus Torvalds 
29391da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
29401da177e4SLinus Torvalds {
29411da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29421da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29431da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29441da177e4SLinus Torvalds 
29451da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29461da177e4SLinus Torvalds 
29471da177e4SLinus Torvalds 	if (!sdbg_host) {
29481da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
29491da177e4SLinus Torvalds 		       __FUNCTION__);
29501da177e4SLinus Torvalds 		return -ENODEV;
29511da177e4SLinus Torvalds 	}
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
29541da177e4SLinus Torvalds 
29551da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
29561da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
29571da177e4SLinus Torvalds                                           dev_list);
29581da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
29591da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
29601da177e4SLinus Torvalds         }
29611da177e4SLinus Torvalds 
29621da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
29631da177e4SLinus Torvalds         return 0;
29641da177e4SLinus Torvalds }
29651da177e4SLinus Torvalds 
29661da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
29671da177e4SLinus Torvalds {
29681da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
29691da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
29701da177e4SLinus Torvalds 
29711da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
29721da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
29731da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
29741da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
29751da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
29761da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
29771da177e4SLinus Torvalds 		else
29781da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
2979c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
29801da177e4SLinus Torvalds 	}
29811da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
29821da177e4SLinus Torvalds }
2983