xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 6ecaff7f)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/kernel/scsi_debug.c
31da177e4SLinus Torvalds  * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
41da177e4SLinus Torvalds  *  Copyright (C) 1992  Eric Youngdale
51da177e4SLinus Torvalds  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
61da177e4SLinus Torvalds  *  to make sure that we are not getting blocks mixed up, and PANIC if
71da177e4SLinus Torvalds  *  anything out of the ordinary is seen.
81da177e4SLinus Torvalds  * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91da177e4SLinus Torvalds  *
101da177e4SLinus Torvalds  *  This version is more generic, simulating a variable number of disk
111da177e4SLinus Torvalds  *  (or disk like devices) sharing a common amount of RAM
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *  For documentation see http://www.torque.net/sg/sdebug26.html
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
171da177e4SLinus Torvalds  *   dpg: work for devfs large number of disks [20010809]
181da177e4SLinus Torvalds  *        forked for lk 2.5 series [20011216, 20020101]
191da177e4SLinus Torvalds  *        use vmalloc() more inquiry+mode_sense [20020302]
201da177e4SLinus Torvalds  *        add timers for delayed responses [20020721]
211da177e4SLinus Torvalds  *   Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
221da177e4SLinus Torvalds  *   Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
231da177e4SLinus Torvalds  *   dpg: change style of boot options to "scsi_debug.num_tgts=2" and
241da177e4SLinus Torvalds  *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
251da177e4SLinus Torvalds  */
261da177e4SLinus Torvalds 
271da177e4SLinus Torvalds #include <linux/module.h>
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds #include <linux/kernel.h>
301da177e4SLinus Torvalds #include <linux/sched.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321da177e4SLinus Torvalds #include <linux/timer.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/genhd.h>
361da177e4SLinus Torvalds #include <linux/fs.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/proc_fs.h>
391da177e4SLinus Torvalds #include <linux/smp_lock.h>
401da177e4SLinus Torvalds #include <linux/vmalloc.h>
411da177e4SLinus Torvalds #include <linux/moduleparam.h>
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #include <linux/blkdev.h>
441da177e4SLinus Torvalds #include "scsi.h"
451da177e4SLinus Torvalds #include <scsi/scsi_host.h>
461da177e4SLinus Torvalds #include <scsi/scsicam.h>
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds #include <linux/stat.h>
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include "scsi_logging.h"
511da177e4SLinus Torvalds #include "scsi_debug.h"
521da177e4SLinus Torvalds 
53c65b1445SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.79"
54c65b1445SDouglas Gilbert static const char * scsi_debug_version_date = "20060604";
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds /* Additional Sense Code (ASC) used */
57c65b1445SDouglas Gilbert #define NO_ADDITIONAL_SENSE 0x0
58c65b1445SDouglas Gilbert #define LOGICAL_UNIT_NOT_READY 0x4
591da177e4SLinus Torvalds #define UNRECOVERED_READ_ERR 0x11
60c65b1445SDouglas Gilbert #define PARAMETER_LIST_LENGTH_ERR 0x1a
611da177e4SLinus Torvalds #define INVALID_OPCODE 0x20
621da177e4SLinus Torvalds #define ADDR_OUT_OF_RANGE 0x21
631da177e4SLinus Torvalds #define INVALID_FIELD_IN_CDB 0x24
64c65b1445SDouglas Gilbert #define INVALID_FIELD_IN_PARAM_LIST 0x26
651da177e4SLinus Torvalds #define POWERON_RESET 0x29
661da177e4SLinus Torvalds #define SAVING_PARAMS_UNSUP 0x39
67c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
68c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
711da177e4SLinus Torvalds 
721da177e4SLinus Torvalds /* Default values for driver parameters */
731da177e4SLinus Torvalds #define DEF_NUM_HOST   1
741da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
751da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
761da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
771da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
781da177e4SLinus Torvalds  */
791da177e4SLinus Torvalds #define DEF_DELAY   1
801da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
811da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
821da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
831da177e4SLinus Torvalds #define DEF_OPTS   0
841da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
851da177e4SLinus Torvalds #define DEF_PTYPE   0
861da177e4SLinus Torvalds #define DEF_D_SENSE   0
87c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
88c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
891da177e4SLinus Torvalds 
901da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
911da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
921da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
931da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
941da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
951da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
961da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
971da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
981da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
991da177e4SLinus Torvalds  *
1001da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1011da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1021da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1031da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1041da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1051da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1061da177e4SLinus Torvalds  */
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1091da177e4SLinus Torvalds  * sector on read commands: */
1101da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1131da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1141da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
115c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1181da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1191da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1201da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1211da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1221da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1231da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1241da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1251da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1261da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1271da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
128c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
129c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1341da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1351da177e4SLinus Torvalds 
136c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
137c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1381da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1411da177e4SLinus Torvalds    may still need them */
1421da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1431da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1441da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1471da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1481da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1491da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1501da177e4SLinus Torvalds 
1511da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds struct sdebug_dev_info {
1561da177e4SLinus Torvalds 	struct list_head dev_list;
1571da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1581da177e4SLinus Torvalds 	unsigned int channel;
1591da177e4SLinus Torvalds 	unsigned int target;
1601da177e4SLinus Torvalds 	unsigned int lun;
1611da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
162c65b1445SDouglas Gilbert 	unsigned int wlun;
1631da177e4SLinus Torvalds 	char reset;
164c65b1445SDouglas Gilbert 	char stopped;
1651da177e4SLinus Torvalds 	char used;
1661da177e4SLinus Torvalds };
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds struct sdebug_host_info {
1691da177e4SLinus Torvalds 	struct list_head host_list;
1701da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1711da177e4SLinus Torvalds 	struct device dev;
1721da177e4SLinus Torvalds 	struct list_head dev_info_list;
1731da177e4SLinus Torvalds };
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1761da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1791da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds struct sdebug_queued_cmd {
1841da177e4SLinus Torvalds 	int in_use;
1851da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
1861da177e4SLinus Torvalds 	done_funct_t done_funct;
1871da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
1881da177e4SLinus Torvalds 	int scsi_result;
1891da177e4SLinus Torvalds };
1901da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
1911da177e4SLinus Torvalds 
192d0be4a7dSChristoph Hellwig static struct scsi_host_template sdebug_driver_template = {
1931da177e4SLinus Torvalds 	.proc_info =		scsi_debug_proc_info,
1941da177e4SLinus Torvalds 	.name =			"SCSI DEBUG",
1951da177e4SLinus Torvalds 	.info =			scsi_debug_info,
1961da177e4SLinus Torvalds 	.slave_alloc =		scsi_debug_slave_alloc,
1971da177e4SLinus Torvalds 	.slave_configure =	scsi_debug_slave_configure,
1981da177e4SLinus Torvalds 	.slave_destroy =	scsi_debug_slave_destroy,
1991da177e4SLinus Torvalds 	.ioctl =		scsi_debug_ioctl,
2001da177e4SLinus Torvalds 	.queuecommand =		scsi_debug_queuecommand,
2011da177e4SLinus Torvalds 	.eh_abort_handler =	scsi_debug_abort,
2021da177e4SLinus Torvalds 	.eh_bus_reset_handler = scsi_debug_bus_reset,
2031da177e4SLinus Torvalds 	.eh_device_reset_handler = scsi_debug_device_reset,
2041da177e4SLinus Torvalds 	.eh_host_reset_handler = scsi_debug_host_reset,
2051da177e4SLinus Torvalds 	.bios_param =		scsi_debug_biosparam,
2061da177e4SLinus Torvalds 	.can_queue =		SCSI_DEBUG_CANQUEUE,
2071da177e4SLinus Torvalds 	.this_id =		7,
208c65b1445SDouglas Gilbert 	.sg_tablesize =		256,
209c65b1445SDouglas Gilbert 	.cmd_per_lun =		16,
210c65b1445SDouglas Gilbert 	.max_sectors =		0xffff,
2111da177e4SLinus Torvalds 	.unchecked_isa_dma = 	0,
212c65b1445SDouglas Gilbert 	.use_clustering = 	ENABLE_CLUSTERING,
2131da177e4SLinus Torvalds 	.module =		THIS_MODULE,
2141da177e4SLinus Torvalds };
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds static int num_aborts = 0;
2191da177e4SLinus Torvalds static int num_dev_resets = 0;
2201da177e4SLinus Torvalds static int num_bus_resets = 0;
2211da177e4SLinus Torvalds static int num_host_resets = 0;
2221da177e4SLinus Torvalds 
2231da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2241da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2291da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2301da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2331da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2341da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2351da177e4SLinus Torvalds };
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds static const int check_condition_result =
2381da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2391da177e4SLinus Torvalds 
240c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
241c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
242c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
243c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
244c65b1445SDouglas Gilbert 
2451da177e4SLinus Torvalds /* function declarations */
2461da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2471da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2481da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2491da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
250c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
251c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2521da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2531da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
254c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2551da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
256c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
257c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
258c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
259c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
260c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
261c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
262c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
263c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
264c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
265c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2661da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2671da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
2681da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2691da177e4SLinus Torvalds                                 int arr_len);
2701da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2711da177e4SLinus Torvalds                                int max_arr_len);
2721da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2731da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2741da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2751da177e4SLinus Torvalds 			    int asc, int asq);
276c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2771da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2781da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2791da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2801da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2811da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2821da177e4SLinus Torvalds static void __init init_all_queued(void);
2831da177e4SLinus Torvalds static void stop_all_queued(void);
2841da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
285c65b1445SDouglas Gilbert static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
286c65b1445SDouglas Gilbert 			   int dev_id_num, const char * dev_id_str,
287c65b1445SDouglas Gilbert 			   int dev_id_str_len);
288c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
2896ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
2901da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
2911da177e4SLinus Torvalds 
2921da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2931da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2941da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds static struct device pseudo_primary;
2971da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2981da177e4SLinus Torvalds 
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds static
3011da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3021da177e4SLinus Torvalds {
3031da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
304c65b1445SDouglas Gilbert 	int len, k, j;
305c65b1445SDouglas Gilbert 	unsigned int num;
306c65b1445SDouglas Gilbert 	unsigned long long lba;
3071da177e4SLinus Torvalds 	int errsts = 0;
308c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3091da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3101da177e4SLinus Torvalds 	int inj_recovered = 0;
311c65b1445SDouglas Gilbert 	int delay_override = 0;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	if (done == NULL)
3141da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3151da177e4SLinus Torvalds 
316c65b1445SDouglas Gilbert 	SCpnt->resid = 0;
3171da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3181da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
319c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3201da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3211da177e4SLinus Torvalds 		printk("\n");
3221da177e4SLinus Torvalds 	}
3231da177e4SLinus Torvalds         if(target == sdebug_driver_template.this_id) {
3241da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3251da177e4SLinus Torvalds 		       "target!\n");
3261da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3271da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3281da177e4SLinus Torvalds         }
3291da177e4SLinus Torvalds 
330c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
331c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3321da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3331da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3341da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3351da177e4SLinus Torvalds 	if (NULL == devip)
3361da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3371da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3401da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3411da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3421da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3431da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3441da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3451da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3461da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3471da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3481da177e4SLinus Torvalds         }
3491da177e4SLinus Torvalds 
350c65b1445SDouglas Gilbert 	if (devip->wlun) {
351c65b1445SDouglas Gilbert 		switch (*cmd) {
352c65b1445SDouglas Gilbert 		case INQUIRY:
353c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
354c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
355c65b1445SDouglas Gilbert 		case REPORT_LUNS:
356c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
357c65b1445SDouglas Gilbert 		default:
358c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
359c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
360c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
361c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
362c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
363c65b1445SDouglas Gilbert 			errsts = check_condition_result;
364c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
365c65b1445SDouglas Gilbert 					     0);
366c65b1445SDouglas Gilbert 		}
367c65b1445SDouglas Gilbert 	}
368c65b1445SDouglas Gilbert 
3691da177e4SLinus Torvalds 	switch (*cmd) {
3701da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
371c65b1445SDouglas Gilbert 		delay_override = 1;
3721da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
3731da177e4SLinus Torvalds 		break;
3741da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
375c65b1445SDouglas Gilbert 		delay_override = 1;
3761da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
3771da177e4SLinus Torvalds 		break;
3781da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3791da177e4SLinus Torvalds 	case START_STOP:
380c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
3811da177e4SLinus Torvalds 		break;
3821da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
383c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
3841da177e4SLinus Torvalds 			break;
3851da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3861da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
3871da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
3881da177e4SLinus Torvalds 		break;
3891da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
390c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
3911da177e4SLinus Torvalds 		break;
3921da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
393c65b1445SDouglas Gilbert 		delay_override = 1;
394c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
3951da177e4SLinus Torvalds 		break;
3961da177e4SLinus Torvalds         case RESERVE:
397c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
3981da177e4SLinus Torvalds                 break;
3991da177e4SLinus Torvalds         case RESERVE_10:
400c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4011da177e4SLinus Torvalds                 break;
4021da177e4SLinus Torvalds         case RELEASE:
403c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4041da177e4SLinus Torvalds                 break;
4051da177e4SLinus Torvalds         case RELEASE_10:
406c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4071da177e4SLinus Torvalds                 break;
4081da177e4SLinus Torvalds 	case READ_CAPACITY:
4091da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4101da177e4SLinus Torvalds 		break;
411c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
412c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
413c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
414c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
415c65b1445SDouglas Gilbert 			errsts = check_condition_result;
416c65b1445SDouglas Gilbert 			break;
417c65b1445SDouglas Gilbert 		}
418c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
419c65b1445SDouglas Gilbert 		break;
4201da177e4SLinus Torvalds 	case READ_16:
4211da177e4SLinus Torvalds 	case READ_12:
4221da177e4SLinus Torvalds 	case READ_10:
4231da177e4SLinus Torvalds 	case READ_6:
424c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4251da177e4SLinus Torvalds 			break;
4261da177e4SLinus Torvalds 		if ((*cmd) == READ_16) {
427c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
428c65b1445SDouglas Gilbert 				if (j > 0)
429c65b1445SDouglas Gilbert 					lba <<= 8;
430c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
431c65b1445SDouglas Gilbert 			}
4321da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4331da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4341da177e4SLinus Torvalds 		} else if ((*cmd) == READ_12) {
435c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4361da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4371da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4381da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4391da177e4SLinus Torvalds 		} else if ((*cmd) == READ_10) {
440c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4411da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4421da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
443c65b1445SDouglas Gilbert 		} else {	/* READ (6) */
444c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4451da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
446c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4471da177e4SLinus Torvalds 		}
448c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4491da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4501da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
451c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4521da177e4SLinus Torvalds 			errsts = check_condition_result;
4531da177e4SLinus Torvalds 		}
4541da177e4SLinus Torvalds 		break;
4551da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
456c65b1445SDouglas Gilbert 		delay_override = 1;
4571da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4581da177e4SLinus Torvalds 		break;
4591da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
460c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4611da177e4SLinus Torvalds 		break;
4621da177e4SLinus Torvalds 	case WRITE_16:
4631da177e4SLinus Torvalds 	case WRITE_12:
4641da177e4SLinus Torvalds 	case WRITE_10:
4651da177e4SLinus Torvalds 	case WRITE_6:
466c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4671da177e4SLinus Torvalds 			break;
4681da177e4SLinus Torvalds 		if ((*cmd) == WRITE_16) {
469c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
470c65b1445SDouglas Gilbert 				if (j > 0)
471c65b1445SDouglas Gilbert 					lba <<= 8;
472c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
473c65b1445SDouglas Gilbert 			}
4741da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4751da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4761da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_12) {
477c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4781da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4791da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4801da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4811da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_10) {
482c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4831da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4841da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
485c65b1445SDouglas Gilbert 		} else {	/* WRITE (6) */
486c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4871da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
488c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4891da177e4SLinus Torvalds 		}
490c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
4911da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4921da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
493c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4941da177e4SLinus Torvalds 			errsts = check_condition_result;
4951da177e4SLinus Torvalds 		}
4961da177e4SLinus Torvalds 		break;
4971da177e4SLinus Torvalds 	case MODE_SENSE:
4981da177e4SLinus Torvalds 	case MODE_SENSE_10:
4991da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5001da177e4SLinus Torvalds 		break;
501c65b1445SDouglas Gilbert 	case MODE_SELECT:
502c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
503c65b1445SDouglas Gilbert 		break;
504c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
505c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
506c65b1445SDouglas Gilbert 		break;
507c65b1445SDouglas Gilbert 	case LOG_SENSE:
508c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
509c65b1445SDouglas Gilbert 		break;
5101da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
511c65b1445SDouglas Gilbert 		delay_override = 1;
512c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5131da177e4SLinus Torvalds 		break;
5141da177e4SLinus Torvalds 	default:
5151da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5161da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5171da177e4SLinus Torvalds 			       "supported\n", *cmd);
518c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5191da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5201da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5211da177e4SLinus Torvalds 		errsts = check_condition_result;
5221da177e4SLinus Torvalds 		break;
5231da177e4SLinus Torvalds 	}
524c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
525c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds 
5281da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5291da177e4SLinus Torvalds {
5301da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5311da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5321da177e4SLinus Torvalds 	}
5331da177e4SLinus Torvalds 	return -EINVAL;
5341da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds 
537c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
538c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5391da177e4SLinus Torvalds {
5401da177e4SLinus Torvalds 	if (devip->reset) {
5411da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5421da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5431da177e4SLinus Torvalds 			       "attention: power on reset\n");
5441da177e4SLinus Torvalds 		devip->reset = 0;
5451da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5461da177e4SLinus Torvalds 		return check_condition_result;
5471da177e4SLinus Torvalds 	}
548c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
549c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
550c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
551c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
552c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
553c65b1445SDouglas Gilbert 				0x2);
554c65b1445SDouglas Gilbert 		return check_condition_result;
555c65b1445SDouglas Gilbert 	}
5561da177e4SLinus Torvalds 	return 0;
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5601da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5611da177e4SLinus Torvalds 				int arr_len)
5621da177e4SLinus Torvalds {
5631da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
5641da177e4SLinus Torvalds 	void * kaddr;
5651da177e4SLinus Torvalds 	void * kaddr_off;
5661da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
5671da177e4SLinus Torvalds 
5681da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
5691da177e4SLinus Torvalds 		return 0;
5701da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
5711da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5721da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
5731da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
5741da177e4SLinus Torvalds 		return (DID_ERROR << 16);
5751da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
5761da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
5771da177e4SLinus Torvalds 		act_len = (req_len < arr_len) ? req_len : arr_len;
5781da177e4SLinus Torvalds 		memcpy(scp->request_buffer, arr, act_len);
579c65b1445SDouglas Gilbert 		if (scp->resid)
580c65b1445SDouglas Gilbert 			scp->resid -= act_len;
581c65b1445SDouglas Gilbert 		else
5821da177e4SLinus Torvalds 			scp->resid = req_len - act_len;
5831da177e4SLinus Torvalds 		return 0;
5841da177e4SLinus Torvalds 	}
5851da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
5861da177e4SLinus Torvalds 	active = 1;
5871da177e4SLinus Torvalds 	for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
5881da177e4SLinus Torvalds 		if (active) {
5891da177e4SLinus Torvalds 			kaddr = (unsigned char *)
5901da177e4SLinus Torvalds 				kmap_atomic(sgpnt->page, KM_USER0);
5911da177e4SLinus Torvalds 			if (NULL == kaddr)
5921da177e4SLinus Torvalds 				return (DID_ERROR << 16);
5931da177e4SLinus Torvalds 			kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
5941da177e4SLinus Torvalds 			len = sgpnt->length;
5951da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
5961da177e4SLinus Torvalds 				active = 0;
5971da177e4SLinus Torvalds 				len = arr_len - req_len;
5981da177e4SLinus Torvalds 			}
5991da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6001da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6011da177e4SLinus Torvalds 			act_len += len;
6021da177e4SLinus Torvalds 		}
6031da177e4SLinus Torvalds 		req_len += sgpnt->length;
6041da177e4SLinus Torvalds 	}
605c65b1445SDouglas Gilbert 	if (scp->resid)
606c65b1445SDouglas Gilbert 		scp->resid -= act_len;
607c65b1445SDouglas Gilbert 	else
6081da177e4SLinus Torvalds 		scp->resid = req_len - act_len;
6091da177e4SLinus Torvalds 	return 0;
6101da177e4SLinus Torvalds }
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6131da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6141da177e4SLinus Torvalds 			       int max_arr_len)
6151da177e4SLinus Torvalds {
6161da177e4SLinus Torvalds 	int k, req_len, len, fin;
6171da177e4SLinus Torvalds 	void * kaddr;
6181da177e4SLinus Torvalds 	void * kaddr_off;
6191da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
6201da177e4SLinus Torvalds 
6211da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
6221da177e4SLinus Torvalds 		return 0;
6231da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
6241da177e4SLinus Torvalds 		return -1;
6251da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6261da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_TO_DEVICE)))
6271da177e4SLinus Torvalds 		return -1;
6281da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
6291da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
6301da177e4SLinus Torvalds 		len = (req_len < max_arr_len) ? req_len : max_arr_len;
6311da177e4SLinus Torvalds 		memcpy(arr, scp->request_buffer, len);
6321da177e4SLinus Torvalds 		return len;
6331da177e4SLinus Torvalds 	}
6341da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6351da177e4SLinus Torvalds 	for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
6361da177e4SLinus Torvalds 		kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
6371da177e4SLinus Torvalds 		if (NULL == kaddr)
6381da177e4SLinus Torvalds 			return -1;
6391da177e4SLinus Torvalds 		kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6401da177e4SLinus Torvalds 		len = sgpnt->length;
6411da177e4SLinus Torvalds 		if ((req_len + len) > max_arr_len) {
6421da177e4SLinus Torvalds 			len = max_arr_len - req_len;
6431da177e4SLinus Torvalds 			fin = 1;
6441da177e4SLinus Torvalds 		}
6451da177e4SLinus Torvalds 		memcpy(arr + req_len, kaddr_off, len);
6461da177e4SLinus Torvalds 		kunmap_atomic(kaddr, KM_USER0);
6471da177e4SLinus Torvalds 		if (fin)
6481da177e4SLinus Torvalds 			return req_len + len;
6491da177e4SLinus Torvalds 		req_len += sgpnt->length;
6501da177e4SLinus Torvalds 	}
6511da177e4SLinus Torvalds 	return req_len;
6521da177e4SLinus Torvalds }
6531da177e4SLinus Torvalds 
6541da177e4SLinus Torvalds 
6551da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6561da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6571da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6581da177e4SLinus Torvalds 
659c65b1445SDouglas Gilbert static int inquiry_evpd_83(unsigned char * arr, int target_dev_id,
660c65b1445SDouglas Gilbert 			   int dev_id_num, const char * dev_id_str,
661c65b1445SDouglas Gilbert 			   int dev_id_str_len)
6621da177e4SLinus Torvalds {
663c65b1445SDouglas Gilbert 	int num, port_a;
664c65b1445SDouglas Gilbert 	char b[32];
6651da177e4SLinus Torvalds 
666c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
6671da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
6681da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
6691da177e4SLinus Torvalds 	arr[1] = 0x1;
6701da177e4SLinus Torvalds 	arr[2] = 0x0;
6711da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
6721da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
6731da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
6741da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
6751da177e4SLinus Torvalds 	arr[3] = num;
6761da177e4SLinus Torvalds 	num += 4;
677c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
678c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
679c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
680c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
681c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
682c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
683c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
684c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
685c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
686c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
687c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
688c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
689c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
690c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
691c65b1445SDouglas Gilbert 		/* Target relative port number */
692c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
693c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
694c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
695c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
696c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
697c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
698c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
699c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
700c65b1445SDouglas Gilbert 	}
701c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
702c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
703c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
704c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
705c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
706c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
707c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
708c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
709c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
710c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
711c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
712c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
713c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
714c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
715c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
716c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
717c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
718c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
719c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
720c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
721c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
722c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
723c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
724c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
725c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
726c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
727c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
728c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
729c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
730c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
731c65b1445SDouglas Gilbert 	arr[num++] = 24;
732c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
733c65b1445SDouglas Gilbert 	num += 12;
734c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
735c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
736c65b1445SDouglas Gilbert 	num += 8;
737c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
738c65b1445SDouglas Gilbert 	num += 4;
739c65b1445SDouglas Gilbert 	return num;
740c65b1445SDouglas Gilbert }
741c65b1445SDouglas Gilbert 
742c65b1445SDouglas Gilbert 
743c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
744c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
745c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
746c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
747c65b1445SDouglas Gilbert };
748c65b1445SDouglas Gilbert 
749c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
750c65b1445SDouglas Gilbert {
751c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
752c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
753c65b1445SDouglas Gilbert }
754c65b1445SDouglas Gilbert 
755c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
756c65b1445SDouglas Gilbert {
757c65b1445SDouglas Gilbert 	int num = 0;
758c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
759c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
760c65b1445SDouglas Gilbert 	int plen, olen;
761c65b1445SDouglas Gilbert 
762c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
763c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
764c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
765c65b1445SDouglas Gilbert 	olen = strlen(na1);
766c65b1445SDouglas Gilbert 	plen = olen + 1;
767c65b1445SDouglas Gilbert 	if (plen % 4)
768c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
769c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
770c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
771c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
772c65b1445SDouglas Gilbert 	num += plen;
773c65b1445SDouglas Gilbert 
774c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
775c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
776c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
777c65b1445SDouglas Gilbert 	olen = strlen(na2);
778c65b1445SDouglas Gilbert 	plen = olen + 1;
779c65b1445SDouglas Gilbert 	if (plen % 4)
780c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
781c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
782c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
783c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
784c65b1445SDouglas Gilbert 	num += plen;
785c65b1445SDouglas Gilbert 
786c65b1445SDouglas Gilbert 	return num;
787c65b1445SDouglas Gilbert }
788c65b1445SDouglas Gilbert 
789c65b1445SDouglas Gilbert /* SCSI ports VPD page */
790c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
791c65b1445SDouglas Gilbert {
792c65b1445SDouglas Gilbert 	int num = 0;
793c65b1445SDouglas Gilbert 	int port_a, port_b;
794c65b1445SDouglas Gilbert 
795c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
796c65b1445SDouglas Gilbert 	port_b = port_a + 1;
797c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
798c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
799c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
800c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
801c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
802c65b1445SDouglas Gilbert 	num += 6;
803c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
804c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
805c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
806c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
807c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
808c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
809c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
810c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
811c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
812c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
813c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
814c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
815c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
816c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
817c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
818c65b1445SDouglas Gilbert 
819c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
820c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
821c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
822c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
823c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
824c65b1445SDouglas Gilbert 	num += 6;
825c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
826c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
827c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
828c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
829c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
830c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
831c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
832c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
833c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
834c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
835c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
836c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
837c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
838c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
839c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
840c65b1445SDouglas Gilbert 
841c65b1445SDouglas Gilbert 	return num;
842c65b1445SDouglas Gilbert }
843c65b1445SDouglas Gilbert 
844c65b1445SDouglas Gilbert 
845c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
846c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
847c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
848c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
849c65b1445SDouglas Gilbert '1','2','3','4',
850c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
851c65b1445SDouglas Gilbert 0xec,0,0,0,
852c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
853c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
854c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
855c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
856c65b1445SDouglas Gilbert 0x53,0x41,
857c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
858c65b1445SDouglas Gilbert 0x20,0x20,
859c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
860c65b1445SDouglas Gilbert 0x10,0x80,
861c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
862c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
863c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
864c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
865c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
866c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
867c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
868c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
869c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
870c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
871c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
872c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
873c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
874c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
875c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
876c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,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 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
881c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
882c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
883c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0xa5,0x51,
887c65b1445SDouglas Gilbert };
888c65b1445SDouglas Gilbert 
889c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
890c65b1445SDouglas Gilbert {
891c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
892c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
893c65b1445SDouglas Gilbert }
894c65b1445SDouglas Gilbert 
895c65b1445SDouglas Gilbert 
896c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
897c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
898c65b1445SDouglas Gilbert 	0,0,0x4,0,
899c65b1445SDouglas Gilbert 	0,0,0,64,
900c65b1445SDouglas Gilbert };
901c65b1445SDouglas Gilbert 
902c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
903c65b1445SDouglas Gilbert {
904c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
905c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
906c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
907c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
908c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
909c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
910c65b1445SDouglas Gilbert 	}
911c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
9121da177e4SLinus Torvalds }
9131da177e4SLinus Torvalds 
9141da177e4SLinus Torvalds 
9151da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
916c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9171da177e4SLinus Torvalds 
9181da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9191da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9201da177e4SLinus Torvalds {
9211da177e4SLinus Torvalds 	unsigned char pq_pdt;
9221da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ];
9231da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
924c65b1445SDouglas Gilbert 	int alloc_len, n;
9251da177e4SLinus Torvalds 
9261da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9271da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ);
928c65b1445SDouglas Gilbert 	if (devip->wlun)
929c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
930c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
931c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
932c65b1445SDouglas Gilbert 	else
9331da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9341da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9351da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9361da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9371da177e4SLinus Torvalds 			       	0);
9381da177e4SLinus Torvalds 		return check_condition_result;
9391da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
940c65b1445SDouglas Gilbert 		int lu_id_num, target_dev_id, len;
941c65b1445SDouglas Gilbert 		char lu_id_str[6];
942c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9431da177e4SLinus Torvalds 
944c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
945c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
946c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
947c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
948c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9491da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
950c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
951c65b1445SDouglas Gilbert 			n = 4;
952c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
953c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
954c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
955c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
956c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
957c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
958c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
959c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
960c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
961c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
962c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
9631da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
964c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
9651da177e4SLinus Torvalds 			arr[3] = len;
966c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
9671da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
968c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
969c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_83(&arr[4], target_dev_id,
970c65b1445SDouglas Gilbert 						 lu_id_num, lu_id_str, len);
971c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
972c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
973c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
974c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
975c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
976c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
977c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
978c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
979c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
980c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
981c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
982c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
983c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
984c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
985c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
986c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
987c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
988c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
989c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
990c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
991c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
992c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
993c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
994c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
995c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
996c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
997c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
998c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
999c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10001da177e4SLinus Torvalds 		} else {
10011da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10021da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10031da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10041da177e4SLinus Torvalds 			return check_condition_result;
10051da177e4SLinus Torvalds 		}
1006c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10071da177e4SLinus Torvalds 		return fill_from_dev_buffer(scp, arr,
1008c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10091da177e4SLinus Torvalds 	}
10101da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10111da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10121da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10131da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10141da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
1015c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10161da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1017c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10181da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10191da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10201da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10211da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1022c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1023c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1024c65b1445SDouglas Gilbert 	n = 62;
10251da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1026c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10271da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1028c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10291da177e4SLinus Torvalds 	}
1030c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10311da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
10321da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10331da177e4SLinus Torvalds }
10341da177e4SLinus Torvalds 
10351da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10361da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10371da177e4SLinus Torvalds {
10381da177e4SLinus Torvalds 	unsigned char * sbuff;
10391da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10401da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1041c65b1445SDouglas Gilbert 	int want_dsense;
10421da177e4SLinus Torvalds 	int len = 18;
10431da177e4SLinus Torvalds 
1044c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10451da177e4SLinus Torvalds 	if (devip->reset == 1)
1046c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1047c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10481da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1049c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1050c65b1445SDouglas Gilbert 		if (want_dsense) {
1051c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1052c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1053c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1054c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1055c65b1445SDouglas Gilbert 		} else {
1056c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1057c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1058c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1059c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1060c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1061c65b1445SDouglas Gilbert 		}
1062c65b1445SDouglas Gilbert 	} else if (devip->stopped) {
1063c65b1445SDouglas Gilbert 		if (want_dsense) {
1064c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1065c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1066c65b1445SDouglas Gilbert 			arr[2] = LOW_POWER_COND_ON;
1067c65b1445SDouglas Gilbert 			arr[3] = 0x0;		/* TEST set and MRIE==6 */
1068c65b1445SDouglas Gilbert 		} else {
1069c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1070c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1071c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1072c65b1445SDouglas Gilbert 			arr[12] = LOW_POWER_COND_ON;
1073c65b1445SDouglas Gilbert 			arr[13] = 0x0;		/* TEST set and MRIE==6 */
1074c65b1445SDouglas Gilbert 		}
1075c65b1445SDouglas Gilbert 	} else {
1076c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
10771da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
10781da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1079c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
10801da177e4SLinus Torvalds 			arr[0] = 0x72;
10811da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
10821da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
10831da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
10841da177e4SLinus Torvalds 			len = 8;
1085c65b1445SDouglas Gilbert 		}
1086c65b1445SDouglas Gilbert 	}
1087c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
10881da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
10891da177e4SLinus Torvalds }
10901da177e4SLinus Torvalds 
1091c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1092c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1093c65b1445SDouglas Gilbert {
1094c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1095c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1096c65b1445SDouglas Gilbert 
1097c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1098c65b1445SDouglas Gilbert 		return errsts;
1099c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1100c65b1445SDouglas Gilbert 	if (power_cond) {
1101c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1102c65b1445SDouglas Gilbert 			       	0);
1103c65b1445SDouglas Gilbert 		return check_condition_result;
1104c65b1445SDouglas Gilbert 	}
1105c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1106c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1107c65b1445SDouglas Gilbert 		devip->stopped = !start;
1108c65b1445SDouglas Gilbert 	return 0;
1109c65b1445SDouglas Gilbert }
1110c65b1445SDouglas Gilbert 
11111da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11121da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11131da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11141da177e4SLinus Torvalds {
11151da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1116c65b1445SDouglas Gilbert 	unsigned int capac;
11171da177e4SLinus Torvalds 	int errsts;
11181da177e4SLinus Torvalds 
1119c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11201da177e4SLinus Torvalds 		return errsts;
1121c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1122c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1123c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1124c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1125c65b1445SDouglas Gilbert 	} else
1126c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11271da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1128c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1129c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11301da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11311da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11321da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11331da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1134c65b1445SDouglas Gilbert 	} else {
1135c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1136c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1137c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1138c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1139c65b1445SDouglas Gilbert 	}
11401da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11411da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11421da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11431da177e4SLinus Torvalds }
11441da177e4SLinus Torvalds 
1145c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1146c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1147c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1148c65b1445SDouglas Gilbert {
1149c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1150c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1151c65b1445SDouglas Gilbert 	unsigned long long capac;
1152c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1153c65b1445SDouglas Gilbert 
1154c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1155c65b1445SDouglas Gilbert 		return errsts;
1156c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1157c65b1445SDouglas Gilbert 		     + cmd[13]);
1158c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1159c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1160c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1161c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1162c65b1445SDouglas Gilbert 	} else
1163c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1164c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1165c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1166c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1167c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1168c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1169c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1170c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1171c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1172c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1173c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1174c65b1445SDouglas Gilbert }
1175c65b1445SDouglas Gilbert 
11761da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
11771da177e4SLinus Torvalds 
11781da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
11791da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
11801da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
11811da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
11821da177e4SLinus Torvalds 
11831da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
11841da177e4SLinus Torvalds 	if (1 == pcontrol)
11851da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
11861da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
11871da177e4SLinus Torvalds }
11881da177e4SLinus Torvalds 
11891da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
11901da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
11911da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
11921da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
11931da177e4SLinus Torvalds 
11941da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
11951da177e4SLinus Torvalds 	if (1 == pcontrol)
11961da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
11971da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
11981da177e4SLinus Torvalds }
11991da177e4SLinus Torvalds 
12001da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
12011da177e4SLinus Torvalds {       /* Format device page for mode_sense */
12021da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
12031da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
12041da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
12051da177e4SLinus Torvalds 
12061da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
12071da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
12081da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
12091da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
12101da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
12111da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
12121da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
12131da177e4SLinus Torvalds         if (1 == pcontrol)
12141da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
12151da177e4SLinus Torvalds         return sizeof(format_pg);
12161da177e4SLinus Torvalds }
12171da177e4SLinus Torvalds 
12181da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
12191da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
12201da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
12211da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
12221da177e4SLinus Torvalds 
12231da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
12241da177e4SLinus Torvalds 	if (1 == pcontrol)
12251da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
12261da177e4SLinus Torvalds 	return sizeof(caching_pg);
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds 
12291da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
12301da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1231c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1232c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1233c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
12341da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
12351da177e4SLinus Torvalds 
12361da177e4SLinus Torvalds 	if (scsi_debug_dsense)
12371da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1238c65b1445SDouglas Gilbert 	else
1239c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
12401da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
12411da177e4SLinus Torvalds 	if (1 == pcontrol)
1242c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1243c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1244c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
12451da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
12461da177e4SLinus Torvalds }
12471da177e4SLinus Torvalds 
1248c65b1445SDouglas Gilbert 
12491da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
12501da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1251c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
12521da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1253c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1254c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1255c65b1445SDouglas Gilbert 
12561da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
12571da177e4SLinus Torvalds 	if (1 == pcontrol)
1258c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1259c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1260c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
12611da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
12621da177e4SLinus Torvalds }
12631da177e4SLinus Torvalds 
1264c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1265c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1266c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1267c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1268c65b1445SDouglas Gilbert 
1269c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1270c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1271c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1272c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1273c65b1445SDouglas Gilbert }
1274c65b1445SDouglas Gilbert 
1275c65b1445SDouglas Gilbert 
1276c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1277c65b1445SDouglas Gilbert 			      int target_dev_id)
1278c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1279c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1280c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1281c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1282c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1283c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1284c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1285c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1286c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1287c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1288c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1289c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1290c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1291c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1292c65b1445SDouglas Gilbert 		};
1293c65b1445SDouglas Gilbert 	int port_a, port_b;
1294c65b1445SDouglas Gilbert 
1295c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1296c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1297c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1298c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1299c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1300c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1301c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1302c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1303c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1304c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1305c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1306c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1307c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1308c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1309c65b1445SDouglas Gilbert }
1310c65b1445SDouglas Gilbert 
1311c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1312c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1313c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1314c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1315c65b1445SDouglas Gilbert 		};
1316c65b1445SDouglas Gilbert 
1317c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1318c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1319c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1320c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1321c65b1445SDouglas Gilbert }
1322c65b1445SDouglas Gilbert 
13231da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
13241da177e4SLinus Torvalds 
13251da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
13261da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
13271da177e4SLinus Torvalds {
13281da177e4SLinus Torvalds 	unsigned char dbd;
13291da177e4SLinus Torvalds 	int pcontrol, pcode, subpcode;
13301da177e4SLinus Torvalds 	unsigned char dev_spec;
1331c65b1445SDouglas Gilbert 	int alloc_len, msense_6, offset, len, errsts, target_dev_id;
13321da177e4SLinus Torvalds 	unsigned char * ap;
13331da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
13341da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
13351da177e4SLinus Torvalds 
1336c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
13371da177e4SLinus Torvalds 		return errsts;
13381da177e4SLinus Torvalds 	dbd = cmd[1] & 0x8;
13391da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
13401da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
13411da177e4SLinus Torvalds 	subpcode = cmd[3];
13421da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
13431da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
13441da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
13451da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
13461da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
13471da177e4SLinus Torvalds 			       	0);
13481da177e4SLinus Torvalds 		return check_condition_result;
13491da177e4SLinus Torvalds 	}
1350c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1351c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
13521da177e4SLinus Torvalds 	dev_spec = DEV_READONLY(target) ? 0x80 : 0x0;
13531da177e4SLinus Torvalds 	if (msense_6) {
13541da177e4SLinus Torvalds 		arr[2] = dev_spec;
13551da177e4SLinus Torvalds 		offset = 4;
13561da177e4SLinus Torvalds 	} else {
13571da177e4SLinus Torvalds 		arr[3] = dev_spec;
13581da177e4SLinus Torvalds 		offset = 8;
13591da177e4SLinus Torvalds 	}
13601da177e4SLinus Torvalds 	ap = arr + offset;
13611da177e4SLinus Torvalds 
1362c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1363c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
13641da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
13651da177e4SLinus Torvalds 			       	0);
13661da177e4SLinus Torvalds 		return check_condition_result;
13671da177e4SLinus Torvalds 	}
13681da177e4SLinus Torvalds 	switch (pcode) {
13691da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
13701da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
13711da177e4SLinus Torvalds 		offset += len;
13721da177e4SLinus Torvalds 		break;
13731da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
13741da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
13751da177e4SLinus Torvalds 		offset += len;
13761da177e4SLinus Torvalds 		break;
13771da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
13781da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
13791da177e4SLinus Torvalds                 offset += len;
13801da177e4SLinus Torvalds                 break;
13811da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
13821da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
13831da177e4SLinus Torvalds 		offset += len;
13841da177e4SLinus Torvalds 		break;
13851da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
13861da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
13871da177e4SLinus Torvalds 		offset += len;
13881da177e4SLinus Torvalds 		break;
1389c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1390c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1391c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1392c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1393c65b1445SDouglas Gilbert 			return check_condition_result;
1394c65b1445SDouglas Gilbert 	        }
1395c65b1445SDouglas Gilbert 		len = 0;
1396c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1397c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1398c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1399c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1400c65b1445SDouglas Gilbert 						  target_dev_id);
1401c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1402c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1403c65b1445SDouglas Gilbert 		offset += len;
1404c65b1445SDouglas Gilbert 		break;
14051da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
14061da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
14071da177e4SLinus Torvalds 		offset += len;
14081da177e4SLinus Torvalds 		break;
14091da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1410c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
14111da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
14121da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
14131da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
14141da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
14151da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1416c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1417c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1418c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1419c65b1445SDouglas Gilbert 						  target, target_dev_id);
1420c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1421c65b1445SDouglas Gilbert 			}
14221da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1423c65b1445SDouglas Gilbert 		} else {
1424c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1425c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1426c65b1445SDouglas Gilbert 			return check_condition_result;
1427c65b1445SDouglas Gilbert                 }
14281da177e4SLinus Torvalds 		offset += len;
14291da177e4SLinus Torvalds 		break;
14301da177e4SLinus Torvalds 	default:
14311da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14321da177e4SLinus Torvalds 			       	0);
14331da177e4SLinus Torvalds 		return check_condition_result;
14341da177e4SLinus Torvalds 	}
14351da177e4SLinus Torvalds 	if (msense_6)
14361da177e4SLinus Torvalds 		arr[0] = offset - 1;
14371da177e4SLinus Torvalds 	else {
14381da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
14391da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
14401da177e4SLinus Torvalds 	}
14411da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
14421da177e4SLinus Torvalds }
14431da177e4SLinus Torvalds 
1444c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1445c65b1445SDouglas Gilbert 
1446c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1447c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1448c65b1445SDouglas Gilbert {
1449c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1450c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1451c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1452c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1453c65b1445SDouglas Gilbert 
1454c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1455c65b1445SDouglas Gilbert 		return errsts;
1456c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1457c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1458c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1459c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1460c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1461c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1462c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1463c65b1445SDouglas Gilbert 		return check_condition_result;
1464c65b1445SDouglas Gilbert 	}
1465c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1466c65b1445SDouglas Gilbert         if (-1 == res)
1467c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1468c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1469c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1470c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1471c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1472c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1473c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
1474c65b1445SDouglas Gilbert 	if ((md_len > 2) || (0 != bd_len)) {
1475c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1476c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1477c65b1445SDouglas Gilbert 		return check_condition_result;
1478c65b1445SDouglas Gilbert 	}
1479c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1480c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1481c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1482c65b1445SDouglas Gilbert 	if (ps) {
1483c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1484c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1485c65b1445SDouglas Gilbert 		return check_condition_result;
1486c65b1445SDouglas Gilbert 	}
1487c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1488c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1489c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1490c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1491c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1492c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1493c65b1445SDouglas Gilbert 		return check_condition_result;
1494c65b1445SDouglas Gilbert 	}
1495c65b1445SDouglas Gilbert 	switch (mpage) {
1496c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1497c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1498c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1499c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1500c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1501c65b1445SDouglas Gilbert 			return 0;
1502c65b1445SDouglas Gilbert 		}
1503c65b1445SDouglas Gilbert 		break;
1504c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1505c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1506c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1507c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1508c65b1445SDouglas Gilbert 			return 0;
1509c65b1445SDouglas Gilbert 		}
1510c65b1445SDouglas Gilbert 		break;
1511c65b1445SDouglas Gilbert 	default:
1512c65b1445SDouglas Gilbert 		break;
1513c65b1445SDouglas Gilbert 	}
1514c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1515c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1516c65b1445SDouglas Gilbert 	return check_condition_result;
1517c65b1445SDouglas Gilbert }
1518c65b1445SDouglas Gilbert 
1519c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1520c65b1445SDouglas Gilbert {
1521c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1522c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1523c65b1445SDouglas Gilbert 		};
1524c65b1445SDouglas Gilbert 
1525c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1526c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1527c65b1445SDouglas Gilbert }
1528c65b1445SDouglas Gilbert 
1529c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1530c65b1445SDouglas Gilbert {
1531c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1532c65b1445SDouglas Gilbert 		};
1533c65b1445SDouglas Gilbert 
1534c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1535c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1536c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1537c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1538c65b1445SDouglas Gilbert 	}
1539c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1540c65b1445SDouglas Gilbert }
1541c65b1445SDouglas Gilbert 
1542c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1543c65b1445SDouglas Gilbert 
1544c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1545c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1546c65b1445SDouglas Gilbert {
1547c65b1445SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, alloc_len, errsts, len, n;
1548c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1549c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1550c65b1445SDouglas Gilbert 
1551c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1552c65b1445SDouglas Gilbert 		return errsts;
1553c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1554c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1555c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1556c65b1445SDouglas Gilbert 	if (ppc || sp) {
1557c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1558c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1559c65b1445SDouglas Gilbert 		return check_condition_result;
1560c65b1445SDouglas Gilbert 	}
1561c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1562c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
1563c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1564c65b1445SDouglas Gilbert 	arr[0] = pcode;
1565c65b1445SDouglas Gilbert 	switch (pcode) {
1566c65b1445SDouglas Gilbert 	case 0x0:	/* Supported log pages log page */
1567c65b1445SDouglas Gilbert 		n = 4;
1568c65b1445SDouglas Gilbert 		arr[n++] = 0x0;		/* this page */
1569c65b1445SDouglas Gilbert 		arr[n++] = 0xd;		/* Temperature */
1570c65b1445SDouglas Gilbert 		arr[n++] = 0x2f;	/* Informational exceptions */
1571c65b1445SDouglas Gilbert 		arr[3] = n - 4;
1572c65b1445SDouglas Gilbert 		break;
1573c65b1445SDouglas Gilbert 	case 0xd:	/* Temperature log page */
1574c65b1445SDouglas Gilbert 		arr[3] = resp_temp_l_pg(arr + 4);
1575c65b1445SDouglas Gilbert 		break;
1576c65b1445SDouglas Gilbert 	case 0x2f:	/* Informational exceptions log page */
1577c65b1445SDouglas Gilbert 		arr[3] = resp_ie_l_pg(arr + 4);
1578c65b1445SDouglas Gilbert 		break;
1579c65b1445SDouglas Gilbert 	default:
1580c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1581c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1582c65b1445SDouglas Gilbert 		return check_condition_result;
1583c65b1445SDouglas Gilbert 	}
1584c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1585c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1586c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1587c65b1445SDouglas Gilbert }
1588c65b1445SDouglas Gilbert 
1589c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1590c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
15911da177e4SLinus Torvalds {
15921da177e4SLinus Torvalds 	unsigned long iflags;
1593c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1594c65b1445SDouglas Gilbert 	unsigned long long u;
15951da177e4SLinus Torvalds 	int ret;
15961da177e4SLinus Torvalds 
1597c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
15981da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
15991da177e4SLinus Torvalds 				0);
16001da177e4SLinus Torvalds 		return check_condition_result;
16011da177e4SLinus Torvalds 	}
1602c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1603c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1604c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1605c65b1445SDouglas Gilbert 				0);
1606c65b1445SDouglas Gilbert 		return check_condition_result;
1607c65b1445SDouglas Gilbert 	}
16081da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1609c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1610c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1611c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
16121da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
16131da177e4SLinus Torvalds 				0);
1614c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1615c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1616c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1617c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1618c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1619c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1620c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1621c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1622c65b1445SDouglas Gilbert 		}
16231da177e4SLinus Torvalds 		return check_condition_result;
16241da177e4SLinus Torvalds 	}
16251da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1626c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1627c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1628c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
16291da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1630c65b1445SDouglas Gilbert 	else {
1631c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1632c65b1445SDouglas Gilbert 		u = lba;
1633c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1634c65b1445SDouglas Gilbert 		from_bottom = 0;
1635c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1636c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1637c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1638c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1639c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1640c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1641c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1642c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1643c65b1445SDouglas Gilbert 	}
16441da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
16451da177e4SLinus Torvalds 	return ret;
16461da177e4SLinus Torvalds }
16471da177e4SLinus Torvalds 
1648c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1649c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
16501da177e4SLinus Torvalds {
16511da177e4SLinus Torvalds 	unsigned long iflags;
1652c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1653c65b1445SDouglas Gilbert 	unsigned long long u;
16541da177e4SLinus Torvalds 	int res;
16551da177e4SLinus Torvalds 
1656c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
16571da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
16581da177e4SLinus Torvalds 			       	0);
16591da177e4SLinus Torvalds 		return check_condition_result;
16601da177e4SLinus Torvalds 	}
1661c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1662c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1663c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1664c65b1445SDouglas Gilbert 				0);
1665c65b1445SDouglas Gilbert 		return check_condition_result;
1666c65b1445SDouglas Gilbert 	}
16671da177e4SLinus Torvalds 
16681da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1669c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1670c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1671c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
16721da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1673c65b1445SDouglas Gilbert 	else {
1674c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1675c65b1445SDouglas Gilbert 		u = lba;
1676c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1677c65b1445SDouglas Gilbert 		to_bottom = 0;
1678c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1679c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1680c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1681c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1682c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1683c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1684c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1685c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1686c65b1445SDouglas Gilbert 	}
16871da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
16881da177e4SLinus Torvalds 	if (-1 == res)
16891da177e4SLinus Torvalds 		return (DID_ERROR << 16);
16901da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
16911da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1692c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
16931da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
16941da177e4SLinus Torvalds 	return 0;
16951da177e4SLinus Torvalds }
16961da177e4SLinus Torvalds 
1697c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
16981da177e4SLinus Torvalds 
16991da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
17001da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
17011da177e4SLinus Torvalds {
17021da177e4SLinus Torvalds 	unsigned int alloc_len;
1703c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
17041da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
17051da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
17061da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
17071da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1708c65b1445SDouglas Gilbert 	unsigned char * max_addr;
17091da177e4SLinus Torvalds 
17101da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1711c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
17121da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
17131da177e4SLinus Torvalds 			       	0);
17141da177e4SLinus Torvalds 		return check_condition_result;
17151da177e4SLinus Torvalds 	}
17161da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
17171da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
17181da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1719c65b1445SDouglas Gilbert 	if (1 == select_report)
1720c65b1445SDouglas Gilbert 		lun_cnt = 0;
1721c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1722c65b1445SDouglas Gilbert 		--lun_cnt;
1723c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1724c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1725c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1726c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1727c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1728c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1729c65b1445SDouglas Gilbert 	if (n < num) {
1730c65b1445SDouglas Gilbert 		wlun = 0;
1731c65b1445SDouglas Gilbert 		lun_cnt = n;
1732c65b1445SDouglas Gilbert 	}
17331da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1734c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1735c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1736c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1737c65b1445SDouglas Gilbert 	     i++, lun++) {
1738c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
17391da177e4SLinus Torvalds 		if (upper)
17401da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
17411da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1742c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
17431da177e4SLinus Torvalds 	}
1744c65b1445SDouglas Gilbert 	if (wlun) {
1745c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1746c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1747c65b1445SDouglas Gilbert 		i++;
1748c65b1445SDouglas Gilbert 	}
1749c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
17501da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
17511da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
17521da177e4SLinus Torvalds }
17531da177e4SLinus Torvalds 
17541da177e4SLinus Torvalds /* When timer goes off this function is called. */
17551da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
17561da177e4SLinus Torvalds {
17571da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
17581da177e4SLinus Torvalds 	unsigned long iflags;
17591da177e4SLinus Torvalds 
17601da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
17611da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
17621da177e4SLinus Torvalds 		       "large\n");
17631da177e4SLinus Torvalds 		return;
17641da177e4SLinus Torvalds 	}
17651da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
17661da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
17671da177e4SLinus Torvalds 	if (! sqcp->in_use) {
17681da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
17691da177e4SLinus Torvalds 		       "interrupt\n");
17701da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
17711da177e4SLinus Torvalds 		return;
17721da177e4SLinus Torvalds 	}
17731da177e4SLinus Torvalds 	sqcp->in_use = 0;
17741da177e4SLinus Torvalds 	if (sqcp->done_funct) {
17751da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
17761da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
17771da177e4SLinus Torvalds 	}
17781da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
17791da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
17801da177e4SLinus Torvalds }
17811da177e4SLinus Torvalds 
17821da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
17831da177e4SLinus Torvalds {
17841da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1785c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
1786c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
17871da177e4SLinus Torvalds 	return 0;
17881da177e4SLinus Torvalds }
17891da177e4SLinus Torvalds 
17901da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
17911da177e4SLinus Torvalds {
17921da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
17931da177e4SLinus Torvalds 
17941da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1795c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1796c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
17971da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
17981da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
17991da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
18001da177e4SLinus Torvalds 	sdp->hostdata = devip;
18011da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
18021da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
18031da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
1804c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
18051da177e4SLinus Torvalds 	return 0;
18061da177e4SLinus Torvalds }
18071da177e4SLinus Torvalds 
18081da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
18091da177e4SLinus Torvalds {
18101da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
18111da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
18121da177e4SLinus Torvalds 
18131da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1814c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
1815c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
18161da177e4SLinus Torvalds 	if (devip) {
18171da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
18181da177e4SLinus Torvalds 		devip->used = 0;
18191da177e4SLinus Torvalds 		sdp->hostdata = NULL;
18201da177e4SLinus Torvalds 	}
18211da177e4SLinus Torvalds }
18221da177e4SLinus Torvalds 
18231da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
18241da177e4SLinus Torvalds {
18251da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
18261da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
18271da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
18281da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
18291da177e4SLinus Torvalds 
18301da177e4SLinus Torvalds 	if (devip)
18311da177e4SLinus Torvalds 		return devip;
18321da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
18331da177e4SLinus Torvalds         if(! sdbg_host) {
18341da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
18351da177e4SLinus Torvalds 		return NULL;
18361da177e4SLinus Torvalds         }
18371da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
18381da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
18391da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
18401da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
18411da177e4SLinus Torvalds                         return devip;
18421da177e4SLinus Torvalds 		else {
18431da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
18441da177e4SLinus Torvalds 				open_devip = devip;
18451da177e4SLinus Torvalds 		}
18461da177e4SLinus Torvalds 	}
18471da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
184824669f75SJes Sorensen 		open_devip = kzalloc(sizeof(*open_devip),GFP_KERNEL);
18491da177e4SLinus Torvalds 		if (NULL == open_devip) {
18501da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
18511da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
18521da177e4SLinus Torvalds 			return NULL;
18531da177e4SLinus Torvalds 		}
18541da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
18551da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
18561da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
18571da177e4SLinus Torvalds 	}
18581da177e4SLinus Torvalds         if (open_devip) {
18591da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
18601da177e4SLinus Torvalds 		open_devip->target = sdev->id;
18611da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
18621da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
18631da177e4SLinus Torvalds 		open_devip->reset = 1;
18641da177e4SLinus Torvalds 		open_devip->used = 1;
18651da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
18661da177e4SLinus Torvalds 		if (scsi_debug_dsense)
18671da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
18681da177e4SLinus Torvalds 		else {
18691da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
18701da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
18711da177e4SLinus Torvalds 		}
1872c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
1873c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
18741da177e4SLinus Torvalds 		return open_devip;
18751da177e4SLinus Torvalds         }
18761da177e4SLinus Torvalds         return NULL;
18771da177e4SLinus Torvalds }
18781da177e4SLinus Torvalds 
18791da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
18801da177e4SLinus Torvalds 			    int asc, int asq)
18811da177e4SLinus Torvalds {
18821da177e4SLinus Torvalds 	unsigned char * sbuff;
18831da177e4SLinus Torvalds 
18841da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
18851da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
18861da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
18871da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
18881da177e4SLinus Torvalds 		sbuff[1] = key;
18891da177e4SLinus Torvalds 		sbuff[2] = asc;
18901da177e4SLinus Torvalds 		sbuff[3] = asq;
18911da177e4SLinus Torvalds 	} else {
18921da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
18931da177e4SLinus Torvalds 		sbuff[2] = key;
18941da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
18951da177e4SLinus Torvalds 		sbuff[12] = asc;
18961da177e4SLinus Torvalds 		sbuff[13] = asq;
18971da177e4SLinus Torvalds 	}
18981da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
18991da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
19001da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
19011da177e4SLinus Torvalds }
19021da177e4SLinus Torvalds 
19031da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
19041da177e4SLinus Torvalds {
19051da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19061da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
19071da177e4SLinus Torvalds 	++num_aborts;
19081da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
19091da177e4SLinus Torvalds 	return SUCCESS;
19101da177e4SLinus Torvalds }
19111da177e4SLinus Torvalds 
19121da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
19131da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
19141da177e4SLinus Torvalds {
19151da177e4SLinus Torvalds 	int res;
19161da177e4SLinus Torvalds 	unsigned char *buf;
19171da177e4SLinus Torvalds 
19181da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19191da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
19201da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
19211da177e4SLinus Torvalds 	if (buf) {
19221da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
19231da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
19241da177e4SLinus Torvalds 		kfree(buf);
19251da177e4SLinus Torvalds 		if (! res)
19261da177e4SLinus Torvalds 			return res;
19271da177e4SLinus Torvalds 	}
19281da177e4SLinus Torvalds 	info[0] = sdebug_heads;
19291da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
19301da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
19311da177e4SLinus Torvalds 	return 0;
19321da177e4SLinus Torvalds }
19331da177e4SLinus Torvalds 
19341da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
19351da177e4SLinus Torvalds {
19361da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
19371da177e4SLinus Torvalds 
19381da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19391da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
19401da177e4SLinus Torvalds 	++num_dev_resets;
19411da177e4SLinus Torvalds 	if (SCpnt) {
19421da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
19431da177e4SLinus Torvalds 		if (devip)
19441da177e4SLinus Torvalds 			devip->reset = 1;
19451da177e4SLinus Torvalds 	}
19461da177e4SLinus Torvalds 	return SUCCESS;
19471da177e4SLinus Torvalds }
19481da177e4SLinus Torvalds 
19491da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
19501da177e4SLinus Torvalds {
19511da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
19521da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
19531da177e4SLinus Torvalds         struct scsi_device * sdp;
19541da177e4SLinus Torvalds         struct Scsi_Host * hp;
19551da177e4SLinus Torvalds 
19561da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19571da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
19581da177e4SLinus Torvalds 	++num_bus_resets;
19591da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
19601da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
19611da177e4SLinus Torvalds 		if (sdbg_host) {
19621da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
19631da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
19641da177e4SLinus Torvalds                                             dev_list)
19651da177e4SLinus Torvalds 				dev_info->reset = 1;
19661da177e4SLinus Torvalds 		}
19671da177e4SLinus Torvalds 	}
19681da177e4SLinus Torvalds 	return SUCCESS;
19691da177e4SLinus Torvalds }
19701da177e4SLinus Torvalds 
19711da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
19721da177e4SLinus Torvalds {
19731da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
19741da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
19771da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
19781da177e4SLinus Torvalds 	++num_host_resets;
19791da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
19801da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
19811da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
19821da177e4SLinus Torvalds                                     dev_list)
19831da177e4SLinus Torvalds                         dev_info->reset = 1;
19841da177e4SLinus Torvalds         }
19851da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
19861da177e4SLinus Torvalds 	stop_all_queued();
19871da177e4SLinus Torvalds 	return SUCCESS;
19881da177e4SLinus Torvalds }
19891da177e4SLinus Torvalds 
19901da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
19911da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
19921da177e4SLinus Torvalds {
19931da177e4SLinus Torvalds 	unsigned long iflags;
19941da177e4SLinus Torvalds 	int k;
19951da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
19961da177e4SLinus Torvalds 
19971da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
19981da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
19991da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
20001da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
20011da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
20021da177e4SLinus Torvalds 			sqcp->in_use = 0;
20031da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
20041da177e4SLinus Torvalds 			break;
20051da177e4SLinus Torvalds 		}
20061da177e4SLinus Torvalds 	}
20071da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20081da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
20091da177e4SLinus Torvalds }
20101da177e4SLinus Torvalds 
20111da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
20121da177e4SLinus Torvalds static void stop_all_queued(void)
20131da177e4SLinus Torvalds {
20141da177e4SLinus Torvalds 	unsigned long iflags;
20151da177e4SLinus Torvalds 	int k;
20161da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
20171da177e4SLinus Torvalds 
20181da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
20191da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
20201da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
20211da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
20221da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
20231da177e4SLinus Torvalds 			sqcp->in_use = 0;
20241da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
20251da177e4SLinus Torvalds 		}
20261da177e4SLinus Torvalds 	}
20271da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20281da177e4SLinus Torvalds }
20291da177e4SLinus Torvalds 
20301da177e4SLinus Torvalds /* Initializes timers in queued array */
20311da177e4SLinus Torvalds static void __init init_all_queued(void)
20321da177e4SLinus Torvalds {
20331da177e4SLinus Torvalds 	unsigned long iflags;
20341da177e4SLinus Torvalds 	int k;
20351da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
20361da177e4SLinus Torvalds 
20371da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
20381da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
20391da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
20401da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
20411da177e4SLinus Torvalds 		sqcp->in_use = 0;
20421da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
20431da177e4SLinus Torvalds 	}
20441da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20451da177e4SLinus Torvalds }
20461da177e4SLinus Torvalds 
20471da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
20481da177e4SLinus Torvalds {
20491da177e4SLinus Torvalds 	struct partition * pp;
20501da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
20511da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
20521da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
20531da177e4SLinus Torvalds 
20541da177e4SLinus Torvalds 	/* assume partition table already zeroed */
20551da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
20561da177e4SLinus Torvalds 		return;
20571da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
20581da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
20591da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
20601da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
20611da177e4SLinus Torvalds 	}
2062c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
20631da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
20641da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
20651da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
20661da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
20671da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
20681da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
20691da177e4SLinus Torvalds 			    * heads_by_sects;
20701da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
20711da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
20721da177e4SLinus Torvalds 
20731da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
20741da177e4SLinus Torvalds 	ramp[511] = 0xAA;
20751da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
20761da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
20771da177e4SLinus Torvalds 		start_sec = starts[k];
20781da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
20791da177e4SLinus Torvalds 		pp->boot_ind = 0;
20801da177e4SLinus Torvalds 
20811da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
20821da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
20831da177e4SLinus Torvalds 			   / sdebug_sectors_per;
20841da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
20851da177e4SLinus Torvalds 
20861da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
20871da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
20881da177e4SLinus Torvalds 			       / sdebug_sectors_per;
20891da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
20901da177e4SLinus Torvalds 
20911da177e4SLinus Torvalds 		pp->start_sect = start_sec;
20921da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
20931da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
20941da177e4SLinus Torvalds 	}
20951da177e4SLinus Torvalds }
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
20981da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
20991da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
21001da177e4SLinus Torvalds {
21011da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
21021da177e4SLinus Torvalds 		if (scsi_result) {
21031da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
21041da177e4SLinus Torvalds 
2105c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2106c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2107c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
21081da177e4SLinus Torvalds 		}
21091da177e4SLinus Torvalds 	}
21101da177e4SLinus Torvalds 	if (cmnd && devip) {
21111da177e4SLinus Torvalds 		/* simulate autosense by this driver */
21121da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
21131da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
21141da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
21151da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
21161da177e4SLinus Torvalds 	}
21171da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
21181da177e4SLinus Torvalds 		if (cmnd)
21191da177e4SLinus Torvalds 			cmnd->result = scsi_result;
21201da177e4SLinus Torvalds 		if (done)
21211da177e4SLinus Torvalds 			done(cmnd);
21221da177e4SLinus Torvalds 		return 0;
21231da177e4SLinus Torvalds 	} else {
21241da177e4SLinus Torvalds 		unsigned long iflags;
21251da177e4SLinus Torvalds 		int k;
21261da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
21271da177e4SLinus Torvalds 
21281da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
21291da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21301da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
21311da177e4SLinus Torvalds 			if (! sqcp->in_use)
21321da177e4SLinus Torvalds 				break;
21331da177e4SLinus Torvalds 		}
21341da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
21351da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
21361da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
21371da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
21381da177e4SLinus Torvalds 		}
21391da177e4SLinus Torvalds 		sqcp->in_use = 1;
21401da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
21411da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
21421da177e4SLinus Torvalds 		sqcp->done_funct = done;
21431da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
21441da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
21451da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
21461da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
21471da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
21481da177e4SLinus Torvalds 		if (cmnd)
21491da177e4SLinus Torvalds 			cmnd->result = 0;
21501da177e4SLinus Torvalds 		return 0;
21511da177e4SLinus Torvalds 	}
21521da177e4SLinus Torvalds }
21531da177e4SLinus Torvalds 
2154c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2155c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2156c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2157c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2158c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
2159c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2160c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2161c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2162c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2163c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2164c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2165c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2166c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
21671da177e4SLinus Torvalds 
21681da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
21691da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
21701da177e4SLinus Torvalds MODULE_LICENSE("GPL");
21711da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
21721da177e4SLinus Torvalds 
21731da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
21741da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2175c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2176c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
21771da177e4SLinus Torvalds MODULE_PARM_DESC(every_nth, "timeout every nth command(def=100)");
2178c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2179c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
21801da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2181c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
2182c65b1445SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_error, 4->... (def=0)");
21831da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
21841da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2185c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
21861da177e4SLinus Torvalds 
21871da177e4SLinus Torvalds 
21881da177e4SLinus Torvalds static char sdebug_info[256];
21891da177e4SLinus Torvalds 
21901da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
21911da177e4SLinus Torvalds {
21921da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
21931da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
21941da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
21951da177e4SLinus Torvalds 		scsi_debug_opts);
21961da177e4SLinus Torvalds 	return sdebug_info;
21971da177e4SLinus Torvalds }
21981da177e4SLinus Torvalds 
21991da177e4SLinus Torvalds /* scsi_debug_proc_info
22001da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
22011da177e4SLinus Torvalds  */
22021da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
22031da177e4SLinus Torvalds 				int length, int inout)
22041da177e4SLinus Torvalds {
22051da177e4SLinus Torvalds 	int len, pos, begin;
22061da177e4SLinus Torvalds 	int orig_length;
22071da177e4SLinus Torvalds 
22081da177e4SLinus Torvalds 	orig_length = length;
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds 	if (inout == 1) {
22111da177e4SLinus Torvalds 		char arr[16];
22121da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
22131da177e4SLinus Torvalds 
22141da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
22151da177e4SLinus Torvalds 			return -EACCES;
22161da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
22171da177e4SLinus Torvalds 		arr[minLen] = '\0';
22181da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
22191da177e4SLinus Torvalds 			return -EINVAL;
22201da177e4SLinus Torvalds 		scsi_debug_opts = pos;
22211da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
22221da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
22231da177e4SLinus Torvalds 		return length;
22241da177e4SLinus Torvalds 	}
22251da177e4SLinus Torvalds 	begin = 0;
22261da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
22271da177e4SLinus Torvalds 	    "%s [%s]\n"
22281da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
22291da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
22301da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
22311da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
22321da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
22331da177e4SLinus Torvalds 	    "host_resets=%d\n",
22341da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
22351da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
22361da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
22371da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
22381da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
22391da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
22401da177e4SLinus Torvalds 	if (pos < offset) {
22411da177e4SLinus Torvalds 		len = 0;
22421da177e4SLinus Torvalds 		begin = pos;
22431da177e4SLinus Torvalds 	}
22441da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
22451da177e4SLinus Torvalds 	len -= (offset - begin);
22461da177e4SLinus Torvalds 	if (len > length)
22471da177e4SLinus Torvalds 		len = length;
22481da177e4SLinus Torvalds 	return len;
22491da177e4SLinus Torvalds }
22501da177e4SLinus Torvalds 
22511da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
22521da177e4SLinus Torvalds {
22531da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
22541da177e4SLinus Torvalds }
22551da177e4SLinus Torvalds 
22561da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
22571da177e4SLinus Torvalds 				  const char * buf, size_t count)
22581da177e4SLinus Torvalds {
22591da177e4SLinus Torvalds         int delay;
22601da177e4SLinus Torvalds 	char work[20];
22611da177e4SLinus Torvalds 
22621da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
22631da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
22641da177e4SLinus Torvalds 			scsi_debug_delay = delay;
22651da177e4SLinus Torvalds 			return count;
22661da177e4SLinus Torvalds 		}
22671da177e4SLinus Torvalds 	}
22681da177e4SLinus Torvalds 	return -EINVAL;
22691da177e4SLinus Torvalds }
22701da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
22711da177e4SLinus Torvalds 	    sdebug_delay_store);
22721da177e4SLinus Torvalds 
22731da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
22741da177e4SLinus Torvalds {
22751da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
22761da177e4SLinus Torvalds }
22771da177e4SLinus Torvalds 
22781da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
22791da177e4SLinus Torvalds 				 const char * buf, size_t count)
22801da177e4SLinus Torvalds {
22811da177e4SLinus Torvalds         int opts;
22821da177e4SLinus Torvalds 	char work[20];
22831da177e4SLinus Torvalds 
22841da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
22851da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
22861da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
22871da177e4SLinus Torvalds 				goto opts_done;
22881da177e4SLinus Torvalds 		} else {
22891da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
22901da177e4SLinus Torvalds 				goto opts_done;
22911da177e4SLinus Torvalds 		}
22921da177e4SLinus Torvalds 	}
22931da177e4SLinus Torvalds 	return -EINVAL;
22941da177e4SLinus Torvalds opts_done:
22951da177e4SLinus Torvalds 	scsi_debug_opts = opts;
22961da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
22971da177e4SLinus Torvalds 	return count;
22981da177e4SLinus Torvalds }
22991da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
23001da177e4SLinus Torvalds 	    sdebug_opts_store);
23011da177e4SLinus Torvalds 
23021da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
23031da177e4SLinus Torvalds {
23041da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
23051da177e4SLinus Torvalds }
23061da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
23071da177e4SLinus Torvalds 				  const char * buf, size_t count)
23081da177e4SLinus Torvalds {
23091da177e4SLinus Torvalds         int n;
23101da177e4SLinus Torvalds 
23111da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
23121da177e4SLinus Torvalds 		scsi_debug_ptype = n;
23131da177e4SLinus Torvalds 		return count;
23141da177e4SLinus Torvalds 	}
23151da177e4SLinus Torvalds 	return -EINVAL;
23161da177e4SLinus Torvalds }
23171da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
23181da177e4SLinus Torvalds 
23191da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
23201da177e4SLinus Torvalds {
23211da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
23221da177e4SLinus Torvalds }
23231da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
23241da177e4SLinus Torvalds 				  const char * buf, size_t count)
23251da177e4SLinus Torvalds {
23261da177e4SLinus Torvalds         int n;
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
23291da177e4SLinus Torvalds 		scsi_debug_dsense = n;
23301da177e4SLinus Torvalds 		return count;
23311da177e4SLinus Torvalds 	}
23321da177e4SLinus Torvalds 	return -EINVAL;
23331da177e4SLinus Torvalds }
23341da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
23351da177e4SLinus Torvalds 	    sdebug_dsense_store);
23361da177e4SLinus Torvalds 
2337c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2338c65b1445SDouglas Gilbert {
2339c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2340c65b1445SDouglas Gilbert }
2341c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2342c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2343c65b1445SDouglas Gilbert {
2344c65b1445SDouglas Gilbert         int n;
2345c65b1445SDouglas Gilbert 
2346c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2347c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2348c65b1445SDouglas Gilbert 		return count;
2349c65b1445SDouglas Gilbert 	}
2350c65b1445SDouglas Gilbert 	return -EINVAL;
2351c65b1445SDouglas Gilbert }
2352c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2353c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2354c65b1445SDouglas Gilbert 
23551da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
23561da177e4SLinus Torvalds {
23571da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
23581da177e4SLinus Torvalds }
23591da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
23601da177e4SLinus Torvalds 				     const char * buf, size_t count)
23611da177e4SLinus Torvalds {
23621da177e4SLinus Torvalds         int n;
23631da177e4SLinus Torvalds 
23641da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
23651da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
23661da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
23671da177e4SLinus Torvalds 		return count;
23681da177e4SLinus Torvalds 	}
23691da177e4SLinus Torvalds 	return -EINVAL;
23701da177e4SLinus Torvalds }
23711da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
23721da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
23731da177e4SLinus Torvalds 
23741da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
23751da177e4SLinus Torvalds {
23761da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
23771da177e4SLinus Torvalds }
23781da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
23791da177e4SLinus Torvalds 
23801da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
23811da177e4SLinus Torvalds {
23821da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
23831da177e4SLinus Torvalds }
23841da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
23851da177e4SLinus Torvalds 
23861da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
23871da177e4SLinus Torvalds {
23881da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
23891da177e4SLinus Torvalds }
23901da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
23911da177e4SLinus Torvalds 				      const char * buf, size_t count)
23921da177e4SLinus Torvalds {
23931da177e4SLinus Torvalds         int nth;
23941da177e4SLinus Torvalds 
23951da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
23961da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
23971da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
23981da177e4SLinus Torvalds 		return count;
23991da177e4SLinus Torvalds 	}
24001da177e4SLinus Torvalds 	return -EINVAL;
24011da177e4SLinus Torvalds }
24021da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
24031da177e4SLinus Torvalds 	    sdebug_every_nth_store);
24041da177e4SLinus Torvalds 
24051da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
24061da177e4SLinus Torvalds {
24071da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
24081da177e4SLinus Torvalds }
24091da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
24101da177e4SLinus Torvalds 				     const char * buf, size_t count)
24111da177e4SLinus Torvalds {
24121da177e4SLinus Torvalds         int n;
24131da177e4SLinus Torvalds 
24141da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
24151da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
24161da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
24171da177e4SLinus Torvalds 		return count;
24181da177e4SLinus Torvalds 	}
24191da177e4SLinus Torvalds 	return -EINVAL;
24201da177e4SLinus Torvalds }
24211da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
24221da177e4SLinus Torvalds 	    sdebug_max_luns_store);
24231da177e4SLinus Torvalds 
24241da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
24251da177e4SLinus Torvalds {
24261da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
24271da177e4SLinus Torvalds }
24281da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
24291da177e4SLinus Torvalds 
2430c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2431c65b1445SDouglas Gilbert {
2432c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2433c65b1445SDouglas Gilbert }
2434c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2435c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2436c65b1445SDouglas Gilbert {
2437c65b1445SDouglas Gilbert         int n;
2438c65b1445SDouglas Gilbert 
2439c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2440c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2441c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2442c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2443c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2444c65b1445SDouglas Gilbert 		} else
2445c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2446c65b1445SDouglas Gilbert 		return count;
2447c65b1445SDouglas Gilbert 	}
2448c65b1445SDouglas Gilbert 	return -EINVAL;
2449c65b1445SDouglas Gilbert }
2450c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2451c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2452c65b1445SDouglas Gilbert 
24531da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
24541da177e4SLinus Torvalds {
24551da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
24561da177e4SLinus Torvalds }
24571da177e4SLinus Torvalds 
24581da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
24591da177e4SLinus Torvalds 				     const char * buf, size_t count)
24601da177e4SLinus Torvalds {
24611da177e4SLinus Torvalds         int delta_hosts;
24621da177e4SLinus Torvalds 	char work[20];
24631da177e4SLinus Torvalds 
24641da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
24651da177e4SLinus Torvalds 		return -EINVAL;
24661da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
24671da177e4SLinus Torvalds 		int neg = 0;
24681da177e4SLinus Torvalds 
24691da177e4SLinus Torvalds 		if ('-' == *work)
24701da177e4SLinus Torvalds 			neg = 1;
24711da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
24721da177e4SLinus Torvalds 			return -EINVAL;
24731da177e4SLinus Torvalds 		if (neg)
24741da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
24751da177e4SLinus Torvalds 	}
24761da177e4SLinus Torvalds 	if (delta_hosts > 0) {
24771da177e4SLinus Torvalds 		do {
24781da177e4SLinus Torvalds 			sdebug_add_adapter();
24791da177e4SLinus Torvalds 		} while (--delta_hosts);
24801da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
24811da177e4SLinus Torvalds 		do {
24821da177e4SLinus Torvalds 			sdebug_remove_adapter();
24831da177e4SLinus Torvalds 		} while (++delta_hosts);
24841da177e4SLinus Torvalds 	}
24851da177e4SLinus Torvalds 	return count;
24861da177e4SLinus Torvalds }
24871da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
24881da177e4SLinus Torvalds 	    sdebug_add_host_store);
24891da177e4SLinus Torvalds 
24906ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
24911da177e4SLinus Torvalds {
24926ecaff7fSRandy Dunlap 	int ret;
24936ecaff7fSRandy Dunlap 
24946ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
24956ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
24966ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
24976ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
24986ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
24996ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
25006ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
25016ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
25026ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
25036ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
25046ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
25056ecaff7fSRandy Dunlap 	return ret;
25061da177e4SLinus Torvalds }
25071da177e4SLinus Torvalds 
25081da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
25091da177e4SLinus Torvalds {
25101da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
25111da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
25121da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
25131da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
25141da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
25151da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
25161da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
25171da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
25181da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
25191da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
25201da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
25211da177e4SLinus Torvalds }
25221da177e4SLinus Torvalds 
25231da177e4SLinus Torvalds static int __init scsi_debug_init(void)
25241da177e4SLinus Torvalds {
2525c65b1445SDouglas Gilbert 	unsigned int sz;
25261da177e4SLinus Torvalds 	int host_to_add;
25271da177e4SLinus Torvalds 	int k;
25286ecaff7fSRandy Dunlap 	int ret;
25291da177e4SLinus Torvalds 
25301da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
25311da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2532c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2533c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2534c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2535c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2536c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2537c65b1445SDouglas Gilbert 	} else
2538c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
25391da177e4SLinus Torvalds 
25401da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
25411da177e4SLinus Torvalds 	sdebug_heads = 8;
25421da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
25431da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
25441da177e4SLinus Torvalds 		sdebug_heads = 32;
25451da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
25461da177e4SLinus Torvalds 		sdebug_heads = 64;
25471da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
25481da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
25491da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
25501da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
25511da177e4SLinus Torvalds 		sdebug_heads = 255;
25521da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
25531da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
25541da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
25551da177e4SLinus Torvalds 	}
25561da177e4SLinus Torvalds 
25571da177e4SLinus Torvalds 	sz = sdebug_store_size;
25581da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
25591da177e4SLinus Torvalds 	if (NULL == fake_storep) {
25601da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
25611da177e4SLinus Torvalds 		return -ENOMEM;
25621da177e4SLinus Torvalds 	}
25631da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
25641da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
25651da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
25661da177e4SLinus Torvalds 
25676ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
25686ecaff7fSRandy Dunlap 	if (ret < 0) {
25696ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
25706ecaff7fSRandy Dunlap 			ret);
25716ecaff7fSRandy Dunlap 		goto free_vm;
25726ecaff7fSRandy Dunlap 	}
25736ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
25746ecaff7fSRandy Dunlap 	if (ret < 0) {
25756ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
25766ecaff7fSRandy Dunlap 			ret);
25776ecaff7fSRandy Dunlap 		goto dev_unreg;
25786ecaff7fSRandy Dunlap 	}
25796ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
25806ecaff7fSRandy Dunlap 	if (ret < 0) {
25816ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
25826ecaff7fSRandy Dunlap 			ret);
25836ecaff7fSRandy Dunlap 		goto bus_unreg;
25846ecaff7fSRandy Dunlap 	}
25856ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
25866ecaff7fSRandy Dunlap 	if (ret < 0) {
25876ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
25886ecaff7fSRandy Dunlap 			ret);
25896ecaff7fSRandy Dunlap 		goto del_files;
25906ecaff7fSRandy Dunlap 	}
25911da177e4SLinus Torvalds 
25926ecaff7fSRandy Dunlap 	init_all_queued();
25931da177e4SLinus Torvalds 
25941da177e4SLinus Torvalds 	sdebug_driver_template.proc_name = (char *)sdebug_proc_name;
25951da177e4SLinus Torvalds 
25961da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
25971da177e4SLinus Torvalds         scsi_debug_add_host = 0;
25981da177e4SLinus Torvalds 
25991da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
26001da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
26011da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
26021da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
26031da177e4SLinus Torvalds                         break;
26041da177e4SLinus Torvalds                 }
26051da177e4SLinus Torvalds         }
26061da177e4SLinus Torvalds 
26071da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
26081da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
26091da177e4SLinus Torvalds 		       scsi_debug_add_host);
26101da177e4SLinus Torvalds 	}
26111da177e4SLinus Torvalds 	return 0;
26126ecaff7fSRandy Dunlap 
26136ecaff7fSRandy Dunlap del_files:
26146ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
26156ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
26166ecaff7fSRandy Dunlap bus_unreg:
26176ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
26186ecaff7fSRandy Dunlap dev_unreg:
26196ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
26206ecaff7fSRandy Dunlap free_vm:
26216ecaff7fSRandy Dunlap 	vfree(fake_storep);
26226ecaff7fSRandy Dunlap 
26236ecaff7fSRandy Dunlap 	return ret;
26241da177e4SLinus Torvalds }
26251da177e4SLinus Torvalds 
26261da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
26271da177e4SLinus Torvalds {
26281da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
26291da177e4SLinus Torvalds 
26301da177e4SLinus Torvalds 	stop_all_queued();
26311da177e4SLinus Torvalds 	for (; k; k--)
26321da177e4SLinus Torvalds 		sdebug_remove_adapter();
26331da177e4SLinus Torvalds 	do_remove_driverfs_files();
26341da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
26351da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
26361da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
26371da177e4SLinus Torvalds 
26381da177e4SLinus Torvalds 	vfree(fake_storep);
26391da177e4SLinus Torvalds }
26401da177e4SLinus Torvalds 
26411da177e4SLinus Torvalds device_initcall(scsi_debug_init);
26421da177e4SLinus Torvalds module_exit(scsi_debug_exit);
26431da177e4SLinus Torvalds 
264452c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
26451da177e4SLinus Torvalds {
26461da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
26471da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
26481da177e4SLinus Torvalds }
26491da177e4SLinus Torvalds 
26501da177e4SLinus Torvalds static struct device pseudo_primary = {
26511da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
26521da177e4SLinus Torvalds 	.release	= pseudo_0_release,
26531da177e4SLinus Torvalds };
26541da177e4SLinus Torvalds 
26551da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
26561da177e4SLinus Torvalds                           struct device_driver *dev_driver)
26571da177e4SLinus Torvalds {
26581da177e4SLinus Torvalds         return 1;
26591da177e4SLinus Torvalds }
26601da177e4SLinus Torvalds 
26611da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
26621da177e4SLinus Torvalds         .name = "pseudo",
26631da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2664bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2665bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
26661da177e4SLinus Torvalds };
26671da177e4SLinus Torvalds 
26681da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
26691da177e4SLinus Torvalds {
26701da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
26711da177e4SLinus Torvalds 
26721da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
26731da177e4SLinus Torvalds         kfree(sdbg_host);
26741da177e4SLinus Torvalds }
26751da177e4SLinus Torvalds 
26761da177e4SLinus Torvalds static int sdebug_add_adapter(void)
26771da177e4SLinus Torvalds {
26781da177e4SLinus Torvalds 	int k, devs_per_host;
26791da177e4SLinus Torvalds         int error = 0;
26801da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
26811da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
26821da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
26831da177e4SLinus Torvalds 
268424669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
26851da177e4SLinus Torvalds 
26861da177e4SLinus Torvalds         if (NULL == sdbg_host) {
26871da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
26881da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
26891da177e4SLinus Torvalds                 return -ENOMEM;
26901da177e4SLinus Torvalds         }
26911da177e4SLinus Torvalds 
26921da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
26931da177e4SLinus Torvalds 
26941da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
26951da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
269624669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
26971da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
26981da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
26991da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
27001da177e4SLinus Torvalds                         error = -ENOMEM;
27011da177e4SLinus Torvalds 			goto clean;
27021da177e4SLinus Torvalds                 }
27031da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
27041da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
27051da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
27061da177e4SLinus Torvalds         }
27071da177e4SLinus Torvalds 
27081da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
27091da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
27101da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
27111da177e4SLinus Torvalds 
27121da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
27131da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
27141da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
27151da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
27161da177e4SLinus Torvalds 
27171da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
27181da177e4SLinus Torvalds 
27191da177e4SLinus Torvalds         if (error)
27201da177e4SLinus Torvalds 		goto clean;
27211da177e4SLinus Torvalds 
27221da177e4SLinus Torvalds 	++scsi_debug_add_host;
27231da177e4SLinus Torvalds         return error;
27241da177e4SLinus Torvalds 
27251da177e4SLinus Torvalds clean:
27261da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
27271da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
27281da177e4SLinus Torvalds 					  dev_list);
27291da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
27301da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
27311da177e4SLinus Torvalds 	}
27321da177e4SLinus Torvalds 
27331da177e4SLinus Torvalds 	kfree(sdbg_host);
27341da177e4SLinus Torvalds         return error;
27351da177e4SLinus Torvalds }
27361da177e4SLinus Torvalds 
27371da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
27381da177e4SLinus Torvalds {
27391da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
27401da177e4SLinus Torvalds 
27411da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
27421da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
27431da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
27441da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
27451da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
27461da177e4SLinus Torvalds 	}
27471da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
27481da177e4SLinus Torvalds 
27491da177e4SLinus Torvalds 	if (!sdbg_host)
27501da177e4SLinus Torvalds 		return;
27511da177e4SLinus Torvalds 
27521da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
27531da177e4SLinus Torvalds         --scsi_debug_add_host;
27541da177e4SLinus Torvalds }
27551da177e4SLinus Torvalds 
27561da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
27571da177e4SLinus Torvalds {
27581da177e4SLinus Torvalds         int error = 0;
27591da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
27601da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
27611da177e4SLinus Torvalds 
27621da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
27631da177e4SLinus Torvalds 
27641da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
27651da177e4SLinus Torvalds         if (NULL == hpnt) {
27661da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
27671da177e4SLinus Torvalds                 error = -ENODEV;
27681da177e4SLinus Torvalds 		return error;
27691da177e4SLinus Torvalds         }
27701da177e4SLinus Torvalds 
27711da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
27721da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
27731da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
27741da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
27751da177e4SLinus Torvalds 	else
27761da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
2777c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
27781da177e4SLinus Torvalds 
27791da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
27801da177e4SLinus Torvalds         if (error) {
27811da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
27821da177e4SLinus Torvalds                 error = -ENODEV;
27831da177e4SLinus Torvalds 		scsi_host_put(hpnt);
27841da177e4SLinus Torvalds         } else
27851da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
27861da177e4SLinus Torvalds 
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds         return error;
27891da177e4SLinus Torvalds }
27901da177e4SLinus Torvalds 
27911da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
27921da177e4SLinus Torvalds {
27931da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
27941da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
27951da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
27961da177e4SLinus Torvalds 
27971da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
27981da177e4SLinus Torvalds 
27991da177e4SLinus Torvalds 	if (!sdbg_host) {
28001da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
28011da177e4SLinus Torvalds 		       __FUNCTION__);
28021da177e4SLinus Torvalds 		return -ENODEV;
28031da177e4SLinus Torvalds 	}
28041da177e4SLinus Torvalds 
28051da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
28061da177e4SLinus Torvalds 
28071da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
28081da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
28091da177e4SLinus Torvalds                                           dev_list);
28101da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
28111da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
28121da177e4SLinus Torvalds         }
28131da177e4SLinus Torvalds 
28141da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
28151da177e4SLinus Torvalds         return 0;
28161da177e4SLinus Torvalds }
28171da177e4SLinus Torvalds 
28181da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
28191da177e4SLinus Torvalds {
28201da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
28211da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
28221da177e4SLinus Torvalds 
28231da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
28241da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
28251da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
28261da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
28271da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
28281da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
28291da177e4SLinus Torvalds 		else
28301da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
2831c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
28321da177e4SLinus Torvalds 	}
28331da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
28341da177e4SLinus Torvalds }
2835