xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 3de9f944)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
31da177e4SLinus Torvalds  *  Copyright (C) 1992  Eric Youngdale
41da177e4SLinus Torvalds  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
51da177e4SLinus Torvalds  *  to make sure that we are not getting blocks mixed up, and PANIC if
61da177e4SLinus Torvalds  *  anything out of the ordinary is seen.
71da177e4SLinus Torvalds  * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This version is more generic, simulating a variable number of disk
1023183910SDouglas Gilbert  *  (or disk like devices) sharing a common amount of RAM. To be more
1123183910SDouglas Gilbert  *  realistic, the simulated devices have the transport attributes of
1223183910SDouglas Gilbert  *  SAS disks.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *  For documentation see http://www.torque.net/sg/sdebug26.html
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
181da177e4SLinus Torvalds  *   dpg: work for devfs large number of disks [20010809]
191da177e4SLinus Torvalds  *        forked for lk 2.5 series [20011216, 20020101]
201da177e4SLinus Torvalds  *        use vmalloc() more inquiry+mode_sense [20020302]
211da177e4SLinus Torvalds  *        add timers for delayed responses [20020721]
221da177e4SLinus Torvalds  *   Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
231da177e4SLinus Torvalds  *   Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
241da177e4SLinus Torvalds  *   dpg: change style of boot options to "scsi_debug.num_tgts=2" and
251da177e4SLinus Torvalds  *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <linux/kernel.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321da177e4SLinus Torvalds #include <linux/timer.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/genhd.h>
361da177e4SLinus Torvalds #include <linux/fs.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/proc_fs.h>
391da177e4SLinus Torvalds #include <linux/vmalloc.h>
401da177e4SLinus Torvalds #include <linux/moduleparam.h>
41852e034dSJens Axboe #include <linux/scatterlist.h>
421da177e4SLinus Torvalds 
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 
536f3cbf55SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.81"
546f3cbf55SDouglas Gilbert static const char * scsi_debug_version_date = "20070104";
551da177e4SLinus Torvalds 
566f3cbf55SDouglas Gilbert /* Additional Sense Code (ASC) */
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
676f3cbf55SDouglas Gilbert #define TRANSPORT_PROBLEM 0x4b
68c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
69c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
701da177e4SLinus Torvalds 
716f3cbf55SDouglas Gilbert /* Additional Sense Code Qualifier (ASCQ) */
726f3cbf55SDouglas Gilbert #define ACK_NAK_TO 0x3
736f3cbf55SDouglas Gilbert 
741da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds /* Default values for driver parameters */
771da177e4SLinus Torvalds #define DEF_NUM_HOST   1
781da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
791da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
801da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
811da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
821da177e4SLinus Torvalds  */
831da177e4SLinus Torvalds #define DEF_DELAY   1
841da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
851da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
861da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
871da177e4SLinus Torvalds #define DEF_OPTS   0
881da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
891da177e4SLinus Torvalds #define DEF_PTYPE   0
901da177e4SLinus Torvalds #define DEF_D_SENSE   0
91c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
92c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
9323183910SDouglas Gilbert #define DEF_FAKE_RW	0
9423183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
951da177e4SLinus Torvalds 
961da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
971da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
981da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
991da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
1001da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1016f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
1021da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1031da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1041da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1051da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1066f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1076f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1081da177e4SLinus Torvalds  *
1091da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1101da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1111da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1121da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1136f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1146f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1151da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1161da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1171da177e4SLinus Torvalds  */
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1201da177e4SLinus Torvalds  * sector on read commands: */
1211da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1241da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1251da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
126c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1291da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1301da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1311da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1321da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1331da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1341da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1351da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1361da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1371da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1381da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
139c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
140c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
14123183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
14223183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1471da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1481da177e4SLinus Torvalds 
149c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
150c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1511da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1541da177e4SLinus Torvalds    may still need them */
1551da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1561da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1571da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1601da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1611da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1621da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds struct sdebug_dev_info {
1691da177e4SLinus Torvalds 	struct list_head dev_list;
1701da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1711da177e4SLinus Torvalds 	unsigned int channel;
1721da177e4SLinus Torvalds 	unsigned int target;
1731da177e4SLinus Torvalds 	unsigned int lun;
1741da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
175c65b1445SDouglas Gilbert 	unsigned int wlun;
1761da177e4SLinus Torvalds 	char reset;
177c65b1445SDouglas Gilbert 	char stopped;
1781da177e4SLinus Torvalds 	char used;
1791da177e4SLinus Torvalds };
1801da177e4SLinus Torvalds 
1811da177e4SLinus Torvalds struct sdebug_host_info {
1821da177e4SLinus Torvalds 	struct list_head host_list;
1831da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1841da177e4SLinus Torvalds 	struct device dev;
1851da177e4SLinus Torvalds 	struct list_head dev_info_list;
1861da177e4SLinus Torvalds };
1871da177e4SLinus Torvalds 
1881da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1891da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1921da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1931da177e4SLinus Torvalds 
1941da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds struct sdebug_queued_cmd {
1971da177e4SLinus Torvalds 	int in_use;
1981da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
1991da177e4SLinus Torvalds 	done_funct_t done_funct;
2001da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2011da177e4SLinus Torvalds 	int scsi_result;
2021da177e4SLinus Torvalds };
2031da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2041da177e4SLinus Torvalds 
205d0be4a7dSChristoph Hellwig static struct scsi_host_template sdebug_driver_template = {
2061da177e4SLinus Torvalds 	.proc_info =		scsi_debug_proc_info,
2071da177e4SLinus Torvalds 	.name =			"SCSI DEBUG",
2081da177e4SLinus Torvalds 	.info =			scsi_debug_info,
2091da177e4SLinus Torvalds 	.slave_alloc =		scsi_debug_slave_alloc,
2101da177e4SLinus Torvalds 	.slave_configure =	scsi_debug_slave_configure,
2111da177e4SLinus Torvalds 	.slave_destroy =	scsi_debug_slave_destroy,
2121da177e4SLinus Torvalds 	.ioctl =		scsi_debug_ioctl,
2131da177e4SLinus Torvalds 	.queuecommand =		scsi_debug_queuecommand,
2141da177e4SLinus Torvalds 	.eh_abort_handler =	scsi_debug_abort,
2151da177e4SLinus Torvalds 	.eh_bus_reset_handler = scsi_debug_bus_reset,
2161da177e4SLinus Torvalds 	.eh_device_reset_handler = scsi_debug_device_reset,
2171da177e4SLinus Torvalds 	.eh_host_reset_handler = scsi_debug_host_reset,
2181da177e4SLinus Torvalds 	.bios_param =		scsi_debug_biosparam,
2191da177e4SLinus Torvalds 	.can_queue =		SCSI_DEBUG_CANQUEUE,
2201da177e4SLinus Torvalds 	.this_id =		7,
221c65b1445SDouglas Gilbert 	.sg_tablesize =		256,
222c65b1445SDouglas Gilbert 	.cmd_per_lun =		16,
223c65b1445SDouglas Gilbert 	.max_sectors =		0xffff,
2241da177e4SLinus Torvalds 	.unchecked_isa_dma = 	0,
225c65b1445SDouglas Gilbert 	.use_clustering = 	ENABLE_CLUSTERING,
2261da177e4SLinus Torvalds 	.module =		THIS_MODULE,
2271da177e4SLinus Torvalds };
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds static int num_aborts = 0;
2321da177e4SLinus Torvalds static int num_dev_resets = 0;
2331da177e4SLinus Torvalds static int num_bus_resets = 0;
2341da177e4SLinus Torvalds static int num_host_resets = 0;
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2371da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2381da177e4SLinus Torvalds 
2391da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2401da177e4SLinus Torvalds 
2411da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2421da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2431da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2441da177e4SLinus Torvalds 
2451da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2461da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2471da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2481da177e4SLinus Torvalds };
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds static const int check_condition_result =
2511da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2521da177e4SLinus Torvalds 
253c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
254c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
255c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
256c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
257c65b1445SDouglas Gilbert 
2581da177e4SLinus Torvalds /* function declarations */
2591da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2601da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2611da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2621da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
263c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
264c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2655a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
2665a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip);
2671da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2681da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
269c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2701da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
271c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
272c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
273c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
274c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
275c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
276c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
277c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
278c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
279c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
280c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2811da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2821da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
2831da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2841da177e4SLinus Torvalds                                 int arr_len);
2851da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2861da177e4SLinus Torvalds                                int max_arr_len);
2871da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2881da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2891da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2901da177e4SLinus Torvalds 			    int asc, int asq);
291c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2921da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2931da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2941da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2951da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2961da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2971da177e4SLinus Torvalds static void __init init_all_queued(void);
2981da177e4SLinus Torvalds static void stop_all_queued(void);
2991da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
3005a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
3015a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
3025a09e398SHannes Reinecke 			   const char * dev_id_str, int dev_id_str_len);
303c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
3046ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
3051da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds static int sdebug_add_adapter(void);
3081da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
3091da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
3101da177e4SLinus Torvalds 
3111da177e4SLinus Torvalds static struct device pseudo_primary;
3121da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
3131da177e4SLinus Torvalds 
3143de9f944SFUJITA Tomonori static void get_data_transfer_info(unsigned char *cmd,
3153de9f944SFUJITA Tomonori 				   unsigned long long *lba, unsigned int *num)
3163de9f944SFUJITA Tomonori {
3173de9f944SFUJITA Tomonori 	int i;
3183de9f944SFUJITA Tomonori 
3193de9f944SFUJITA Tomonori 	switch (*cmd) {
3203de9f944SFUJITA Tomonori 	case WRITE_16:
3213de9f944SFUJITA Tomonori 	case READ_16:
3223de9f944SFUJITA Tomonori 		for (*lba = 0, i = 0; i < 8; ++i) {
3233de9f944SFUJITA Tomonori 			if (i > 0)
3243de9f944SFUJITA Tomonori 				*lba <<= 8;
3253de9f944SFUJITA Tomonori 			*lba += cmd[2 + i];
3263de9f944SFUJITA Tomonori 		}
3273de9f944SFUJITA Tomonori 		*num = cmd[13] + (cmd[12] << 8) +
3283de9f944SFUJITA Tomonori 			(cmd[11] << 16) + (cmd[10] << 24);
3293de9f944SFUJITA Tomonori 		break;
3303de9f944SFUJITA Tomonori 	case WRITE_12:
3313de9f944SFUJITA Tomonori 	case READ_12:
3323de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3333de9f944SFUJITA Tomonori 		*num = cmd[9] + (cmd[8] << 8) +	(cmd[7] << 16) + (cmd[6] << 24);
3343de9f944SFUJITA Tomonori 		break;
3353de9f944SFUJITA Tomonori 	case WRITE_10:
3363de9f944SFUJITA Tomonori 	case READ_10:
3373de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3383de9f944SFUJITA Tomonori 		*num = cmd[8] + (cmd[7] << 8);
3393de9f944SFUJITA Tomonori 		break;
3403de9f944SFUJITA Tomonori 	case WRITE_6:
3413de9f944SFUJITA Tomonori 	case READ_6:
3423de9f944SFUJITA Tomonori 		*lba = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
3433de9f944SFUJITA Tomonori 		*num = (0 == cmd[4]) ? 256 : cmd[4];
3443de9f944SFUJITA Tomonori 		break;
3453de9f944SFUJITA Tomonori 	default:
3463de9f944SFUJITA Tomonori 		break;
3473de9f944SFUJITA Tomonori 	}
3483de9f944SFUJITA Tomonori }
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds static
3511da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3521da177e4SLinus Torvalds {
3531da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
3543de9f944SFUJITA Tomonori 	int len, k;
355c65b1445SDouglas Gilbert 	unsigned int num;
356c65b1445SDouglas Gilbert 	unsigned long long lba;
3571da177e4SLinus Torvalds 	int errsts = 0;
358c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3591da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3601da177e4SLinus Torvalds 	int inj_recovered = 0;
3616f3cbf55SDouglas Gilbert 	int inj_transport = 0;
362c65b1445SDouglas Gilbert 	int delay_override = 0;
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	if (done == NULL)
3651da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3661da177e4SLinus Torvalds 
367c73961e5SBoaz Harrosh 	scsi_set_resid(SCpnt, 0);
3681da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3691da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
370c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3711da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3721da177e4SLinus Torvalds 		printk("\n");
3731da177e4SLinus Torvalds 	}
3741da177e4SLinus Torvalds         if(target == sdebug_driver_template.this_id) {
3751da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3761da177e4SLinus Torvalds 		       "target!\n");
3771da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3781da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3791da177e4SLinus Torvalds         }
3801da177e4SLinus Torvalds 
381c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
382c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3831da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3841da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3851da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3861da177e4SLinus Torvalds 	if (NULL == devip)
3871da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3881da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3891da177e4SLinus Torvalds 
3901da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3911da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3921da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3931da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3941da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3951da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3961da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3971da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3981da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3996f3cbf55SDouglas Gilbert 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
4006f3cbf55SDouglas Gilbert 			inj_transport = 1; /* to reads and writes below */
4011da177e4SLinus Torvalds         }
4021da177e4SLinus Torvalds 
403c65b1445SDouglas Gilbert 	if (devip->wlun) {
404c65b1445SDouglas Gilbert 		switch (*cmd) {
405c65b1445SDouglas Gilbert 		case INQUIRY:
406c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
407c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
408c65b1445SDouglas Gilbert 		case REPORT_LUNS:
409c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
410c65b1445SDouglas Gilbert 		default:
411c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
412c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
413c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
414c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
415c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
416c65b1445SDouglas Gilbert 			errsts = check_condition_result;
417c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
418c65b1445SDouglas Gilbert 					     0);
419c65b1445SDouglas Gilbert 		}
420c65b1445SDouglas Gilbert 	}
421c65b1445SDouglas Gilbert 
4221da177e4SLinus Torvalds 	switch (*cmd) {
4231da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
424c65b1445SDouglas Gilbert 		delay_override = 1;
4251da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
4261da177e4SLinus Torvalds 		break;
4271da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
428c65b1445SDouglas Gilbert 		delay_override = 1;
4291da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
4301da177e4SLinus Torvalds 		break;
4311da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
4321da177e4SLinus Torvalds 	case START_STOP:
433c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
4341da177e4SLinus Torvalds 		break;
4351da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
436c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
4371da177e4SLinus Torvalds 			break;
4381da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
4391da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
4401da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
4411da177e4SLinus Torvalds 		break;
4421da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
443c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4441da177e4SLinus Torvalds 		break;
4451da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
446c65b1445SDouglas Gilbert 		delay_override = 1;
447c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4481da177e4SLinus Torvalds 		break;
4491da177e4SLinus Torvalds         case RESERVE:
450c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4511da177e4SLinus Torvalds                 break;
4521da177e4SLinus Torvalds         case RESERVE_10:
453c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4541da177e4SLinus Torvalds                 break;
4551da177e4SLinus Torvalds         case RELEASE:
456c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4571da177e4SLinus Torvalds                 break;
4581da177e4SLinus Torvalds         case RELEASE_10:
459c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4601da177e4SLinus Torvalds                 break;
4611da177e4SLinus Torvalds 	case READ_CAPACITY:
4621da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4631da177e4SLinus Torvalds 		break;
464c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
465c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
466c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
467c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
468c65b1445SDouglas Gilbert 			errsts = check_condition_result;
469c65b1445SDouglas Gilbert 			break;
470c65b1445SDouglas Gilbert 		}
471c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
472c65b1445SDouglas Gilbert 		break;
4735a09e398SHannes Reinecke 	case MAINTENANCE_IN:
4745a09e398SHannes Reinecke 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
4755a09e398SHannes Reinecke 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
4765a09e398SHannes Reinecke 					INVALID_OPCODE, 0);
4775a09e398SHannes Reinecke 			errsts = check_condition_result;
4785a09e398SHannes Reinecke 			break;
4795a09e398SHannes Reinecke 		}
4805a09e398SHannes Reinecke 		errsts = resp_report_tgtpgs(SCpnt, devip);
4815a09e398SHannes Reinecke 		break;
4821da177e4SLinus Torvalds 	case READ_16:
4831da177e4SLinus Torvalds 	case READ_12:
4841da177e4SLinus Torvalds 	case READ_10:
4851da177e4SLinus Torvalds 	case READ_6:
486c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4871da177e4SLinus Torvalds 			break;
48823183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
48923183910SDouglas Gilbert 			break;
4903de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
491c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4921da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4931da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
494c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4951da177e4SLinus Torvalds 			errsts = check_condition_result;
4966f3cbf55SDouglas Gilbert 		} else if (inj_transport && (0 == errsts)) {
4976f3cbf55SDouglas Gilbert                         mk_sense_buffer(devip, ABORTED_COMMAND,
4986f3cbf55SDouglas Gilbert                                         TRANSPORT_PROBLEM, ACK_NAK_TO);
4996f3cbf55SDouglas Gilbert                         errsts = check_condition_result;
5001da177e4SLinus Torvalds                 }
5011da177e4SLinus Torvalds 		break;
5021da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
503c65b1445SDouglas Gilbert 		delay_override = 1;
5041da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
5051da177e4SLinus Torvalds 		break;
5061da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
507c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5081da177e4SLinus Torvalds 		break;
5091da177e4SLinus Torvalds 	case WRITE_16:
5101da177e4SLinus Torvalds 	case WRITE_12:
5111da177e4SLinus Torvalds 	case WRITE_10:
5121da177e4SLinus Torvalds 	case WRITE_6:
513c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
5141da177e4SLinus Torvalds 			break;
51523183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
51623183910SDouglas Gilbert 			break;
5173de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
518c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5191da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5201da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
521c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5221da177e4SLinus Torvalds 			errsts = check_condition_result;
5231da177e4SLinus Torvalds 		}
5241da177e4SLinus Torvalds 		break;
5251da177e4SLinus Torvalds 	case MODE_SENSE:
5261da177e4SLinus Torvalds 	case MODE_SENSE_10:
5271da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5281da177e4SLinus Torvalds 		break;
529c65b1445SDouglas Gilbert 	case MODE_SELECT:
530c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
531c65b1445SDouglas Gilbert 		break;
532c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
533c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
534c65b1445SDouglas Gilbert 		break;
535c65b1445SDouglas Gilbert 	case LOG_SENSE:
536c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
537c65b1445SDouglas Gilbert 		break;
5381da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
539c65b1445SDouglas Gilbert 		delay_override = 1;
540c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5411da177e4SLinus Torvalds 		break;
5426f3cbf55SDouglas Gilbert 	case WRITE_BUFFER:
5436f3cbf55SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
5446f3cbf55SDouglas Gilbert 		break;
5451da177e4SLinus Torvalds 	default:
5461da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5471da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5481da177e4SLinus Torvalds 			       "supported\n", *cmd);
549c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5501da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5511da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5521da177e4SLinus Torvalds 		errsts = check_condition_result;
5531da177e4SLinus Torvalds 		break;
5541da177e4SLinus Torvalds 	}
555c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
556c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5571da177e4SLinus Torvalds }
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5601da177e4SLinus Torvalds {
5611da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5621da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5631da177e4SLinus Torvalds 	}
5641da177e4SLinus Torvalds 	return -EINVAL;
5651da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5661da177e4SLinus Torvalds }
5671da177e4SLinus Torvalds 
568c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
569c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5701da177e4SLinus Torvalds {
5711da177e4SLinus Torvalds 	if (devip->reset) {
5721da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5731da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5741da177e4SLinus Torvalds 			       "attention: power on reset\n");
5751da177e4SLinus Torvalds 		devip->reset = 0;
5761da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5771da177e4SLinus Torvalds 		return check_condition_result;
5781da177e4SLinus Torvalds 	}
579c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
580c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
581c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
582c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
583c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
584c65b1445SDouglas Gilbert 				0x2);
585c65b1445SDouglas Gilbert 		return check_condition_result;
586c65b1445SDouglas Gilbert 	}
5871da177e4SLinus Torvalds 	return 0;
5881da177e4SLinus Torvalds }
5891da177e4SLinus Torvalds 
5901da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5911da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5921da177e4SLinus Torvalds 				int arr_len)
5931da177e4SLinus Torvalds {
5941da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
5951da177e4SLinus Torvalds 	void * kaddr;
5961da177e4SLinus Torvalds 	void * kaddr_off;
597852e034dSJens Axboe 	struct scatterlist * sg;
5981da177e4SLinus Torvalds 
599c73961e5SBoaz Harrosh 	if (0 == scsi_bufflen(scp))
6001da177e4SLinus Torvalds 		return 0;
601c73961e5SBoaz Harrosh 	if (NULL == scsi_sglist(scp))
6021da177e4SLinus Torvalds 		return (DID_ERROR << 16);
6031da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6041da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
6051da177e4SLinus Torvalds 		return (DID_ERROR << 16);
6061da177e4SLinus Torvalds 	active = 1;
607852e034dSJens Axboe 	req_len = act_len = 0;
608c73961e5SBoaz Harrosh 	scsi_for_each_sg(scp, sg, scsi_sg_count(scp), k) {
6091da177e4SLinus Torvalds 		if (active) {
6101da177e4SLinus Torvalds 			kaddr = (unsigned char *)
61145711f1aSJens Axboe 				kmap_atomic(sg_page(sg), KM_USER0);
6121da177e4SLinus Torvalds 			if (NULL == kaddr)
6131da177e4SLinus Torvalds 				return (DID_ERROR << 16);
614852e034dSJens Axboe 			kaddr_off = (unsigned char *)kaddr + sg->offset;
615852e034dSJens Axboe 			len = sg->length;
6161da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
6171da177e4SLinus Torvalds 				active = 0;
6181da177e4SLinus Torvalds 				len = arr_len - req_len;
6191da177e4SLinus Torvalds 			}
6201da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6211da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6221da177e4SLinus Torvalds 			act_len += len;
6231da177e4SLinus Torvalds 		}
624852e034dSJens Axboe 		req_len += sg->length;
6251da177e4SLinus Torvalds 	}
626c73961e5SBoaz Harrosh 	if (scsi_get_resid(scp))
627c73961e5SBoaz Harrosh 		scsi_set_resid(scp, scsi_get_resid(scp) - act_len);
628c65b1445SDouglas Gilbert 	else
629c73961e5SBoaz Harrosh 		scsi_set_resid(scp, req_len - act_len);
6301da177e4SLinus Torvalds 	return 0;
6311da177e4SLinus Torvalds }
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6341da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6351da177e4SLinus Torvalds 			       int max_arr_len)
6361da177e4SLinus Torvalds {
6371da177e4SLinus Torvalds 	int k, req_len, len, fin;
6381da177e4SLinus Torvalds 	void * kaddr;
6391da177e4SLinus Torvalds 	void * kaddr_off;
640852e034dSJens Axboe 	struct scatterlist * sg;
6411da177e4SLinus Torvalds 
642c73961e5SBoaz Harrosh 	if (0 == scsi_bufflen(scp))
6431da177e4SLinus Torvalds 		return 0;
644c73961e5SBoaz Harrosh 	if (NULL == scsi_sglist(scp))
6451da177e4SLinus Torvalds 		return -1;
6461da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6471da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_TO_DEVICE)))
6481da177e4SLinus Torvalds 		return -1;
649852e034dSJens Axboe 	req_len = fin = 0;
650c73961e5SBoaz Harrosh 	scsi_for_each_sg(scp, sg, scsi_sg_count(scp), k) {
65145711f1aSJens Axboe 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
6521da177e4SLinus Torvalds 		if (NULL == kaddr)
6531da177e4SLinus Torvalds 			return -1;
654852e034dSJens Axboe 		kaddr_off = (unsigned char *)kaddr + sg->offset;
655852e034dSJens Axboe 		len = sg->length;
6561da177e4SLinus Torvalds 		if ((req_len + len) > max_arr_len) {
6571da177e4SLinus Torvalds 			len = max_arr_len - req_len;
6581da177e4SLinus Torvalds 			fin = 1;
6591da177e4SLinus Torvalds 		}
6601da177e4SLinus Torvalds 		memcpy(arr + req_len, kaddr_off, len);
6611da177e4SLinus Torvalds 		kunmap_atomic(kaddr, KM_USER0);
6621da177e4SLinus Torvalds 		if (fin)
6631da177e4SLinus Torvalds 			return req_len + len;
664852e034dSJens Axboe 		req_len += sg->length;
6651da177e4SLinus Torvalds 	}
6661da177e4SLinus Torvalds 	return req_len;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 
6701da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6711da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6721da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6731da177e4SLinus Torvalds 
6745a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
6755a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
6765a09e398SHannes Reinecke 			   const char * dev_id_str,
677c65b1445SDouglas Gilbert 			   int dev_id_str_len)
6781da177e4SLinus Torvalds {
679c65b1445SDouglas Gilbert 	int num, port_a;
680c65b1445SDouglas Gilbert 	char b[32];
6811da177e4SLinus Torvalds 
682c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
6831da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
6841da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
6851da177e4SLinus Torvalds 	arr[1] = 0x1;
6861da177e4SLinus Torvalds 	arr[2] = 0x0;
6871da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
6881da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
6891da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
6901da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
6911da177e4SLinus Torvalds 	arr[3] = num;
6921da177e4SLinus Torvalds 	num += 4;
693c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
694c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
695c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
696c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
697c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
698c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
699c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
700c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
701c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
702c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
703c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
704c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
705c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
706c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
707c65b1445SDouglas Gilbert 		/* Target relative port number */
708c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
709c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
710c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
711c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
712c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
713c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
714c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
715c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
716c65b1445SDouglas Gilbert 	}
717c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
718c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
719c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
720c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
721c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
722c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
723c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
724c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
725c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
726c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
727c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
728c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
729c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
7305a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
7315a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
7325a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
7335a09e398SHannes Reinecke 	arr[num++] = 0x0;
7345a09e398SHannes Reinecke 	arr[num++] = 0x4;
7355a09e398SHannes Reinecke 	arr[num++] = 0;
7365a09e398SHannes Reinecke 	arr[num++] = 0;
7375a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
7385a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
739c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
740c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
741c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
742c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
743c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
744c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
745c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
746c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
747c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
748c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
749c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
750c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
751c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
752c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
753c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
754c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
755c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
756c65b1445SDouglas Gilbert 	arr[num++] = 24;
757c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
758c65b1445SDouglas Gilbert 	num += 12;
759c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
760c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
761c65b1445SDouglas Gilbert 	num += 8;
762c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
763c65b1445SDouglas Gilbert 	num += 4;
764c65b1445SDouglas Gilbert 	return num;
765c65b1445SDouglas Gilbert }
766c65b1445SDouglas Gilbert 
767c65b1445SDouglas Gilbert 
768c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
769c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
770c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
771c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
772c65b1445SDouglas Gilbert };
773c65b1445SDouglas Gilbert 
774c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
775c65b1445SDouglas Gilbert {
776c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
777c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
778c65b1445SDouglas Gilbert }
779c65b1445SDouglas Gilbert 
780c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
781c65b1445SDouglas Gilbert {
782c65b1445SDouglas Gilbert 	int num = 0;
783c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
784c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
785c65b1445SDouglas Gilbert 	int plen, olen;
786c65b1445SDouglas Gilbert 
787c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
788c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
789c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
790c65b1445SDouglas Gilbert 	olen = strlen(na1);
791c65b1445SDouglas Gilbert 	plen = olen + 1;
792c65b1445SDouglas Gilbert 	if (plen % 4)
793c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
794c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
795c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
796c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
797c65b1445SDouglas Gilbert 	num += plen;
798c65b1445SDouglas Gilbert 
799c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
800c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
801c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
802c65b1445SDouglas Gilbert 	olen = strlen(na2);
803c65b1445SDouglas Gilbert 	plen = olen + 1;
804c65b1445SDouglas Gilbert 	if (plen % 4)
805c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
806c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
807c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
808c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
809c65b1445SDouglas Gilbert 	num += plen;
810c65b1445SDouglas Gilbert 
811c65b1445SDouglas Gilbert 	return num;
812c65b1445SDouglas Gilbert }
813c65b1445SDouglas Gilbert 
814c65b1445SDouglas Gilbert /* SCSI ports VPD page */
815c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
816c65b1445SDouglas Gilbert {
817c65b1445SDouglas Gilbert 	int num = 0;
818c65b1445SDouglas Gilbert 	int port_a, port_b;
819c65b1445SDouglas Gilbert 
820c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
821c65b1445SDouglas Gilbert 	port_b = port_a + 1;
822c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
823c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
824c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
825c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
826c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
827c65b1445SDouglas Gilbert 	num += 6;
828c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
829c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
830c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
831c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
832c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
833c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
834c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
835c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
836c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
837c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
838c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
839c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
840c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
841c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
842c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
843c65b1445SDouglas Gilbert 
844c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
845c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
846c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
847c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
848c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
849c65b1445SDouglas Gilbert 	num += 6;
850c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
851c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
852c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
853c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
854c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
855c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
856c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
857c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
858c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
859c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
860c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
861c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
862c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
863c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
864c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
865c65b1445SDouglas Gilbert 
866c65b1445SDouglas Gilbert 	return num;
867c65b1445SDouglas Gilbert }
868c65b1445SDouglas Gilbert 
869c65b1445SDouglas Gilbert 
870c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
871c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
872c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
873c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
874c65b1445SDouglas Gilbert '1','2','3','4',
875c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
876c65b1445SDouglas Gilbert 0xec,0,0,0,
877c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
878c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
879c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
880c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
881c65b1445SDouglas Gilbert 0x53,0x41,
882c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
883c65b1445SDouglas Gilbert 0x20,0x20,
884c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
885c65b1445SDouglas Gilbert 0x10,0x80,
886c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
887c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
888c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
889c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
890c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
891c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
892c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
893c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
894c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
895c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
896c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
897c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
898c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
899c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
900c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
901c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
902c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
903c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
904c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
905c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
906c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
907c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
908c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
909c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
910c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
911c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
912c65b1445SDouglas Gilbert };
913c65b1445SDouglas Gilbert 
914c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
915c65b1445SDouglas Gilbert {
916c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
917c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
918c65b1445SDouglas Gilbert }
919c65b1445SDouglas Gilbert 
920c65b1445SDouglas Gilbert 
921c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
922c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
923c65b1445SDouglas Gilbert 	0,0,0x4,0,
924c65b1445SDouglas Gilbert 	0,0,0,64,
925c65b1445SDouglas Gilbert };
926c65b1445SDouglas Gilbert 
927c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
928c65b1445SDouglas Gilbert {
929c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
930c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
931c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
932c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
933c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
934c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
935c65b1445SDouglas Gilbert 	}
936c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds 
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
941c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9421da177e4SLinus Torvalds 
9431da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9441da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9451da177e4SLinus Torvalds {
9461da177e4SLinus Torvalds 	unsigned char pq_pdt;
9475a09e398SHannes Reinecke 	unsigned char * arr;
9481da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
9495a09e398SHannes Reinecke 	int alloc_len, n, ret;
9501da177e4SLinus Torvalds 
9511da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9526f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
9536f3cbf55SDouglas Gilbert 	if (! arr)
9546f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
955c65b1445SDouglas Gilbert 	if (devip->wlun)
956c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
957c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
958c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
959c65b1445SDouglas Gilbert 	else
9601da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9611da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9621da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9631da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9641da177e4SLinus Torvalds 			       	0);
9655a09e398SHannes Reinecke 		kfree(arr);
9661da177e4SLinus Torvalds 		return check_condition_result;
9671da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
9685a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
969c65b1445SDouglas Gilbert 		char lu_id_str[6];
970c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9711da177e4SLinus Torvalds 
9725a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
9735a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
97423183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
97523183910SDouglas Gilbert 			host_no = 0;
976c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
977c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
978c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
979c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
980c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9811da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
982c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
983c65b1445SDouglas Gilbert 			n = 4;
984c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
985c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
986c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
987c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
988c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
989c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
990c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
991c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
992c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
993c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
994c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
9951da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
996c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
9971da177e4SLinus Torvalds 			arr[3] = len;
998c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
9991da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
1000c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10015a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
10025a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
10035a09e398SHannes Reinecke 						 lu_id_str, len);
1004c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
1005c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1006c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
1007c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
1008c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1009c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
1010c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
1011c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1012c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
1013c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
1014c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
1015c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
1016c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1017c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
1018c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
1019c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
1020c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
1021c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
1022c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
1023c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1024c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
1025c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
1026c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1027c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
1028c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
1029c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
1030c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
1031c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1032c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10331da177e4SLinus Torvalds 		} else {
10341da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10351da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10361da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10375a09e398SHannes Reinecke 			kfree(arr);
10381da177e4SLinus Torvalds 			return check_condition_result;
10391da177e4SLinus Torvalds 		}
1040c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10415a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
1042c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10435a09e398SHannes Reinecke 		kfree(arr);
10445a09e398SHannes Reinecke 		return ret;
10451da177e4SLinus Torvalds 	}
10461da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10471da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10481da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10491da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10501da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
10515a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
10525a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
1053c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10541da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1055c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10561da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10571da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10581da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10591da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1060c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1061c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1062c65b1445SDouglas Gilbert 	n = 62;
10631da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1064c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10651da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1066c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10671da177e4SLinus Torvalds 	}
1068c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10695a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
10701da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10715a09e398SHannes Reinecke 	kfree(arr);
10725a09e398SHannes Reinecke 	return ret;
10731da177e4SLinus Torvalds }
10741da177e4SLinus Torvalds 
10751da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10761da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10771da177e4SLinus Torvalds {
10781da177e4SLinus Torvalds 	unsigned char * sbuff;
10791da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10801da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1081c65b1445SDouglas Gilbert 	int want_dsense;
10821da177e4SLinus Torvalds 	int len = 18;
10831da177e4SLinus Torvalds 
1084c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10851da177e4SLinus Torvalds 	if (devip->reset == 1)
1086c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1087c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10881da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1089c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1090c65b1445SDouglas Gilbert 		if (want_dsense) {
1091c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1092c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1093c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1094c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1095c65b1445SDouglas Gilbert 		} else {
1096c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1097c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1098c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1099c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1100c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1101c65b1445SDouglas Gilbert 		}
1102c65b1445SDouglas Gilbert 	} else {
1103c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
11041da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
11051da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1106c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
11071da177e4SLinus Torvalds 			arr[0] = 0x72;
11081da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
11091da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
11101da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
11111da177e4SLinus Torvalds 			len = 8;
1112c65b1445SDouglas Gilbert 		}
1113c65b1445SDouglas Gilbert 	}
1114c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
11151da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
11161da177e4SLinus Torvalds }
11171da177e4SLinus Torvalds 
1118c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1119c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1120c65b1445SDouglas Gilbert {
1121c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1122c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1123c65b1445SDouglas Gilbert 
1124c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1125c65b1445SDouglas Gilbert 		return errsts;
1126c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1127c65b1445SDouglas Gilbert 	if (power_cond) {
1128c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1129c65b1445SDouglas Gilbert 			       	0);
1130c65b1445SDouglas Gilbert 		return check_condition_result;
1131c65b1445SDouglas Gilbert 	}
1132c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1133c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1134c65b1445SDouglas Gilbert 		devip->stopped = !start;
1135c65b1445SDouglas Gilbert 	return 0;
1136c65b1445SDouglas Gilbert }
1137c65b1445SDouglas Gilbert 
11381da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11391da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11401da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11411da177e4SLinus Torvalds {
11421da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1143c65b1445SDouglas Gilbert 	unsigned int capac;
11441da177e4SLinus Torvalds 	int errsts;
11451da177e4SLinus Torvalds 
1146c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11471da177e4SLinus Torvalds 		return errsts;
1148c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1149c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1150c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1151c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1152c65b1445SDouglas Gilbert 	} else
1153c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11541da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1155c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1156c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11571da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11581da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11591da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11601da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1161c65b1445SDouglas Gilbert 	} else {
1162c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1163c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1164c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1165c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1166c65b1445SDouglas Gilbert 	}
11671da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11681da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11691da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11701da177e4SLinus Torvalds }
11711da177e4SLinus Torvalds 
1172c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1173c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1174c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1175c65b1445SDouglas Gilbert {
1176c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1177c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1178c65b1445SDouglas Gilbert 	unsigned long long capac;
1179c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1180c65b1445SDouglas Gilbert 
1181c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1182c65b1445SDouglas Gilbert 		return errsts;
1183c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1184c65b1445SDouglas Gilbert 		     + cmd[13]);
1185c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1186c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1187c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1188c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1189c65b1445SDouglas Gilbert 	} else
1190c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1191c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1192c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1193c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1194c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1195c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1196c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1197c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1198c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1199c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1200c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1201c65b1445SDouglas Gilbert }
1202c65b1445SDouglas Gilbert 
12035a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
12045a09e398SHannes Reinecke 
12055a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
12065a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
12075a09e398SHannes Reinecke {
12085a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
12095a09e398SHannes Reinecke 	unsigned char * arr;
12105a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
12115a09e398SHannes Reinecke 	int n, ret, alen, rlen;
12125a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
12135a09e398SHannes Reinecke 
12145a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
12155a09e398SHannes Reinecke 		+ cmd[9]);
12165a09e398SHannes Reinecke 
12176f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
12186f3cbf55SDouglas Gilbert 	if (! arr)
12196f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
12205a09e398SHannes Reinecke 	/*
12215a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
12225a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
12235a09e398SHannes Reinecke 	 * So we create two port groups with one port each
12245a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
12255a09e398SHannes Reinecke 	 */
12265a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
12275a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
12285a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
12295a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
12305a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
12315a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
12325a09e398SHannes Reinecke 
12335a09e398SHannes Reinecke 	/*
12345a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
12355a09e398SHannes Reinecke 	 */
12365a09e398SHannes Reinecke 	n = 4;
12375a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
12385a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
12395a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
12405a09e398SHannes Reinecke 	} else {
12415a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
12425a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
12435a09e398SHannes Reinecke 	}
12445a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
12455a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
12465a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12475a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12485a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12495a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12505a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12515a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12525a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
12535a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
12545a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
12555a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
12565a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
12575a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
12585a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12595a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12605a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12615a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12625a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12635a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12645a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
12655a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
12665a09e398SHannes Reinecke 
12675a09e398SHannes Reinecke 	rlen = n - 4;
12685a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
12695a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
12705a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
12715a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
12725a09e398SHannes Reinecke 
12735a09e398SHannes Reinecke 	/*
12745a09e398SHannes Reinecke 	 * Return the smallest value of either
12755a09e398SHannes Reinecke 	 * - The allocated length
12765a09e398SHannes Reinecke 	 * - The constructed command length
12775a09e398SHannes Reinecke 	 * - The maximum array size
12785a09e398SHannes Reinecke 	 */
12795a09e398SHannes Reinecke 	rlen = min(alen,n);
12805a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
12815a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
12825a09e398SHannes Reinecke 	kfree(arr);
12835a09e398SHannes Reinecke 	return ret;
12845a09e398SHannes Reinecke }
12855a09e398SHannes Reinecke 
12861da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
12871da177e4SLinus Torvalds 
12881da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
12891da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
12901da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
12911da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
12921da177e4SLinus Torvalds 
12931da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
12941da177e4SLinus Torvalds 	if (1 == pcontrol)
12951da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
12961da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
12971da177e4SLinus Torvalds }
12981da177e4SLinus Torvalds 
12991da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
13001da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
13011da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
13021da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
13051da177e4SLinus Torvalds 	if (1 == pcontrol)
13061da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
13071da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
13081da177e4SLinus Torvalds }
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
13111da177e4SLinus Torvalds {       /* Format device page for mode_sense */
13121da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
13131da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
13141da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
13171da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
13181da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
13191da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
13201da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
13211da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
13221da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
13231da177e4SLinus Torvalds         if (1 == pcontrol)
13241da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
13251da177e4SLinus Torvalds         return sizeof(format_pg);
13261da177e4SLinus Torvalds }
13271da177e4SLinus Torvalds 
13281da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
13291da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
13301da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
13311da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
13321da177e4SLinus Torvalds 
13331da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
13341da177e4SLinus Torvalds 	if (1 == pcontrol)
13351da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
13361da177e4SLinus Torvalds 	return sizeof(caching_pg);
13371da177e4SLinus Torvalds }
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
13401da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1341c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1342c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1343c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
13441da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
13451da177e4SLinus Torvalds 
13461da177e4SLinus Torvalds 	if (scsi_debug_dsense)
13471da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1348c65b1445SDouglas Gilbert 	else
1349c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
13501da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
13511da177e4SLinus Torvalds 	if (1 == pcontrol)
1352c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1353c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1354c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
13551da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
13561da177e4SLinus Torvalds }
13571da177e4SLinus Torvalds 
1358c65b1445SDouglas Gilbert 
13591da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
13601da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1361c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
13621da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1363c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1364c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1365c65b1445SDouglas Gilbert 
13661da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
13671da177e4SLinus Torvalds 	if (1 == pcontrol)
1368c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1369c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1370c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
13711da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
13721da177e4SLinus Torvalds }
13731da177e4SLinus Torvalds 
1374c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1375c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1376c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1377c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1378c65b1445SDouglas Gilbert 
1379c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1380c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1381c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1382c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1383c65b1445SDouglas Gilbert }
1384c65b1445SDouglas Gilbert 
1385c65b1445SDouglas Gilbert 
1386c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1387c65b1445SDouglas Gilbert 			      int target_dev_id)
1388c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1389c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1390c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1391c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1392c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1393c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1394c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1395c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1396c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1397c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1398c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1399c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1400c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1401c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1402c65b1445SDouglas Gilbert 		};
1403c65b1445SDouglas Gilbert 	int port_a, port_b;
1404c65b1445SDouglas Gilbert 
1405c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1406c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1407c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1408c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1409c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1410c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1411c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1412c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1413c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1414c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1415c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1416c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1417c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1418c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1419c65b1445SDouglas Gilbert }
1420c65b1445SDouglas Gilbert 
1421c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1422c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1423c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1424c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1425c65b1445SDouglas Gilbert 		};
1426c65b1445SDouglas Gilbert 
1427c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1428c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1429c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1430c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1431c65b1445SDouglas Gilbert }
1432c65b1445SDouglas Gilbert 
14331da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
14341da177e4SLinus Torvalds 
14351da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
14361da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
14371da177e4SLinus Torvalds {
143823183910SDouglas Gilbert 	unsigned char dbd, llbaa;
143923183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
14401da177e4SLinus Torvalds 	unsigned char dev_spec;
144123183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
14421da177e4SLinus Torvalds 	unsigned char * ap;
14431da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
14441da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
14451da177e4SLinus Torvalds 
1446c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
14471da177e4SLinus Torvalds 		return errsts;
144823183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
14491da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
14501da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
14511da177e4SLinus Torvalds 	subpcode = cmd[3];
14521da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
145323183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
145423183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
145523183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
145623183910SDouglas Gilbert 	else
145723183910SDouglas Gilbert 		bd_len = 0;
14581da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
14591da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
14601da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
14611da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
14621da177e4SLinus Torvalds 			       	0);
14631da177e4SLinus Torvalds 		return check_condition_result;
14641da177e4SLinus Torvalds 	}
1465c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1466c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
146723183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
146823183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
146923183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
147023183910SDouglas Gilbert 	else
147123183910SDouglas Gilbert 		dev_spec = 0x0;
14721da177e4SLinus Torvalds 	if (msense_6) {
14731da177e4SLinus Torvalds 		arr[2] = dev_spec;
147423183910SDouglas Gilbert 		arr[3] = bd_len;
14751da177e4SLinus Torvalds 		offset = 4;
14761da177e4SLinus Torvalds 	} else {
14771da177e4SLinus Torvalds 		arr[3] = dev_spec;
147823183910SDouglas Gilbert 		if (16 == bd_len)
147923183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
148023183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
14811da177e4SLinus Torvalds 		offset = 8;
14821da177e4SLinus Torvalds 	}
14831da177e4SLinus Torvalds 	ap = arr + offset;
148423183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
148523183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
148623183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
148723183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
148823183910SDouglas Gilbert 		} else
148923183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
149023183910SDouglas Gilbert 	}
149123183910SDouglas Gilbert 	if (8 == bd_len) {
149223183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
149323183910SDouglas Gilbert 			ap[0] = 0xff;
149423183910SDouglas Gilbert 			ap[1] = 0xff;
149523183910SDouglas Gilbert 			ap[2] = 0xff;
149623183910SDouglas Gilbert 			ap[3] = 0xff;
149723183910SDouglas Gilbert 		} else {
149823183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
149923183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
150023183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
150123183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
150223183910SDouglas Gilbert 		}
150323183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
150423183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
150523183910SDouglas Gilbert 		offset += bd_len;
150623183910SDouglas Gilbert 		ap = arr + offset;
150723183910SDouglas Gilbert 	} else if (16 == bd_len) {
150823183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
150923183910SDouglas Gilbert 
151023183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
151123183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
151223183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
151323183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
151423183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
151523183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
151623183910SDouglas Gilbert 		offset += bd_len;
151723183910SDouglas Gilbert 		ap = arr + offset;
151823183910SDouglas Gilbert 	}
15191da177e4SLinus Torvalds 
1520c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1521c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
15221da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15231da177e4SLinus Torvalds 			       	0);
15241da177e4SLinus Torvalds 		return check_condition_result;
15251da177e4SLinus Torvalds 	}
15261da177e4SLinus Torvalds 	switch (pcode) {
15271da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
15281da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
15291da177e4SLinus Torvalds 		offset += len;
15301da177e4SLinus Torvalds 		break;
15311da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
15321da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
15331da177e4SLinus Torvalds 		offset += len;
15341da177e4SLinus Torvalds 		break;
15351da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
15361da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
15371da177e4SLinus Torvalds                 offset += len;
15381da177e4SLinus Torvalds                 break;
15391da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
15401da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
15411da177e4SLinus Torvalds 		offset += len;
15421da177e4SLinus Torvalds 		break;
15431da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
15441da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
15451da177e4SLinus Torvalds 		offset += len;
15461da177e4SLinus Torvalds 		break;
1547c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1548c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1549c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1550c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1551c65b1445SDouglas Gilbert 			return check_condition_result;
1552c65b1445SDouglas Gilbert 	        }
1553c65b1445SDouglas Gilbert 		len = 0;
1554c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1555c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1556c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1557c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1558c65b1445SDouglas Gilbert 						  target_dev_id);
1559c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1560c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1561c65b1445SDouglas Gilbert 		offset += len;
1562c65b1445SDouglas Gilbert 		break;
15631da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
15641da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
15651da177e4SLinus Torvalds 		offset += len;
15661da177e4SLinus Torvalds 		break;
15671da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1568c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
15691da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
15701da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
15711da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
15721da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
15731da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1574c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1575c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1576c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1577c65b1445SDouglas Gilbert 						  target, target_dev_id);
1578c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1579c65b1445SDouglas Gilbert 			}
15801da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1581c65b1445SDouglas Gilbert 		} else {
1582c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1583c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1584c65b1445SDouglas Gilbert 			return check_condition_result;
1585c65b1445SDouglas Gilbert                 }
15861da177e4SLinus Torvalds 		offset += len;
15871da177e4SLinus Torvalds 		break;
15881da177e4SLinus Torvalds 	default:
15891da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15901da177e4SLinus Torvalds 			       	0);
15911da177e4SLinus Torvalds 		return check_condition_result;
15921da177e4SLinus Torvalds 	}
15931da177e4SLinus Torvalds 	if (msense_6)
15941da177e4SLinus Torvalds 		arr[0] = offset - 1;
15951da177e4SLinus Torvalds 	else {
15961da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
15971da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
15981da177e4SLinus Torvalds 	}
15991da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
16001da177e4SLinus Torvalds }
16011da177e4SLinus Torvalds 
1602c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1603c65b1445SDouglas Gilbert 
1604c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1605c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1606c65b1445SDouglas Gilbert {
1607c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1608c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1609c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1610c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1611c65b1445SDouglas Gilbert 
1612c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1613c65b1445SDouglas Gilbert 		return errsts;
1614c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1615c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1616c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1617c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1618c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1619c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1620c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1621c65b1445SDouglas Gilbert 		return check_condition_result;
1622c65b1445SDouglas Gilbert 	}
1623c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1624c65b1445SDouglas Gilbert         if (-1 == res)
1625c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1626c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1627c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1628c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1629c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1630c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1631c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
163223183910SDouglas Gilbert 	if (md_len > 2) {
1633c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1634c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1635c65b1445SDouglas Gilbert 		return check_condition_result;
1636c65b1445SDouglas Gilbert 	}
1637c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1638c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1639c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1640c65b1445SDouglas Gilbert 	if (ps) {
1641c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1642c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1643c65b1445SDouglas Gilbert 		return check_condition_result;
1644c65b1445SDouglas Gilbert 	}
1645c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1646c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1647c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1648c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1649c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1650c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1651c65b1445SDouglas Gilbert 		return check_condition_result;
1652c65b1445SDouglas Gilbert 	}
1653c65b1445SDouglas Gilbert 	switch (mpage) {
1654c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1655c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1656c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1657c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1658c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1659c65b1445SDouglas Gilbert 			return 0;
1660c65b1445SDouglas Gilbert 		}
1661c65b1445SDouglas Gilbert 		break;
1662c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1663c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1664c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1665c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1666c65b1445SDouglas Gilbert 			return 0;
1667c65b1445SDouglas Gilbert 		}
1668c65b1445SDouglas Gilbert 		break;
1669c65b1445SDouglas Gilbert 	default:
1670c65b1445SDouglas Gilbert 		break;
1671c65b1445SDouglas Gilbert 	}
1672c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1673c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1674c65b1445SDouglas Gilbert 	return check_condition_result;
1675c65b1445SDouglas Gilbert }
1676c65b1445SDouglas Gilbert 
1677c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1678c65b1445SDouglas Gilbert {
1679c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1680c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1681c65b1445SDouglas Gilbert 		};
1682c65b1445SDouglas Gilbert 
1683c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1684c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1685c65b1445SDouglas Gilbert }
1686c65b1445SDouglas Gilbert 
1687c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1688c65b1445SDouglas Gilbert {
1689c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1690c65b1445SDouglas Gilbert 		};
1691c65b1445SDouglas Gilbert 
1692c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1693c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1694c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1695c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1696c65b1445SDouglas Gilbert 	}
1697c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1698c65b1445SDouglas Gilbert }
1699c65b1445SDouglas Gilbert 
1700c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1701c65b1445SDouglas Gilbert 
1702c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1703c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1704c65b1445SDouglas Gilbert {
170523183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1706c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1707c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1708c65b1445SDouglas Gilbert 
1709c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1710c65b1445SDouglas Gilbert 		return errsts;
1711c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1712c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1713c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1714c65b1445SDouglas Gilbert 	if (ppc || sp) {
1715c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1716c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1717c65b1445SDouglas Gilbert 		return check_condition_result;
1718c65b1445SDouglas Gilbert 	}
1719c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1720c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
172123183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1722c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1723c65b1445SDouglas Gilbert 	arr[0] = pcode;
172423183910SDouglas Gilbert 	if (0 == subpcode) {
1725c65b1445SDouglas Gilbert 		switch (pcode) {
1726c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1727c65b1445SDouglas Gilbert 			n = 4;
1728c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1729c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1730c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1731c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1732c65b1445SDouglas Gilbert 			break;
1733c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1734c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1735c65b1445SDouglas Gilbert 			break;
1736c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1737c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1738c65b1445SDouglas Gilbert 			break;
1739c65b1445SDouglas Gilbert 		default:
1740c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1741c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1742c65b1445SDouglas Gilbert 			return check_condition_result;
1743c65b1445SDouglas Gilbert 		}
174423183910SDouglas Gilbert 	} else if (0xff == subpcode) {
174523183910SDouglas Gilbert 		arr[0] |= 0x40;
174623183910SDouglas Gilbert 		arr[1] = subpcode;
174723183910SDouglas Gilbert 		switch (pcode) {
174823183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
174923183910SDouglas Gilbert 			n = 4;
175023183910SDouglas Gilbert 			arr[n++] = 0x0;
175123183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
175223183910SDouglas Gilbert 			arr[n++] = 0x0;
175323183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
175423183910SDouglas Gilbert 			arr[n++] = 0xd;
175523183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
175623183910SDouglas Gilbert 			arr[n++] = 0x2f;
175723183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
175823183910SDouglas Gilbert 			arr[3] = n - 4;
175923183910SDouglas Gilbert 			break;
176023183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
176123183910SDouglas Gilbert 			n = 4;
176223183910SDouglas Gilbert 			arr[n++] = 0xd;
176323183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
176423183910SDouglas Gilbert 			arr[3] = n - 4;
176523183910SDouglas Gilbert 			break;
176623183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
176723183910SDouglas Gilbert 			n = 4;
176823183910SDouglas Gilbert 			arr[n++] = 0x2f;
176923183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
177023183910SDouglas Gilbert 			arr[3] = n - 4;
177123183910SDouglas Gilbert 			break;
177223183910SDouglas Gilbert 		default:
177323183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
177423183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
177523183910SDouglas Gilbert 			return check_condition_result;
177623183910SDouglas Gilbert 		}
177723183910SDouglas Gilbert 	} else {
177823183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
177923183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
178023183910SDouglas Gilbert 		return check_condition_result;
178123183910SDouglas Gilbert 	}
1782c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1783c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1784c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1785c65b1445SDouglas Gilbert }
1786c65b1445SDouglas Gilbert 
1787c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1788c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
17891da177e4SLinus Torvalds {
17901da177e4SLinus Torvalds 	unsigned long iflags;
1791c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1792c65b1445SDouglas Gilbert 	unsigned long long u;
17931da177e4SLinus Torvalds 	int ret;
17941da177e4SLinus Torvalds 
1795c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
17961da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
17971da177e4SLinus Torvalds 				0);
17981da177e4SLinus Torvalds 		return check_condition_result;
17991da177e4SLinus Torvalds 	}
1800c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1801c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1802c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1803c65b1445SDouglas Gilbert 				0);
1804c65b1445SDouglas Gilbert 		return check_condition_result;
1805c65b1445SDouglas Gilbert 	}
18061da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1807c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1808c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1809c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
18101da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
18111da177e4SLinus Torvalds 				0);
1812c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1813c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1814c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1815c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1816c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1817c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1818c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1819c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1820c65b1445SDouglas Gilbert 		}
18211da177e4SLinus Torvalds 		return check_condition_result;
18221da177e4SLinus Torvalds 	}
18231da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1824c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1825c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1826c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
18271da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1828c65b1445SDouglas Gilbert 	else {
1829c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1830c65b1445SDouglas Gilbert 		u = lba;
1831c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1832c65b1445SDouglas Gilbert 		from_bottom = 0;
1833c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1834c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1835c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1836c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1837c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1838c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1839c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1840c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1841c65b1445SDouglas Gilbert 	}
18421da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18431da177e4SLinus Torvalds 	return ret;
18441da177e4SLinus Torvalds }
18451da177e4SLinus Torvalds 
1846c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1847c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
18481da177e4SLinus Torvalds {
18491da177e4SLinus Torvalds 	unsigned long iflags;
1850c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1851c65b1445SDouglas Gilbert 	unsigned long long u;
18521da177e4SLinus Torvalds 	int res;
18531da177e4SLinus Torvalds 
1854c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18551da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18561da177e4SLinus Torvalds 			       	0);
18571da177e4SLinus Torvalds 		return check_condition_result;
18581da177e4SLinus Torvalds 	}
1859c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1860c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1861c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1862c65b1445SDouglas Gilbert 				0);
1863c65b1445SDouglas Gilbert 		return check_condition_result;
1864c65b1445SDouglas Gilbert 	}
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1867c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1868c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1869c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
18701da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1871c65b1445SDouglas Gilbert 	else {
1872c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1873c65b1445SDouglas Gilbert 		u = lba;
1874c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1875c65b1445SDouglas Gilbert 		to_bottom = 0;
1876c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1877c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1878c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1879c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1880c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1881c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1882c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1883c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1884c65b1445SDouglas Gilbert 	}
18851da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
18861da177e4SLinus Torvalds 	if (-1 == res)
18871da177e4SLinus Torvalds 		return (DID_ERROR << 16);
18881da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
18891da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1890c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
18911da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
18921da177e4SLinus Torvalds 	return 0;
18931da177e4SLinus Torvalds }
18941da177e4SLinus Torvalds 
1895c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
18961da177e4SLinus Torvalds 
18971da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
18981da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
18991da177e4SLinus Torvalds {
19001da177e4SLinus Torvalds 	unsigned int alloc_len;
1901c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
19021da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
19031da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
19041da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
19051da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1906c65b1445SDouglas Gilbert 	unsigned char * max_addr;
19071da177e4SLinus Torvalds 
19081da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1909c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
19101da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
19111da177e4SLinus Torvalds 			       	0);
19121da177e4SLinus Torvalds 		return check_condition_result;
19131da177e4SLinus Torvalds 	}
19141da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
19151da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
19161da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1917c65b1445SDouglas Gilbert 	if (1 == select_report)
1918c65b1445SDouglas Gilbert 		lun_cnt = 0;
1919c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1920c65b1445SDouglas Gilbert 		--lun_cnt;
1921c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1922c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1923c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1924c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1925c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1926c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1927c65b1445SDouglas Gilbert 	if (n < num) {
1928c65b1445SDouglas Gilbert 		wlun = 0;
1929c65b1445SDouglas Gilbert 		lun_cnt = n;
1930c65b1445SDouglas Gilbert 	}
19311da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1932c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1933c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1934c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1935c65b1445SDouglas Gilbert 	     i++, lun++) {
1936c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
19371da177e4SLinus Torvalds 		if (upper)
19381da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
19391da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1940c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
19411da177e4SLinus Torvalds 	}
1942c65b1445SDouglas Gilbert 	if (wlun) {
1943c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1944c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1945c65b1445SDouglas Gilbert 		i++;
1946c65b1445SDouglas Gilbert 	}
1947c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
19481da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
19491da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
19501da177e4SLinus Torvalds }
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds /* When timer goes off this function is called. */
19531da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
19541da177e4SLinus Torvalds {
19551da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
19561da177e4SLinus Torvalds 	unsigned long iflags;
19571da177e4SLinus Torvalds 
19581da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
19591da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
19601da177e4SLinus Torvalds 		       "large\n");
19611da177e4SLinus Torvalds 		return;
19621da177e4SLinus Torvalds 	}
19631da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
19641da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
19651da177e4SLinus Torvalds 	if (! sqcp->in_use) {
19661da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
19671da177e4SLinus Torvalds 		       "interrupt\n");
19681da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
19691da177e4SLinus Torvalds 		return;
19701da177e4SLinus Torvalds 	}
19711da177e4SLinus Torvalds 	sqcp->in_use = 0;
19721da177e4SLinus Torvalds 	if (sqcp->done_funct) {
19731da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
19741da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
19751da177e4SLinus Torvalds 	}
19761da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
19771da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
19781da177e4SLinus Torvalds }
19791da177e4SLinus Torvalds 
19801da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
19811da177e4SLinus Torvalds {
19821da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1983c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
1984c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19851da177e4SLinus Torvalds 	return 0;
19861da177e4SLinus Torvalds }
19871da177e4SLinus Torvalds 
19881da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
19891da177e4SLinus Torvalds {
19901da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
19911da177e4SLinus Torvalds 
19921da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1993c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1994c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19951da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
19961da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
19971da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
19986f3cbf55SDouglas Gilbert 	if (NULL == devip)
19996f3cbf55SDouglas Gilbert 		return 1;	/* no resources, will be marked offline */
20001da177e4SLinus Torvalds 	sdp->hostdata = devip;
20011da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
20021da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
20031da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
2004c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
20051da177e4SLinus Torvalds 	return 0;
20061da177e4SLinus Torvalds }
20071da177e4SLinus Torvalds 
20081da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
20091da177e4SLinus Torvalds {
20101da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20111da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2014c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
2015c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20161da177e4SLinus Torvalds 	if (devip) {
20171da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
20181da177e4SLinus Torvalds 		devip->used = 0;
20191da177e4SLinus Torvalds 		sdp->hostdata = NULL;
20201da177e4SLinus Torvalds 	}
20211da177e4SLinus Torvalds }
20221da177e4SLinus Torvalds 
20231da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
20241da177e4SLinus Torvalds {
20251da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20261da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
20271da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20281da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
20291da177e4SLinus Torvalds 
20301da177e4SLinus Torvalds 	if (devip)
20311da177e4SLinus Torvalds 		return devip;
20321da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
20331da177e4SLinus Torvalds         if(! sdbg_host) {
20341da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
20351da177e4SLinus Torvalds 		return NULL;
20361da177e4SLinus Torvalds         }
20371da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
20381da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
20391da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
20401da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
20411da177e4SLinus Torvalds                         return devip;
20421da177e4SLinus Torvalds 		else {
20431da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
20441da177e4SLinus Torvalds 				open_devip = devip;
20451da177e4SLinus Torvalds 		}
20461da177e4SLinus Torvalds 	}
20471da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
20486f3cbf55SDouglas Gilbert 		open_devip = kzalloc(sizeof(*open_devip),GFP_ATOMIC);
20491da177e4SLinus Torvalds 		if (NULL == open_devip) {
20501da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
20511da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
20521da177e4SLinus Torvalds 			return NULL;
20531da177e4SLinus Torvalds 		}
20541da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20551da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
20561da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
20571da177e4SLinus Torvalds 	}
20581da177e4SLinus Torvalds         if (open_devip) {
20591da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
20601da177e4SLinus Torvalds 		open_devip->target = sdev->id;
20611da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
20621da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20631da177e4SLinus Torvalds 		open_devip->reset = 1;
20641da177e4SLinus Torvalds 		open_devip->used = 1;
20651da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
20661da177e4SLinus Torvalds 		if (scsi_debug_dsense)
20671da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
20681da177e4SLinus Torvalds 		else {
20691da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
20701da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
20711da177e4SLinus Torvalds 		}
2072c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2073c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
20741da177e4SLinus Torvalds 		return open_devip;
20751da177e4SLinus Torvalds         }
20761da177e4SLinus Torvalds         return NULL;
20771da177e4SLinus Torvalds }
20781da177e4SLinus Torvalds 
20791da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
20801da177e4SLinus Torvalds 			    int asc, int asq)
20811da177e4SLinus Torvalds {
20821da177e4SLinus Torvalds 	unsigned char * sbuff;
20831da177e4SLinus Torvalds 
20841da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
20851da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
20861da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
20871da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
20881da177e4SLinus Torvalds 		sbuff[1] = key;
20891da177e4SLinus Torvalds 		sbuff[2] = asc;
20901da177e4SLinus Torvalds 		sbuff[3] = asq;
20911da177e4SLinus Torvalds 	} else {
20921da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
20931da177e4SLinus Torvalds 		sbuff[2] = key;
20941da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
20951da177e4SLinus Torvalds 		sbuff[12] = asc;
20961da177e4SLinus Torvalds 		sbuff[13] = asq;
20971da177e4SLinus Torvalds 	}
20981da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20991da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
21001da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
21011da177e4SLinus Torvalds }
21021da177e4SLinus Torvalds 
21031da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
21041da177e4SLinus Torvalds {
21051da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21061da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
21071da177e4SLinus Torvalds 	++num_aborts;
21081da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
21091da177e4SLinus Torvalds 	return SUCCESS;
21101da177e4SLinus Torvalds }
21111da177e4SLinus Torvalds 
21121da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
21131da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
21141da177e4SLinus Torvalds {
21151da177e4SLinus Torvalds 	int res;
21161da177e4SLinus Torvalds 	unsigned char *buf;
21171da177e4SLinus Torvalds 
21181da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21191da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
21201da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
21211da177e4SLinus Torvalds 	if (buf) {
21221da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
21231da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
21241da177e4SLinus Torvalds 		kfree(buf);
21251da177e4SLinus Torvalds 		if (! res)
21261da177e4SLinus Torvalds 			return res;
21271da177e4SLinus Torvalds 	}
21281da177e4SLinus Torvalds 	info[0] = sdebug_heads;
21291da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
21301da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
21311da177e4SLinus Torvalds 	return 0;
21321da177e4SLinus Torvalds }
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
21351da177e4SLinus Torvalds {
21361da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
21371da177e4SLinus Torvalds 
21381da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21391da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
21401da177e4SLinus Torvalds 	++num_dev_resets;
21411da177e4SLinus Torvalds 	if (SCpnt) {
21421da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
21431da177e4SLinus Torvalds 		if (devip)
21441da177e4SLinus Torvalds 			devip->reset = 1;
21451da177e4SLinus Torvalds 	}
21461da177e4SLinus Torvalds 	return SUCCESS;
21471da177e4SLinus Torvalds }
21481da177e4SLinus Torvalds 
21491da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
21501da177e4SLinus Torvalds {
21511da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
21521da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21531da177e4SLinus Torvalds         struct scsi_device * sdp;
21541da177e4SLinus Torvalds         struct Scsi_Host * hp;
21551da177e4SLinus Torvalds 
21561da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21571da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
21581da177e4SLinus Torvalds 	++num_bus_resets;
21591da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
21601da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
21611da177e4SLinus Torvalds 		if (sdbg_host) {
21621da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
21631da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
21641da177e4SLinus Torvalds                                             dev_list)
21651da177e4SLinus Torvalds 				dev_info->reset = 1;
21661da177e4SLinus Torvalds 		}
21671da177e4SLinus Torvalds 	}
21681da177e4SLinus Torvalds 	return SUCCESS;
21691da177e4SLinus Torvalds }
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
21721da177e4SLinus Torvalds {
21731da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
21741da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21751da177e4SLinus Torvalds 
21761da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21771da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
21781da177e4SLinus Torvalds 	++num_host_resets;
21791da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
21801da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
21811da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
21821da177e4SLinus Torvalds                                     dev_list)
21831da177e4SLinus Torvalds                         dev_info->reset = 1;
21841da177e4SLinus Torvalds         }
21851da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
21861da177e4SLinus Torvalds 	stop_all_queued();
21871da177e4SLinus Torvalds 	return SUCCESS;
21881da177e4SLinus Torvalds }
21891da177e4SLinus Torvalds 
21901da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
21911da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
21921da177e4SLinus Torvalds {
21931da177e4SLinus Torvalds 	unsigned long iflags;
21941da177e4SLinus Torvalds 	int k;
21951da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
21981da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21991da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22001da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
22011da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22021da177e4SLinus Torvalds 			sqcp->in_use = 0;
22031da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22041da177e4SLinus Torvalds 			break;
22051da177e4SLinus Torvalds 		}
22061da177e4SLinus Torvalds 	}
22071da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22081da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
22091da177e4SLinus Torvalds }
22101da177e4SLinus Torvalds 
22111da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
22121da177e4SLinus Torvalds static void stop_all_queued(void)
22131da177e4SLinus Torvalds {
22141da177e4SLinus Torvalds 	unsigned long iflags;
22151da177e4SLinus Torvalds 	int k;
22161da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22171da177e4SLinus Torvalds 
22181da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22191da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22201da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22211da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
22221da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22231da177e4SLinus Torvalds 			sqcp->in_use = 0;
22241da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22251da177e4SLinus Torvalds 		}
22261da177e4SLinus Torvalds 	}
22271da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22281da177e4SLinus Torvalds }
22291da177e4SLinus Torvalds 
22301da177e4SLinus Torvalds /* Initializes timers in queued array */
22311da177e4SLinus Torvalds static void __init init_all_queued(void)
22321da177e4SLinus Torvalds {
22331da177e4SLinus Torvalds 	unsigned long iflags;
22341da177e4SLinus Torvalds 	int k;
22351da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22361da177e4SLinus Torvalds 
22371da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22381da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22391da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22401da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
22411da177e4SLinus Torvalds 		sqcp->in_use = 0;
22421da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
22431da177e4SLinus Torvalds 	}
22441da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22451da177e4SLinus Torvalds }
22461da177e4SLinus Torvalds 
22471da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
22481da177e4SLinus Torvalds {
22491da177e4SLinus Torvalds 	struct partition * pp;
22501da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
22511da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
22521da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
22531da177e4SLinus Torvalds 
22541da177e4SLinus Torvalds 	/* assume partition table already zeroed */
22551da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
22561da177e4SLinus Torvalds 		return;
22571da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
22581da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
22591da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
22601da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
22611da177e4SLinus Torvalds 	}
2262c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
22631da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
22641da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
22651da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
22661da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
22671da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
22681da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
22691da177e4SLinus Torvalds 			    * heads_by_sects;
22701da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
22711da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
22721da177e4SLinus Torvalds 
22731da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
22741da177e4SLinus Torvalds 	ramp[511] = 0xAA;
22751da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
22761da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
22771da177e4SLinus Torvalds 		start_sec = starts[k];
22781da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
22791da177e4SLinus Torvalds 		pp->boot_ind = 0;
22801da177e4SLinus Torvalds 
22811da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
22821da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
22831da177e4SLinus Torvalds 			   / sdebug_sectors_per;
22841da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
22871da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
22881da177e4SLinus Torvalds 			       / sdebug_sectors_per;
22891da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
22901da177e4SLinus Torvalds 
22911da177e4SLinus Torvalds 		pp->start_sect = start_sec;
22921da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
22931da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
22941da177e4SLinus Torvalds 	}
22951da177e4SLinus Torvalds }
22961da177e4SLinus Torvalds 
22971da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
22981da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
22991da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
23001da177e4SLinus Torvalds {
23011da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
23021da177e4SLinus Torvalds 		if (scsi_result) {
23031da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
23041da177e4SLinus Torvalds 
2305c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2306c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2307c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
23081da177e4SLinus Torvalds 		}
23091da177e4SLinus Torvalds 	}
23101da177e4SLinus Torvalds 	if (cmnd && devip) {
23111da177e4SLinus Torvalds 		/* simulate autosense by this driver */
23121da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
23131da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
23141da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
23151da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
23161da177e4SLinus Torvalds 	}
23171da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
23181da177e4SLinus Torvalds 		if (cmnd)
23191da177e4SLinus Torvalds 			cmnd->result = scsi_result;
23201da177e4SLinus Torvalds 		if (done)
23211da177e4SLinus Torvalds 			done(cmnd);
23221da177e4SLinus Torvalds 		return 0;
23231da177e4SLinus Torvalds 	} else {
23241da177e4SLinus Torvalds 		unsigned long iflags;
23251da177e4SLinus Torvalds 		int k;
23261da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
23271da177e4SLinus Torvalds 
23281da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
23291da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
23301da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
23311da177e4SLinus Torvalds 			if (! sqcp->in_use)
23321da177e4SLinus Torvalds 				break;
23331da177e4SLinus Torvalds 		}
23341da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
23351da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
23361da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
23371da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
23381da177e4SLinus Torvalds 		}
23391da177e4SLinus Torvalds 		sqcp->in_use = 1;
23401da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
23411da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
23421da177e4SLinus Torvalds 		sqcp->done_funct = done;
23431da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
23441da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
23451da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
23461da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
23471da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23481da177e4SLinus Torvalds 		if (cmnd)
23491da177e4SLinus Torvalds 			cmnd->result = 0;
23501da177e4SLinus Torvalds 		return 0;
23511da177e4SLinus Torvalds 	}
23521da177e4SLinus Torvalds }
23531da177e4SLinus Torvalds 
235423183910SDouglas Gilbert /* Note: The following macros create attribute files in the
235523183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
235623183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
235723183910SDouglas Gilbert    as it can when the corresponding attribute in the
235823183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
235923183910SDouglas Gilbert  */
2360c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2361c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2362c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2363c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2364c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
236523183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2366c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2367c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2368c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2369c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2370c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2371c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2372c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2373c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
237423183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
237523183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
23761da177e4SLinus Torvalds 
23771da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
23781da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
23791da177e4SLinus Torvalds MODULE_LICENSE("GPL");
23801da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
23811da177e4SLinus Torvalds 
23821da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
23831da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2384c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2385c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2386beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
238723183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2388c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2389c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
23901da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2391c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
23926f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
23931da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
23941da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2395c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
239623183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
23971da177e4SLinus Torvalds 
23981da177e4SLinus Torvalds 
23991da177e4SLinus Torvalds static char sdebug_info[256];
24001da177e4SLinus Torvalds 
24011da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
24021da177e4SLinus Torvalds {
24031da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
24041da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
24051da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
24061da177e4SLinus Torvalds 		scsi_debug_opts);
24071da177e4SLinus Torvalds 	return sdebug_info;
24081da177e4SLinus Torvalds }
24091da177e4SLinus Torvalds 
24101da177e4SLinus Torvalds /* scsi_debug_proc_info
24111da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
24121da177e4SLinus Torvalds  */
24131da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
24141da177e4SLinus Torvalds 				int length, int inout)
24151da177e4SLinus Torvalds {
24161da177e4SLinus Torvalds 	int len, pos, begin;
24171da177e4SLinus Torvalds 	int orig_length;
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 	orig_length = length;
24201da177e4SLinus Torvalds 
24211da177e4SLinus Torvalds 	if (inout == 1) {
24221da177e4SLinus Torvalds 		char arr[16];
24231da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
24241da177e4SLinus Torvalds 
24251da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
24261da177e4SLinus Torvalds 			return -EACCES;
24271da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
24281da177e4SLinus Torvalds 		arr[minLen] = '\0';
24291da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
24301da177e4SLinus Torvalds 			return -EINVAL;
24311da177e4SLinus Torvalds 		scsi_debug_opts = pos;
24321da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
24331da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
24341da177e4SLinus Torvalds 		return length;
24351da177e4SLinus Torvalds 	}
24361da177e4SLinus Torvalds 	begin = 0;
24371da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
24381da177e4SLinus Torvalds 	    "%s [%s]\n"
24391da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
24401da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
24411da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
24421da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
24431da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
24441da177e4SLinus Torvalds 	    "host_resets=%d\n",
24451da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
24461da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
24471da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
24481da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
24491da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
24501da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
24511da177e4SLinus Torvalds 	if (pos < offset) {
24521da177e4SLinus Torvalds 		len = 0;
24531da177e4SLinus Torvalds 		begin = pos;
24541da177e4SLinus Torvalds 	}
24551da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
24561da177e4SLinus Torvalds 	len -= (offset - begin);
24571da177e4SLinus Torvalds 	if (len > length)
24581da177e4SLinus Torvalds 		len = length;
24591da177e4SLinus Torvalds 	return len;
24601da177e4SLinus Torvalds }
24611da177e4SLinus Torvalds 
24621da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
24631da177e4SLinus Torvalds {
24641da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
24651da177e4SLinus Torvalds }
24661da177e4SLinus Torvalds 
24671da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
24681da177e4SLinus Torvalds 				  const char * buf, size_t count)
24691da177e4SLinus Torvalds {
24701da177e4SLinus Torvalds         int delay;
24711da177e4SLinus Torvalds 	char work[20];
24721da177e4SLinus Torvalds 
24731da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24741da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
24751da177e4SLinus Torvalds 			scsi_debug_delay = delay;
24761da177e4SLinus Torvalds 			return count;
24771da177e4SLinus Torvalds 		}
24781da177e4SLinus Torvalds 	}
24791da177e4SLinus Torvalds 	return -EINVAL;
24801da177e4SLinus Torvalds }
24811da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
24821da177e4SLinus Torvalds 	    sdebug_delay_store);
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
24851da177e4SLinus Torvalds {
24861da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
24871da177e4SLinus Torvalds }
24881da177e4SLinus Torvalds 
24891da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
24901da177e4SLinus Torvalds 				 const char * buf, size_t count)
24911da177e4SLinus Torvalds {
24921da177e4SLinus Torvalds         int opts;
24931da177e4SLinus Torvalds 	char work[20];
24941da177e4SLinus Torvalds 
24951da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24961da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
24971da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
24981da177e4SLinus Torvalds 				goto opts_done;
24991da177e4SLinus Torvalds 		} else {
25001da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
25011da177e4SLinus Torvalds 				goto opts_done;
25021da177e4SLinus Torvalds 		}
25031da177e4SLinus Torvalds 	}
25041da177e4SLinus Torvalds 	return -EINVAL;
25051da177e4SLinus Torvalds opts_done:
25061da177e4SLinus Torvalds 	scsi_debug_opts = opts;
25071da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
25081da177e4SLinus Torvalds 	return count;
25091da177e4SLinus Torvalds }
25101da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
25111da177e4SLinus Torvalds 	    sdebug_opts_store);
25121da177e4SLinus Torvalds 
25131da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
25141da177e4SLinus Torvalds {
25151da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
25161da177e4SLinus Torvalds }
25171da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
25181da177e4SLinus Torvalds 				  const char * buf, size_t count)
25191da177e4SLinus Torvalds {
25201da177e4SLinus Torvalds         int n;
25211da177e4SLinus Torvalds 
25221da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25231da177e4SLinus Torvalds 		scsi_debug_ptype = n;
25241da177e4SLinus Torvalds 		return count;
25251da177e4SLinus Torvalds 	}
25261da177e4SLinus Torvalds 	return -EINVAL;
25271da177e4SLinus Torvalds }
25281da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
25291da177e4SLinus Torvalds 
25301da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
25311da177e4SLinus Torvalds {
25321da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
25331da177e4SLinus Torvalds }
25341da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
25351da177e4SLinus Torvalds 				  const char * buf, size_t count)
25361da177e4SLinus Torvalds {
25371da177e4SLinus Torvalds         int n;
25381da177e4SLinus Torvalds 
25391da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25401da177e4SLinus Torvalds 		scsi_debug_dsense = n;
25411da177e4SLinus Torvalds 		return count;
25421da177e4SLinus Torvalds 	}
25431da177e4SLinus Torvalds 	return -EINVAL;
25441da177e4SLinus Torvalds }
25451da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
25461da177e4SLinus Torvalds 	    sdebug_dsense_store);
25471da177e4SLinus Torvalds 
254823183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
254923183910SDouglas Gilbert {
255023183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
255123183910SDouglas Gilbert }
255223183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
255323183910SDouglas Gilbert 				    const char * buf, size_t count)
255423183910SDouglas Gilbert {
255523183910SDouglas Gilbert         int n;
255623183910SDouglas Gilbert 
255723183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
255823183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
255923183910SDouglas Gilbert 		return count;
256023183910SDouglas Gilbert 	}
256123183910SDouglas Gilbert 	return -EINVAL;
256223183910SDouglas Gilbert }
256323183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
256423183910SDouglas Gilbert 	    sdebug_fake_rw_store);
256523183910SDouglas Gilbert 
2566c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2567c65b1445SDouglas Gilbert {
2568c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2569c65b1445SDouglas Gilbert }
2570c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2571c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2572c65b1445SDouglas Gilbert {
2573c65b1445SDouglas Gilbert         int n;
2574c65b1445SDouglas Gilbert 
2575c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2576c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2577c65b1445SDouglas Gilbert 		return count;
2578c65b1445SDouglas Gilbert 	}
2579c65b1445SDouglas Gilbert 	return -EINVAL;
2580c65b1445SDouglas Gilbert }
2581c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2582c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2583c65b1445SDouglas Gilbert 
25841da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
25851da177e4SLinus Torvalds {
25861da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
25871da177e4SLinus Torvalds }
25881da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
25891da177e4SLinus Torvalds 				     const char * buf, size_t count)
25901da177e4SLinus Torvalds {
25911da177e4SLinus Torvalds         int n;
25921da177e4SLinus Torvalds 
25931da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25941da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
25951da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
25961da177e4SLinus Torvalds 		return count;
25971da177e4SLinus Torvalds 	}
25981da177e4SLinus Torvalds 	return -EINVAL;
25991da177e4SLinus Torvalds }
26001da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
26011da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
26021da177e4SLinus Torvalds 
26031da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
26041da177e4SLinus Torvalds {
26051da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
26061da177e4SLinus Torvalds }
26071da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
26081da177e4SLinus Torvalds 
26091da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
26101da177e4SLinus Torvalds {
26111da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
26121da177e4SLinus Torvalds }
26131da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
26141da177e4SLinus Torvalds 
26151da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
26161da177e4SLinus Torvalds {
26171da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
26181da177e4SLinus Torvalds }
26191da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
26201da177e4SLinus Torvalds 				      const char * buf, size_t count)
26211da177e4SLinus Torvalds {
26221da177e4SLinus Torvalds         int nth;
26231da177e4SLinus Torvalds 
26241da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
26251da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
26261da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
26271da177e4SLinus Torvalds 		return count;
26281da177e4SLinus Torvalds 	}
26291da177e4SLinus Torvalds 	return -EINVAL;
26301da177e4SLinus Torvalds }
26311da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
26321da177e4SLinus Torvalds 	    sdebug_every_nth_store);
26331da177e4SLinus Torvalds 
26341da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
26351da177e4SLinus Torvalds {
26361da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
26371da177e4SLinus Torvalds }
26381da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
26391da177e4SLinus Torvalds 				     const char * buf, size_t count)
26401da177e4SLinus Torvalds {
26411da177e4SLinus Torvalds         int n;
26421da177e4SLinus Torvalds 
26431da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26441da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
26451da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26461da177e4SLinus Torvalds 		return count;
26471da177e4SLinus Torvalds 	}
26481da177e4SLinus Torvalds 	return -EINVAL;
26491da177e4SLinus Torvalds }
26501da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
26511da177e4SLinus Torvalds 	    sdebug_max_luns_store);
26521da177e4SLinus Torvalds 
26531da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
26541da177e4SLinus Torvalds {
26551da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
26561da177e4SLinus Torvalds }
26571da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
26581da177e4SLinus Torvalds 
2659c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2660c65b1445SDouglas Gilbert {
2661c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2662c65b1445SDouglas Gilbert }
2663c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2664c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2665c65b1445SDouglas Gilbert {
2666c65b1445SDouglas Gilbert         int n;
2667c65b1445SDouglas Gilbert 
2668c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2669c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2670c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2671c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2672c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2673c65b1445SDouglas Gilbert 		} else
2674c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2675c65b1445SDouglas Gilbert 		return count;
2676c65b1445SDouglas Gilbert 	}
2677c65b1445SDouglas Gilbert 	return -EINVAL;
2678c65b1445SDouglas Gilbert }
2679c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2680c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2681c65b1445SDouglas Gilbert 
26821da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
26831da177e4SLinus Torvalds {
26841da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
26851da177e4SLinus Torvalds }
26861da177e4SLinus Torvalds 
26871da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
26881da177e4SLinus Torvalds 				     const char * buf, size_t count)
26891da177e4SLinus Torvalds {
26901da177e4SLinus Torvalds         int delta_hosts;
26911da177e4SLinus Torvalds 	char work[20];
26921da177e4SLinus Torvalds 
26931da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
26941da177e4SLinus Torvalds 		return -EINVAL;
26951da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
26961da177e4SLinus Torvalds 		int neg = 0;
26971da177e4SLinus Torvalds 
26981da177e4SLinus Torvalds 		if ('-' == *work)
26991da177e4SLinus Torvalds 			neg = 1;
27001da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
27011da177e4SLinus Torvalds 			return -EINVAL;
27021da177e4SLinus Torvalds 		if (neg)
27031da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
27041da177e4SLinus Torvalds 	}
27051da177e4SLinus Torvalds 	if (delta_hosts > 0) {
27061da177e4SLinus Torvalds 		do {
27071da177e4SLinus Torvalds 			sdebug_add_adapter();
27081da177e4SLinus Torvalds 		} while (--delta_hosts);
27091da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
27101da177e4SLinus Torvalds 		do {
27111da177e4SLinus Torvalds 			sdebug_remove_adapter();
27121da177e4SLinus Torvalds 		} while (++delta_hosts);
27131da177e4SLinus Torvalds 	}
27141da177e4SLinus Torvalds 	return count;
27151da177e4SLinus Torvalds }
27161da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
27171da177e4SLinus Torvalds 	    sdebug_add_host_store);
27181da177e4SLinus Torvalds 
271923183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
272023183910SDouglas Gilbert 					  char * buf)
272123183910SDouglas Gilbert {
272223183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
272323183910SDouglas Gilbert }
272423183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
272523183910SDouglas Gilbert 					   const char * buf, size_t count)
272623183910SDouglas Gilbert {
272723183910SDouglas Gilbert 	int n;
272823183910SDouglas Gilbert 
272923183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
273023183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
273123183910SDouglas Gilbert 		return count;
273223183910SDouglas Gilbert 	}
273323183910SDouglas Gilbert 	return -EINVAL;
273423183910SDouglas Gilbert }
273523183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
273623183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
273723183910SDouglas Gilbert 
273823183910SDouglas Gilbert /* Note: The following function creates attribute files in the
273923183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
274023183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
274123183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
274223183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
274323183910SDouglas Gilbert  */
27446ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
27451da177e4SLinus Torvalds {
27466ecaff7fSRandy Dunlap 	int ret;
27476ecaff7fSRandy Dunlap 
27486ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27496ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
27506ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27516ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27526ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
275323183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27546ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
275523183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27566ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
275723183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
27586ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27596ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
27606ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
276123183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
276223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
27636ecaff7fSRandy Dunlap 	return ret;
27641da177e4SLinus Torvalds }
27651da177e4SLinus Torvalds 
27661da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
27671da177e4SLinus Torvalds {
276823183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
276923183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
27701da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
27711da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
27721da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27731da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
277423183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
277523183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27761da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
277723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27781da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
27791da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27801da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27811da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
27821da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27831da177e4SLinus Torvalds }
27841da177e4SLinus Torvalds 
27851da177e4SLinus Torvalds static int __init scsi_debug_init(void)
27861da177e4SLinus Torvalds {
2787c65b1445SDouglas Gilbert 	unsigned int sz;
27881da177e4SLinus Torvalds 	int host_to_add;
27891da177e4SLinus Torvalds 	int k;
27906ecaff7fSRandy Dunlap 	int ret;
27911da177e4SLinus Torvalds 
27921da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
27931da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2794c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2795c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2796c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2797c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2798c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2799c65b1445SDouglas Gilbert 	} else
2800c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
28011da177e4SLinus Torvalds 
28021da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
28031da177e4SLinus Torvalds 	sdebug_heads = 8;
28041da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
28051da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
28061da177e4SLinus Torvalds 		sdebug_heads = 32;
28071da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
28081da177e4SLinus Torvalds 		sdebug_heads = 64;
28091da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28101da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28111da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
28121da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
28131da177e4SLinus Torvalds 		sdebug_heads = 255;
28141da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
28151da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28161da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28171da177e4SLinus Torvalds 	}
28181da177e4SLinus Torvalds 
28191da177e4SLinus Torvalds 	sz = sdebug_store_size;
28201da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
28211da177e4SLinus Torvalds 	if (NULL == fake_storep) {
28221da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
28231da177e4SLinus Torvalds 		return -ENOMEM;
28241da177e4SLinus Torvalds 	}
28251da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
28261da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
28271da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
28281da177e4SLinus Torvalds 
28296ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
28306ecaff7fSRandy Dunlap 	if (ret < 0) {
28316ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
28326ecaff7fSRandy Dunlap 			ret);
28336ecaff7fSRandy Dunlap 		goto free_vm;
28346ecaff7fSRandy Dunlap 	}
28356ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
28366ecaff7fSRandy Dunlap 	if (ret < 0) {
28376ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
28386ecaff7fSRandy Dunlap 			ret);
28396ecaff7fSRandy Dunlap 		goto dev_unreg;
28406ecaff7fSRandy Dunlap 	}
28416ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
28426ecaff7fSRandy Dunlap 	if (ret < 0) {
28436ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
28446ecaff7fSRandy Dunlap 			ret);
28456ecaff7fSRandy Dunlap 		goto bus_unreg;
28466ecaff7fSRandy Dunlap 	}
28476ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
28486ecaff7fSRandy Dunlap 	if (ret < 0) {
28496ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
28506ecaff7fSRandy Dunlap 			ret);
28516ecaff7fSRandy Dunlap 		goto del_files;
28526ecaff7fSRandy Dunlap 	}
28531da177e4SLinus Torvalds 
28546ecaff7fSRandy Dunlap 	init_all_queued();
28551da177e4SLinus Torvalds 
2856b02b6bc4SKristian Høgsberg 	sdebug_driver_template.proc_name = sdebug_proc_name;
28571da177e4SLinus Torvalds 
28581da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
28591da177e4SLinus Torvalds         scsi_debug_add_host = 0;
28601da177e4SLinus Torvalds 
28611da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
28621da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
28631da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
28641da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
28651da177e4SLinus Torvalds                         break;
28661da177e4SLinus Torvalds                 }
28671da177e4SLinus Torvalds         }
28681da177e4SLinus Torvalds 
28691da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
28701da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
28711da177e4SLinus Torvalds 		       scsi_debug_add_host);
28721da177e4SLinus Torvalds 	}
28731da177e4SLinus Torvalds 	return 0;
28746ecaff7fSRandy Dunlap 
28756ecaff7fSRandy Dunlap del_files:
28766ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
28776ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
28786ecaff7fSRandy Dunlap bus_unreg:
28796ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
28806ecaff7fSRandy Dunlap dev_unreg:
28816ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
28826ecaff7fSRandy Dunlap free_vm:
28836ecaff7fSRandy Dunlap 	vfree(fake_storep);
28846ecaff7fSRandy Dunlap 
28856ecaff7fSRandy Dunlap 	return ret;
28861da177e4SLinus Torvalds }
28871da177e4SLinus Torvalds 
28881da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
28891da177e4SLinus Torvalds {
28901da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
28911da177e4SLinus Torvalds 
28921da177e4SLinus Torvalds 	stop_all_queued();
28931da177e4SLinus Torvalds 	for (; k; k--)
28941da177e4SLinus Torvalds 		sdebug_remove_adapter();
28951da177e4SLinus Torvalds 	do_remove_driverfs_files();
28961da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
28971da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
28981da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
28991da177e4SLinus Torvalds 
29001da177e4SLinus Torvalds 	vfree(fake_storep);
29011da177e4SLinus Torvalds }
29021da177e4SLinus Torvalds 
29031da177e4SLinus Torvalds device_initcall(scsi_debug_init);
29041da177e4SLinus Torvalds module_exit(scsi_debug_exit);
29051da177e4SLinus Torvalds 
290652c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
29071da177e4SLinus Torvalds {
29081da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
29091da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
29101da177e4SLinus Torvalds }
29111da177e4SLinus Torvalds 
29121da177e4SLinus Torvalds static struct device pseudo_primary = {
29131da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
29141da177e4SLinus Torvalds 	.release	= pseudo_0_release,
29151da177e4SLinus Torvalds };
29161da177e4SLinus Torvalds 
29171da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
29181da177e4SLinus Torvalds                           struct device_driver *dev_driver)
29191da177e4SLinus Torvalds {
29201da177e4SLinus Torvalds         return 1;
29211da177e4SLinus Torvalds }
29221da177e4SLinus Torvalds 
29231da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
29241da177e4SLinus Torvalds         .name = "pseudo",
29251da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2926bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2927bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
29281da177e4SLinus Torvalds };
29291da177e4SLinus Torvalds 
29301da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
29311da177e4SLinus Torvalds {
29321da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29351da177e4SLinus Torvalds         kfree(sdbg_host);
29361da177e4SLinus Torvalds }
29371da177e4SLinus Torvalds 
29381da177e4SLinus Torvalds static int sdebug_add_adapter(void)
29391da177e4SLinus Torvalds {
29401da177e4SLinus Torvalds 	int k, devs_per_host;
29411da177e4SLinus Torvalds         int error = 0;
29421da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29431da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29441da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29451da177e4SLinus Torvalds 
294624669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
29471da177e4SLinus Torvalds         if (NULL == sdbg_host) {
29481da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
29491da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
29501da177e4SLinus Torvalds                 return -ENOMEM;
29511da177e4SLinus Torvalds         }
29521da177e4SLinus Torvalds 
29531da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
29541da177e4SLinus Torvalds 
29551da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
29561da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
295724669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
29581da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
29591da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
29601da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
29611da177e4SLinus Torvalds                         error = -ENOMEM;
29621da177e4SLinus Torvalds 			goto clean;
29631da177e4SLinus Torvalds                 }
29641da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
29651da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
29661da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
29671da177e4SLinus Torvalds         }
29681da177e4SLinus Torvalds 
29691da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
29701da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
29711da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
29741da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
29751da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
29761da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
29771da177e4SLinus Torvalds 
29781da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
29791da177e4SLinus Torvalds 
29801da177e4SLinus Torvalds         if (error)
29811da177e4SLinus Torvalds 		goto clean;
29821da177e4SLinus Torvalds 
29831da177e4SLinus Torvalds 	++scsi_debug_add_host;
29841da177e4SLinus Torvalds         return error;
29851da177e4SLinus Torvalds 
29861da177e4SLinus Torvalds clean:
29871da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
29881da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
29891da177e4SLinus Torvalds 					  dev_list);
29901da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
29911da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
29921da177e4SLinus Torvalds 	}
29931da177e4SLinus Torvalds 
29941da177e4SLinus Torvalds 	kfree(sdbg_host);
29951da177e4SLinus Torvalds         return error;
29961da177e4SLinus Torvalds }
29971da177e4SLinus Torvalds 
29981da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
29991da177e4SLinus Torvalds {
30001da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
30011da177e4SLinus Torvalds 
30021da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
30031da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
30041da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
30051da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
30061da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
30071da177e4SLinus Torvalds 	}
30081da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
30091da177e4SLinus Torvalds 
30101da177e4SLinus Torvalds 	if (!sdbg_host)
30111da177e4SLinus Torvalds 		return;
30121da177e4SLinus Torvalds 
30131da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
30141da177e4SLinus Torvalds         --scsi_debug_add_host;
30151da177e4SLinus Torvalds }
30161da177e4SLinus Torvalds 
30171da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
30181da177e4SLinus Torvalds {
30191da177e4SLinus Torvalds         int error = 0;
30201da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30211da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
30221da177e4SLinus Torvalds 
30231da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30241da177e4SLinus Torvalds 
30251da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
30261da177e4SLinus Torvalds         if (NULL == hpnt) {
30271da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
30281da177e4SLinus Torvalds                 error = -ENODEV;
30291da177e4SLinus Torvalds 		return error;
30301da177e4SLinus Torvalds         }
30311da177e4SLinus Torvalds 
30321da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
30331da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
30341da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
30351da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
30361da177e4SLinus Torvalds 	else
30371da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3038c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
30391da177e4SLinus Torvalds 
30401da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
30411da177e4SLinus Torvalds         if (error) {
30421da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
30431da177e4SLinus Torvalds                 error = -ENODEV;
30441da177e4SLinus Torvalds 		scsi_host_put(hpnt);
30451da177e4SLinus Torvalds         } else
30461da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
30471da177e4SLinus Torvalds 
30481da177e4SLinus Torvalds 
30491da177e4SLinus Torvalds         return error;
30501da177e4SLinus Torvalds }
30511da177e4SLinus Torvalds 
30521da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
30531da177e4SLinus Torvalds {
30541da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
30551da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30561da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
30571da177e4SLinus Torvalds 
30581da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30591da177e4SLinus Torvalds 
30601da177e4SLinus Torvalds 	if (!sdbg_host) {
30611da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
30621da177e4SLinus Torvalds 		       __FUNCTION__);
30631da177e4SLinus Torvalds 		return -ENODEV;
30641da177e4SLinus Torvalds 	}
30651da177e4SLinus Torvalds 
30661da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
30671da177e4SLinus Torvalds 
30681da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30691da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30701da177e4SLinus Torvalds                                           dev_list);
30711da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
30721da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
30731da177e4SLinus Torvalds         }
30741da177e4SLinus Torvalds 
30751da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
30761da177e4SLinus Torvalds         return 0;
30771da177e4SLinus Torvalds }
30781da177e4SLinus Torvalds 
30791da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
30801da177e4SLinus Torvalds {
30811da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
30821da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
30831da177e4SLinus Torvalds 
30841da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
30851da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
30861da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
30871da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
30881da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
30891da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
30901da177e4SLinus Torvalds 		else
30911da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
3092c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
30931da177e4SLinus Torvalds 	}
30941da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
30951da177e4SLinus Torvalds }
3096