xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision b02b6bc4)
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>
411da177e4SLinus Torvalds 
421da177e4SLinus Torvalds #include <linux/blkdev.h>
431da177e4SLinus Torvalds #include "scsi.h"
441da177e4SLinus Torvalds #include <scsi/scsi_host.h>
451da177e4SLinus Torvalds #include <scsi/scsicam.h>
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #include <linux/stat.h>
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds #include "scsi_logging.h"
501da177e4SLinus Torvalds #include "scsi_debug.h"
511da177e4SLinus Torvalds 
526f3cbf55SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.81"
536f3cbf55SDouglas Gilbert static const char * scsi_debug_version_date = "20070104";
541da177e4SLinus Torvalds 
556f3cbf55SDouglas Gilbert /* Additional Sense Code (ASC) */
56c65b1445SDouglas Gilbert #define NO_ADDITIONAL_SENSE 0x0
57c65b1445SDouglas Gilbert #define LOGICAL_UNIT_NOT_READY 0x4
581da177e4SLinus Torvalds #define UNRECOVERED_READ_ERR 0x11
59c65b1445SDouglas Gilbert #define PARAMETER_LIST_LENGTH_ERR 0x1a
601da177e4SLinus Torvalds #define INVALID_OPCODE 0x20
611da177e4SLinus Torvalds #define ADDR_OUT_OF_RANGE 0x21
621da177e4SLinus Torvalds #define INVALID_FIELD_IN_CDB 0x24
63c65b1445SDouglas Gilbert #define INVALID_FIELD_IN_PARAM_LIST 0x26
641da177e4SLinus Torvalds #define POWERON_RESET 0x29
651da177e4SLinus Torvalds #define SAVING_PARAMS_UNSUP 0x39
666f3cbf55SDouglas Gilbert #define TRANSPORT_PROBLEM 0x4b
67c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
68c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
691da177e4SLinus Torvalds 
706f3cbf55SDouglas Gilbert /* Additional Sense Code Qualifier (ASCQ) */
716f3cbf55SDouglas Gilbert #define ACK_NAK_TO 0x3
726f3cbf55SDouglas Gilbert 
731da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
741da177e4SLinus Torvalds 
751da177e4SLinus Torvalds /* Default values for driver parameters */
761da177e4SLinus Torvalds #define DEF_NUM_HOST   1
771da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
781da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
791da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
801da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
811da177e4SLinus Torvalds  */
821da177e4SLinus Torvalds #define DEF_DELAY   1
831da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
841da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
851da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
861da177e4SLinus Torvalds #define DEF_OPTS   0
871da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
881da177e4SLinus Torvalds #define DEF_PTYPE   0
891da177e4SLinus Torvalds #define DEF_D_SENSE   0
90c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
91c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
9223183910SDouglas Gilbert #define DEF_FAKE_RW	0
9323183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
941da177e4SLinus Torvalds 
951da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
961da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
971da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
981da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
991da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1006f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
1011da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1021da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1031da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1041da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1056f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1066f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1071da177e4SLinus Torvalds  *
1081da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1091da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1101da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1111da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1126f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1136f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1141da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1151da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1161da177e4SLinus Torvalds  */
1171da177e4SLinus Torvalds 
1181da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1191da177e4SLinus Torvalds  * sector on read commands: */
1201da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1231da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1241da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
125c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1281da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1291da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1301da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1311da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1321da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1331da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1341da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1351da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1361da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1371da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
138c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
139c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
14023183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
14123183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1461da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1471da177e4SLinus Torvalds 
148c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
149c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1501da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1511da177e4SLinus Torvalds 
1521da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1531da177e4SLinus Torvalds    may still need them */
1541da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1551da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1561da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1591da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1601da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1611da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds struct sdebug_dev_info {
1681da177e4SLinus Torvalds 	struct list_head dev_list;
1691da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1701da177e4SLinus Torvalds 	unsigned int channel;
1711da177e4SLinus Torvalds 	unsigned int target;
1721da177e4SLinus Torvalds 	unsigned int lun;
1731da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
174c65b1445SDouglas Gilbert 	unsigned int wlun;
1751da177e4SLinus Torvalds 	char reset;
176c65b1445SDouglas Gilbert 	char stopped;
1771da177e4SLinus Torvalds 	char used;
1781da177e4SLinus Torvalds };
1791da177e4SLinus Torvalds 
1801da177e4SLinus Torvalds struct sdebug_host_info {
1811da177e4SLinus Torvalds 	struct list_head host_list;
1821da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1831da177e4SLinus Torvalds 	struct device dev;
1841da177e4SLinus Torvalds 	struct list_head dev_info_list;
1851da177e4SLinus Torvalds };
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1881da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1911da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds struct sdebug_queued_cmd {
1961da177e4SLinus Torvalds 	int in_use;
1971da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
1981da177e4SLinus Torvalds 	done_funct_t done_funct;
1991da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2001da177e4SLinus Torvalds 	int scsi_result;
2011da177e4SLinus Torvalds };
2021da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2031da177e4SLinus Torvalds 
204d0be4a7dSChristoph Hellwig static struct scsi_host_template sdebug_driver_template = {
2051da177e4SLinus Torvalds 	.proc_info =		scsi_debug_proc_info,
2061da177e4SLinus Torvalds 	.name =			"SCSI DEBUG",
2071da177e4SLinus Torvalds 	.info =			scsi_debug_info,
2081da177e4SLinus Torvalds 	.slave_alloc =		scsi_debug_slave_alloc,
2091da177e4SLinus Torvalds 	.slave_configure =	scsi_debug_slave_configure,
2101da177e4SLinus Torvalds 	.slave_destroy =	scsi_debug_slave_destroy,
2111da177e4SLinus Torvalds 	.ioctl =		scsi_debug_ioctl,
2121da177e4SLinus Torvalds 	.queuecommand =		scsi_debug_queuecommand,
2131da177e4SLinus Torvalds 	.eh_abort_handler =	scsi_debug_abort,
2141da177e4SLinus Torvalds 	.eh_bus_reset_handler = scsi_debug_bus_reset,
2151da177e4SLinus Torvalds 	.eh_device_reset_handler = scsi_debug_device_reset,
2161da177e4SLinus Torvalds 	.eh_host_reset_handler = scsi_debug_host_reset,
2171da177e4SLinus Torvalds 	.bios_param =		scsi_debug_biosparam,
2181da177e4SLinus Torvalds 	.can_queue =		SCSI_DEBUG_CANQUEUE,
2191da177e4SLinus Torvalds 	.this_id =		7,
220c65b1445SDouglas Gilbert 	.sg_tablesize =		256,
221c65b1445SDouglas Gilbert 	.cmd_per_lun =		16,
222c65b1445SDouglas Gilbert 	.max_sectors =		0xffff,
2231da177e4SLinus Torvalds 	.unchecked_isa_dma = 	0,
224c65b1445SDouglas Gilbert 	.use_clustering = 	ENABLE_CLUSTERING,
2251da177e4SLinus Torvalds 	.module =		THIS_MODULE,
2261da177e4SLinus Torvalds };
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static int num_aborts = 0;
2311da177e4SLinus Torvalds static int num_dev_resets = 0;
2321da177e4SLinus Torvalds static int num_bus_resets = 0;
2331da177e4SLinus Torvalds static int num_host_resets = 0;
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2361da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2371da177e4SLinus Torvalds 
2381da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2411da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2421da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2451da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2461da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2471da177e4SLinus Torvalds };
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds static const int check_condition_result =
2501da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2511da177e4SLinus Torvalds 
252c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
253c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
254c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
255c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
256c65b1445SDouglas Gilbert 
2571da177e4SLinus Torvalds /* function declarations */
2581da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2591da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2601da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2611da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
262c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
263c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2645a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
2655a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip);
2661da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2671da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
268c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2691da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
270c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
271c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
272c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
273c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
274c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
275c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
276c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
277c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
278c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
279c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2801da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2811da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
2821da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2831da177e4SLinus Torvalds                                 int arr_len);
2841da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2851da177e4SLinus Torvalds                                int max_arr_len);
2861da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2871da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2881da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2891da177e4SLinus Torvalds 			    int asc, int asq);
290c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2911da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2921da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2931da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2941da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2951da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2961da177e4SLinus Torvalds static void __init init_all_queued(void);
2971da177e4SLinus Torvalds static void stop_all_queued(void);
2981da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
2995a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
3005a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
3015a09e398SHannes Reinecke 			   const char * dev_id_str, int dev_id_str_len);
302c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
3036ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
3041da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds static int sdebug_add_adapter(void);
3071da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
3081da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds static struct device pseudo_primary;
3111da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds static
3151da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3161da177e4SLinus Torvalds {
3171da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
318c65b1445SDouglas Gilbert 	int len, k, j;
319c65b1445SDouglas Gilbert 	unsigned int num;
320c65b1445SDouglas Gilbert 	unsigned long long lba;
3211da177e4SLinus Torvalds 	int errsts = 0;
322c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3231da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3241da177e4SLinus Torvalds 	int inj_recovered = 0;
3256f3cbf55SDouglas Gilbert 	int inj_transport = 0;
326c65b1445SDouglas Gilbert 	int delay_override = 0;
3271da177e4SLinus Torvalds 
3281da177e4SLinus Torvalds 	if (done == NULL)
3291da177e4SLinus Torvalds 		return 0;	/* assume mid level reprocessing command */
3301da177e4SLinus Torvalds 
331c65b1445SDouglas Gilbert 	SCpnt->resid = 0;
3321da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3331da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
334c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3351da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3361da177e4SLinus Torvalds 		printk("\n");
3371da177e4SLinus Torvalds 	}
3381da177e4SLinus Torvalds         if(target == sdebug_driver_template.this_id) {
3391da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3401da177e4SLinus Torvalds 		       "target!\n");
3411da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3421da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3431da177e4SLinus Torvalds         }
3441da177e4SLinus Torvalds 
345c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
346c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3471da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3481da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3491da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3501da177e4SLinus Torvalds 	if (NULL == devip)
3511da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3521da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3551da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3561da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3571da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3581da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3591da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3601da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3611da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3621da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3636f3cbf55SDouglas Gilbert 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
3646f3cbf55SDouglas Gilbert 			inj_transport = 1; /* to reads and writes below */
3651da177e4SLinus Torvalds         }
3661da177e4SLinus Torvalds 
367c65b1445SDouglas Gilbert 	if (devip->wlun) {
368c65b1445SDouglas Gilbert 		switch (*cmd) {
369c65b1445SDouglas Gilbert 		case INQUIRY:
370c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
371c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
372c65b1445SDouglas Gilbert 		case REPORT_LUNS:
373c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
374c65b1445SDouglas Gilbert 		default:
375c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
376c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
377c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
378c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
379c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
380c65b1445SDouglas Gilbert 			errsts = check_condition_result;
381c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
382c65b1445SDouglas Gilbert 					     0);
383c65b1445SDouglas Gilbert 		}
384c65b1445SDouglas Gilbert 	}
385c65b1445SDouglas Gilbert 
3861da177e4SLinus Torvalds 	switch (*cmd) {
3871da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
388c65b1445SDouglas Gilbert 		delay_override = 1;
3891da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
3901da177e4SLinus Torvalds 		break;
3911da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
392c65b1445SDouglas Gilbert 		delay_override = 1;
3931da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
3941da177e4SLinus Torvalds 		break;
3951da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3961da177e4SLinus Torvalds 	case START_STOP:
397c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
3981da177e4SLinus Torvalds 		break;
3991da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
400c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
4011da177e4SLinus Torvalds 			break;
4021da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
4031da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
4041da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
4051da177e4SLinus Torvalds 		break;
4061da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
407c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4081da177e4SLinus Torvalds 		break;
4091da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
410c65b1445SDouglas Gilbert 		delay_override = 1;
411c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4121da177e4SLinus Torvalds 		break;
4131da177e4SLinus Torvalds         case RESERVE:
414c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4151da177e4SLinus Torvalds                 break;
4161da177e4SLinus Torvalds         case RESERVE_10:
417c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4181da177e4SLinus Torvalds                 break;
4191da177e4SLinus Torvalds         case RELEASE:
420c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4211da177e4SLinus Torvalds                 break;
4221da177e4SLinus Torvalds         case RELEASE_10:
423c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4241da177e4SLinus Torvalds                 break;
4251da177e4SLinus Torvalds 	case READ_CAPACITY:
4261da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4271da177e4SLinus Torvalds 		break;
428c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
429c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
430c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
431c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
432c65b1445SDouglas Gilbert 			errsts = check_condition_result;
433c65b1445SDouglas Gilbert 			break;
434c65b1445SDouglas Gilbert 		}
435c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
436c65b1445SDouglas Gilbert 		break;
4375a09e398SHannes Reinecke 	case MAINTENANCE_IN:
4385a09e398SHannes Reinecke 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
4395a09e398SHannes Reinecke 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
4405a09e398SHannes Reinecke 					INVALID_OPCODE, 0);
4415a09e398SHannes Reinecke 			errsts = check_condition_result;
4425a09e398SHannes Reinecke 			break;
4435a09e398SHannes Reinecke 		}
4445a09e398SHannes Reinecke 		errsts = resp_report_tgtpgs(SCpnt, devip);
4455a09e398SHannes Reinecke 		break;
4461da177e4SLinus Torvalds 	case READ_16:
4471da177e4SLinus Torvalds 	case READ_12:
4481da177e4SLinus Torvalds 	case READ_10:
4491da177e4SLinus Torvalds 	case READ_6:
450c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4511da177e4SLinus Torvalds 			break;
45223183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
45323183910SDouglas Gilbert 			break;
4541da177e4SLinus Torvalds 		if ((*cmd) == READ_16) {
455c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
456c65b1445SDouglas Gilbert 				if (j > 0)
457c65b1445SDouglas Gilbert 					lba <<= 8;
458c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
459c65b1445SDouglas Gilbert 			}
4601da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
4611da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
4621da177e4SLinus Torvalds 		} else if ((*cmd) == READ_12) {
463c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4641da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4651da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
4661da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
4671da177e4SLinus Torvalds 		} else if ((*cmd) == READ_10) {
468c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
4691da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
4701da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
471c65b1445SDouglas Gilbert 		} else {	/* READ (6) */
472c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
4731da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
474c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
4751da177e4SLinus Torvalds 		}
476c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4771da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4781da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
479c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4801da177e4SLinus Torvalds 			errsts = check_condition_result;
4816f3cbf55SDouglas Gilbert 		} else if (inj_transport && (0 == errsts)) {
4826f3cbf55SDouglas Gilbert                         mk_sense_buffer(devip, ABORTED_COMMAND,
4836f3cbf55SDouglas Gilbert                                         TRANSPORT_PROBLEM, ACK_NAK_TO);
4846f3cbf55SDouglas Gilbert                         errsts = check_condition_result;
4851da177e4SLinus Torvalds                 }
4861da177e4SLinus Torvalds 		break;
4871da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
488c65b1445SDouglas Gilbert 		delay_override = 1;
4891da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4901da177e4SLinus Torvalds 		break;
4911da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
492c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4931da177e4SLinus Torvalds 		break;
4941da177e4SLinus Torvalds 	case WRITE_16:
4951da177e4SLinus Torvalds 	case WRITE_12:
4961da177e4SLinus Torvalds 	case WRITE_10:
4971da177e4SLinus Torvalds 	case WRITE_6:
498c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4991da177e4SLinus Torvalds 			break;
50023183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
50123183910SDouglas Gilbert 			break;
5021da177e4SLinus Torvalds 		if ((*cmd) == WRITE_16) {
503c65b1445SDouglas Gilbert 			for (lba = 0, j = 0; j < 8; ++j) {
504c65b1445SDouglas Gilbert 				if (j > 0)
505c65b1445SDouglas Gilbert 					lba <<= 8;
506c65b1445SDouglas Gilbert 				lba += cmd[2 + j];
507c65b1445SDouglas Gilbert 			}
5081da177e4SLinus Torvalds 			num = cmd[13] + (cmd[12] << 8) +
5091da177e4SLinus Torvalds 				(cmd[11] << 16) + (cmd[10] << 24);
5101da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_12) {
511c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
5121da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
5131da177e4SLinus Torvalds 			num = cmd[9] + (cmd[8] << 8) +
5141da177e4SLinus Torvalds 				(cmd[7] << 16) + (cmd[6] << 24);
5151da177e4SLinus Torvalds 		} else if ((*cmd) == WRITE_10) {
516c65b1445SDouglas Gilbert 			lba = cmd[5] + (cmd[4] << 8) +
5171da177e4SLinus Torvalds 				(cmd[3] << 16) + (cmd[2] << 24);
5181da177e4SLinus Torvalds 			num = cmd[8] + (cmd[7] << 8);
519c65b1445SDouglas Gilbert 		} else {	/* WRITE (6) */
520c65b1445SDouglas Gilbert 			lba = cmd[3] + (cmd[2] << 8) +
5211da177e4SLinus Torvalds 				((cmd[1] & 0x1f) << 16);
522c65b1445SDouglas Gilbert 			num = (0 == cmd[4]) ? 256 : cmd[4];
5231da177e4SLinus Torvalds 		}
524c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5251da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5261da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
527c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5281da177e4SLinus Torvalds 			errsts = check_condition_result;
5291da177e4SLinus Torvalds 		}
5301da177e4SLinus Torvalds 		break;
5311da177e4SLinus Torvalds 	case MODE_SENSE:
5321da177e4SLinus Torvalds 	case MODE_SENSE_10:
5331da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5341da177e4SLinus Torvalds 		break;
535c65b1445SDouglas Gilbert 	case MODE_SELECT:
536c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
537c65b1445SDouglas Gilbert 		break;
538c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
539c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
540c65b1445SDouglas Gilbert 		break;
541c65b1445SDouglas Gilbert 	case LOG_SENSE:
542c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
543c65b1445SDouglas Gilbert 		break;
5441da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
545c65b1445SDouglas Gilbert 		delay_override = 1;
546c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5471da177e4SLinus Torvalds 		break;
5486f3cbf55SDouglas Gilbert 	case WRITE_BUFFER:
5496f3cbf55SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
5506f3cbf55SDouglas Gilbert 		break;
5511da177e4SLinus Torvalds 	default:
5521da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5531da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5541da177e4SLinus Torvalds 			       "supported\n", *cmd);
555c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5561da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5571da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5581da177e4SLinus Torvalds 		errsts = check_condition_result;
5591da177e4SLinus Torvalds 		break;
5601da177e4SLinus Torvalds 	}
561c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
562c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5661da177e4SLinus Torvalds {
5671da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5681da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5691da177e4SLinus Torvalds 	}
5701da177e4SLinus Torvalds 	return -EINVAL;
5711da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5721da177e4SLinus Torvalds }
5731da177e4SLinus Torvalds 
574c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
575c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds 	if (devip->reset) {
5781da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5791da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5801da177e4SLinus Torvalds 			       "attention: power on reset\n");
5811da177e4SLinus Torvalds 		devip->reset = 0;
5821da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5831da177e4SLinus Torvalds 		return check_condition_result;
5841da177e4SLinus Torvalds 	}
585c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
586c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
587c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
588c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
589c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
590c65b1445SDouglas Gilbert 				0x2);
591c65b1445SDouglas Gilbert 		return check_condition_result;
592c65b1445SDouglas Gilbert 	}
5931da177e4SLinus Torvalds 	return 0;
5941da177e4SLinus Torvalds }
5951da177e4SLinus Torvalds 
5961da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5971da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
5981da177e4SLinus Torvalds 				int arr_len)
5991da177e4SLinus Torvalds {
6001da177e4SLinus Torvalds 	int k, req_len, act_len, len, active;
6011da177e4SLinus Torvalds 	void * kaddr;
6021da177e4SLinus Torvalds 	void * kaddr_off;
6031da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
6061da177e4SLinus Torvalds 		return 0;
6071da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
6081da177e4SLinus Torvalds 		return (DID_ERROR << 16);
6091da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6101da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_FROM_DEVICE)))
6111da177e4SLinus Torvalds 		return (DID_ERROR << 16);
6121da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
6131da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
6141da177e4SLinus Torvalds 		act_len = (req_len < arr_len) ? req_len : arr_len;
6151da177e4SLinus Torvalds 		memcpy(scp->request_buffer, arr, act_len);
616c65b1445SDouglas Gilbert 		if (scp->resid)
617c65b1445SDouglas Gilbert 			scp->resid -= act_len;
618c65b1445SDouglas Gilbert 		else
6191da177e4SLinus Torvalds 			scp->resid = req_len - act_len;
6201da177e4SLinus Torvalds 		return 0;
6211da177e4SLinus Torvalds 	}
6221da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6231da177e4SLinus Torvalds 	active = 1;
6241da177e4SLinus Torvalds 	for (k = 0, req_len = 0, act_len = 0; k < scp->use_sg; ++k, ++sgpnt) {
6251da177e4SLinus Torvalds 		if (active) {
6261da177e4SLinus Torvalds 			kaddr = (unsigned char *)
6271da177e4SLinus Torvalds 				kmap_atomic(sgpnt->page, KM_USER0);
6281da177e4SLinus Torvalds 			if (NULL == kaddr)
6291da177e4SLinus Torvalds 				return (DID_ERROR << 16);
6301da177e4SLinus Torvalds 			kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6311da177e4SLinus Torvalds 			len = sgpnt->length;
6321da177e4SLinus Torvalds 			if ((req_len + len) > arr_len) {
6331da177e4SLinus Torvalds 				active = 0;
6341da177e4SLinus Torvalds 				len = arr_len - req_len;
6351da177e4SLinus Torvalds 			}
6361da177e4SLinus Torvalds 			memcpy(kaddr_off, arr + req_len, len);
6371da177e4SLinus Torvalds 			kunmap_atomic(kaddr, KM_USER0);
6381da177e4SLinus Torvalds 			act_len += len;
6391da177e4SLinus Torvalds 		}
6401da177e4SLinus Torvalds 		req_len += sgpnt->length;
6411da177e4SLinus Torvalds 	}
642c65b1445SDouglas Gilbert 	if (scp->resid)
643c65b1445SDouglas Gilbert 		scp->resid -= act_len;
644c65b1445SDouglas Gilbert 	else
6451da177e4SLinus Torvalds 		scp->resid = req_len - act_len;
6461da177e4SLinus Torvalds 	return 0;
6471da177e4SLinus Torvalds }
6481da177e4SLinus Torvalds 
6491da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6501da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
6511da177e4SLinus Torvalds 			       int max_arr_len)
6521da177e4SLinus Torvalds {
6531da177e4SLinus Torvalds 	int k, req_len, len, fin;
6541da177e4SLinus Torvalds 	void * kaddr;
6551da177e4SLinus Torvalds 	void * kaddr_off;
6561da177e4SLinus Torvalds 	struct scatterlist * sgpnt;
6571da177e4SLinus Torvalds 
6581da177e4SLinus Torvalds 	if (0 == scp->request_bufflen)
6591da177e4SLinus Torvalds 		return 0;
6601da177e4SLinus Torvalds 	if (NULL == scp->request_buffer)
6611da177e4SLinus Torvalds 		return -1;
6621da177e4SLinus Torvalds 	if (! ((scp->sc_data_direction == DMA_BIDIRECTIONAL) ||
6631da177e4SLinus Torvalds 	      (scp->sc_data_direction == DMA_TO_DEVICE)))
6641da177e4SLinus Torvalds 		return -1;
6651da177e4SLinus Torvalds 	if (0 == scp->use_sg) {
6661da177e4SLinus Torvalds 		req_len = scp->request_bufflen;
6671da177e4SLinus Torvalds 		len = (req_len < max_arr_len) ? req_len : max_arr_len;
6681da177e4SLinus Torvalds 		memcpy(arr, scp->request_buffer, len);
6691da177e4SLinus Torvalds 		return len;
6701da177e4SLinus Torvalds 	}
6711da177e4SLinus Torvalds 	sgpnt = (struct scatterlist *)scp->request_buffer;
6721da177e4SLinus Torvalds 	for (k = 0, req_len = 0, fin = 0; k < scp->use_sg; ++k, ++sgpnt) {
6731da177e4SLinus Torvalds 		kaddr = (unsigned char *)kmap_atomic(sgpnt->page, KM_USER0);
6741da177e4SLinus Torvalds 		if (NULL == kaddr)
6751da177e4SLinus Torvalds 			return -1;
6761da177e4SLinus Torvalds 		kaddr_off = (unsigned char *)kaddr + sgpnt->offset;
6771da177e4SLinus Torvalds 		len = sgpnt->length;
6781da177e4SLinus Torvalds 		if ((req_len + len) > max_arr_len) {
6791da177e4SLinus Torvalds 			len = max_arr_len - req_len;
6801da177e4SLinus Torvalds 			fin = 1;
6811da177e4SLinus Torvalds 		}
6821da177e4SLinus Torvalds 		memcpy(arr + req_len, kaddr_off, len);
6831da177e4SLinus Torvalds 		kunmap_atomic(kaddr, KM_USER0);
6841da177e4SLinus Torvalds 		if (fin)
6851da177e4SLinus Torvalds 			return req_len + len;
6861da177e4SLinus Torvalds 		req_len += sgpnt->length;
6871da177e4SLinus Torvalds 	}
6881da177e4SLinus Torvalds 	return req_len;
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds 
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6931da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6941da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6951da177e4SLinus Torvalds 
6965a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
6975a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
6985a09e398SHannes Reinecke 			   const char * dev_id_str,
699c65b1445SDouglas Gilbert 			   int dev_id_str_len)
7001da177e4SLinus Torvalds {
701c65b1445SDouglas Gilbert 	int num, port_a;
702c65b1445SDouglas Gilbert 	char b[32];
7031da177e4SLinus Torvalds 
704c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
7051da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
7061da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
7071da177e4SLinus Torvalds 	arr[1] = 0x1;
7081da177e4SLinus Torvalds 	arr[2] = 0x0;
7091da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
7101da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
7111da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
7121da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
7131da177e4SLinus Torvalds 	arr[3] = num;
7141da177e4SLinus Torvalds 	num += 4;
715c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
716c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
717c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
718c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
719c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
720c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
721c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
722c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
723c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
724c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
725c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
726c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
727c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
728c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
729c65b1445SDouglas Gilbert 		/* Target relative port number */
730c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
731c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
732c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
733c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
734c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
735c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
736c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
737c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
738c65b1445SDouglas Gilbert 	}
739c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
740c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
741c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, 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++] = (port_a >> 24);
749c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
750c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
751c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
7525a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
7535a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
7545a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
7555a09e398SHannes Reinecke 	arr[num++] = 0x0;
7565a09e398SHannes Reinecke 	arr[num++] = 0x4;
7575a09e398SHannes Reinecke 	arr[num++] = 0;
7585a09e398SHannes Reinecke 	arr[num++] = 0;
7595a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
7605a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
761c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
762c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
763c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
764c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
765c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
766c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
767c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
768c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
769c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
770c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
771c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
772c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
773c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
774c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
775c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
776c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
777c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
778c65b1445SDouglas Gilbert 	arr[num++] = 24;
779c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
780c65b1445SDouglas Gilbert 	num += 12;
781c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
782c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
783c65b1445SDouglas Gilbert 	num += 8;
784c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
785c65b1445SDouglas Gilbert 	num += 4;
786c65b1445SDouglas Gilbert 	return num;
787c65b1445SDouglas Gilbert }
788c65b1445SDouglas Gilbert 
789c65b1445SDouglas Gilbert 
790c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
791c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
792c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
793c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
794c65b1445SDouglas Gilbert };
795c65b1445SDouglas Gilbert 
796c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
797c65b1445SDouglas Gilbert {
798c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
799c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
800c65b1445SDouglas Gilbert }
801c65b1445SDouglas Gilbert 
802c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
803c65b1445SDouglas Gilbert {
804c65b1445SDouglas Gilbert 	int num = 0;
805c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
806c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
807c65b1445SDouglas Gilbert 	int plen, olen;
808c65b1445SDouglas Gilbert 
809c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
810c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
811c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
812c65b1445SDouglas Gilbert 	olen = strlen(na1);
813c65b1445SDouglas Gilbert 	plen = olen + 1;
814c65b1445SDouglas Gilbert 	if (plen % 4)
815c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
816c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
817c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
818c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
819c65b1445SDouglas Gilbert 	num += plen;
820c65b1445SDouglas Gilbert 
821c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
822c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
823c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
824c65b1445SDouglas Gilbert 	olen = strlen(na2);
825c65b1445SDouglas Gilbert 	plen = olen + 1;
826c65b1445SDouglas Gilbert 	if (plen % 4)
827c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
828c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
829c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
830c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
831c65b1445SDouglas Gilbert 	num += plen;
832c65b1445SDouglas Gilbert 
833c65b1445SDouglas Gilbert 	return num;
834c65b1445SDouglas Gilbert }
835c65b1445SDouglas Gilbert 
836c65b1445SDouglas Gilbert /* SCSI ports VPD page */
837c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
838c65b1445SDouglas Gilbert {
839c65b1445SDouglas Gilbert 	int num = 0;
840c65b1445SDouglas Gilbert 	int port_a, port_b;
841c65b1445SDouglas Gilbert 
842c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
843c65b1445SDouglas Gilbert 	port_b = port_a + 1;
844c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
845c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
846c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
847c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
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 (A) */
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_a >> 24);
862c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
863c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
864c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
865c65b1445SDouglas Gilbert 
866c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
867c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
868c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
869c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
870c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
871c65b1445SDouglas Gilbert 	num += 6;
872c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
873c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
874c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
875c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
876c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
877c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
878c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
879c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
880c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
881c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
882c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
883c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
884c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
885c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
886c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
887c65b1445SDouglas Gilbert 
888c65b1445SDouglas Gilbert 	return num;
889c65b1445SDouglas Gilbert }
890c65b1445SDouglas Gilbert 
891c65b1445SDouglas Gilbert 
892c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
893c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
894c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
895c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
896c65b1445SDouglas Gilbert '1','2','3','4',
897c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
898c65b1445SDouglas Gilbert 0xec,0,0,0,
899c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
900c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
901c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
902c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
903c65b1445SDouglas Gilbert 0x53,0x41,
904c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
905c65b1445SDouglas Gilbert 0x20,0x20,
906c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
907c65b1445SDouglas Gilbert 0x10,0x80,
908c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
909c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
910c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
911c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
912c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
913c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
914c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
915c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
916c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
917c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
918c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
919c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
920c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
921c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
922c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
923c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
924c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
925c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
926c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
927c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
928c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
929c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
930c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
931c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
932c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
933c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
934c65b1445SDouglas Gilbert };
935c65b1445SDouglas Gilbert 
936c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
937c65b1445SDouglas Gilbert {
938c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
939c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
940c65b1445SDouglas Gilbert }
941c65b1445SDouglas Gilbert 
942c65b1445SDouglas Gilbert 
943c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
944c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
945c65b1445SDouglas Gilbert 	0,0,0x4,0,
946c65b1445SDouglas Gilbert 	0,0,0,64,
947c65b1445SDouglas Gilbert };
948c65b1445SDouglas Gilbert 
949c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
950c65b1445SDouglas Gilbert {
951c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
952c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
953c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
954c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
955c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
956c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
957c65b1445SDouglas Gilbert 	}
958c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds 
9611da177e4SLinus Torvalds 
9621da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
963c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9641da177e4SLinus Torvalds 
9651da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9661da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9671da177e4SLinus Torvalds {
9681da177e4SLinus Torvalds 	unsigned char pq_pdt;
9695a09e398SHannes Reinecke 	unsigned char * arr;
9701da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
9715a09e398SHannes Reinecke 	int alloc_len, n, ret;
9721da177e4SLinus Torvalds 
9731da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9746f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
9756f3cbf55SDouglas Gilbert 	if (! arr)
9766f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
977c65b1445SDouglas Gilbert 	if (devip->wlun)
978c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
979c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
980c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
981c65b1445SDouglas Gilbert 	else
9821da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9831da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9841da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9851da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9861da177e4SLinus Torvalds 			       	0);
9875a09e398SHannes Reinecke 		kfree(arr);
9881da177e4SLinus Torvalds 		return check_condition_result;
9891da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
9905a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
991c65b1445SDouglas Gilbert 		char lu_id_str[6];
992c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9931da177e4SLinus Torvalds 
9945a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
9955a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
99623183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
99723183910SDouglas Gilbert 			host_no = 0;
998c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
999c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
1000c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
1001c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
1002c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
10031da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
1004c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1005c65b1445SDouglas Gilbert 			n = 4;
1006c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
1007c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
1008c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
1009c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
1010c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
1011c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
1012c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
1013c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
1014c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
1015c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
1016c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
10171da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
1018c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10191da177e4SLinus Torvalds 			arr[3] = len;
1020c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
10211da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
1022c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
10235a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
10245a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
10255a09e398SHannes Reinecke 						 lu_id_str, len);
1026c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
1027c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1028c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
1029c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
1030c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1031c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
1032c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
1033c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1034c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
1035c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
1036c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
1037c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
1038c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1039c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
1040c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
1041c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
1042c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
1043c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
1044c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
1045c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
1046c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
1047c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
1048c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1049c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
1050c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
1051c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
1052c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
1053c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
1054c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
10551da177e4SLinus Torvalds 		} else {
10561da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
10571da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
10581da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
10595a09e398SHannes Reinecke 			kfree(arr);
10601da177e4SLinus Torvalds 			return check_condition_result;
10611da177e4SLinus Torvalds 		}
1062c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
10635a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
1064c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10655a09e398SHannes Reinecke 		kfree(arr);
10665a09e398SHannes Reinecke 		return ret;
10671da177e4SLinus Torvalds 	}
10681da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10691da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10701da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10711da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10721da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
10735a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
10745a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
1075c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10761da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1077c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10781da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10791da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10801da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10811da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1082c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1083c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1084c65b1445SDouglas Gilbert 	n = 62;
10851da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1086c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10871da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1088c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10891da177e4SLinus Torvalds 	}
1090c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10915a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
10921da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10935a09e398SHannes Reinecke 	kfree(arr);
10945a09e398SHannes Reinecke 	return ret;
10951da177e4SLinus Torvalds }
10961da177e4SLinus Torvalds 
10971da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10981da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10991da177e4SLinus Torvalds {
11001da177e4SLinus Torvalds 	unsigned char * sbuff;
11011da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
11021da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1103c65b1445SDouglas Gilbert 	int want_dsense;
11041da177e4SLinus Torvalds 	int len = 18;
11051da177e4SLinus Torvalds 
1106c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
11071da177e4SLinus Torvalds 	if (devip->reset == 1)
1108c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1109c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
11101da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1111c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1112c65b1445SDouglas Gilbert 		if (want_dsense) {
1113c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1114c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1115c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1116c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1117c65b1445SDouglas Gilbert 		} else {
1118c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1119c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1120c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1121c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1122c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1123c65b1445SDouglas Gilbert 		}
1124c65b1445SDouglas Gilbert 	} else {
1125c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
11261da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
11271da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1128c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
11291da177e4SLinus Torvalds 			arr[0] = 0x72;
11301da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
11311da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
11321da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
11331da177e4SLinus Torvalds 			len = 8;
1134c65b1445SDouglas Gilbert 		}
1135c65b1445SDouglas Gilbert 	}
1136c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
11371da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
11381da177e4SLinus Torvalds }
11391da177e4SLinus Torvalds 
1140c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1141c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1142c65b1445SDouglas Gilbert {
1143c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1144c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1145c65b1445SDouglas Gilbert 
1146c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1147c65b1445SDouglas Gilbert 		return errsts;
1148c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1149c65b1445SDouglas Gilbert 	if (power_cond) {
1150c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1151c65b1445SDouglas Gilbert 			       	0);
1152c65b1445SDouglas Gilbert 		return check_condition_result;
1153c65b1445SDouglas Gilbert 	}
1154c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1155c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1156c65b1445SDouglas Gilbert 		devip->stopped = !start;
1157c65b1445SDouglas Gilbert 	return 0;
1158c65b1445SDouglas Gilbert }
1159c65b1445SDouglas Gilbert 
11601da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
11611da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
11621da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
11631da177e4SLinus Torvalds {
11641da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1165c65b1445SDouglas Gilbert 	unsigned int capac;
11661da177e4SLinus Torvalds 	int errsts;
11671da177e4SLinus Torvalds 
1168c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11691da177e4SLinus Torvalds 		return errsts;
1170c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1171c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1172c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1173c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1174c65b1445SDouglas Gilbert 	} else
1175c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11761da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1177c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1178c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11791da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11801da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11811da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11821da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1183c65b1445SDouglas Gilbert 	} else {
1184c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1185c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1186c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1187c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1188c65b1445SDouglas Gilbert 	}
11891da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11901da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11911da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11921da177e4SLinus Torvalds }
11931da177e4SLinus Torvalds 
1194c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1195c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1196c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1197c65b1445SDouglas Gilbert {
1198c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1199c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1200c65b1445SDouglas Gilbert 	unsigned long long capac;
1201c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1202c65b1445SDouglas Gilbert 
1203c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1204c65b1445SDouglas Gilbert 		return errsts;
1205c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1206c65b1445SDouglas Gilbert 		     + cmd[13]);
1207c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1208c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1209c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1210c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1211c65b1445SDouglas Gilbert 	} else
1212c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1213c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1214c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1215c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1216c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1217c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1218c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1219c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1220c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1221c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1222c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1223c65b1445SDouglas Gilbert }
1224c65b1445SDouglas Gilbert 
12255a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
12265a09e398SHannes Reinecke 
12275a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
12285a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
12295a09e398SHannes Reinecke {
12305a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
12315a09e398SHannes Reinecke 	unsigned char * arr;
12325a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
12335a09e398SHannes Reinecke 	int n, ret, alen, rlen;
12345a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
12355a09e398SHannes Reinecke 
12365a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
12375a09e398SHannes Reinecke 		+ cmd[9]);
12385a09e398SHannes Reinecke 
12396f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
12406f3cbf55SDouglas Gilbert 	if (! arr)
12416f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
12425a09e398SHannes Reinecke 	/*
12435a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
12445a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
12455a09e398SHannes Reinecke 	 * So we create two port groups with one port each
12465a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
12475a09e398SHannes Reinecke 	 */
12485a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
12495a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
12505a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
12515a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
12525a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
12535a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
12545a09e398SHannes Reinecke 
12555a09e398SHannes Reinecke 	/*
12565a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
12575a09e398SHannes Reinecke 	 */
12585a09e398SHannes Reinecke 	n = 4;
12595a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
12605a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
12615a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
12625a09e398SHannes Reinecke 	} else {
12635a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
12645a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
12655a09e398SHannes Reinecke 	}
12665a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
12675a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
12685a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12695a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12705a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12715a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12725a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12735a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12745a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
12755a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
12765a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
12775a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
12785a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
12795a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
12805a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12815a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12825a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12835a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12845a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12855a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12865a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
12875a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
12885a09e398SHannes Reinecke 
12895a09e398SHannes Reinecke 	rlen = n - 4;
12905a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
12915a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
12925a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
12935a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
12945a09e398SHannes Reinecke 
12955a09e398SHannes Reinecke 	/*
12965a09e398SHannes Reinecke 	 * Return the smallest value of either
12975a09e398SHannes Reinecke 	 * - The allocated length
12985a09e398SHannes Reinecke 	 * - The constructed command length
12995a09e398SHannes Reinecke 	 * - The maximum array size
13005a09e398SHannes Reinecke 	 */
13015a09e398SHannes Reinecke 	rlen = min(alen,n);
13025a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
13035a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
13045a09e398SHannes Reinecke 	kfree(arr);
13055a09e398SHannes Reinecke 	return ret;
13065a09e398SHannes Reinecke }
13075a09e398SHannes Reinecke 
13081da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
13091da177e4SLinus Torvalds 
13101da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
13111da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
13121da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
13131da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
13141da177e4SLinus Torvalds 
13151da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
13161da177e4SLinus Torvalds 	if (1 == pcontrol)
13171da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
13181da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
13191da177e4SLinus Torvalds }
13201da177e4SLinus Torvalds 
13211da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
13221da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
13231da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
13241da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
13251da177e4SLinus Torvalds 
13261da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
13271da177e4SLinus Torvalds 	if (1 == pcontrol)
13281da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
13291da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
13301da177e4SLinus Torvalds }
13311da177e4SLinus Torvalds 
13321da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
13331da177e4SLinus Torvalds {       /* Format device page for mode_sense */
13341da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
13351da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
13361da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
13371da177e4SLinus Torvalds 
13381da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
13391da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
13401da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
13411da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
13421da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
13431da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
13441da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
13451da177e4SLinus Torvalds         if (1 == pcontrol)
13461da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
13471da177e4SLinus Torvalds         return sizeof(format_pg);
13481da177e4SLinus Torvalds }
13491da177e4SLinus Torvalds 
13501da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
13511da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
13521da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
13531da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
13561da177e4SLinus Torvalds 	if (1 == pcontrol)
13571da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
13581da177e4SLinus Torvalds 	return sizeof(caching_pg);
13591da177e4SLinus Torvalds }
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
13621da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1363c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1364c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1365c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
13661da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
13671da177e4SLinus Torvalds 
13681da177e4SLinus Torvalds 	if (scsi_debug_dsense)
13691da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1370c65b1445SDouglas Gilbert 	else
1371c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
13721da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
13731da177e4SLinus Torvalds 	if (1 == pcontrol)
1374c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1375c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1376c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
13771da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
13781da177e4SLinus Torvalds }
13791da177e4SLinus Torvalds 
1380c65b1445SDouglas Gilbert 
13811da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
13821da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1383c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
13841da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1385c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1386c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1387c65b1445SDouglas Gilbert 
13881da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
13891da177e4SLinus Torvalds 	if (1 == pcontrol)
1390c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1391c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1392c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
13931da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
13941da177e4SLinus Torvalds }
13951da177e4SLinus Torvalds 
1396c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1397c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1398c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1399c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1400c65b1445SDouglas Gilbert 
1401c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1402c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1403c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1404c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1405c65b1445SDouglas Gilbert }
1406c65b1445SDouglas Gilbert 
1407c65b1445SDouglas Gilbert 
1408c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1409c65b1445SDouglas Gilbert 			      int target_dev_id)
1410c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1411c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1412c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1413c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1414c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1415c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1416c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1417c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1418c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1419c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1420c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1421c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1422c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1423c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1424c65b1445SDouglas Gilbert 		};
1425c65b1445SDouglas Gilbert 	int port_a, port_b;
1426c65b1445SDouglas Gilbert 
1427c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1428c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1429c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1430c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1431c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1432c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1433c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1434c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1435c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1436c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1437c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1438c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1439c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1440c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1441c65b1445SDouglas Gilbert }
1442c65b1445SDouglas Gilbert 
1443c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1444c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1445c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1446c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1447c65b1445SDouglas Gilbert 		};
1448c65b1445SDouglas Gilbert 
1449c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1450c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1451c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1452c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1453c65b1445SDouglas Gilbert }
1454c65b1445SDouglas Gilbert 
14551da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
14561da177e4SLinus Torvalds 
14571da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
14581da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
14591da177e4SLinus Torvalds {
146023183910SDouglas Gilbert 	unsigned char dbd, llbaa;
146123183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
14621da177e4SLinus Torvalds 	unsigned char dev_spec;
146323183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
14641da177e4SLinus Torvalds 	unsigned char * ap;
14651da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
14661da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
14671da177e4SLinus Torvalds 
1468c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
14691da177e4SLinus Torvalds 		return errsts;
147023183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
14711da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
14721da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
14731da177e4SLinus Torvalds 	subpcode = cmd[3];
14741da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
147523183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
147623183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
147723183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
147823183910SDouglas Gilbert 	else
147923183910SDouglas Gilbert 		bd_len = 0;
14801da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
14811da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
14821da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
14831da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
14841da177e4SLinus Torvalds 			       	0);
14851da177e4SLinus Torvalds 		return check_condition_result;
14861da177e4SLinus Torvalds 	}
1487c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1488c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
148923183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
149023183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
149123183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
149223183910SDouglas Gilbert 	else
149323183910SDouglas Gilbert 		dev_spec = 0x0;
14941da177e4SLinus Torvalds 	if (msense_6) {
14951da177e4SLinus Torvalds 		arr[2] = dev_spec;
149623183910SDouglas Gilbert 		arr[3] = bd_len;
14971da177e4SLinus Torvalds 		offset = 4;
14981da177e4SLinus Torvalds 	} else {
14991da177e4SLinus Torvalds 		arr[3] = dev_spec;
150023183910SDouglas Gilbert 		if (16 == bd_len)
150123183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
150223183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
15031da177e4SLinus Torvalds 		offset = 8;
15041da177e4SLinus Torvalds 	}
15051da177e4SLinus Torvalds 	ap = arr + offset;
150623183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
150723183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
150823183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
150923183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
151023183910SDouglas Gilbert 		} else
151123183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
151223183910SDouglas Gilbert 	}
151323183910SDouglas Gilbert 	if (8 == bd_len) {
151423183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
151523183910SDouglas Gilbert 			ap[0] = 0xff;
151623183910SDouglas Gilbert 			ap[1] = 0xff;
151723183910SDouglas Gilbert 			ap[2] = 0xff;
151823183910SDouglas Gilbert 			ap[3] = 0xff;
151923183910SDouglas Gilbert 		} else {
152023183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
152123183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
152223183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
152323183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
152423183910SDouglas Gilbert 		}
152523183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
152623183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
152723183910SDouglas Gilbert 		offset += bd_len;
152823183910SDouglas Gilbert 		ap = arr + offset;
152923183910SDouglas Gilbert 	} else if (16 == bd_len) {
153023183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
153123183910SDouglas Gilbert 
153223183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
153323183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
153423183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
153523183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
153623183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
153723183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
153823183910SDouglas Gilbert 		offset += bd_len;
153923183910SDouglas Gilbert 		ap = arr + offset;
154023183910SDouglas Gilbert 	}
15411da177e4SLinus Torvalds 
1542c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1543c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
15441da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15451da177e4SLinus Torvalds 			       	0);
15461da177e4SLinus Torvalds 		return check_condition_result;
15471da177e4SLinus Torvalds 	}
15481da177e4SLinus Torvalds 	switch (pcode) {
15491da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
15501da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
15511da177e4SLinus Torvalds 		offset += len;
15521da177e4SLinus Torvalds 		break;
15531da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
15541da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
15551da177e4SLinus Torvalds 		offset += len;
15561da177e4SLinus Torvalds 		break;
15571da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
15581da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
15591da177e4SLinus Torvalds                 offset += len;
15601da177e4SLinus Torvalds                 break;
15611da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
15621da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
15631da177e4SLinus Torvalds 		offset += len;
15641da177e4SLinus Torvalds 		break;
15651da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
15661da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
15671da177e4SLinus Torvalds 		offset += len;
15681da177e4SLinus Torvalds 		break;
1569c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1570c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1571c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1572c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1573c65b1445SDouglas Gilbert 			return check_condition_result;
1574c65b1445SDouglas Gilbert 	        }
1575c65b1445SDouglas Gilbert 		len = 0;
1576c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1577c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1578c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1579c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1580c65b1445SDouglas Gilbert 						  target_dev_id);
1581c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1582c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1583c65b1445SDouglas Gilbert 		offset += len;
1584c65b1445SDouglas Gilbert 		break;
15851da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
15861da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
15871da177e4SLinus Torvalds 		offset += len;
15881da177e4SLinus Torvalds 		break;
15891da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1590c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
15911da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
15921da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
15931da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
15941da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
15951da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1596c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1597c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1598c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1599c65b1445SDouglas Gilbert 						  target, target_dev_id);
1600c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1601c65b1445SDouglas Gilbert 			}
16021da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1603c65b1445SDouglas Gilbert 		} else {
1604c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1605c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1606c65b1445SDouglas Gilbert 			return check_condition_result;
1607c65b1445SDouglas Gilbert                 }
16081da177e4SLinus Torvalds 		offset += len;
16091da177e4SLinus Torvalds 		break;
16101da177e4SLinus Torvalds 	default:
16111da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
16121da177e4SLinus Torvalds 			       	0);
16131da177e4SLinus Torvalds 		return check_condition_result;
16141da177e4SLinus Torvalds 	}
16151da177e4SLinus Torvalds 	if (msense_6)
16161da177e4SLinus Torvalds 		arr[0] = offset - 1;
16171da177e4SLinus Torvalds 	else {
16181da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
16191da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
16201da177e4SLinus Torvalds 	}
16211da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
16221da177e4SLinus Torvalds }
16231da177e4SLinus Torvalds 
1624c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1625c65b1445SDouglas Gilbert 
1626c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1627c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1628c65b1445SDouglas Gilbert {
1629c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1630c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1631c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1632c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1633c65b1445SDouglas Gilbert 
1634c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1635c65b1445SDouglas Gilbert 		return errsts;
1636c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1637c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1638c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1639c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1640c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1641c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1642c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1643c65b1445SDouglas Gilbert 		return check_condition_result;
1644c65b1445SDouglas Gilbert 	}
1645c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1646c65b1445SDouglas Gilbert         if (-1 == res)
1647c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1648c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1649c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1650c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1651c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1652c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1653c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
165423183910SDouglas Gilbert 	if (md_len > 2) {
1655c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1656c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1657c65b1445SDouglas Gilbert 		return check_condition_result;
1658c65b1445SDouglas Gilbert 	}
1659c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1660c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1661c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1662c65b1445SDouglas Gilbert 	if (ps) {
1663c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1664c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1665c65b1445SDouglas Gilbert 		return check_condition_result;
1666c65b1445SDouglas Gilbert 	}
1667c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1668c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1669c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1670c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1671c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1672c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1673c65b1445SDouglas Gilbert 		return check_condition_result;
1674c65b1445SDouglas Gilbert 	}
1675c65b1445SDouglas Gilbert 	switch (mpage) {
1676c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1677c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1678c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1679c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1680c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1681c65b1445SDouglas Gilbert 			return 0;
1682c65b1445SDouglas Gilbert 		}
1683c65b1445SDouglas Gilbert 		break;
1684c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1685c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1686c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1687c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1688c65b1445SDouglas Gilbert 			return 0;
1689c65b1445SDouglas Gilbert 		}
1690c65b1445SDouglas Gilbert 		break;
1691c65b1445SDouglas Gilbert 	default:
1692c65b1445SDouglas Gilbert 		break;
1693c65b1445SDouglas Gilbert 	}
1694c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1695c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1696c65b1445SDouglas Gilbert 	return check_condition_result;
1697c65b1445SDouglas Gilbert }
1698c65b1445SDouglas Gilbert 
1699c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1700c65b1445SDouglas Gilbert {
1701c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1702c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1703c65b1445SDouglas Gilbert 		};
1704c65b1445SDouglas Gilbert 
1705c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1706c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1707c65b1445SDouglas Gilbert }
1708c65b1445SDouglas Gilbert 
1709c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1710c65b1445SDouglas Gilbert {
1711c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1712c65b1445SDouglas Gilbert 		};
1713c65b1445SDouglas Gilbert 
1714c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1715c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1716c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1717c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1718c65b1445SDouglas Gilbert 	}
1719c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1720c65b1445SDouglas Gilbert }
1721c65b1445SDouglas Gilbert 
1722c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1723c65b1445SDouglas Gilbert 
1724c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1725c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1726c65b1445SDouglas Gilbert {
172723183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1728c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1729c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1730c65b1445SDouglas Gilbert 
1731c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1732c65b1445SDouglas Gilbert 		return errsts;
1733c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1734c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1735c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1736c65b1445SDouglas Gilbert 	if (ppc || sp) {
1737c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1738c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1739c65b1445SDouglas Gilbert 		return check_condition_result;
1740c65b1445SDouglas Gilbert 	}
1741c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1742c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
174323183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1744c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1745c65b1445SDouglas Gilbert 	arr[0] = pcode;
174623183910SDouglas Gilbert 	if (0 == subpcode) {
1747c65b1445SDouglas Gilbert 		switch (pcode) {
1748c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1749c65b1445SDouglas Gilbert 			n = 4;
1750c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1751c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1752c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1753c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1754c65b1445SDouglas Gilbert 			break;
1755c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1756c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1757c65b1445SDouglas Gilbert 			break;
1758c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1759c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1760c65b1445SDouglas Gilbert 			break;
1761c65b1445SDouglas Gilbert 		default:
1762c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1763c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1764c65b1445SDouglas Gilbert 			return check_condition_result;
1765c65b1445SDouglas Gilbert 		}
176623183910SDouglas Gilbert 	} else if (0xff == subpcode) {
176723183910SDouglas Gilbert 		arr[0] |= 0x40;
176823183910SDouglas Gilbert 		arr[1] = subpcode;
176923183910SDouglas Gilbert 		switch (pcode) {
177023183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
177123183910SDouglas Gilbert 			n = 4;
177223183910SDouglas Gilbert 			arr[n++] = 0x0;
177323183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
177423183910SDouglas Gilbert 			arr[n++] = 0x0;
177523183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
177623183910SDouglas Gilbert 			arr[n++] = 0xd;
177723183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
177823183910SDouglas Gilbert 			arr[n++] = 0x2f;
177923183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
178023183910SDouglas Gilbert 			arr[3] = n - 4;
178123183910SDouglas Gilbert 			break;
178223183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
178323183910SDouglas Gilbert 			n = 4;
178423183910SDouglas Gilbert 			arr[n++] = 0xd;
178523183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
178623183910SDouglas Gilbert 			arr[3] = n - 4;
178723183910SDouglas Gilbert 			break;
178823183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
178923183910SDouglas Gilbert 			n = 4;
179023183910SDouglas Gilbert 			arr[n++] = 0x2f;
179123183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
179223183910SDouglas Gilbert 			arr[3] = n - 4;
179323183910SDouglas Gilbert 			break;
179423183910SDouglas Gilbert 		default:
179523183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
179623183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
179723183910SDouglas Gilbert 			return check_condition_result;
179823183910SDouglas Gilbert 		}
179923183910SDouglas Gilbert 	} else {
180023183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
180123183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
180223183910SDouglas Gilbert 		return check_condition_result;
180323183910SDouglas Gilbert 	}
1804c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1805c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1806c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1807c65b1445SDouglas Gilbert }
1808c65b1445SDouglas Gilbert 
1809c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1810c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
18111da177e4SLinus Torvalds {
18121da177e4SLinus Torvalds 	unsigned long iflags;
1813c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1814c65b1445SDouglas Gilbert 	unsigned long long u;
18151da177e4SLinus Torvalds 	int ret;
18161da177e4SLinus Torvalds 
1817c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18181da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18191da177e4SLinus Torvalds 				0);
18201da177e4SLinus Torvalds 		return check_condition_result;
18211da177e4SLinus Torvalds 	}
1822c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1823c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1824c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1825c65b1445SDouglas Gilbert 				0);
1826c65b1445SDouglas Gilbert 		return check_condition_result;
1827c65b1445SDouglas Gilbert 	}
18281da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1829c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1830c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1831c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
18321da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
18331da177e4SLinus Torvalds 				0);
1834c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1835c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1836c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1837c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1838c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1839c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1840c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1841c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1842c65b1445SDouglas Gilbert 		}
18431da177e4SLinus Torvalds 		return check_condition_result;
18441da177e4SLinus Torvalds 	}
18451da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1846c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1847c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1848c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
18491da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1850c65b1445SDouglas Gilbert 	else {
1851c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1852c65b1445SDouglas Gilbert 		u = lba;
1853c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1854c65b1445SDouglas Gilbert 		from_bottom = 0;
1855c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1856c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1857c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1858c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1859c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1860c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1861c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1862c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1863c65b1445SDouglas Gilbert 	}
18641da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18651da177e4SLinus Torvalds 	return ret;
18661da177e4SLinus Torvalds }
18671da177e4SLinus Torvalds 
1868c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1869c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
18701da177e4SLinus Torvalds {
18711da177e4SLinus Torvalds 	unsigned long iflags;
1872c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1873c65b1445SDouglas Gilbert 	unsigned long long u;
18741da177e4SLinus Torvalds 	int res;
18751da177e4SLinus Torvalds 
1876c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18771da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18781da177e4SLinus Torvalds 			       	0);
18791da177e4SLinus Torvalds 		return check_condition_result;
18801da177e4SLinus Torvalds 	}
1881c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1882c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1883c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1884c65b1445SDouglas Gilbert 				0);
1885c65b1445SDouglas Gilbert 		return check_condition_result;
1886c65b1445SDouglas Gilbert 	}
18871da177e4SLinus Torvalds 
18881da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1889c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1890c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1891c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
18921da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1893c65b1445SDouglas Gilbert 	else {
1894c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1895c65b1445SDouglas Gilbert 		u = lba;
1896c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1897c65b1445SDouglas Gilbert 		to_bottom = 0;
1898c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1899c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1900c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1901c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1902c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1903c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1904c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1905c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1906c65b1445SDouglas Gilbert 	}
19071da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
19081da177e4SLinus Torvalds 	if (-1 == res)
19091da177e4SLinus Torvalds 		return (DID_ERROR << 16);
19101da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
19111da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1912c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
19131da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
19141da177e4SLinus Torvalds 	return 0;
19151da177e4SLinus Torvalds }
19161da177e4SLinus Torvalds 
1917c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
19181da177e4SLinus Torvalds 
19191da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
19201da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
19211da177e4SLinus Torvalds {
19221da177e4SLinus Torvalds 	unsigned int alloc_len;
1923c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
19241da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
19251da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
19261da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
19271da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1928c65b1445SDouglas Gilbert 	unsigned char * max_addr;
19291da177e4SLinus Torvalds 
19301da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1931c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
19321da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
19331da177e4SLinus Torvalds 			       	0);
19341da177e4SLinus Torvalds 		return check_condition_result;
19351da177e4SLinus Torvalds 	}
19361da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
19371da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
19381da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1939c65b1445SDouglas Gilbert 	if (1 == select_report)
1940c65b1445SDouglas Gilbert 		lun_cnt = 0;
1941c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1942c65b1445SDouglas Gilbert 		--lun_cnt;
1943c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1944c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1945c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1946c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1947c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1948c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1949c65b1445SDouglas Gilbert 	if (n < num) {
1950c65b1445SDouglas Gilbert 		wlun = 0;
1951c65b1445SDouglas Gilbert 		lun_cnt = n;
1952c65b1445SDouglas Gilbert 	}
19531da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1954c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1955c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1956c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1957c65b1445SDouglas Gilbert 	     i++, lun++) {
1958c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
19591da177e4SLinus Torvalds 		if (upper)
19601da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
19611da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1962c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
19631da177e4SLinus Torvalds 	}
1964c65b1445SDouglas Gilbert 	if (wlun) {
1965c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1966c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1967c65b1445SDouglas Gilbert 		i++;
1968c65b1445SDouglas Gilbert 	}
1969c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
19701da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
19711da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
19721da177e4SLinus Torvalds }
19731da177e4SLinus Torvalds 
19741da177e4SLinus Torvalds /* When timer goes off this function is called. */
19751da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
19761da177e4SLinus Torvalds {
19771da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
19781da177e4SLinus Torvalds 	unsigned long iflags;
19791da177e4SLinus Torvalds 
19801da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
19811da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
19821da177e4SLinus Torvalds 		       "large\n");
19831da177e4SLinus Torvalds 		return;
19841da177e4SLinus Torvalds 	}
19851da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
19861da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
19871da177e4SLinus Torvalds 	if (! sqcp->in_use) {
19881da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
19891da177e4SLinus Torvalds 		       "interrupt\n");
19901da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
19911da177e4SLinus Torvalds 		return;
19921da177e4SLinus Torvalds 	}
19931da177e4SLinus Torvalds 	sqcp->in_use = 0;
19941da177e4SLinus Torvalds 	if (sqcp->done_funct) {
19951da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
19961da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
19971da177e4SLinus Torvalds 	}
19981da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
19991da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
20001da177e4SLinus Torvalds }
20011da177e4SLinus Torvalds 
20021da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
20031da177e4SLinus Torvalds {
20041da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2005c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
2006c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20071da177e4SLinus Torvalds 	return 0;
20081da177e4SLinus Torvalds }
20091da177e4SLinus Torvalds 
20101da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
20111da177e4SLinus Torvalds {
20121da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
20131da177e4SLinus Torvalds 
20141da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2015c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
2016c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20171da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
20181da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
20191da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
20206f3cbf55SDouglas Gilbert 	if (NULL == devip)
20216f3cbf55SDouglas Gilbert 		return 1;	/* no resources, will be marked offline */
20221da177e4SLinus Torvalds 	sdp->hostdata = devip;
20231da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
20241da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
20251da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
2026c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
20271da177e4SLinus Torvalds 	return 0;
20281da177e4SLinus Torvalds }
20291da177e4SLinus Torvalds 
20301da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
20311da177e4SLinus Torvalds {
20321da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20331da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
20341da177e4SLinus Torvalds 
20351da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2036c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
2037c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20381da177e4SLinus Torvalds 	if (devip) {
20391da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
20401da177e4SLinus Torvalds 		devip->used = 0;
20411da177e4SLinus Torvalds 		sdp->hostdata = NULL;
20421da177e4SLinus Torvalds 	}
20431da177e4SLinus Torvalds }
20441da177e4SLinus Torvalds 
20451da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
20461da177e4SLinus Torvalds {
20471da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20481da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
20491da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20501da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
20511da177e4SLinus Torvalds 
20521da177e4SLinus Torvalds 	if (devip)
20531da177e4SLinus Torvalds 		return devip;
20541da177e4SLinus Torvalds 	sdbg_host = *(struct sdebug_host_info **) sdev->host->hostdata;
20551da177e4SLinus Torvalds         if(! sdbg_host) {
20561da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
20571da177e4SLinus Torvalds 		return NULL;
20581da177e4SLinus Torvalds         }
20591da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
20601da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
20611da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
20621da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
20631da177e4SLinus Torvalds                         return devip;
20641da177e4SLinus Torvalds 		else {
20651da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
20661da177e4SLinus Torvalds 				open_devip = devip;
20671da177e4SLinus Torvalds 		}
20681da177e4SLinus Torvalds 	}
20691da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
20706f3cbf55SDouglas Gilbert 		open_devip = kzalloc(sizeof(*open_devip),GFP_ATOMIC);
20711da177e4SLinus Torvalds 		if (NULL == open_devip) {
20721da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
20731da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
20741da177e4SLinus Torvalds 			return NULL;
20751da177e4SLinus Torvalds 		}
20761da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20771da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
20781da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
20791da177e4SLinus Torvalds 	}
20801da177e4SLinus Torvalds         if (open_devip) {
20811da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
20821da177e4SLinus Torvalds 		open_devip->target = sdev->id;
20831da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
20841da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20851da177e4SLinus Torvalds 		open_devip->reset = 1;
20861da177e4SLinus Torvalds 		open_devip->used = 1;
20871da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
20881da177e4SLinus Torvalds 		if (scsi_debug_dsense)
20891da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
20901da177e4SLinus Torvalds 		else {
20911da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
20921da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
20931da177e4SLinus Torvalds 		}
2094c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2095c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
20961da177e4SLinus Torvalds 		return open_devip;
20971da177e4SLinus Torvalds         }
20981da177e4SLinus Torvalds         return NULL;
20991da177e4SLinus Torvalds }
21001da177e4SLinus Torvalds 
21011da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
21021da177e4SLinus Torvalds 			    int asc, int asq)
21031da177e4SLinus Torvalds {
21041da177e4SLinus Torvalds 	unsigned char * sbuff;
21051da177e4SLinus Torvalds 
21061da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
21071da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
21081da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
21091da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
21101da177e4SLinus Torvalds 		sbuff[1] = key;
21111da177e4SLinus Torvalds 		sbuff[2] = asc;
21121da177e4SLinus Torvalds 		sbuff[3] = asq;
21131da177e4SLinus Torvalds 	} else {
21141da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
21151da177e4SLinus Torvalds 		sbuff[2] = key;
21161da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
21171da177e4SLinus Torvalds 		sbuff[12] = asc;
21181da177e4SLinus Torvalds 		sbuff[13] = asq;
21191da177e4SLinus Torvalds 	}
21201da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21211da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
21221da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
21231da177e4SLinus Torvalds }
21241da177e4SLinus Torvalds 
21251da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
21261da177e4SLinus Torvalds {
21271da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21281da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
21291da177e4SLinus Torvalds 	++num_aborts;
21301da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
21311da177e4SLinus Torvalds 	return SUCCESS;
21321da177e4SLinus Torvalds }
21331da177e4SLinus Torvalds 
21341da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
21351da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
21361da177e4SLinus Torvalds {
21371da177e4SLinus Torvalds 	int res;
21381da177e4SLinus Torvalds 	unsigned char *buf;
21391da177e4SLinus Torvalds 
21401da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21411da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
21421da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
21431da177e4SLinus Torvalds 	if (buf) {
21441da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
21451da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
21461da177e4SLinus Torvalds 		kfree(buf);
21471da177e4SLinus Torvalds 		if (! res)
21481da177e4SLinus Torvalds 			return res;
21491da177e4SLinus Torvalds 	}
21501da177e4SLinus Torvalds 	info[0] = sdebug_heads;
21511da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
21521da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
21531da177e4SLinus Torvalds 	return 0;
21541da177e4SLinus Torvalds }
21551da177e4SLinus Torvalds 
21561da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
21571da177e4SLinus Torvalds {
21581da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
21591da177e4SLinus Torvalds 
21601da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21611da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
21621da177e4SLinus Torvalds 	++num_dev_resets;
21631da177e4SLinus Torvalds 	if (SCpnt) {
21641da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
21651da177e4SLinus Torvalds 		if (devip)
21661da177e4SLinus Torvalds 			devip->reset = 1;
21671da177e4SLinus Torvalds 	}
21681da177e4SLinus Torvalds 	return SUCCESS;
21691da177e4SLinus Torvalds }
21701da177e4SLinus Torvalds 
21711da177e4SLinus Torvalds static int scsi_debug_bus_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         struct scsi_device * sdp;
21761da177e4SLinus Torvalds         struct Scsi_Host * hp;
21771da177e4SLinus Torvalds 
21781da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21791da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
21801da177e4SLinus Torvalds 	++num_bus_resets;
21811da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
21821da177e4SLinus Torvalds 		sdbg_host = *(struct sdebug_host_info **) hp->hostdata;
21831da177e4SLinus Torvalds 		if (sdbg_host) {
21841da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
21851da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
21861da177e4SLinus Torvalds                                             dev_list)
21871da177e4SLinus Torvalds 				dev_info->reset = 1;
21881da177e4SLinus Torvalds 		}
21891da177e4SLinus Torvalds 	}
21901da177e4SLinus Torvalds 	return SUCCESS;
21911da177e4SLinus Torvalds }
21921da177e4SLinus Torvalds 
21931da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
21941da177e4SLinus Torvalds {
21951da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
21961da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21971da177e4SLinus Torvalds 
21981da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21991da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
22001da177e4SLinus Torvalds 	++num_host_resets;
22011da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
22021da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
22031da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
22041da177e4SLinus Torvalds                                     dev_list)
22051da177e4SLinus Torvalds                         dev_info->reset = 1;
22061da177e4SLinus Torvalds         }
22071da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
22081da177e4SLinus Torvalds 	stop_all_queued();
22091da177e4SLinus Torvalds 	return SUCCESS;
22101da177e4SLinus Torvalds }
22111da177e4SLinus Torvalds 
22121da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
22131da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
22141da177e4SLinus Torvalds {
22151da177e4SLinus Torvalds 	unsigned long iflags;
22161da177e4SLinus Torvalds 	int k;
22171da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22181da177e4SLinus Torvalds 
22191da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22201da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22211da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22221da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
22231da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22241da177e4SLinus Torvalds 			sqcp->in_use = 0;
22251da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22261da177e4SLinus Torvalds 			break;
22271da177e4SLinus Torvalds 		}
22281da177e4SLinus Torvalds 	}
22291da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22301da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
22311da177e4SLinus Torvalds }
22321da177e4SLinus Torvalds 
22331da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
22341da177e4SLinus Torvalds static void stop_all_queued(void)
22351da177e4SLinus Torvalds {
22361da177e4SLinus Torvalds 	unsigned long iflags;
22371da177e4SLinus Torvalds 	int k;
22381da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22391da177e4SLinus Torvalds 
22401da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22411da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22421da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22431da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
22441da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22451da177e4SLinus Torvalds 			sqcp->in_use = 0;
22461da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22471da177e4SLinus Torvalds 		}
22481da177e4SLinus Torvalds 	}
22491da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22501da177e4SLinus Torvalds }
22511da177e4SLinus Torvalds 
22521da177e4SLinus Torvalds /* Initializes timers in queued array */
22531da177e4SLinus Torvalds static void __init init_all_queued(void)
22541da177e4SLinus Torvalds {
22551da177e4SLinus Torvalds 	unsigned long iflags;
22561da177e4SLinus Torvalds 	int k;
22571da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22581da177e4SLinus Torvalds 
22591da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22601da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22611da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22621da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
22631da177e4SLinus Torvalds 		sqcp->in_use = 0;
22641da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
22651da177e4SLinus Torvalds 	}
22661da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22671da177e4SLinus Torvalds }
22681da177e4SLinus Torvalds 
22691da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
22701da177e4SLinus Torvalds {
22711da177e4SLinus Torvalds 	struct partition * pp;
22721da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
22731da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
22741da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
22751da177e4SLinus Torvalds 
22761da177e4SLinus Torvalds 	/* assume partition table already zeroed */
22771da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
22781da177e4SLinus Torvalds 		return;
22791da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
22801da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
22811da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
22821da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
22831da177e4SLinus Torvalds 	}
2284c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
22851da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
22861da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
22871da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
22881da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
22891da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
22901da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
22911da177e4SLinus Torvalds 			    * heads_by_sects;
22921da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
22931da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
22941da177e4SLinus Torvalds 
22951da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
22961da177e4SLinus Torvalds 	ramp[511] = 0xAA;
22971da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
22981da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
22991da177e4SLinus Torvalds 		start_sec = starts[k];
23001da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
23011da177e4SLinus Torvalds 		pp->boot_ind = 0;
23021da177e4SLinus Torvalds 
23031da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
23041da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
23051da177e4SLinus Torvalds 			   / sdebug_sectors_per;
23061da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
23071da177e4SLinus Torvalds 
23081da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
23091da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
23101da177e4SLinus Torvalds 			       / sdebug_sectors_per;
23111da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
23121da177e4SLinus Torvalds 
23131da177e4SLinus Torvalds 		pp->start_sect = start_sec;
23141da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
23151da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
23161da177e4SLinus Torvalds 	}
23171da177e4SLinus Torvalds }
23181da177e4SLinus Torvalds 
23191da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
23201da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
23211da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
23221da177e4SLinus Torvalds {
23231da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
23241da177e4SLinus Torvalds 		if (scsi_result) {
23251da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
23261da177e4SLinus Torvalds 
2327c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2328c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2329c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
23301da177e4SLinus Torvalds 		}
23311da177e4SLinus Torvalds 	}
23321da177e4SLinus Torvalds 	if (cmnd && devip) {
23331da177e4SLinus Torvalds 		/* simulate autosense by this driver */
23341da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
23351da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
23361da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
23371da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
23381da177e4SLinus Torvalds 	}
23391da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
23401da177e4SLinus Torvalds 		if (cmnd)
23411da177e4SLinus Torvalds 			cmnd->result = scsi_result;
23421da177e4SLinus Torvalds 		if (done)
23431da177e4SLinus Torvalds 			done(cmnd);
23441da177e4SLinus Torvalds 		return 0;
23451da177e4SLinus Torvalds 	} else {
23461da177e4SLinus Torvalds 		unsigned long iflags;
23471da177e4SLinus Torvalds 		int k;
23481da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
23491da177e4SLinus Torvalds 
23501da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
23511da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
23521da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
23531da177e4SLinus Torvalds 			if (! sqcp->in_use)
23541da177e4SLinus Torvalds 				break;
23551da177e4SLinus Torvalds 		}
23561da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
23571da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
23581da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
23591da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
23601da177e4SLinus Torvalds 		}
23611da177e4SLinus Torvalds 		sqcp->in_use = 1;
23621da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
23631da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
23641da177e4SLinus Torvalds 		sqcp->done_funct = done;
23651da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
23661da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
23671da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
23681da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
23691da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23701da177e4SLinus Torvalds 		if (cmnd)
23711da177e4SLinus Torvalds 			cmnd->result = 0;
23721da177e4SLinus Torvalds 		return 0;
23731da177e4SLinus Torvalds 	}
23741da177e4SLinus Torvalds }
23751da177e4SLinus Torvalds 
237623183910SDouglas Gilbert /* Note: The following macros create attribute files in the
237723183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
237823183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
237923183910SDouglas Gilbert    as it can when the corresponding attribute in the
238023183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
238123183910SDouglas Gilbert  */
2382c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2383c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2384c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2385c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2386c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
238723183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2388c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2389c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2390c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2391c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2392c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2393c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2394c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2395c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
239623183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
239723183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
23981da177e4SLinus Torvalds 
23991da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
24001da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
24011da177e4SLinus Torvalds MODULE_LICENSE("GPL");
24021da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
24031da177e4SLinus Torvalds 
24041da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
24051da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2406c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2407c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2408beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
240923183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2410c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2411c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
24121da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2413c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
24146f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
24151da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
24161da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2417c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
241823183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
24191da177e4SLinus Torvalds 
24201da177e4SLinus Torvalds 
24211da177e4SLinus Torvalds static char sdebug_info[256];
24221da177e4SLinus Torvalds 
24231da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
24241da177e4SLinus Torvalds {
24251da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
24261da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
24271da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
24281da177e4SLinus Torvalds 		scsi_debug_opts);
24291da177e4SLinus Torvalds 	return sdebug_info;
24301da177e4SLinus Torvalds }
24311da177e4SLinus Torvalds 
24321da177e4SLinus Torvalds /* scsi_debug_proc_info
24331da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
24341da177e4SLinus Torvalds  */
24351da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
24361da177e4SLinus Torvalds 				int length, int inout)
24371da177e4SLinus Torvalds {
24381da177e4SLinus Torvalds 	int len, pos, begin;
24391da177e4SLinus Torvalds 	int orig_length;
24401da177e4SLinus Torvalds 
24411da177e4SLinus Torvalds 	orig_length = length;
24421da177e4SLinus Torvalds 
24431da177e4SLinus Torvalds 	if (inout == 1) {
24441da177e4SLinus Torvalds 		char arr[16];
24451da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
24461da177e4SLinus Torvalds 
24471da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
24481da177e4SLinus Torvalds 			return -EACCES;
24491da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
24501da177e4SLinus Torvalds 		arr[minLen] = '\0';
24511da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
24521da177e4SLinus Torvalds 			return -EINVAL;
24531da177e4SLinus Torvalds 		scsi_debug_opts = pos;
24541da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
24551da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
24561da177e4SLinus Torvalds 		return length;
24571da177e4SLinus Torvalds 	}
24581da177e4SLinus Torvalds 	begin = 0;
24591da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
24601da177e4SLinus Torvalds 	    "%s [%s]\n"
24611da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
24621da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
24631da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
24641da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
24651da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
24661da177e4SLinus Torvalds 	    "host_resets=%d\n",
24671da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
24681da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
24691da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
24701da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
24711da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
24721da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
24731da177e4SLinus Torvalds 	if (pos < offset) {
24741da177e4SLinus Torvalds 		len = 0;
24751da177e4SLinus Torvalds 		begin = pos;
24761da177e4SLinus Torvalds 	}
24771da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
24781da177e4SLinus Torvalds 	len -= (offset - begin);
24791da177e4SLinus Torvalds 	if (len > length)
24801da177e4SLinus Torvalds 		len = length;
24811da177e4SLinus Torvalds 	return len;
24821da177e4SLinus Torvalds }
24831da177e4SLinus Torvalds 
24841da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
24851da177e4SLinus Torvalds {
24861da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
24871da177e4SLinus Torvalds }
24881da177e4SLinus Torvalds 
24891da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
24901da177e4SLinus Torvalds 				  const char * buf, size_t count)
24911da177e4SLinus Torvalds {
24921da177e4SLinus Torvalds         int delay;
24931da177e4SLinus Torvalds 	char work[20];
24941da177e4SLinus Torvalds 
24951da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24961da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
24971da177e4SLinus Torvalds 			scsi_debug_delay = delay;
24981da177e4SLinus Torvalds 			return count;
24991da177e4SLinus Torvalds 		}
25001da177e4SLinus Torvalds 	}
25011da177e4SLinus Torvalds 	return -EINVAL;
25021da177e4SLinus Torvalds }
25031da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
25041da177e4SLinus Torvalds 	    sdebug_delay_store);
25051da177e4SLinus Torvalds 
25061da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
25071da177e4SLinus Torvalds {
25081da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
25091da177e4SLinus Torvalds }
25101da177e4SLinus Torvalds 
25111da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
25121da177e4SLinus Torvalds 				 const char * buf, size_t count)
25131da177e4SLinus Torvalds {
25141da177e4SLinus Torvalds         int opts;
25151da177e4SLinus Torvalds 	char work[20];
25161da177e4SLinus Torvalds 
25171da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
25181da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
25191da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
25201da177e4SLinus Torvalds 				goto opts_done;
25211da177e4SLinus Torvalds 		} else {
25221da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
25231da177e4SLinus Torvalds 				goto opts_done;
25241da177e4SLinus Torvalds 		}
25251da177e4SLinus Torvalds 	}
25261da177e4SLinus Torvalds 	return -EINVAL;
25271da177e4SLinus Torvalds opts_done:
25281da177e4SLinus Torvalds 	scsi_debug_opts = opts;
25291da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
25301da177e4SLinus Torvalds 	return count;
25311da177e4SLinus Torvalds }
25321da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
25331da177e4SLinus Torvalds 	    sdebug_opts_store);
25341da177e4SLinus Torvalds 
25351da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
25361da177e4SLinus Torvalds {
25371da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
25381da177e4SLinus Torvalds }
25391da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
25401da177e4SLinus Torvalds 				  const char * buf, size_t count)
25411da177e4SLinus Torvalds {
25421da177e4SLinus Torvalds         int n;
25431da177e4SLinus Torvalds 
25441da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25451da177e4SLinus Torvalds 		scsi_debug_ptype = n;
25461da177e4SLinus Torvalds 		return count;
25471da177e4SLinus Torvalds 	}
25481da177e4SLinus Torvalds 	return -EINVAL;
25491da177e4SLinus Torvalds }
25501da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
25511da177e4SLinus Torvalds 
25521da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
25531da177e4SLinus Torvalds {
25541da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
25551da177e4SLinus Torvalds }
25561da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
25571da177e4SLinus Torvalds 				  const char * buf, size_t count)
25581da177e4SLinus Torvalds {
25591da177e4SLinus Torvalds         int n;
25601da177e4SLinus Torvalds 
25611da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25621da177e4SLinus Torvalds 		scsi_debug_dsense = n;
25631da177e4SLinus Torvalds 		return count;
25641da177e4SLinus Torvalds 	}
25651da177e4SLinus Torvalds 	return -EINVAL;
25661da177e4SLinus Torvalds }
25671da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
25681da177e4SLinus Torvalds 	    sdebug_dsense_store);
25691da177e4SLinus Torvalds 
257023183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
257123183910SDouglas Gilbert {
257223183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
257323183910SDouglas Gilbert }
257423183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
257523183910SDouglas Gilbert 				    const char * buf, size_t count)
257623183910SDouglas Gilbert {
257723183910SDouglas Gilbert         int n;
257823183910SDouglas Gilbert 
257923183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
258023183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
258123183910SDouglas Gilbert 		return count;
258223183910SDouglas Gilbert 	}
258323183910SDouglas Gilbert 	return -EINVAL;
258423183910SDouglas Gilbert }
258523183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
258623183910SDouglas Gilbert 	    sdebug_fake_rw_store);
258723183910SDouglas Gilbert 
2588c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2589c65b1445SDouglas Gilbert {
2590c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2591c65b1445SDouglas Gilbert }
2592c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2593c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2594c65b1445SDouglas Gilbert {
2595c65b1445SDouglas Gilbert         int n;
2596c65b1445SDouglas Gilbert 
2597c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2598c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2599c65b1445SDouglas Gilbert 		return count;
2600c65b1445SDouglas Gilbert 	}
2601c65b1445SDouglas Gilbert 	return -EINVAL;
2602c65b1445SDouglas Gilbert }
2603c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2604c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2605c65b1445SDouglas Gilbert 
26061da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
26071da177e4SLinus Torvalds {
26081da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
26091da177e4SLinus Torvalds }
26101da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
26111da177e4SLinus Torvalds 				     const char * buf, size_t count)
26121da177e4SLinus Torvalds {
26131da177e4SLinus Torvalds         int n;
26141da177e4SLinus Torvalds 
26151da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26161da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
26171da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26181da177e4SLinus Torvalds 		return count;
26191da177e4SLinus Torvalds 	}
26201da177e4SLinus Torvalds 	return -EINVAL;
26211da177e4SLinus Torvalds }
26221da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
26231da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
26241da177e4SLinus Torvalds 
26251da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
26261da177e4SLinus Torvalds {
26271da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
26281da177e4SLinus Torvalds }
26291da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
26301da177e4SLinus Torvalds 
26311da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
26321da177e4SLinus Torvalds {
26331da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
26341da177e4SLinus Torvalds }
26351da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
26361da177e4SLinus Torvalds 
26371da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
26381da177e4SLinus Torvalds {
26391da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
26401da177e4SLinus Torvalds }
26411da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
26421da177e4SLinus Torvalds 				      const char * buf, size_t count)
26431da177e4SLinus Torvalds {
26441da177e4SLinus Torvalds         int nth;
26451da177e4SLinus Torvalds 
26461da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
26471da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
26481da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
26491da177e4SLinus Torvalds 		return count;
26501da177e4SLinus Torvalds 	}
26511da177e4SLinus Torvalds 	return -EINVAL;
26521da177e4SLinus Torvalds }
26531da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
26541da177e4SLinus Torvalds 	    sdebug_every_nth_store);
26551da177e4SLinus Torvalds 
26561da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
26571da177e4SLinus Torvalds {
26581da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
26591da177e4SLinus Torvalds }
26601da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
26611da177e4SLinus Torvalds 				     const char * buf, size_t count)
26621da177e4SLinus Torvalds {
26631da177e4SLinus Torvalds         int n;
26641da177e4SLinus Torvalds 
26651da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26661da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
26671da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26681da177e4SLinus Torvalds 		return count;
26691da177e4SLinus Torvalds 	}
26701da177e4SLinus Torvalds 	return -EINVAL;
26711da177e4SLinus Torvalds }
26721da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
26731da177e4SLinus Torvalds 	    sdebug_max_luns_store);
26741da177e4SLinus Torvalds 
26751da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
26761da177e4SLinus Torvalds {
26771da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
26781da177e4SLinus Torvalds }
26791da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
26801da177e4SLinus Torvalds 
2681c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2682c65b1445SDouglas Gilbert {
2683c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2684c65b1445SDouglas Gilbert }
2685c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2686c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2687c65b1445SDouglas Gilbert {
2688c65b1445SDouglas Gilbert         int n;
2689c65b1445SDouglas Gilbert 
2690c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2691c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2692c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2693c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2694c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2695c65b1445SDouglas Gilbert 		} else
2696c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2697c65b1445SDouglas Gilbert 		return count;
2698c65b1445SDouglas Gilbert 	}
2699c65b1445SDouglas Gilbert 	return -EINVAL;
2700c65b1445SDouglas Gilbert }
2701c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2702c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2703c65b1445SDouglas Gilbert 
27041da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
27051da177e4SLinus Torvalds {
27061da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
27071da177e4SLinus Torvalds }
27081da177e4SLinus Torvalds 
27091da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
27101da177e4SLinus Torvalds 				     const char * buf, size_t count)
27111da177e4SLinus Torvalds {
27121da177e4SLinus Torvalds         int delta_hosts;
27131da177e4SLinus Torvalds 	char work[20];
27141da177e4SLinus Torvalds 
27151da177e4SLinus Torvalds         if (1 != sscanf(buf, "%10s", work))
27161da177e4SLinus Torvalds 		return -EINVAL;
27171da177e4SLinus Torvalds 	{	/* temporary hack around sscanf() problem with -ve nums */
27181da177e4SLinus Torvalds 		int neg = 0;
27191da177e4SLinus Torvalds 
27201da177e4SLinus Torvalds 		if ('-' == *work)
27211da177e4SLinus Torvalds 			neg = 1;
27221da177e4SLinus Torvalds 		if (1 != sscanf(work + neg, "%d", &delta_hosts))
27231da177e4SLinus Torvalds 			return -EINVAL;
27241da177e4SLinus Torvalds 		if (neg)
27251da177e4SLinus Torvalds 			delta_hosts = -delta_hosts;
27261da177e4SLinus Torvalds 	}
27271da177e4SLinus Torvalds 	if (delta_hosts > 0) {
27281da177e4SLinus Torvalds 		do {
27291da177e4SLinus Torvalds 			sdebug_add_adapter();
27301da177e4SLinus Torvalds 		} while (--delta_hosts);
27311da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
27321da177e4SLinus Torvalds 		do {
27331da177e4SLinus Torvalds 			sdebug_remove_adapter();
27341da177e4SLinus Torvalds 		} while (++delta_hosts);
27351da177e4SLinus Torvalds 	}
27361da177e4SLinus Torvalds 	return count;
27371da177e4SLinus Torvalds }
27381da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
27391da177e4SLinus Torvalds 	    sdebug_add_host_store);
27401da177e4SLinus Torvalds 
274123183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
274223183910SDouglas Gilbert 					  char * buf)
274323183910SDouglas Gilbert {
274423183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
274523183910SDouglas Gilbert }
274623183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
274723183910SDouglas Gilbert 					   const char * buf, size_t count)
274823183910SDouglas Gilbert {
274923183910SDouglas Gilbert 	int n;
275023183910SDouglas Gilbert 
275123183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
275223183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
275323183910SDouglas Gilbert 		return count;
275423183910SDouglas Gilbert 	}
275523183910SDouglas Gilbert 	return -EINVAL;
275623183910SDouglas Gilbert }
275723183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
275823183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
275923183910SDouglas Gilbert 
276023183910SDouglas Gilbert /* Note: The following function creates attribute files in the
276123183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
276223183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
276323183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
276423183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
276523183910SDouglas Gilbert  */
27666ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
27671da177e4SLinus Torvalds {
27686ecaff7fSRandy Dunlap 	int ret;
27696ecaff7fSRandy Dunlap 
27706ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27716ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
27726ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27736ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27746ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
277523183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27766ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
277723183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27786ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
277923183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
27806ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27816ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
27826ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
278323183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
278423183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
27856ecaff7fSRandy Dunlap 	return ret;
27861da177e4SLinus Torvalds }
27871da177e4SLinus Torvalds 
27881da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
27891da177e4SLinus Torvalds {
279023183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
279123183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
27921da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
27931da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
27941da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27951da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
279623183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
279723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27981da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
279923183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
28001da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
28011da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
28021da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
28031da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
28041da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
28051da177e4SLinus Torvalds }
28061da177e4SLinus Torvalds 
28071da177e4SLinus Torvalds static int __init scsi_debug_init(void)
28081da177e4SLinus Torvalds {
2809c65b1445SDouglas Gilbert 	unsigned int sz;
28101da177e4SLinus Torvalds 	int host_to_add;
28111da177e4SLinus Torvalds 	int k;
28126ecaff7fSRandy Dunlap 	int ret;
28131da177e4SLinus Torvalds 
28141da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
28151da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2816c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2817c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2818c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2819c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2820c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2821c65b1445SDouglas Gilbert 	} else
2822c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
28231da177e4SLinus Torvalds 
28241da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
28251da177e4SLinus Torvalds 	sdebug_heads = 8;
28261da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
28271da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
28281da177e4SLinus Torvalds 		sdebug_heads = 32;
28291da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
28301da177e4SLinus Torvalds 		sdebug_heads = 64;
28311da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28321da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28331da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
28341da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
28351da177e4SLinus Torvalds 		sdebug_heads = 255;
28361da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
28371da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
28381da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28391da177e4SLinus Torvalds 	}
28401da177e4SLinus Torvalds 
28411da177e4SLinus Torvalds 	sz = sdebug_store_size;
28421da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
28431da177e4SLinus Torvalds 	if (NULL == fake_storep) {
28441da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
28451da177e4SLinus Torvalds 		return -ENOMEM;
28461da177e4SLinus Torvalds 	}
28471da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
28481da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
28491da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
28501da177e4SLinus Torvalds 
28516ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
28526ecaff7fSRandy Dunlap 	if (ret < 0) {
28536ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
28546ecaff7fSRandy Dunlap 			ret);
28556ecaff7fSRandy Dunlap 		goto free_vm;
28566ecaff7fSRandy Dunlap 	}
28576ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
28586ecaff7fSRandy Dunlap 	if (ret < 0) {
28596ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
28606ecaff7fSRandy Dunlap 			ret);
28616ecaff7fSRandy Dunlap 		goto dev_unreg;
28626ecaff7fSRandy Dunlap 	}
28636ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
28646ecaff7fSRandy Dunlap 	if (ret < 0) {
28656ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
28666ecaff7fSRandy Dunlap 			ret);
28676ecaff7fSRandy Dunlap 		goto bus_unreg;
28686ecaff7fSRandy Dunlap 	}
28696ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
28706ecaff7fSRandy Dunlap 	if (ret < 0) {
28716ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
28726ecaff7fSRandy Dunlap 			ret);
28736ecaff7fSRandy Dunlap 		goto del_files;
28746ecaff7fSRandy Dunlap 	}
28751da177e4SLinus Torvalds 
28766ecaff7fSRandy Dunlap 	init_all_queued();
28771da177e4SLinus Torvalds 
2878b02b6bc4SKristian Høgsberg 	sdebug_driver_template.proc_name = sdebug_proc_name;
28791da177e4SLinus Torvalds 
28801da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
28811da177e4SLinus Torvalds         scsi_debug_add_host = 0;
28821da177e4SLinus Torvalds 
28831da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
28841da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
28851da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
28861da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
28871da177e4SLinus Torvalds                         break;
28881da177e4SLinus Torvalds                 }
28891da177e4SLinus Torvalds         }
28901da177e4SLinus Torvalds 
28911da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
28921da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
28931da177e4SLinus Torvalds 		       scsi_debug_add_host);
28941da177e4SLinus Torvalds 	}
28951da177e4SLinus Torvalds 	return 0;
28966ecaff7fSRandy Dunlap 
28976ecaff7fSRandy Dunlap del_files:
28986ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
28996ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
29006ecaff7fSRandy Dunlap bus_unreg:
29016ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
29026ecaff7fSRandy Dunlap dev_unreg:
29036ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
29046ecaff7fSRandy Dunlap free_vm:
29056ecaff7fSRandy Dunlap 	vfree(fake_storep);
29066ecaff7fSRandy Dunlap 
29076ecaff7fSRandy Dunlap 	return ret;
29081da177e4SLinus Torvalds }
29091da177e4SLinus Torvalds 
29101da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
29111da177e4SLinus Torvalds {
29121da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
29131da177e4SLinus Torvalds 
29141da177e4SLinus Torvalds 	stop_all_queued();
29151da177e4SLinus Torvalds 	for (; k; k--)
29161da177e4SLinus Torvalds 		sdebug_remove_adapter();
29171da177e4SLinus Torvalds 	do_remove_driverfs_files();
29181da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
29191da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
29201da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
29211da177e4SLinus Torvalds 
29221da177e4SLinus Torvalds 	vfree(fake_storep);
29231da177e4SLinus Torvalds }
29241da177e4SLinus Torvalds 
29251da177e4SLinus Torvalds device_initcall(scsi_debug_init);
29261da177e4SLinus Torvalds module_exit(scsi_debug_exit);
29271da177e4SLinus Torvalds 
292852c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
29291da177e4SLinus Torvalds {
29301da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
29311da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
29321da177e4SLinus Torvalds }
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds static struct device pseudo_primary = {
29351da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
29361da177e4SLinus Torvalds 	.release	= pseudo_0_release,
29371da177e4SLinus Torvalds };
29381da177e4SLinus Torvalds 
29391da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
29401da177e4SLinus Torvalds                           struct device_driver *dev_driver)
29411da177e4SLinus Torvalds {
29421da177e4SLinus Torvalds         return 1;
29431da177e4SLinus Torvalds }
29441da177e4SLinus Torvalds 
29451da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
29461da177e4SLinus Torvalds         .name = "pseudo",
29471da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2948bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2949bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
29501da177e4SLinus Torvalds };
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
29531da177e4SLinus Torvalds {
29541da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29551da177e4SLinus Torvalds 
29561da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29571da177e4SLinus Torvalds         kfree(sdbg_host);
29581da177e4SLinus Torvalds }
29591da177e4SLinus Torvalds 
29601da177e4SLinus Torvalds static int sdebug_add_adapter(void)
29611da177e4SLinus Torvalds {
29621da177e4SLinus Torvalds 	int k, devs_per_host;
29631da177e4SLinus Torvalds         int error = 0;
29641da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29651da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29661da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29671da177e4SLinus Torvalds 
296824669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
29691da177e4SLinus Torvalds         if (NULL == sdbg_host) {
29701da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
29711da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
29721da177e4SLinus Torvalds                 return -ENOMEM;
29731da177e4SLinus Torvalds         }
29741da177e4SLinus Torvalds 
29751da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
29761da177e4SLinus Torvalds 
29771da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
29781da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
297924669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
29801da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
29811da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
29821da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
29831da177e4SLinus Torvalds                         error = -ENOMEM;
29841da177e4SLinus Torvalds 			goto clean;
29851da177e4SLinus Torvalds                 }
29861da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
29871da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
29881da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
29891da177e4SLinus Torvalds         }
29901da177e4SLinus Torvalds 
29911da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
29921da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
29931da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
29941da177e4SLinus Torvalds 
29951da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
29961da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
29971da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
29981da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
29991da177e4SLinus Torvalds 
30001da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
30011da177e4SLinus Torvalds 
30021da177e4SLinus Torvalds         if (error)
30031da177e4SLinus Torvalds 		goto clean;
30041da177e4SLinus Torvalds 
30051da177e4SLinus Torvalds 	++scsi_debug_add_host;
30061da177e4SLinus Torvalds         return error;
30071da177e4SLinus Torvalds 
30081da177e4SLinus Torvalds clean:
30091da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30101da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30111da177e4SLinus Torvalds 					  dev_list);
30121da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
30131da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
30141da177e4SLinus Torvalds 	}
30151da177e4SLinus Torvalds 
30161da177e4SLinus Torvalds 	kfree(sdbg_host);
30171da177e4SLinus Torvalds         return error;
30181da177e4SLinus Torvalds }
30191da177e4SLinus Torvalds 
30201da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
30211da177e4SLinus Torvalds {
30221da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
30231da177e4SLinus Torvalds 
30241da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
30251da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
30261da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
30271da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
30281da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
30291da177e4SLinus Torvalds 	}
30301da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
30311da177e4SLinus Torvalds 
30321da177e4SLinus Torvalds 	if (!sdbg_host)
30331da177e4SLinus Torvalds 		return;
30341da177e4SLinus Torvalds 
30351da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
30361da177e4SLinus Torvalds         --scsi_debug_add_host;
30371da177e4SLinus Torvalds }
30381da177e4SLinus Torvalds 
30391da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
30401da177e4SLinus Torvalds {
30411da177e4SLinus Torvalds         int error = 0;
30421da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30431da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
30441da177e4SLinus Torvalds 
30451da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30461da177e4SLinus Torvalds 
30471da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
30481da177e4SLinus Torvalds         if (NULL == hpnt) {
30491da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
30501da177e4SLinus Torvalds                 error = -ENODEV;
30511da177e4SLinus Torvalds 		return error;
30521da177e4SLinus Torvalds         }
30531da177e4SLinus Torvalds 
30541da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
30551da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
30561da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
30571da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
30581da177e4SLinus Torvalds 	else
30591da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3060c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
30611da177e4SLinus Torvalds 
30621da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
30631da177e4SLinus Torvalds         if (error) {
30641da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
30651da177e4SLinus Torvalds                 error = -ENODEV;
30661da177e4SLinus Torvalds 		scsi_host_put(hpnt);
30671da177e4SLinus Torvalds         } else
30681da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
30691da177e4SLinus Torvalds 
30701da177e4SLinus Torvalds 
30711da177e4SLinus Torvalds         return error;
30721da177e4SLinus Torvalds }
30731da177e4SLinus Torvalds 
30741da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
30751da177e4SLinus Torvalds {
30761da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
30771da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30781da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
30791da177e4SLinus Torvalds 
30801da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30811da177e4SLinus Torvalds 
30821da177e4SLinus Torvalds 	if (!sdbg_host) {
30831da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
30841da177e4SLinus Torvalds 		       __FUNCTION__);
30851da177e4SLinus Torvalds 		return -ENODEV;
30861da177e4SLinus Torvalds 	}
30871da177e4SLinus Torvalds 
30881da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
30891da177e4SLinus Torvalds 
30901da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30911da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30921da177e4SLinus Torvalds                                           dev_list);
30931da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
30941da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
30951da177e4SLinus Torvalds         }
30961da177e4SLinus Torvalds 
30971da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
30981da177e4SLinus Torvalds         return 0;
30991da177e4SLinus Torvalds }
31001da177e4SLinus Torvalds 
31011da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
31021da177e4SLinus Torvalds {
31031da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
31041da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
31051da177e4SLinus Torvalds 
31061da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
31071da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
31081da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
31091da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
31101da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
31111da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
31121da177e4SLinus Torvalds 		else
31131da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
3114c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
31151da177e4SLinus Torvalds 	}
31161da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
31171da177e4SLinus Torvalds }
3118