xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision f3df41cf)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * vvvvvvvvvvvvvvvvvvvvvvv Original vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
31da177e4SLinus Torvalds  *  Copyright (C) 1992  Eric Youngdale
41da177e4SLinus Torvalds  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
51da177e4SLinus Torvalds  *  to make sure that we are not getting blocks mixed up, and PANIC if
61da177e4SLinus Torvalds  *  anything out of the ordinary is seen.
71da177e4SLinus Torvalds  * ^^^^^^^^^^^^^^^^^^^^^^^ Original ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  *  This version is more generic, simulating a variable number of disk
1023183910SDouglas Gilbert  *  (or disk like devices) sharing a common amount of RAM. To be more
1123183910SDouglas Gilbert  *  realistic, the simulated devices have the transport attributes of
1223183910SDouglas Gilbert  *  SAS disks.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  *  For documentation see http://www.torque.net/sg/sdebug26.html
161da177e4SLinus Torvalds  *
171da177e4SLinus Torvalds  *   D. Gilbert (dpg) work for Magneto-Optical device test [20010421]
181da177e4SLinus Torvalds  *   dpg: work for devfs large number of disks [20010809]
191da177e4SLinus Torvalds  *        forked for lk 2.5 series [20011216, 20020101]
201da177e4SLinus Torvalds  *        use vmalloc() more inquiry+mode_sense [20020302]
211da177e4SLinus Torvalds  *        add timers for delayed responses [20020721]
221da177e4SLinus Torvalds  *   Patrick Mansfield <patmans@us.ibm.com> max_luns+scsi_level [20021031]
231da177e4SLinus Torvalds  *   Mike Anderson <andmike@us.ibm.com> sysfs work [20021118]
241da177e4SLinus Torvalds  *   dpg: change style of boot options to "scsi_debug.num_tgts=2" and
251da177e4SLinus Torvalds  *        module options to "modprobe scsi_debug num_tgts=2" [20021221]
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <linux/kernel.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321da177e4SLinus Torvalds #include <linux/timer.h>
331da177e4SLinus Torvalds #include <linux/types.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/genhd.h>
361da177e4SLinus Torvalds #include <linux/fs.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <linux/proc_fs.h>
391da177e4SLinus Torvalds #include <linux/vmalloc.h>
401da177e4SLinus Torvalds #include <linux/moduleparam.h>
41852e034dSJens Axboe #include <linux/scatterlist.h>
421da177e4SLinus Torvalds #include <linux/blkdev.h>
439ff26eefSFUJITA Tomonori 
449ff26eefSFUJITA Tomonori #include <scsi/scsi.h>
459ff26eefSFUJITA Tomonori #include <scsi/scsi_cmnd.h>
469ff26eefSFUJITA Tomonori #include <scsi/scsi_device.h>
471da177e4SLinus Torvalds #include <scsi/scsi_host.h>
481da177e4SLinus Torvalds #include <scsi/scsicam.h>
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #include <linux/stat.h>
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds #include "scsi_logging.h"
531da177e4SLinus Torvalds 
546f3cbf55SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.81"
556f3cbf55SDouglas Gilbert static const char * scsi_debug_version_date = "20070104";
561da177e4SLinus Torvalds 
576f3cbf55SDouglas Gilbert /* Additional Sense Code (ASC) */
58c65b1445SDouglas Gilbert #define NO_ADDITIONAL_SENSE 0x0
59c65b1445SDouglas Gilbert #define LOGICAL_UNIT_NOT_READY 0x4
601da177e4SLinus Torvalds #define UNRECOVERED_READ_ERR 0x11
61c65b1445SDouglas Gilbert #define PARAMETER_LIST_LENGTH_ERR 0x1a
621da177e4SLinus Torvalds #define INVALID_OPCODE 0x20
631da177e4SLinus Torvalds #define ADDR_OUT_OF_RANGE 0x21
641da177e4SLinus Torvalds #define INVALID_FIELD_IN_CDB 0x24
65c65b1445SDouglas Gilbert #define INVALID_FIELD_IN_PARAM_LIST 0x26
661da177e4SLinus Torvalds #define POWERON_RESET 0x29
671da177e4SLinus Torvalds #define SAVING_PARAMS_UNSUP 0x39
686f3cbf55SDouglas Gilbert #define TRANSPORT_PROBLEM 0x4b
69c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
70c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
711da177e4SLinus Torvalds 
726f3cbf55SDouglas Gilbert /* Additional Sense Code Qualifier (ASCQ) */
736f3cbf55SDouglas Gilbert #define ACK_NAK_TO 0x3
746f3cbf55SDouglas Gilbert 
751da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds /* Default values for driver parameters */
781da177e4SLinus Torvalds #define DEF_NUM_HOST   1
791da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
801da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
811da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
821da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
831da177e4SLinus Torvalds  */
841da177e4SLinus Torvalds #define DEF_DELAY   1
851da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
861da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
871da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
881da177e4SLinus Torvalds #define DEF_OPTS   0
891da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
901da177e4SLinus Torvalds #define DEF_PTYPE   0
911da177e4SLinus Torvalds #define DEF_D_SENSE   0
92c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
93c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
9423183910SDouglas Gilbert #define DEF_FAKE_RW	0
9523183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
961da177e4SLinus Torvalds 
971da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
981da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
991da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
1001da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
1011da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1026f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
1031da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1041da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1051da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1061da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1076f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1086f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1091da177e4SLinus Torvalds  *
1101da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1111da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1121da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1131da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1146f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1156f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1161da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1171da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1181da177e4SLinus Torvalds  */
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1211da177e4SLinus Torvalds  * sector on read commands: */
1221da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1231da177e4SLinus Torvalds 
1241da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1251da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1261da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
127c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1301da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1311da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1321da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1331da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1341da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1351da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1361da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1371da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1381da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1391da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
140c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
141c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
14223183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
14323183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1461da177e4SLinus Torvalds 
1471da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1481da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1491da177e4SLinus Torvalds 
150c65b1445SDouglas Gilbert static unsigned int sdebug_store_size;	/* in bytes */
151c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1521da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1551da177e4SLinus Torvalds    may still need them */
1561da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1571da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1581da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds /* default sector size is 512 bytes, 2**9 bytes */
1611da177e4SLinus Torvalds #define POW2_SECT_SIZE 9
1621da177e4SLinus Torvalds #define SECT_SIZE (1 << POW2_SECT_SIZE)
1631da177e4SLinus Torvalds #define SECT_SIZE_PER(TGT) SECT_SIZE
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1681da177e4SLinus Torvalds 
1699e603ca0SFUJITA Tomonori #define SCSI_DEBUG_CANQUEUE  255
1709e603ca0SFUJITA Tomonori #define SCSI_DEBUG_MAX_CMD_LEN 16
1719e603ca0SFUJITA Tomonori 
1721da177e4SLinus Torvalds struct sdebug_dev_info {
1731da177e4SLinus Torvalds 	struct list_head dev_list;
1741da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
1751da177e4SLinus Torvalds 	unsigned int channel;
1761da177e4SLinus Torvalds 	unsigned int target;
1771da177e4SLinus Torvalds 	unsigned int lun;
1781da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
179c65b1445SDouglas Gilbert 	unsigned int wlun;
1801da177e4SLinus Torvalds 	char reset;
181c65b1445SDouglas Gilbert 	char stopped;
1821da177e4SLinus Torvalds 	char used;
1831da177e4SLinus Torvalds };
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds struct sdebug_host_info {
1861da177e4SLinus Torvalds 	struct list_head host_list;
1871da177e4SLinus Torvalds 	struct Scsi_Host *shost;
1881da177e4SLinus Torvalds 	struct device dev;
1891da177e4SLinus Torvalds 	struct list_head dev_info_list;
1901da177e4SLinus Torvalds };
1911da177e4SLinus Torvalds 
1921da177e4SLinus Torvalds #define to_sdebug_host(d)	\
1931da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
1961da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds struct sdebug_queued_cmd {
2011da177e4SLinus Torvalds 	int in_use;
2021da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
2031da177e4SLinus Torvalds 	done_funct_t done_funct;
2041da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2051da177e4SLinus Torvalds 	int scsi_result;
2061da177e4SLinus Torvalds };
2071da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2081da177e4SLinus Torvalds 
2091da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds static int num_aborts = 0;
2121da177e4SLinus Torvalds static int num_dev_resets = 0;
2131da177e4SLinus Torvalds static int num_bus_resets = 0;
2141da177e4SLinus Torvalds static int num_host_resets = 0;
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2171da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static int sdebug_driver_probe(struct device *);
2221da177e4SLinus Torvalds static int sdebug_driver_remove(struct device *);
2231da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2261da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2271da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2281da177e4SLinus Torvalds };
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds static const int check_condition_result =
2311da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2321da177e4SLinus Torvalds 
233c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
234c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
235c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
236c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
237c65b1445SDouglas Gilbert 
2381da177e4SLinus Torvalds /* function declarations */
2391da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * SCpnt, int target,
2401da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
2411da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * SCpnt,
2421da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip);
243c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
244c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
2455a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
2465a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip);
2471da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * SCpnt,
2481da177e4SLinus Torvalds 			struct sdebug_dev_info * devip);
249c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * SCpnt,
2501da177e4SLinus Torvalds 			  struct sdebug_dev_info * devip);
251c65b1445SDouglas Gilbert static int resp_mode_sense(struct scsi_cmnd * scp, int target,
252c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip);
253c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
254c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip);
255c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
256c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip);
257c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
258c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip);
259c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
260c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip);
2611da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * SCpnt,
2621da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip);
263c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
264c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip);
2651da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2661da177e4SLinus Torvalds                                 int arr_len);
2671da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd * scp, unsigned char * arr,
2681da177e4SLinus Torvalds                                int max_arr_len);
2691da177e4SLinus Torvalds static void timer_intr_handler(unsigned long);
2701da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev);
2711da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
2721da177e4SLinus Torvalds 			    int asc, int asq);
273c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
2741da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip);
2751da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
2761da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
2771da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff);
2781da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp);
2791da177e4SLinus Torvalds static void __init init_all_queued(void);
2801da177e4SLinus Torvalds static void stop_all_queued(void);
2811da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd);
2825a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
2835a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
2845a09e398SHannes Reinecke 			   const char * dev_id_str, int dev_id_str_len);
285c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id);
2866ecaff7fSRandy Dunlap static int do_create_driverfs_files(void);
2871da177e4SLinus Torvalds static void do_remove_driverfs_files(void);
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2901da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2911da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void);
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds static struct device pseudo_primary;
2941da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2951da177e4SLinus Torvalds 
2963de9f944SFUJITA Tomonori static void get_data_transfer_info(unsigned char *cmd,
2973de9f944SFUJITA Tomonori 				   unsigned long long *lba, unsigned int *num)
2983de9f944SFUJITA Tomonori {
2993de9f944SFUJITA Tomonori 	int i;
3003de9f944SFUJITA Tomonori 
3013de9f944SFUJITA Tomonori 	switch (*cmd) {
3023de9f944SFUJITA Tomonori 	case WRITE_16:
3033de9f944SFUJITA Tomonori 	case READ_16:
3043de9f944SFUJITA Tomonori 		for (*lba = 0, i = 0; i < 8; ++i) {
3053de9f944SFUJITA Tomonori 			if (i > 0)
3063de9f944SFUJITA Tomonori 				*lba <<= 8;
3073de9f944SFUJITA Tomonori 			*lba += cmd[2 + i];
3083de9f944SFUJITA Tomonori 		}
3093de9f944SFUJITA Tomonori 		*num = cmd[13] + (cmd[12] << 8) +
3103de9f944SFUJITA Tomonori 			(cmd[11] << 16) + (cmd[10] << 24);
3113de9f944SFUJITA Tomonori 		break;
3123de9f944SFUJITA Tomonori 	case WRITE_12:
3133de9f944SFUJITA Tomonori 	case READ_12:
3143de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3153de9f944SFUJITA Tomonori 		*num = cmd[9] + (cmd[8] << 8) +	(cmd[7] << 16) + (cmd[6] << 24);
3163de9f944SFUJITA Tomonori 		break;
3173de9f944SFUJITA Tomonori 	case WRITE_10:
3183de9f944SFUJITA Tomonori 	case READ_10:
319c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
3203de9f944SFUJITA Tomonori 		*lba = cmd[5] + (cmd[4] << 8) +	(cmd[3] << 16) + (cmd[2] << 24);
3213de9f944SFUJITA Tomonori 		*num = cmd[8] + (cmd[7] << 8);
3223de9f944SFUJITA Tomonori 		break;
3233de9f944SFUJITA Tomonori 	case WRITE_6:
3243de9f944SFUJITA Tomonori 	case READ_6:
3253de9f944SFUJITA Tomonori 		*lba = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
3263de9f944SFUJITA Tomonori 		*num = (0 == cmd[4]) ? 256 : cmd[4];
3273de9f944SFUJITA Tomonori 		break;
3283de9f944SFUJITA Tomonori 	default:
3293de9f944SFUJITA Tomonori 		break;
3303de9f944SFUJITA Tomonori 	}
3313de9f944SFUJITA Tomonori }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds static
3341da177e4SLinus Torvalds int scsi_debug_queuecommand(struct scsi_cmnd * SCpnt, done_funct_t done)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
3373de9f944SFUJITA Tomonori 	int len, k;
338c65b1445SDouglas Gilbert 	unsigned int num;
339c65b1445SDouglas Gilbert 	unsigned long long lba;
3401da177e4SLinus Torvalds 	int errsts = 0;
341c65b1445SDouglas Gilbert 	int target = SCpnt->device->id;
3421da177e4SLinus Torvalds 	struct sdebug_dev_info * devip = NULL;
3431da177e4SLinus Torvalds 	int inj_recovered = 0;
3446f3cbf55SDouglas Gilbert 	int inj_transport = 0;
345c65b1445SDouglas Gilbert 	int delay_override = 0;
3461da177e4SLinus Torvalds 
347c73961e5SBoaz Harrosh 	scsi_set_resid(SCpnt, 0);
3481da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3491da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: cmd ");
350c65b1445SDouglas Gilbert 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3511da177e4SLinus Torvalds 			printk("%02x ", (int)cmd[k]);
3521da177e4SLinus Torvalds 		printk("\n");
3531da177e4SLinus Torvalds 	}
3549e603ca0SFUJITA Tomonori 
3559e603ca0SFUJITA Tomonori 	if (target == SCpnt->device->host->hostt->this_id) {
3561da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3571da177e4SLinus Torvalds 		       "target!\n");
3581da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3591da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3601da177e4SLinus Torvalds         }
3611da177e4SLinus Torvalds 
362c65b1445SDouglas Gilbert 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
363c65b1445SDouglas Gilbert 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3641da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3651da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3661da177e4SLinus Torvalds 	devip = devInfoReg(SCpnt->device);
3671da177e4SLinus Torvalds 	if (NULL == devip)
3681da177e4SLinus Torvalds 		return schedule_resp(SCpnt, NULL, done,
3691da177e4SLinus Torvalds 				     DID_NO_CONNECT << 16, 0);
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds         if ((scsi_debug_every_nth != 0) &&
3721da177e4SLinus Torvalds             (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3731da177e4SLinus Torvalds                 scsi_debug_cmnd_count = 0;
3741da177e4SLinus Torvalds 		if (scsi_debug_every_nth < -1)
3751da177e4SLinus Torvalds 			scsi_debug_every_nth = -1;
3761da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3771da177e4SLinus Torvalds 			return 0; /* ignore command causing timeout */
3781da177e4SLinus Torvalds 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3791da177e4SLinus Torvalds 			inj_recovered = 1; /* to reads and writes below */
3806f3cbf55SDouglas Gilbert 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
3816f3cbf55SDouglas Gilbert 			inj_transport = 1; /* to reads and writes below */
3821da177e4SLinus Torvalds         }
3831da177e4SLinus Torvalds 
384c65b1445SDouglas Gilbert 	if (devip->wlun) {
385c65b1445SDouglas Gilbert 		switch (*cmd) {
386c65b1445SDouglas Gilbert 		case INQUIRY:
387c65b1445SDouglas Gilbert 		case REQUEST_SENSE:
388c65b1445SDouglas Gilbert 		case TEST_UNIT_READY:
389c65b1445SDouglas Gilbert 		case REPORT_LUNS:
390c65b1445SDouglas Gilbert 			break;  /* only allowable wlun commands */
391c65b1445SDouglas Gilbert 		default:
392c65b1445SDouglas Gilbert 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
393c65b1445SDouglas Gilbert 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
394c65b1445SDouglas Gilbert 				       "not supported for wlun\n", *cmd);
395c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
396c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
397c65b1445SDouglas Gilbert 			errsts = check_condition_result;
398c65b1445SDouglas Gilbert 			return schedule_resp(SCpnt, devip, done, errsts,
399c65b1445SDouglas Gilbert 					     0);
400c65b1445SDouglas Gilbert 		}
401c65b1445SDouglas Gilbert 	}
402c65b1445SDouglas Gilbert 
4031da177e4SLinus Torvalds 	switch (*cmd) {
4041da177e4SLinus Torvalds 	case INQUIRY:     /* mandatory, ignore unit attention */
405c65b1445SDouglas Gilbert 		delay_override = 1;
4061da177e4SLinus Torvalds 		errsts = resp_inquiry(SCpnt, target, devip);
4071da177e4SLinus Torvalds 		break;
4081da177e4SLinus Torvalds 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
409c65b1445SDouglas Gilbert 		delay_override = 1;
4101da177e4SLinus Torvalds 		errsts = resp_requests(SCpnt, devip);
4111da177e4SLinus Torvalds 		break;
4121da177e4SLinus Torvalds 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
4131da177e4SLinus Torvalds 	case START_STOP:
414c65b1445SDouglas Gilbert 		errsts = resp_start_stop(SCpnt, devip);
4151da177e4SLinus Torvalds 		break;
4161da177e4SLinus Torvalds 	case ALLOW_MEDIUM_REMOVAL:
417c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
4181da177e4SLinus Torvalds 			break;
4191da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
4201da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
4211da177e4SLinus Torvalds 			        cmd[4] ? "inhibited" : "enabled");
4221da177e4SLinus Torvalds 		break;
4231da177e4SLinus Torvalds 	case SEND_DIAGNOSTIC:     /* mandatory */
424c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4251da177e4SLinus Torvalds 		break;
4261da177e4SLinus Torvalds 	case TEST_UNIT_READY:     /* mandatory */
427c65b1445SDouglas Gilbert 		delay_override = 1;
428c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4291da177e4SLinus Torvalds 		break;
4301da177e4SLinus Torvalds         case RESERVE:
431c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4321da177e4SLinus Torvalds                 break;
4331da177e4SLinus Torvalds         case RESERVE_10:
434c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4351da177e4SLinus Torvalds                 break;
4361da177e4SLinus Torvalds         case RELEASE:
437c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4381da177e4SLinus Torvalds                 break;
4391da177e4SLinus Torvalds         case RELEASE_10:
440c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
4411da177e4SLinus Torvalds                 break;
4421da177e4SLinus Torvalds 	case READ_CAPACITY:
4431da177e4SLinus Torvalds 		errsts = resp_readcap(SCpnt, devip);
4441da177e4SLinus Torvalds 		break;
445c65b1445SDouglas Gilbert 	case SERVICE_ACTION_IN:
446c65b1445SDouglas Gilbert 		if (SAI_READ_CAPACITY_16 != cmd[1]) {
447c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
448c65b1445SDouglas Gilbert 					INVALID_OPCODE, 0);
449c65b1445SDouglas Gilbert 			errsts = check_condition_result;
450c65b1445SDouglas Gilbert 			break;
451c65b1445SDouglas Gilbert 		}
452c65b1445SDouglas Gilbert 		errsts = resp_readcap16(SCpnt, devip);
453c65b1445SDouglas Gilbert 		break;
4545a09e398SHannes Reinecke 	case MAINTENANCE_IN:
4555a09e398SHannes Reinecke 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
4565a09e398SHannes Reinecke 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
4575a09e398SHannes Reinecke 					INVALID_OPCODE, 0);
4585a09e398SHannes Reinecke 			errsts = check_condition_result;
4595a09e398SHannes Reinecke 			break;
4605a09e398SHannes Reinecke 		}
4615a09e398SHannes Reinecke 		errsts = resp_report_tgtpgs(SCpnt, devip);
4625a09e398SHannes Reinecke 		break;
4631da177e4SLinus Torvalds 	case READ_16:
4641da177e4SLinus Torvalds 	case READ_12:
4651da177e4SLinus Torvalds 	case READ_10:
4661da177e4SLinus Torvalds 	case READ_6:
467c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4681da177e4SLinus Torvalds 			break;
46923183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
47023183910SDouglas Gilbert 			break;
4713de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
472c65b1445SDouglas Gilbert 		errsts = resp_read(SCpnt, lba, num, devip);
4731da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
4741da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
475c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
4761da177e4SLinus Torvalds 			errsts = check_condition_result;
4776f3cbf55SDouglas Gilbert 		} else if (inj_transport && (0 == errsts)) {
4786f3cbf55SDouglas Gilbert                         mk_sense_buffer(devip, ABORTED_COMMAND,
4796f3cbf55SDouglas Gilbert                                         TRANSPORT_PROBLEM, ACK_NAK_TO);
4806f3cbf55SDouglas Gilbert                         errsts = check_condition_result;
4811da177e4SLinus Torvalds                 }
4821da177e4SLinus Torvalds 		break;
4831da177e4SLinus Torvalds 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
484c65b1445SDouglas Gilbert 		delay_override = 1;
4851da177e4SLinus Torvalds 		errsts = resp_report_luns(SCpnt, devip);
4861da177e4SLinus Torvalds 		break;
4871da177e4SLinus Torvalds 	case VERIFY:		/* 10 byte SBC-2 command */
488c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
4891da177e4SLinus Torvalds 		break;
4901da177e4SLinus Torvalds 	case WRITE_16:
4911da177e4SLinus Torvalds 	case WRITE_12:
4921da177e4SLinus Torvalds 	case WRITE_10:
4931da177e4SLinus Torvalds 	case WRITE_6:
494c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 0, devip)))
4951da177e4SLinus Torvalds 			break;
49623183910SDouglas Gilbert 		if (scsi_debug_fake_rw)
49723183910SDouglas Gilbert 			break;
4983de9f944SFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
499c65b1445SDouglas Gilbert 		errsts = resp_write(SCpnt, lba, num, devip);
5001da177e4SLinus Torvalds 		if (inj_recovered && (0 == errsts)) {
5011da177e4SLinus Torvalds 			mk_sense_buffer(devip, RECOVERED_ERROR,
502c65b1445SDouglas Gilbert 					THRESHOLD_EXCEEDED, 0);
5031da177e4SLinus Torvalds 			errsts = check_condition_result;
5041da177e4SLinus Torvalds 		}
5051da177e4SLinus Torvalds 		break;
5061da177e4SLinus Torvalds 	case MODE_SENSE:
5071da177e4SLinus Torvalds 	case MODE_SENSE_10:
5081da177e4SLinus Torvalds 		errsts = resp_mode_sense(SCpnt, target, devip);
5091da177e4SLinus Torvalds 		break;
510c65b1445SDouglas Gilbert 	case MODE_SELECT:
511c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 1, devip);
512c65b1445SDouglas Gilbert 		break;
513c65b1445SDouglas Gilbert 	case MODE_SELECT_10:
514c65b1445SDouglas Gilbert 		errsts = resp_mode_select(SCpnt, 0, devip);
515c65b1445SDouglas Gilbert 		break;
516c65b1445SDouglas Gilbert 	case LOG_SENSE:
517c65b1445SDouglas Gilbert 		errsts = resp_log_sense(SCpnt, devip);
518c65b1445SDouglas Gilbert 		break;
5191da177e4SLinus Torvalds 	case SYNCHRONIZE_CACHE:
520c65b1445SDouglas Gilbert 		delay_override = 1;
521c65b1445SDouglas Gilbert 		errsts = check_readiness(SCpnt, 0, devip);
5221da177e4SLinus Torvalds 		break;
5236f3cbf55SDouglas Gilbert 	case WRITE_BUFFER:
5246f3cbf55SDouglas Gilbert 		errsts = check_readiness(SCpnt, 1, devip);
5256f3cbf55SDouglas Gilbert 		break;
526c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
527c639d14eSFUJITA Tomonori 		if (!scsi_bidi_cmnd(SCpnt)) {
528c639d14eSFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
529c639d14eSFUJITA Tomonori 					INVALID_FIELD_IN_CDB, 0);
530c639d14eSFUJITA Tomonori 			errsts = check_condition_result;
531c639d14eSFUJITA Tomonori 			break;
532c639d14eSFUJITA Tomonori 		}
533c639d14eSFUJITA Tomonori 
534c639d14eSFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
535c639d14eSFUJITA Tomonori 		if (errsts)
536c639d14eSFUJITA Tomonori 			break;
537c639d14eSFUJITA Tomonori 		if (scsi_debug_fake_rw)
538c639d14eSFUJITA Tomonori 			break;
539c639d14eSFUJITA Tomonori 		get_data_transfer_info(cmd, &lba, &num);
540c639d14eSFUJITA Tomonori 		errsts = resp_read(SCpnt, lba, num, devip);
541c639d14eSFUJITA Tomonori 		if (errsts)
542c639d14eSFUJITA Tomonori 			break;
543c639d14eSFUJITA Tomonori 		errsts = resp_write(SCpnt, lba, num, devip);
544c639d14eSFUJITA Tomonori 		if (errsts)
545c639d14eSFUJITA Tomonori 			break;
546c639d14eSFUJITA Tomonori 		errsts = resp_xdwriteread(SCpnt, lba, num, devip);
547c639d14eSFUJITA Tomonori 		break;
5481da177e4SLinus Torvalds 	default:
5491da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5501da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
5511da177e4SLinus Torvalds 			       "supported\n", *cmd);
552c65b1445SDouglas Gilbert 		if ((errsts = check_readiness(SCpnt, 1, devip)))
5531da177e4SLinus Torvalds 			break;	/* Unit attention takes precedence */
5541da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
5551da177e4SLinus Torvalds 		errsts = check_condition_result;
5561da177e4SLinus Torvalds 		break;
5571da177e4SLinus Torvalds 	}
558c65b1445SDouglas Gilbert 	return schedule_resp(SCpnt, devip, done, errsts,
559c65b1445SDouglas Gilbert 			     (delay_override ? 0 : scsi_debug_delay));
5601da177e4SLinus Torvalds }
5611da177e4SLinus Torvalds 
5621da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
5631da177e4SLinus Torvalds {
5641da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
5651da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
5661da177e4SLinus Torvalds 	}
5671da177e4SLinus Torvalds 	return -EINVAL;
5681da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
5691da177e4SLinus Torvalds }
5701da177e4SLinus Torvalds 
571c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
572c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
5731da177e4SLinus Torvalds {
5741da177e4SLinus Torvalds 	if (devip->reset) {
5751da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
5761da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
5771da177e4SLinus Torvalds 			       "attention: power on reset\n");
5781da177e4SLinus Torvalds 		devip->reset = 0;
5791da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
5801da177e4SLinus Torvalds 		return check_condition_result;
5811da177e4SLinus Torvalds 	}
582c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
583c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
584c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
585c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
586c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
587c65b1445SDouglas Gilbert 				0x2);
588c65b1445SDouglas Gilbert 		return check_condition_result;
589c65b1445SDouglas Gilbert 	}
5901da177e4SLinus Torvalds 	return 0;
5911da177e4SLinus Torvalds }
5921da177e4SLinus Torvalds 
5931da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
5941da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
5951da177e4SLinus Torvalds 				int arr_len)
5961da177e4SLinus Torvalds {
59721a61829SFUJITA Tomonori 	int act_len;
598072d0bb3SFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
5991da177e4SLinus Torvalds 
600072d0bb3SFUJITA Tomonori 	if (!sdb->length)
6011da177e4SLinus Torvalds 		return 0;
602072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
6031da177e4SLinus Torvalds 		return (DID_ERROR << 16);
60421a61829SFUJITA Tomonori 
60521a61829SFUJITA Tomonori 	act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
60621a61829SFUJITA Tomonori 				      arr, arr_len);
607072d0bb3SFUJITA Tomonori 	if (sdb->resid)
608072d0bb3SFUJITA Tomonori 		sdb->resid -= act_len;
609c65b1445SDouglas Gilbert 	else
61021a61829SFUJITA Tomonori 		sdb->resid = scsi_bufflen(scp) - act_len;
61121a61829SFUJITA Tomonori 
6121da177e4SLinus Torvalds 	return 0;
6131da177e4SLinus Torvalds }
6141da177e4SLinus Torvalds 
6151da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
6161da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
61721a61829SFUJITA Tomonori 			       int arr_len)
6181da177e4SLinus Torvalds {
61921a61829SFUJITA Tomonori 	if (!scsi_bufflen(scp))
6201da177e4SLinus Torvalds 		return 0;
621072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE))
6221da177e4SLinus Torvalds 		return -1;
62321a61829SFUJITA Tomonori 
62421a61829SFUJITA Tomonori 	return scsi_sg_copy_to_buffer(scp, arr, arr_len);
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
6291da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
6301da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
6311da177e4SLinus Torvalds 
6325a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
6335a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
6345a09e398SHannes Reinecke 			   const char * dev_id_str,
635c65b1445SDouglas Gilbert 			   int dev_id_str_len)
6361da177e4SLinus Torvalds {
637c65b1445SDouglas Gilbert 	int num, port_a;
638c65b1445SDouglas Gilbert 	char b[32];
6391da177e4SLinus Torvalds 
640c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
6411da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
6421da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
6431da177e4SLinus Torvalds 	arr[1] = 0x1;
6441da177e4SLinus Torvalds 	arr[2] = 0x0;
6451da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
6461da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
6471da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
6481da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
6491da177e4SLinus Torvalds 	arr[3] = num;
6501da177e4SLinus Torvalds 	num += 4;
651c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
652c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
653c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
654c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
655c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
656c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
657c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
658c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
659c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
660c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
661c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
662c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
663c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
664c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
665c65b1445SDouglas Gilbert 		/* Target relative port number */
666c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
667c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
668c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
669c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
670c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
671c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
672c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
673c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
674c65b1445SDouglas Gilbert 	}
675c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
676c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
677c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
678c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
679c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
680c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
681c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
682c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
683c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
684c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
685c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
686c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
687c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
6885a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
6895a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
6905a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
6915a09e398SHannes Reinecke 	arr[num++] = 0x0;
6925a09e398SHannes Reinecke 	arr[num++] = 0x4;
6935a09e398SHannes Reinecke 	arr[num++] = 0;
6945a09e398SHannes Reinecke 	arr[num++] = 0;
6955a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
6965a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
697c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
698c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
699c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
700c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
701c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
702c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
703c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
704c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
705c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
706c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
707c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
708c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
709c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
710c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
711c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
712c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
713c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
714c65b1445SDouglas Gilbert 	arr[num++] = 24;
715c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
716c65b1445SDouglas Gilbert 	num += 12;
717c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
718c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
719c65b1445SDouglas Gilbert 	num += 8;
720c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
721c65b1445SDouglas Gilbert 	num += 4;
722c65b1445SDouglas Gilbert 	return num;
723c65b1445SDouglas Gilbert }
724c65b1445SDouglas Gilbert 
725c65b1445SDouglas Gilbert 
726c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
727c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
728c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
729c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
730c65b1445SDouglas Gilbert };
731c65b1445SDouglas Gilbert 
732c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
733c65b1445SDouglas Gilbert {
734c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
735c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
736c65b1445SDouglas Gilbert }
737c65b1445SDouglas Gilbert 
738c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
739c65b1445SDouglas Gilbert {
740c65b1445SDouglas Gilbert 	int num = 0;
741c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
742c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
743c65b1445SDouglas Gilbert 	int plen, olen;
744c65b1445SDouglas Gilbert 
745c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
746c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
747c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
748c65b1445SDouglas Gilbert 	olen = strlen(na1);
749c65b1445SDouglas Gilbert 	plen = olen + 1;
750c65b1445SDouglas Gilbert 	if (plen % 4)
751c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
752c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
753c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
754c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
755c65b1445SDouglas Gilbert 	num += plen;
756c65b1445SDouglas Gilbert 
757c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
758c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
759c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
760c65b1445SDouglas Gilbert 	olen = strlen(na2);
761c65b1445SDouglas Gilbert 	plen = olen + 1;
762c65b1445SDouglas Gilbert 	if (plen % 4)
763c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
764c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
765c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
766c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
767c65b1445SDouglas Gilbert 	num += plen;
768c65b1445SDouglas Gilbert 
769c65b1445SDouglas Gilbert 	return num;
770c65b1445SDouglas Gilbert }
771c65b1445SDouglas Gilbert 
772c65b1445SDouglas Gilbert /* SCSI ports VPD page */
773c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
774c65b1445SDouglas Gilbert {
775c65b1445SDouglas Gilbert 	int num = 0;
776c65b1445SDouglas Gilbert 	int port_a, port_b;
777c65b1445SDouglas Gilbert 
778c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
779c65b1445SDouglas Gilbert 	port_b = port_a + 1;
780c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
781c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
782c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
783c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
784c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
785c65b1445SDouglas Gilbert 	num += 6;
786c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
787c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
788c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
789c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
790c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
791c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
792c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
793c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
794c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
795c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
796c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
797c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
798c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
799c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
800c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
801c65b1445SDouglas Gilbert 
802c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
803c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
804c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
805c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
806c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
807c65b1445SDouglas Gilbert 	num += 6;
808c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
809c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
810c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
811c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
812c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
813c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
814c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
815c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
816c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
817c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
818c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
819c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
820c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
821c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
822c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
823c65b1445SDouglas Gilbert 
824c65b1445SDouglas Gilbert 	return num;
825c65b1445SDouglas Gilbert }
826c65b1445SDouglas Gilbert 
827c65b1445SDouglas Gilbert 
828c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
829c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
830c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
831c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
832c65b1445SDouglas Gilbert '1','2','3','4',
833c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
834c65b1445SDouglas Gilbert 0xec,0,0,0,
835c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
836c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
837c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
838c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
839c65b1445SDouglas Gilbert 0x53,0x41,
840c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
841c65b1445SDouglas Gilbert 0x20,0x20,
842c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
843c65b1445SDouglas Gilbert 0x10,0x80,
844c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
845c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
846c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
847c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
848c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
849c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
850c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
851c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
852c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
853c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
854c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
855c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
856c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
857c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
858c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
859c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
860c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
861c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
862c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
863c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
864c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
865c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
866c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
867c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
868c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
869c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
870c65b1445SDouglas Gilbert };
871c65b1445SDouglas Gilbert 
872c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
873c65b1445SDouglas Gilbert {
874c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
875c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
876c65b1445SDouglas Gilbert }
877c65b1445SDouglas Gilbert 
878c65b1445SDouglas Gilbert 
879c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
880c65b1445SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4,
881c65b1445SDouglas Gilbert 	0,0,0x4,0,
882c65b1445SDouglas Gilbert 	0,0,0,64,
883c65b1445SDouglas Gilbert };
884c65b1445SDouglas Gilbert 
885c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
886c65b1445SDouglas Gilbert {
887c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
888c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
889c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
890c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
891c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
892c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
893c65b1445SDouglas Gilbert 	}
894c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
8951da177e4SLinus Torvalds }
8961da177e4SLinus Torvalds 
8971da177e4SLinus Torvalds 
8981da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
899c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
9001da177e4SLinus Torvalds 
9011da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
9021da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9031da177e4SLinus Torvalds {
9041da177e4SLinus Torvalds 	unsigned char pq_pdt;
9055a09e398SHannes Reinecke 	unsigned char * arr;
9061da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
9075a09e398SHannes Reinecke 	int alloc_len, n, ret;
9081da177e4SLinus Torvalds 
9091da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
9106f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
9116f3cbf55SDouglas Gilbert 	if (! arr)
9126f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
913c65b1445SDouglas Gilbert 	if (devip->wlun)
914c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
915c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
916c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
917c65b1445SDouglas Gilbert 	else
9181da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
9191da177e4SLinus Torvalds 	arr[0] = pq_pdt;
9201da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
9211da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
9221da177e4SLinus Torvalds 			       	0);
9235a09e398SHannes Reinecke 		kfree(arr);
9241da177e4SLinus Torvalds 		return check_condition_result;
9251da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
9265a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
927c65b1445SDouglas Gilbert 		char lu_id_str[6];
928c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
9291da177e4SLinus Torvalds 
9305a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
9315a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
93223183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
93323183910SDouglas Gilbert 			host_no = 0;
934c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
935c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
936c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
937c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
938c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
9391da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
940c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
941c65b1445SDouglas Gilbert 			n = 4;
942c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
943c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
944c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
945c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
946c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
947c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
948c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
949c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
950c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
951c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
952c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
9531da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
954c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
9551da177e4SLinus Torvalds 			arr[3] = len;
956c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
9571da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
958c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
9595a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
9605a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
9615a09e398SHannes Reinecke 						 lu_id_str, len);
962c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
963c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
964c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
965c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
966c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
967c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
968c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
969c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
970c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
971c65b1445SDouglas Gilbert 			arr[4] = 0x0;   /* no protection stuff */
972c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
973c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
974c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
975c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
976c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
977c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
978c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
979c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
980c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
981c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
982c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
983c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
984c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
985c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
986c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
987c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
988c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
989c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
990c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
9911da177e4SLinus Torvalds 		} else {
9921da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
9931da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
9941da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
9955a09e398SHannes Reinecke 			kfree(arr);
9961da177e4SLinus Torvalds 			return check_condition_result;
9971da177e4SLinus Torvalds 		}
998c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
9995a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
1000c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
10015a09e398SHannes Reinecke 		kfree(arr);
10025a09e398SHannes Reinecke 		return ret;
10031da177e4SLinus Torvalds 	}
10041da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
10051da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
10061da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
10071da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
10081da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
10095a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
10105a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
1011c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
10121da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
1013c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
10141da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
10151da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
10161da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
10171da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
1018c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
1019c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
1020c65b1445SDouglas Gilbert 	n = 62;
10211da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
1022c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
10231da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
1024c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
10251da177e4SLinus Torvalds 	}
1026c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
10275a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
10281da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
10295a09e398SHannes Reinecke 	kfree(arr);
10305a09e398SHannes Reinecke 	return ret;
10311da177e4SLinus Torvalds }
10321da177e4SLinus Torvalds 
10331da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
10341da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
10351da177e4SLinus Torvalds {
10361da177e4SLinus Torvalds 	unsigned char * sbuff;
10371da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10381da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
1039c65b1445SDouglas Gilbert 	int want_dsense;
10401da177e4SLinus Torvalds 	int len = 18;
10411da177e4SLinus Torvalds 
1042c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
10431da177e4SLinus Torvalds 	if (devip->reset == 1)
1044c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
1045c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
10461da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
1047c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
1048c65b1445SDouglas Gilbert 		if (want_dsense) {
1049c65b1445SDouglas Gilbert 			arr[0] = 0x72;
1050c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
1051c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
1052c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
1053c65b1445SDouglas Gilbert 		} else {
1054c65b1445SDouglas Gilbert 			arr[0] = 0x70;
1055c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
1056c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
1057c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
1058c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
1059c65b1445SDouglas Gilbert 		}
1060c65b1445SDouglas Gilbert 	} else {
1061c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
10621da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
10631da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
1064c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
10651da177e4SLinus Torvalds 			arr[0] = 0x72;
10661da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
10671da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
10681da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
10691da177e4SLinus Torvalds 			len = 8;
1070c65b1445SDouglas Gilbert 		}
1071c65b1445SDouglas Gilbert 	}
1072c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
10731da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
10741da177e4SLinus Torvalds }
10751da177e4SLinus Torvalds 
1076c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
1077c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
1078c65b1445SDouglas Gilbert {
1079c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1080c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
1081c65b1445SDouglas Gilbert 
1082c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1083c65b1445SDouglas Gilbert 		return errsts;
1084c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
1085c65b1445SDouglas Gilbert 	if (power_cond) {
1086c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1087c65b1445SDouglas Gilbert 			       	0);
1088c65b1445SDouglas Gilbert 		return check_condition_result;
1089c65b1445SDouglas Gilbert 	}
1090c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
1091c65b1445SDouglas Gilbert 	if (start == devip->stopped)
1092c65b1445SDouglas Gilbert 		devip->stopped = !start;
1093c65b1445SDouglas Gilbert 	return 0;
1094c65b1445SDouglas Gilbert }
1095c65b1445SDouglas Gilbert 
10961da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
10971da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
10981da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
10991da177e4SLinus Torvalds {
11001da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
1101c65b1445SDouglas Gilbert 	unsigned int capac;
11021da177e4SLinus Torvalds 	int errsts;
11031da177e4SLinus Torvalds 
1104c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
11051da177e4SLinus Torvalds 		return errsts;
1106c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1107c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1108c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1109c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1110c65b1445SDouglas Gilbert 	} else
1111c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
11121da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
1113c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
1114c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
11151da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
11161da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
11171da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
11181da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1119c65b1445SDouglas Gilbert 	} else {
1120c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1121c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1122c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1123c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1124c65b1445SDouglas Gilbert 	}
11251da177e4SLinus Torvalds 	arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
11261da177e4SLinus Torvalds 	arr[7] = SECT_SIZE_PER(target) & 0xff;
11271da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
11281da177e4SLinus Torvalds }
11291da177e4SLinus Torvalds 
1130c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1131c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1132c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1133c65b1445SDouglas Gilbert {
1134c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1135c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1136c65b1445SDouglas Gilbert 	unsigned long long capac;
1137c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1138c65b1445SDouglas Gilbert 
1139c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1140c65b1445SDouglas Gilbert 		return errsts;
1141c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1142c65b1445SDouglas Gilbert 		     + cmd[13]);
1143c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
1144c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
1145c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
1146c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
1147c65b1445SDouglas Gilbert 	} else
1148c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
1149c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1150c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1151c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1152c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1153c65b1445SDouglas Gilbert 	arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
1154c65b1445SDouglas Gilbert 	arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
1155c65b1445SDouglas Gilbert 	arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
1156c65b1445SDouglas Gilbert 	arr[11] = SECT_SIZE_PER(target) & 0xff;
1157c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1158c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1159c65b1445SDouglas Gilbert }
1160c65b1445SDouglas Gilbert 
11615a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
11625a09e398SHannes Reinecke 
11635a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
11645a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
11655a09e398SHannes Reinecke {
11665a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
11675a09e398SHannes Reinecke 	unsigned char * arr;
11685a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
11695a09e398SHannes Reinecke 	int n, ret, alen, rlen;
11705a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
11715a09e398SHannes Reinecke 
11725a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
11735a09e398SHannes Reinecke 		+ cmd[9]);
11745a09e398SHannes Reinecke 
11756f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
11766f3cbf55SDouglas Gilbert 	if (! arr)
11776f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
11785a09e398SHannes Reinecke 	/*
11795a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
11805a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
11815a09e398SHannes Reinecke 	 * So we create two port groups with one port each
11825a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
11835a09e398SHannes Reinecke 	 */
11845a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
11855a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
11865a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
11875a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
11885a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
11895a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
11905a09e398SHannes Reinecke 
11915a09e398SHannes Reinecke 	/*
11925a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
11935a09e398SHannes Reinecke 	 */
11945a09e398SHannes Reinecke 	n = 4;
11955a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
11965a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
11975a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
11985a09e398SHannes Reinecke 	} else {
11995a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
12005a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
12015a09e398SHannes Reinecke 	}
12025a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
12035a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
12045a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12055a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12065a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12075a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12085a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12095a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12105a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
12115a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
12125a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
12135a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
12145a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
12155a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
12165a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12175a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
12185a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
12195a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
12205a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12215a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
12225a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
12235a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
12245a09e398SHannes Reinecke 
12255a09e398SHannes Reinecke 	rlen = n - 4;
12265a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
12275a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
12285a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
12295a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
12305a09e398SHannes Reinecke 
12315a09e398SHannes Reinecke 	/*
12325a09e398SHannes Reinecke 	 * Return the smallest value of either
12335a09e398SHannes Reinecke 	 * - The allocated length
12345a09e398SHannes Reinecke 	 * - The constructed command length
12355a09e398SHannes Reinecke 	 * - The maximum array size
12365a09e398SHannes Reinecke 	 */
12375a09e398SHannes Reinecke 	rlen = min(alen,n);
12385a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
12395a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
12405a09e398SHannes Reinecke 	kfree(arr);
12415a09e398SHannes Reinecke 	return ret;
12425a09e398SHannes Reinecke }
12435a09e398SHannes Reinecke 
12441da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
12471da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
12481da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
12491da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
12501da177e4SLinus Torvalds 
12511da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
12521da177e4SLinus Torvalds 	if (1 == pcontrol)
12531da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
12541da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
12551da177e4SLinus Torvalds }
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
12581da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
12591da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
12601da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
12611da177e4SLinus Torvalds 
12621da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
12631da177e4SLinus Torvalds 	if (1 == pcontrol)
12641da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
12651da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
12661da177e4SLinus Torvalds }
12671da177e4SLinus Torvalds 
12681da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
12691da177e4SLinus Torvalds {       /* Format device page for mode_sense */
12701da177e4SLinus Torvalds         unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
12711da177e4SLinus Torvalds                                      0, 0, 0, 0, 0, 0, 0, 0,
12721da177e4SLinus Torvalds                                      0, 0, 0, 0, 0x40, 0, 0, 0};
12731da177e4SLinus Torvalds 
12741da177e4SLinus Torvalds         memcpy(p, format_pg, sizeof(format_pg));
12751da177e4SLinus Torvalds         p[10] = (sdebug_sectors_per >> 8) & 0xff;
12761da177e4SLinus Torvalds         p[11] = sdebug_sectors_per & 0xff;
12771da177e4SLinus Torvalds         p[12] = (SECT_SIZE >> 8) & 0xff;
12781da177e4SLinus Torvalds         p[13] = SECT_SIZE & 0xff;
12791da177e4SLinus Torvalds         if (DEV_REMOVEABLE(target))
12801da177e4SLinus Torvalds                 p[20] |= 0x20; /* should agree with INQUIRY */
12811da177e4SLinus Torvalds         if (1 == pcontrol)
12821da177e4SLinus Torvalds                 memset(p + 2, 0, sizeof(format_pg) - 2);
12831da177e4SLinus Torvalds         return sizeof(format_pg);
12841da177e4SLinus Torvalds }
12851da177e4SLinus Torvalds 
12861da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
12871da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
12881da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
12891da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
12921da177e4SLinus Torvalds 	if (1 == pcontrol)
12931da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
12941da177e4SLinus Torvalds 	return sizeof(caching_pg);
12951da177e4SLinus Torvalds }
12961da177e4SLinus Torvalds 
12971da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
12981da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1299c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1300c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1301c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
13021da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	if (scsi_debug_dsense)
13051da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1306c65b1445SDouglas Gilbert 	else
1307c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
13081da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
13091da177e4SLinus Torvalds 	if (1 == pcontrol)
1310c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1311c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1312c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
13131da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
13141da177e4SLinus Torvalds }
13151da177e4SLinus Torvalds 
1316c65b1445SDouglas Gilbert 
13171da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
13181da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1319c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
13201da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1321c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1322c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1323c65b1445SDouglas Gilbert 
13241da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
13251da177e4SLinus Torvalds 	if (1 == pcontrol)
1326c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1327c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1328c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
13291da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
13301da177e4SLinus Torvalds }
13311da177e4SLinus Torvalds 
1332c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1333c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1334c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1335c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1336c65b1445SDouglas Gilbert 
1337c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1338c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1339c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1340c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1341c65b1445SDouglas Gilbert }
1342c65b1445SDouglas Gilbert 
1343c65b1445SDouglas Gilbert 
1344c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1345c65b1445SDouglas Gilbert 			      int target_dev_id)
1346c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1347c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1348c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1349c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1350c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1351c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1352c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1353c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1354c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1355c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1356c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1357c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1358c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1359c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1360c65b1445SDouglas Gilbert 		};
1361c65b1445SDouglas Gilbert 	int port_a, port_b;
1362c65b1445SDouglas Gilbert 
1363c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1364c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1365c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1366c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1367c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1368c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1369c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1370c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1371c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1372c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1373c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1374c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1375c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1376c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1377c65b1445SDouglas Gilbert }
1378c65b1445SDouglas Gilbert 
1379c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1380c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1381c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1382c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1383c65b1445SDouglas Gilbert 		};
1384c65b1445SDouglas Gilbert 
1385c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1386c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1387c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1388c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1389c65b1445SDouglas Gilbert }
1390c65b1445SDouglas Gilbert 
13911da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
13921da177e4SLinus Torvalds 
13931da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
13941da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
13951da177e4SLinus Torvalds {
139623183910SDouglas Gilbert 	unsigned char dbd, llbaa;
139723183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
13981da177e4SLinus Torvalds 	unsigned char dev_spec;
139923183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
14001da177e4SLinus Torvalds 	unsigned char * ap;
14011da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
14021da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
14031da177e4SLinus Torvalds 
1404c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
14051da177e4SLinus Torvalds 		return errsts;
140623183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
14071da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
14081da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
14091da177e4SLinus Torvalds 	subpcode = cmd[3];
14101da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
141123183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
141223183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
141323183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
141423183910SDouglas Gilbert 	else
141523183910SDouglas Gilbert 		bd_len = 0;
14161da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
14171da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
14181da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
14191da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
14201da177e4SLinus Torvalds 			       	0);
14211da177e4SLinus Torvalds 		return check_condition_result;
14221da177e4SLinus Torvalds 	}
1423c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1424c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
142523183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
142623183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
142723183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
142823183910SDouglas Gilbert 	else
142923183910SDouglas Gilbert 		dev_spec = 0x0;
14301da177e4SLinus Torvalds 	if (msense_6) {
14311da177e4SLinus Torvalds 		arr[2] = dev_spec;
143223183910SDouglas Gilbert 		arr[3] = bd_len;
14331da177e4SLinus Torvalds 		offset = 4;
14341da177e4SLinus Torvalds 	} else {
14351da177e4SLinus Torvalds 		arr[3] = dev_spec;
143623183910SDouglas Gilbert 		if (16 == bd_len)
143723183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
143823183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
14391da177e4SLinus Torvalds 		offset = 8;
14401da177e4SLinus Torvalds 	}
14411da177e4SLinus Torvalds 	ap = arr + offset;
144223183910SDouglas Gilbert 	if ((bd_len > 0) && (0 == sdebug_capacity)) {
144323183910SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
144423183910SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
144523183910SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
144623183910SDouglas Gilbert 		} else
144723183910SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
144823183910SDouglas Gilbert 	}
144923183910SDouglas Gilbert 	if (8 == bd_len) {
145023183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
145123183910SDouglas Gilbert 			ap[0] = 0xff;
145223183910SDouglas Gilbert 			ap[1] = 0xff;
145323183910SDouglas Gilbert 			ap[2] = 0xff;
145423183910SDouglas Gilbert 			ap[3] = 0xff;
145523183910SDouglas Gilbert 		} else {
145623183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
145723183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
145823183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
145923183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
146023183910SDouglas Gilbert 		}
146123183910SDouglas Gilbert         	ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
146223183910SDouglas Gilbert         	ap[7] = SECT_SIZE_PER(target) & 0xff;
146323183910SDouglas Gilbert 		offset += bd_len;
146423183910SDouglas Gilbert 		ap = arr + offset;
146523183910SDouglas Gilbert 	} else if (16 == bd_len) {
146623183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
146723183910SDouglas Gilbert 
146823183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
146923183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
147023183910SDouglas Gilbert         	ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
147123183910SDouglas Gilbert         	ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
147223183910SDouglas Gilbert         	ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
147323183910SDouglas Gilbert         	ap[15] = SECT_SIZE_PER(target) & 0xff;
147423183910SDouglas Gilbert 		offset += bd_len;
147523183910SDouglas Gilbert 		ap = arr + offset;
147623183910SDouglas Gilbert 	}
14771da177e4SLinus Torvalds 
1478c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1479c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
14801da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14811da177e4SLinus Torvalds 			       	0);
14821da177e4SLinus Torvalds 		return check_condition_result;
14831da177e4SLinus Torvalds 	}
14841da177e4SLinus Torvalds 	switch (pcode) {
14851da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
14861da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
14871da177e4SLinus Torvalds 		offset += len;
14881da177e4SLinus Torvalds 		break;
14891da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
14901da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
14911da177e4SLinus Torvalds 		offset += len;
14921da177e4SLinus Torvalds 		break;
14931da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
14941da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
14951da177e4SLinus Torvalds                 offset += len;
14961da177e4SLinus Torvalds                 break;
14971da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
14981da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
14991da177e4SLinus Torvalds 		offset += len;
15001da177e4SLinus Torvalds 		break;
15011da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
15021da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
15031da177e4SLinus Torvalds 		offset += len;
15041da177e4SLinus Torvalds 		break;
1505c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1506c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1507c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1508c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1509c65b1445SDouglas Gilbert 			return check_condition_result;
1510c65b1445SDouglas Gilbert 	        }
1511c65b1445SDouglas Gilbert 		len = 0;
1512c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1513c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1514c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1515c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1516c65b1445SDouglas Gilbert 						  target_dev_id);
1517c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1518c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1519c65b1445SDouglas Gilbert 		offset += len;
1520c65b1445SDouglas Gilbert 		break;
15211da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
15221da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
15231da177e4SLinus Torvalds 		offset += len;
15241da177e4SLinus Torvalds 		break;
15251da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1526c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
15271da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
15281da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
15291da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
15301da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
15311da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1532c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1533c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1534c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1535c65b1445SDouglas Gilbert 						  target, target_dev_id);
1536c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1537c65b1445SDouglas Gilbert 			}
15381da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1539c65b1445SDouglas Gilbert 		} else {
1540c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1541c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1542c65b1445SDouglas Gilbert 			return check_condition_result;
1543c65b1445SDouglas Gilbert                 }
15441da177e4SLinus Torvalds 		offset += len;
15451da177e4SLinus Torvalds 		break;
15461da177e4SLinus Torvalds 	default:
15471da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
15481da177e4SLinus Torvalds 			       	0);
15491da177e4SLinus Torvalds 		return check_condition_result;
15501da177e4SLinus Torvalds 	}
15511da177e4SLinus Torvalds 	if (msense_6)
15521da177e4SLinus Torvalds 		arr[0] = offset - 1;
15531da177e4SLinus Torvalds 	else {
15541da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
15551da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
15561da177e4SLinus Torvalds 	}
15571da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
15581da177e4SLinus Torvalds }
15591da177e4SLinus Torvalds 
1560c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1561c65b1445SDouglas Gilbert 
1562c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1563c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1564c65b1445SDouglas Gilbert {
1565c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1566c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1567c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1568c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1569c65b1445SDouglas Gilbert 
1570c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1571c65b1445SDouglas Gilbert 		return errsts;
1572c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1573c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1574c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1575c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1576c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1577c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1578c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1579c65b1445SDouglas Gilbert 		return check_condition_result;
1580c65b1445SDouglas Gilbert 	}
1581c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1582c65b1445SDouglas Gilbert         if (-1 == res)
1583c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1584c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1585c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1586c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1587c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1588c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1589c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
159023183910SDouglas Gilbert 	if (md_len > 2) {
1591c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1592c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1593c65b1445SDouglas Gilbert 		return check_condition_result;
1594c65b1445SDouglas Gilbert 	}
1595c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1596c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1597c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1598c65b1445SDouglas Gilbert 	if (ps) {
1599c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1600c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1601c65b1445SDouglas Gilbert 		return check_condition_result;
1602c65b1445SDouglas Gilbert 	}
1603c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1604c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1605c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1606c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1607c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1608c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1609c65b1445SDouglas Gilbert 		return check_condition_result;
1610c65b1445SDouglas Gilbert 	}
1611c65b1445SDouglas Gilbert 	switch (mpage) {
1612c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1613c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1614c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1615c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1616c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1617c65b1445SDouglas Gilbert 			return 0;
1618c65b1445SDouglas Gilbert 		}
1619c65b1445SDouglas Gilbert 		break;
1620c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1621c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1622c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1623c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1624c65b1445SDouglas Gilbert 			return 0;
1625c65b1445SDouglas Gilbert 		}
1626c65b1445SDouglas Gilbert 		break;
1627c65b1445SDouglas Gilbert 	default:
1628c65b1445SDouglas Gilbert 		break;
1629c65b1445SDouglas Gilbert 	}
1630c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1631c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1632c65b1445SDouglas Gilbert 	return check_condition_result;
1633c65b1445SDouglas Gilbert }
1634c65b1445SDouglas Gilbert 
1635c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1636c65b1445SDouglas Gilbert {
1637c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1638c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1639c65b1445SDouglas Gilbert 		};
1640c65b1445SDouglas Gilbert 
1641c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1642c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1643c65b1445SDouglas Gilbert }
1644c65b1445SDouglas Gilbert 
1645c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1646c65b1445SDouglas Gilbert {
1647c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1648c65b1445SDouglas Gilbert 		};
1649c65b1445SDouglas Gilbert 
1650c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1651c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1652c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1653c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1654c65b1445SDouglas Gilbert 	}
1655c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1656c65b1445SDouglas Gilbert }
1657c65b1445SDouglas Gilbert 
1658c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1659c65b1445SDouglas Gilbert 
1660c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1661c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1662c65b1445SDouglas Gilbert {
166323183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1664c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1665c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1666c65b1445SDouglas Gilbert 
1667c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1668c65b1445SDouglas Gilbert 		return errsts;
1669c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1670c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1671c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1672c65b1445SDouglas Gilbert 	if (ppc || sp) {
1673c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1674c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1675c65b1445SDouglas Gilbert 		return check_condition_result;
1676c65b1445SDouglas Gilbert 	}
1677c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1678c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
167923183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1680c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1681c65b1445SDouglas Gilbert 	arr[0] = pcode;
168223183910SDouglas Gilbert 	if (0 == subpcode) {
1683c65b1445SDouglas Gilbert 		switch (pcode) {
1684c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1685c65b1445SDouglas Gilbert 			n = 4;
1686c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1687c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1688c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1689c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1690c65b1445SDouglas Gilbert 			break;
1691c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1692c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1693c65b1445SDouglas Gilbert 			break;
1694c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1695c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1696c65b1445SDouglas Gilbert 			break;
1697c65b1445SDouglas Gilbert 		default:
1698c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1699c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1700c65b1445SDouglas Gilbert 			return check_condition_result;
1701c65b1445SDouglas Gilbert 		}
170223183910SDouglas Gilbert 	} else if (0xff == subpcode) {
170323183910SDouglas Gilbert 		arr[0] |= 0x40;
170423183910SDouglas Gilbert 		arr[1] = subpcode;
170523183910SDouglas Gilbert 		switch (pcode) {
170623183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
170723183910SDouglas Gilbert 			n = 4;
170823183910SDouglas Gilbert 			arr[n++] = 0x0;
170923183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
171023183910SDouglas Gilbert 			arr[n++] = 0x0;
171123183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
171223183910SDouglas Gilbert 			arr[n++] = 0xd;
171323183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
171423183910SDouglas Gilbert 			arr[n++] = 0x2f;
171523183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
171623183910SDouglas Gilbert 			arr[3] = n - 4;
171723183910SDouglas Gilbert 			break;
171823183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
171923183910SDouglas Gilbert 			n = 4;
172023183910SDouglas Gilbert 			arr[n++] = 0xd;
172123183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
172223183910SDouglas Gilbert 			arr[3] = n - 4;
172323183910SDouglas Gilbert 			break;
172423183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
172523183910SDouglas Gilbert 			n = 4;
172623183910SDouglas Gilbert 			arr[n++] = 0x2f;
172723183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
172823183910SDouglas Gilbert 			arr[3] = n - 4;
172923183910SDouglas Gilbert 			break;
173023183910SDouglas Gilbert 		default:
173123183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
173223183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
173323183910SDouglas Gilbert 			return check_condition_result;
173423183910SDouglas Gilbert 		}
173523183910SDouglas Gilbert 	} else {
173623183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
173723183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
173823183910SDouglas Gilbert 		return check_condition_result;
173923183910SDouglas Gilbert 	}
1740c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1741c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1742c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1743c65b1445SDouglas Gilbert }
1744c65b1445SDouglas Gilbert 
1745c65b1445SDouglas Gilbert static int resp_read(struct scsi_cmnd * SCpnt, unsigned long long lba,
1746c65b1445SDouglas Gilbert 		     unsigned int num, struct sdebug_dev_info * devip)
17471da177e4SLinus Torvalds {
17481da177e4SLinus Torvalds 	unsigned long iflags;
1749c65b1445SDouglas Gilbert 	unsigned int block, from_bottom;
1750c65b1445SDouglas Gilbert 	unsigned long long u;
17511da177e4SLinus Torvalds 	int ret;
17521da177e4SLinus Torvalds 
1753c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
17541da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
17551da177e4SLinus Torvalds 				0);
17561da177e4SLinus Torvalds 		return check_condition_result;
17571da177e4SLinus Torvalds 	}
1758c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1759c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1760c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1761c65b1445SDouglas Gilbert 				0);
1762c65b1445SDouglas Gilbert 		return check_condition_result;
1763c65b1445SDouglas Gilbert 	}
17641da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1765c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1766c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1767c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
17681da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
17691da177e4SLinus Torvalds 				0);
1770c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1771c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1772c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1773c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1774c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1775c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1776c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1777c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1778c65b1445SDouglas Gilbert 		}
17791da177e4SLinus Torvalds 		return check_condition_result;
17801da177e4SLinus Torvalds 	}
17811da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
1782c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1783c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1784c65b1445SDouglas Gilbert 					   fake_storep + (lba * SECT_SIZE),
17851da177e4SLinus Torvalds 			   		   num * SECT_SIZE);
1786c65b1445SDouglas Gilbert 	else {
1787c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1788c65b1445SDouglas Gilbert 		u = lba;
1789c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1790c65b1445SDouglas Gilbert 		from_bottom = 0;
1791c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1792c65b1445SDouglas Gilbert 			from_bottom = (block + num) - sdebug_store_sectors;
1793c65b1445SDouglas Gilbert 		ret = fill_from_dev_buffer(SCpnt,
1794c65b1445SDouglas Gilbert 					   fake_storep + (block * SECT_SIZE),
1795c65b1445SDouglas Gilbert 			   		   (num - from_bottom) * SECT_SIZE);
1796c65b1445SDouglas Gilbert 		if ((0 == ret) && (from_bottom > 0))
1797c65b1445SDouglas Gilbert 			ret = fill_from_dev_buffer(SCpnt, fake_storep,
1798c65b1445SDouglas Gilbert 						   from_bottom * SECT_SIZE);
1799c65b1445SDouglas Gilbert 	}
18001da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18011da177e4SLinus Torvalds 	return ret;
18021da177e4SLinus Torvalds }
18031da177e4SLinus Torvalds 
1804c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd * SCpnt, unsigned long long lba,
1805c65b1445SDouglas Gilbert 		      unsigned int num, struct sdebug_dev_info * devip)
18061da177e4SLinus Torvalds {
18071da177e4SLinus Torvalds 	unsigned long iflags;
1808c65b1445SDouglas Gilbert 	unsigned int block, to_bottom;
1809c65b1445SDouglas Gilbert 	unsigned long long u;
18101da177e4SLinus Torvalds 	int res;
18111da177e4SLinus Torvalds 
1812c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
18131da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE,
18141da177e4SLinus Torvalds 			       	0);
18151da177e4SLinus Torvalds 		return check_condition_result;
18161da177e4SLinus Torvalds 	}
1817c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1818c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
1819c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
1820c65b1445SDouglas Gilbert 				0);
1821c65b1445SDouglas Gilbert 		return check_condition_result;
1822c65b1445SDouglas Gilbert 	}
18231da177e4SLinus Torvalds 
18241da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
1825c65b1445SDouglas Gilbert 	if ((lba + num) <= sdebug_store_sectors)
1826c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1827c65b1445SDouglas Gilbert 					  fake_storep + (lba * SECT_SIZE),
18281da177e4SLinus Torvalds 			   		  num * SECT_SIZE);
1829c65b1445SDouglas Gilbert 	else {
1830c65b1445SDouglas Gilbert 		/* modulo when one arg is 64 bits needs do_div() */
1831c65b1445SDouglas Gilbert 		u = lba;
1832c65b1445SDouglas Gilbert 		block = do_div(u, sdebug_store_sectors);
1833c65b1445SDouglas Gilbert 		to_bottom = 0;
1834c65b1445SDouglas Gilbert 		if ((block + num) > sdebug_store_sectors)
1835c65b1445SDouglas Gilbert 			to_bottom = (block + num) - sdebug_store_sectors;
1836c65b1445SDouglas Gilbert 		res = fetch_to_dev_buffer(SCpnt,
1837c65b1445SDouglas Gilbert 					  fake_storep + (block * SECT_SIZE),
1838c65b1445SDouglas Gilbert 			   		  (num - to_bottom) * SECT_SIZE);
1839c65b1445SDouglas Gilbert 		if ((0 == res) && (to_bottom > 0))
1840c65b1445SDouglas Gilbert 			res = fetch_to_dev_buffer(SCpnt, fake_storep,
1841c65b1445SDouglas Gilbert 						  to_bottom * SECT_SIZE);
1842c65b1445SDouglas Gilbert 	}
18431da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
18441da177e4SLinus Torvalds 	if (-1 == res)
18451da177e4SLinus Torvalds 		return (DID_ERROR << 16);
18461da177e4SLinus Torvalds 	else if ((res < (num * SECT_SIZE)) &&
18471da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1848c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
18491da177e4SLinus Torvalds 		       " IO sent=%d bytes\n", num * SECT_SIZE, res);
18501da177e4SLinus Torvalds 	return 0;
18511da177e4SLinus Torvalds }
18521da177e4SLinus Torvalds 
1853c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
18541da177e4SLinus Torvalds 
18551da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
18561da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
18571da177e4SLinus Torvalds {
18581da177e4SLinus Torvalds 	unsigned int alloc_len;
1859c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
18601da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
18611da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
18621da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
18631da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
1864c65b1445SDouglas Gilbert 	unsigned char * max_addr;
18651da177e4SLinus Torvalds 
18661da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
1867c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
18681da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
18691da177e4SLinus Torvalds 			       	0);
18701da177e4SLinus Torvalds 		return check_condition_result;
18711da177e4SLinus Torvalds 	}
18721da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
18731da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
18741da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
1875c65b1445SDouglas Gilbert 	if (1 == select_report)
1876c65b1445SDouglas Gilbert 		lun_cnt = 0;
1877c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
1878c65b1445SDouglas Gilbert 		--lun_cnt;
1879c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
1880c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
1881c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
1882c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
1883c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
1884c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
1885c65b1445SDouglas Gilbert 	if (n < num) {
1886c65b1445SDouglas Gilbert 		wlun = 0;
1887c65b1445SDouglas Gilbert 		lun_cnt = n;
1888c65b1445SDouglas Gilbert 	}
18891da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
1890c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
1891c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
1892c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
1893c65b1445SDouglas Gilbert 	     i++, lun++) {
1894c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
18951da177e4SLinus Torvalds 		if (upper)
18961da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
18971da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
1898c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
18991da177e4SLinus Torvalds 	}
1900c65b1445SDouglas Gilbert 	if (wlun) {
1901c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
1902c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
1903c65b1445SDouglas Gilbert 		i++;
1904c65b1445SDouglas Gilbert 	}
1905c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
19061da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
19071da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
19081da177e4SLinus Torvalds }
19091da177e4SLinus Torvalds 
1910c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
1911c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip)
1912c639d14eSFUJITA Tomonori {
1913c639d14eSFUJITA Tomonori 	int i, j, ret = -1;
1914c639d14eSFUJITA Tomonori 	unsigned char *kaddr, *buf;
1915c639d14eSFUJITA Tomonori 	unsigned int offset;
1916c639d14eSFUJITA Tomonori 	struct scatterlist *sg;
1917c639d14eSFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
1918c639d14eSFUJITA Tomonori 
1919c639d14eSFUJITA Tomonori 	/* better not to use temporary buffer. */
1920c639d14eSFUJITA Tomonori 	buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
1921c639d14eSFUJITA Tomonori 	if (!buf)
1922c639d14eSFUJITA Tomonori 		return ret;
1923c639d14eSFUJITA Tomonori 
192421a61829SFUJITA Tomonori 	scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp));
1925c639d14eSFUJITA Tomonori 
1926c639d14eSFUJITA Tomonori 	offset = 0;
1927c639d14eSFUJITA Tomonori 	for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
1928c639d14eSFUJITA Tomonori 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
1929c639d14eSFUJITA Tomonori 		if (!kaddr)
1930c639d14eSFUJITA Tomonori 			goto out;
1931c639d14eSFUJITA Tomonori 
1932c639d14eSFUJITA Tomonori 		for (j = 0; j < sg->length; j++)
1933c639d14eSFUJITA Tomonori 			*(kaddr + sg->offset + j) ^= *(buf + offset + j);
1934c639d14eSFUJITA Tomonori 
1935c639d14eSFUJITA Tomonori 		offset += sg->length;
1936c639d14eSFUJITA Tomonori 		kunmap_atomic(kaddr, KM_USER0);
1937c639d14eSFUJITA Tomonori 	}
1938c639d14eSFUJITA Tomonori 	ret = 0;
1939c639d14eSFUJITA Tomonori out:
1940c639d14eSFUJITA Tomonori 	kfree(buf);
1941c639d14eSFUJITA Tomonori 
1942c639d14eSFUJITA Tomonori 	return ret;
1943c639d14eSFUJITA Tomonori }
1944c639d14eSFUJITA Tomonori 
19451da177e4SLinus Torvalds /* When timer goes off this function is called. */
19461da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
19471da177e4SLinus Torvalds {
19481da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
19491da177e4SLinus Torvalds 	unsigned long iflags;
19501da177e4SLinus Torvalds 
19511da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
19521da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
19531da177e4SLinus Torvalds 		       "large\n");
19541da177e4SLinus Torvalds 		return;
19551da177e4SLinus Torvalds 	}
19561da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
19571da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
19581da177e4SLinus Torvalds 	if (! sqcp->in_use) {
19591da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
19601da177e4SLinus Torvalds 		       "interrupt\n");
19611da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
19621da177e4SLinus Torvalds 		return;
19631da177e4SLinus Torvalds 	}
19641da177e4SLinus Torvalds 	sqcp->in_use = 0;
19651da177e4SLinus Torvalds 	if (sqcp->done_funct) {
19661da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
19671da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
19681da177e4SLinus Torvalds 	}
19691da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
19701da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
19711da177e4SLinus Torvalds }
19721da177e4SLinus Torvalds 
19731da177e4SLinus Torvalds static int scsi_debug_slave_alloc(struct scsi_device * sdp)
19741da177e4SLinus Torvalds {
19751da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1976c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
1977c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
1978c639d14eSFUJITA Tomonori 	set_bit(QUEUE_FLAG_BIDI, &sdp->request_queue->queue_flags);
19791da177e4SLinus Torvalds 	return 0;
19801da177e4SLinus Torvalds }
19811da177e4SLinus Torvalds 
19821da177e4SLinus Torvalds static int scsi_debug_slave_configure(struct scsi_device * sdp)
19831da177e4SLinus Torvalds {
19841da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
19851da177e4SLinus Torvalds 
19861da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
1987c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
1988c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
19891da177e4SLinus Torvalds 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
19901da177e4SLinus Torvalds 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
19911da177e4SLinus Torvalds 	devip = devInfoReg(sdp);
19926f3cbf55SDouglas Gilbert 	if (NULL == devip)
19936f3cbf55SDouglas Gilbert 		return 1;	/* no resources, will be marked offline */
19941da177e4SLinus Torvalds 	sdp->hostdata = devip;
19951da177e4SLinus Torvalds 	if (sdp->host->cmd_per_lun)
19961da177e4SLinus Torvalds 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
19971da177e4SLinus Torvalds 					sdp->host->cmd_per_lun);
1998c65b1445SDouglas Gilbert 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
19991da177e4SLinus Torvalds 	return 0;
20001da177e4SLinus Torvalds }
20011da177e4SLinus Torvalds 
20021da177e4SLinus Torvalds static void scsi_debug_slave_destroy(struct scsi_device * sdp)
20031da177e4SLinus Torvalds {
20041da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20051da177e4SLinus Torvalds 				(struct sdebug_dev_info *)sdp->hostdata;
20061da177e4SLinus Torvalds 
20071da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
2008c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
2009c65b1445SDouglas Gilbert 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
20101da177e4SLinus Torvalds 	if (devip) {
20111da177e4SLinus Torvalds 		/* make this slot avaliable for re-use */
20121da177e4SLinus Torvalds 		devip->used = 0;
20131da177e4SLinus Torvalds 		sdp->hostdata = NULL;
20141da177e4SLinus Torvalds 	}
20151da177e4SLinus Torvalds }
20161da177e4SLinus Torvalds 
20171da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
20181da177e4SLinus Torvalds {
20191da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
20201da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
20211da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
20221da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds 	if (devip)
20251da177e4SLinus Torvalds 		return devip;
2026d1e4c9c5SFUJITA Tomonori 	sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host);
20271da177e4SLinus Torvalds 	if (!sdbg_host) {
20281da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
20291da177e4SLinus Torvalds 		return NULL;
20301da177e4SLinus Torvalds         }
20311da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
20321da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
20331da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
20341da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
20351da177e4SLinus Torvalds                         return devip;
20361da177e4SLinus Torvalds 		else {
20371da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
20381da177e4SLinus Torvalds 				open_devip = devip;
20391da177e4SLinus Torvalds 		}
20401da177e4SLinus Torvalds 	}
20411da177e4SLinus Torvalds 	if (NULL == open_devip) { /* try and make a new one */
20426f3cbf55SDouglas Gilbert 		open_devip = kzalloc(sizeof(*open_devip),GFP_ATOMIC);
20431da177e4SLinus Torvalds 		if (NULL == open_devip) {
20441da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
20451da177e4SLinus Torvalds 				__FUNCTION__, __LINE__);
20461da177e4SLinus Torvalds 			return NULL;
20471da177e4SLinus Torvalds 		}
20481da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20491da177e4SLinus Torvalds 		list_add_tail(&open_devip->dev_list,
20501da177e4SLinus Torvalds 		&sdbg_host->dev_info_list);
20511da177e4SLinus Torvalds 	}
20521da177e4SLinus Torvalds         if (open_devip) {
20531da177e4SLinus Torvalds 		open_devip->channel = sdev->channel;
20541da177e4SLinus Torvalds 		open_devip->target = sdev->id;
20551da177e4SLinus Torvalds 		open_devip->lun = sdev->lun;
20561da177e4SLinus Torvalds 		open_devip->sdbg_host = sdbg_host;
20571da177e4SLinus Torvalds 		open_devip->reset = 1;
20581da177e4SLinus Torvalds 		open_devip->used = 1;
20591da177e4SLinus Torvalds 		memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
20601da177e4SLinus Torvalds 		if (scsi_debug_dsense)
20611da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x72;
20621da177e4SLinus Torvalds 		else {
20631da177e4SLinus Torvalds 			open_devip->sense_buff[0] = 0x70;
20641da177e4SLinus Torvalds 			open_devip->sense_buff[7] = 0xa;
20651da177e4SLinus Torvalds 		}
2066c65b1445SDouglas Gilbert 		if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2067c65b1445SDouglas Gilbert 			open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
20681da177e4SLinus Torvalds 		return open_devip;
20691da177e4SLinus Torvalds         }
20701da177e4SLinus Torvalds         return NULL;
20711da177e4SLinus Torvalds }
20721da177e4SLinus Torvalds 
20731da177e4SLinus Torvalds static void mk_sense_buffer(struct sdebug_dev_info * devip, int key,
20741da177e4SLinus Torvalds 			    int asc, int asq)
20751da177e4SLinus Torvalds {
20761da177e4SLinus Torvalds 	unsigned char * sbuff;
20771da177e4SLinus Torvalds 
20781da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
20791da177e4SLinus Torvalds 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
20801da177e4SLinus Torvalds 	if (scsi_debug_dsense) {
20811da177e4SLinus Torvalds 		sbuff[0] = 0x72;  /* descriptor, current */
20821da177e4SLinus Torvalds 		sbuff[1] = key;
20831da177e4SLinus Torvalds 		sbuff[2] = asc;
20841da177e4SLinus Torvalds 		sbuff[3] = asq;
20851da177e4SLinus Torvalds 	} else {
20861da177e4SLinus Torvalds 		sbuff[0] = 0x70;  /* fixed, current */
20871da177e4SLinus Torvalds 		sbuff[2] = key;
20881da177e4SLinus Torvalds 		sbuff[7] = 0xa;	  /* implies 18 byte sense buffer */
20891da177e4SLinus Torvalds 		sbuff[12] = asc;
20901da177e4SLinus Torvalds 		sbuff[13] = asq;
20911da177e4SLinus Torvalds 	}
20921da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
20931da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
20941da177e4SLinus Torvalds 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
20951da177e4SLinus Torvalds }
20961da177e4SLinus Torvalds 
20971da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
20981da177e4SLinus Torvalds {
20991da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21001da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
21011da177e4SLinus Torvalds 	++num_aborts;
21021da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
21031da177e4SLinus Torvalds 	return SUCCESS;
21041da177e4SLinus Torvalds }
21051da177e4SLinus Torvalds 
21061da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
21071da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
21081da177e4SLinus Torvalds {
21091da177e4SLinus Torvalds 	int res;
21101da177e4SLinus Torvalds 	unsigned char *buf;
21111da177e4SLinus Torvalds 
21121da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21131da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
21141da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
21151da177e4SLinus Torvalds 	if (buf) {
21161da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
21171da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
21181da177e4SLinus Torvalds 		kfree(buf);
21191da177e4SLinus Torvalds 		if (! res)
21201da177e4SLinus Torvalds 			return res;
21211da177e4SLinus Torvalds 	}
21221da177e4SLinus Torvalds 	info[0] = sdebug_heads;
21231da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
21241da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
21251da177e4SLinus Torvalds 	return 0;
21261da177e4SLinus Torvalds }
21271da177e4SLinus Torvalds 
21281da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
21291da177e4SLinus Torvalds {
21301da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
21311da177e4SLinus Torvalds 
21321da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21331da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
21341da177e4SLinus Torvalds 	++num_dev_resets;
21351da177e4SLinus Torvalds 	if (SCpnt) {
21361da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
21371da177e4SLinus Torvalds 		if (devip)
21381da177e4SLinus Torvalds 			devip->reset = 1;
21391da177e4SLinus Torvalds 	}
21401da177e4SLinus Torvalds 	return SUCCESS;
21411da177e4SLinus Torvalds }
21421da177e4SLinus Torvalds 
21431da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
21441da177e4SLinus Torvalds {
21451da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
21461da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21471da177e4SLinus Torvalds         struct scsi_device * sdp;
21481da177e4SLinus Torvalds         struct Scsi_Host * hp;
21491da177e4SLinus Torvalds 
21501da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21511da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
21521da177e4SLinus Torvalds 	++num_bus_resets;
21531da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
2154d1e4c9c5SFUJITA Tomonori 		sdbg_host = *(struct sdebug_host_info **)shost_priv(hp);
21551da177e4SLinus Torvalds 		if (sdbg_host) {
21561da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
21571da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
21581da177e4SLinus Torvalds                                             dev_list)
21591da177e4SLinus Torvalds 				dev_info->reset = 1;
21601da177e4SLinus Torvalds 		}
21611da177e4SLinus Torvalds 	}
21621da177e4SLinus Torvalds 	return SUCCESS;
21631da177e4SLinus Torvalds }
21641da177e4SLinus Torvalds 
21651da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
21661da177e4SLinus Torvalds {
21671da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
21681da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
21711da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
21721da177e4SLinus Torvalds 	++num_host_resets;
21731da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
21741da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
21751da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
21761da177e4SLinus Torvalds                                     dev_list)
21771da177e4SLinus Torvalds                         dev_info->reset = 1;
21781da177e4SLinus Torvalds         }
21791da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
21801da177e4SLinus Torvalds 	stop_all_queued();
21811da177e4SLinus Torvalds 	return SUCCESS;
21821da177e4SLinus Torvalds }
21831da177e4SLinus Torvalds 
21841da177e4SLinus Torvalds /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
21851da177e4SLinus Torvalds static int stop_queued_cmnd(struct scsi_cmnd * cmnd)
21861da177e4SLinus Torvalds {
21871da177e4SLinus Torvalds 	unsigned long iflags;
21881da177e4SLinus Torvalds 	int k;
21891da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
21901da177e4SLinus Torvalds 
21911da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
21921da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
21931da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
21941da177e4SLinus Torvalds 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
21951da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
21961da177e4SLinus Torvalds 			sqcp->in_use = 0;
21971da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
21981da177e4SLinus Torvalds 			break;
21991da177e4SLinus Torvalds 		}
22001da177e4SLinus Torvalds 	}
22011da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22021da177e4SLinus Torvalds 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
22031da177e4SLinus Torvalds }
22041da177e4SLinus Torvalds 
22051da177e4SLinus Torvalds /* Deletes (stops) timers of all queued commands */
22061da177e4SLinus Torvalds static void stop_all_queued(void)
22071da177e4SLinus Torvalds {
22081da177e4SLinus Torvalds 	unsigned long iflags;
22091da177e4SLinus Torvalds 	int k;
22101da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22111da177e4SLinus Torvalds 
22121da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22131da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22141da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22151da177e4SLinus Torvalds 		if (sqcp->in_use && sqcp->a_cmnd) {
22161da177e4SLinus Torvalds 			del_timer_sync(&sqcp->cmnd_timer);
22171da177e4SLinus Torvalds 			sqcp->in_use = 0;
22181da177e4SLinus Torvalds 			sqcp->a_cmnd = NULL;
22191da177e4SLinus Torvalds 		}
22201da177e4SLinus Torvalds 	}
22211da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22221da177e4SLinus Torvalds }
22231da177e4SLinus Torvalds 
22241da177e4SLinus Torvalds /* Initializes timers in queued array */
22251da177e4SLinus Torvalds static void __init init_all_queued(void)
22261da177e4SLinus Torvalds {
22271da177e4SLinus Torvalds 	unsigned long iflags;
22281da177e4SLinus Torvalds 	int k;
22291da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22301da177e4SLinus Torvalds 
22311da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22321da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
22331da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
22341da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
22351da177e4SLinus Torvalds 		sqcp->in_use = 0;
22361da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
22371da177e4SLinus Torvalds 	}
22381da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22391da177e4SLinus Torvalds }
22401da177e4SLinus Torvalds 
22411da177e4SLinus Torvalds static void __init sdebug_build_parts(unsigned char * ramp)
22421da177e4SLinus Torvalds {
22431da177e4SLinus Torvalds 	struct partition * pp;
22441da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
22451da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
22461da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
22471da177e4SLinus Torvalds 
22481da177e4SLinus Torvalds 	/* assume partition table already zeroed */
22491da177e4SLinus Torvalds 	if ((scsi_debug_num_parts < 1) || (sdebug_store_size < 1048576))
22501da177e4SLinus Torvalds 		return;
22511da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
22521da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
22531da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
22541da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
22551da177e4SLinus Torvalds 	}
2256c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
22571da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
22581da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
22591da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
22601da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
22611da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
22621da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
22631da177e4SLinus Torvalds 			    * heads_by_sects;
22641da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
22651da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
22661da177e4SLinus Torvalds 
22671da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
22681da177e4SLinus Torvalds 	ramp[511] = 0xAA;
22691da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
22701da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
22711da177e4SLinus Torvalds 		start_sec = starts[k];
22721da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
22731da177e4SLinus Torvalds 		pp->boot_ind = 0;
22741da177e4SLinus Torvalds 
22751da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
22761da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
22771da177e4SLinus Torvalds 			   / sdebug_sectors_per;
22781da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
22791da177e4SLinus Torvalds 
22801da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
22811da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
22821da177e4SLinus Torvalds 			       / sdebug_sectors_per;
22831da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
22841da177e4SLinus Torvalds 
22851da177e4SLinus Torvalds 		pp->start_sect = start_sec;
22861da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
22871da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
22881da177e4SLinus Torvalds 	}
22891da177e4SLinus Torvalds }
22901da177e4SLinus Torvalds 
22911da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
22921da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
22931da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
22941da177e4SLinus Torvalds {
22951da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
22961da177e4SLinus Torvalds 		if (scsi_result) {
22971da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
22981da177e4SLinus Torvalds 
2299c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2300c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2301c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
23021da177e4SLinus Torvalds 		}
23031da177e4SLinus Torvalds 	}
23041da177e4SLinus Torvalds 	if (cmnd && devip) {
23051da177e4SLinus Torvalds 		/* simulate autosense by this driver */
23061da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
23071da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
23081da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
23091da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
23101da177e4SLinus Torvalds 	}
23111da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
23121da177e4SLinus Torvalds 		if (cmnd)
23131da177e4SLinus Torvalds 			cmnd->result = scsi_result;
23141da177e4SLinus Torvalds 		if (done)
23151da177e4SLinus Torvalds 			done(cmnd);
23161da177e4SLinus Torvalds 		return 0;
23171da177e4SLinus Torvalds 	} else {
23181da177e4SLinus Torvalds 		unsigned long iflags;
23191da177e4SLinus Torvalds 		int k;
23201da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
23211da177e4SLinus Torvalds 
23221da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
23231da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
23241da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
23251da177e4SLinus Torvalds 			if (! sqcp->in_use)
23261da177e4SLinus Torvalds 				break;
23271da177e4SLinus Torvalds 		}
23281da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
23291da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
23301da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
23311da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
23321da177e4SLinus Torvalds 		}
23331da177e4SLinus Torvalds 		sqcp->in_use = 1;
23341da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
23351da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
23361da177e4SLinus Torvalds 		sqcp->done_funct = done;
23371da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
23381da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
23391da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
23401da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
23411da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23421da177e4SLinus Torvalds 		if (cmnd)
23431da177e4SLinus Torvalds 			cmnd->result = 0;
23441da177e4SLinus Torvalds 		return 0;
23451da177e4SLinus Torvalds 	}
23461da177e4SLinus Torvalds }
23471da177e4SLinus Torvalds 
234823183910SDouglas Gilbert /* Note: The following macros create attribute files in the
234923183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
235023183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
235123183910SDouglas Gilbert    as it can when the corresponding attribute in the
235223183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
235323183910SDouglas Gilbert  */
2354c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2355c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2356c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2357c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2358c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
235923183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2360c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2361c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2362c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2363c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2364c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2365c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2366c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2367c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
236823183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
236923183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
23701da177e4SLinus Torvalds 
23711da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
23721da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
23731da177e4SLinus Torvalds MODULE_LICENSE("GPL");
23741da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
23751da177e4SLinus Torvalds 
23761da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
23771da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2378c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2379c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2380beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
238123183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2382c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2383c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
23841da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2385c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
23866f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
23871da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
23881da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2389c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
239023183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
23911da177e4SLinus Torvalds 
23921da177e4SLinus Torvalds 
23931da177e4SLinus Torvalds static char sdebug_info[256];
23941da177e4SLinus Torvalds 
23951da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
23961da177e4SLinus Torvalds {
23971da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
23981da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
23991da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
24001da177e4SLinus Torvalds 		scsi_debug_opts);
24011da177e4SLinus Torvalds 	return sdebug_info;
24021da177e4SLinus Torvalds }
24031da177e4SLinus Torvalds 
24041da177e4SLinus Torvalds /* scsi_debug_proc_info
24051da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
24061da177e4SLinus Torvalds  */
24071da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
24081da177e4SLinus Torvalds 				int length, int inout)
24091da177e4SLinus Torvalds {
24101da177e4SLinus Torvalds 	int len, pos, begin;
24111da177e4SLinus Torvalds 	int orig_length;
24121da177e4SLinus Torvalds 
24131da177e4SLinus Torvalds 	orig_length = length;
24141da177e4SLinus Torvalds 
24151da177e4SLinus Torvalds 	if (inout == 1) {
24161da177e4SLinus Torvalds 		char arr[16];
24171da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
24181da177e4SLinus Torvalds 
24191da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
24201da177e4SLinus Torvalds 			return -EACCES;
24211da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
24221da177e4SLinus Torvalds 		arr[minLen] = '\0';
24231da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
24241da177e4SLinus Torvalds 			return -EINVAL;
24251da177e4SLinus Torvalds 		scsi_debug_opts = pos;
24261da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
24271da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
24281da177e4SLinus Torvalds 		return length;
24291da177e4SLinus Torvalds 	}
24301da177e4SLinus Torvalds 	begin = 0;
24311da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
24321da177e4SLinus Torvalds 	    "%s [%s]\n"
24331da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
24341da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
24351da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
24361da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
24371da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
24381da177e4SLinus Torvalds 	    "host_resets=%d\n",
24391da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
24401da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
24411da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
24421da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
24431da177e4SLinus Torvalds 	    SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
24441da177e4SLinus Torvalds 	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
24451da177e4SLinus Torvalds 	if (pos < offset) {
24461da177e4SLinus Torvalds 		len = 0;
24471da177e4SLinus Torvalds 		begin = pos;
24481da177e4SLinus Torvalds 	}
24491da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
24501da177e4SLinus Torvalds 	len -= (offset - begin);
24511da177e4SLinus Torvalds 	if (len > length)
24521da177e4SLinus Torvalds 		len = length;
24531da177e4SLinus Torvalds 	return len;
24541da177e4SLinus Torvalds }
24551da177e4SLinus Torvalds 
24561da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
24571da177e4SLinus Torvalds {
24581da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
24591da177e4SLinus Torvalds }
24601da177e4SLinus Torvalds 
24611da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
24621da177e4SLinus Torvalds 				  const char * buf, size_t count)
24631da177e4SLinus Torvalds {
24641da177e4SLinus Torvalds         int delay;
24651da177e4SLinus Torvalds 	char work[20];
24661da177e4SLinus Torvalds 
24671da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24681da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
24691da177e4SLinus Torvalds 			scsi_debug_delay = delay;
24701da177e4SLinus Torvalds 			return count;
24711da177e4SLinus Torvalds 		}
24721da177e4SLinus Torvalds 	}
24731da177e4SLinus Torvalds 	return -EINVAL;
24741da177e4SLinus Torvalds }
24751da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
24761da177e4SLinus Torvalds 	    sdebug_delay_store);
24771da177e4SLinus Torvalds 
24781da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
24791da177e4SLinus Torvalds {
24801da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
24811da177e4SLinus Torvalds }
24821da177e4SLinus Torvalds 
24831da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
24841da177e4SLinus Torvalds 				 const char * buf, size_t count)
24851da177e4SLinus Torvalds {
24861da177e4SLinus Torvalds         int opts;
24871da177e4SLinus Torvalds 	char work[20];
24881da177e4SLinus Torvalds 
24891da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
24901da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
24911da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
24921da177e4SLinus Torvalds 				goto opts_done;
24931da177e4SLinus Torvalds 		} else {
24941da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
24951da177e4SLinus Torvalds 				goto opts_done;
24961da177e4SLinus Torvalds 		}
24971da177e4SLinus Torvalds 	}
24981da177e4SLinus Torvalds 	return -EINVAL;
24991da177e4SLinus Torvalds opts_done:
25001da177e4SLinus Torvalds 	scsi_debug_opts = opts;
25011da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
25021da177e4SLinus Torvalds 	return count;
25031da177e4SLinus Torvalds }
25041da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
25051da177e4SLinus Torvalds 	    sdebug_opts_store);
25061da177e4SLinus Torvalds 
25071da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
25081da177e4SLinus Torvalds {
25091da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
25101da177e4SLinus Torvalds }
25111da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
25121da177e4SLinus Torvalds 				  const char * buf, size_t count)
25131da177e4SLinus Torvalds {
25141da177e4SLinus Torvalds         int n;
25151da177e4SLinus Torvalds 
25161da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25171da177e4SLinus Torvalds 		scsi_debug_ptype = n;
25181da177e4SLinus Torvalds 		return count;
25191da177e4SLinus Torvalds 	}
25201da177e4SLinus Torvalds 	return -EINVAL;
25211da177e4SLinus Torvalds }
25221da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
25231da177e4SLinus Torvalds 
25241da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
25251da177e4SLinus Torvalds {
25261da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
25271da177e4SLinus Torvalds }
25281da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
25291da177e4SLinus Torvalds 				  const char * buf, size_t count)
25301da177e4SLinus Torvalds {
25311da177e4SLinus Torvalds         int n;
25321da177e4SLinus Torvalds 
25331da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25341da177e4SLinus Torvalds 		scsi_debug_dsense = n;
25351da177e4SLinus Torvalds 		return count;
25361da177e4SLinus Torvalds 	}
25371da177e4SLinus Torvalds 	return -EINVAL;
25381da177e4SLinus Torvalds }
25391da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
25401da177e4SLinus Torvalds 	    sdebug_dsense_store);
25411da177e4SLinus Torvalds 
254223183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
254323183910SDouglas Gilbert {
254423183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
254523183910SDouglas Gilbert }
254623183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
254723183910SDouglas Gilbert 				    const char * buf, size_t count)
254823183910SDouglas Gilbert {
254923183910SDouglas Gilbert         int n;
255023183910SDouglas Gilbert 
255123183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
255223183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
255323183910SDouglas Gilbert 		return count;
255423183910SDouglas Gilbert 	}
255523183910SDouglas Gilbert 	return -EINVAL;
255623183910SDouglas Gilbert }
255723183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
255823183910SDouglas Gilbert 	    sdebug_fake_rw_store);
255923183910SDouglas Gilbert 
2560c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2561c65b1445SDouglas Gilbert {
2562c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2563c65b1445SDouglas Gilbert }
2564c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2565c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2566c65b1445SDouglas Gilbert {
2567c65b1445SDouglas Gilbert         int n;
2568c65b1445SDouglas Gilbert 
2569c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2570c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2571c65b1445SDouglas Gilbert 		return count;
2572c65b1445SDouglas Gilbert 	}
2573c65b1445SDouglas Gilbert 	return -EINVAL;
2574c65b1445SDouglas Gilbert }
2575c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2576c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2577c65b1445SDouglas Gilbert 
25781da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
25791da177e4SLinus Torvalds {
25801da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
25811da177e4SLinus Torvalds }
25821da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
25831da177e4SLinus Torvalds 				     const char * buf, size_t count)
25841da177e4SLinus Torvalds {
25851da177e4SLinus Torvalds         int n;
25861da177e4SLinus Torvalds 
25871da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
25881da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
25891da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
25901da177e4SLinus Torvalds 		return count;
25911da177e4SLinus Torvalds 	}
25921da177e4SLinus Torvalds 	return -EINVAL;
25931da177e4SLinus Torvalds }
25941da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
25951da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
25961da177e4SLinus Torvalds 
25971da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
25981da177e4SLinus Torvalds {
25991da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
26001da177e4SLinus Torvalds }
26011da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
26021da177e4SLinus Torvalds 
26031da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
26041da177e4SLinus Torvalds {
26051da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
26061da177e4SLinus Torvalds }
26071da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
26081da177e4SLinus Torvalds 
26091da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
26101da177e4SLinus Torvalds {
26111da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
26121da177e4SLinus Torvalds }
26131da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
26141da177e4SLinus Torvalds 				      const char * buf, size_t count)
26151da177e4SLinus Torvalds {
26161da177e4SLinus Torvalds         int nth;
26171da177e4SLinus Torvalds 
26181da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
26191da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
26201da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
26211da177e4SLinus Torvalds 		return count;
26221da177e4SLinus Torvalds 	}
26231da177e4SLinus Torvalds 	return -EINVAL;
26241da177e4SLinus Torvalds }
26251da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
26261da177e4SLinus Torvalds 	    sdebug_every_nth_store);
26271da177e4SLinus Torvalds 
26281da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
26291da177e4SLinus Torvalds {
26301da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
26311da177e4SLinus Torvalds }
26321da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
26331da177e4SLinus Torvalds 				     const char * buf, size_t count)
26341da177e4SLinus Torvalds {
26351da177e4SLinus Torvalds         int n;
26361da177e4SLinus Torvalds 
26371da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
26381da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
26391da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
26401da177e4SLinus Torvalds 		return count;
26411da177e4SLinus Torvalds 	}
26421da177e4SLinus Torvalds 	return -EINVAL;
26431da177e4SLinus Torvalds }
26441da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
26451da177e4SLinus Torvalds 	    sdebug_max_luns_store);
26461da177e4SLinus Torvalds 
26471da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
26481da177e4SLinus Torvalds {
26491da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
26501da177e4SLinus Torvalds }
26511da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
26521da177e4SLinus Torvalds 
2653c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2654c65b1445SDouglas Gilbert {
2655c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2656c65b1445SDouglas Gilbert }
2657c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2658c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2659c65b1445SDouglas Gilbert {
2660c65b1445SDouglas Gilbert         int n;
2661c65b1445SDouglas Gilbert 
2662c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2663c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
2664c65b1445SDouglas Gilbert 		if (scsi_debug_virtual_gb > 0) {
2665c65b1445SDouglas Gilbert 			sdebug_capacity = 2048 * 1024;
2666c65b1445SDouglas Gilbert 			sdebug_capacity *= scsi_debug_virtual_gb;
2667c65b1445SDouglas Gilbert 		} else
2668c65b1445SDouglas Gilbert 			sdebug_capacity = sdebug_store_sectors;
2669c65b1445SDouglas Gilbert 		return count;
2670c65b1445SDouglas Gilbert 	}
2671c65b1445SDouglas Gilbert 	return -EINVAL;
2672c65b1445SDouglas Gilbert }
2673c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2674c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2675c65b1445SDouglas Gilbert 
26761da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
26771da177e4SLinus Torvalds {
26781da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
26791da177e4SLinus Torvalds }
26801da177e4SLinus Torvalds 
26811da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
26821da177e4SLinus Torvalds 				     const char * buf, size_t count)
26831da177e4SLinus Torvalds {
26841da177e4SLinus Torvalds 	int delta_hosts;
26851da177e4SLinus Torvalds 
2686f3df41cfSFUJITA Tomonori 	if (sscanf(buf, "%d", &delta_hosts) != 1)
26871da177e4SLinus Torvalds 		return -EINVAL;
26881da177e4SLinus Torvalds 	if (delta_hosts > 0) {
26891da177e4SLinus Torvalds 		do {
26901da177e4SLinus Torvalds 			sdebug_add_adapter();
26911da177e4SLinus Torvalds 		} while (--delta_hosts);
26921da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
26931da177e4SLinus Torvalds 		do {
26941da177e4SLinus Torvalds 			sdebug_remove_adapter();
26951da177e4SLinus Torvalds 		} while (++delta_hosts);
26961da177e4SLinus Torvalds 	}
26971da177e4SLinus Torvalds 	return count;
26981da177e4SLinus Torvalds }
26991da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
27001da177e4SLinus Torvalds 	    sdebug_add_host_store);
27011da177e4SLinus Torvalds 
270223183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
270323183910SDouglas Gilbert 					  char * buf)
270423183910SDouglas Gilbert {
270523183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
270623183910SDouglas Gilbert }
270723183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
270823183910SDouglas Gilbert 					   const char * buf, size_t count)
270923183910SDouglas Gilbert {
271023183910SDouglas Gilbert 	int n;
271123183910SDouglas Gilbert 
271223183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
271323183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
271423183910SDouglas Gilbert 		return count;
271523183910SDouglas Gilbert 	}
271623183910SDouglas Gilbert 	return -EINVAL;
271723183910SDouglas Gilbert }
271823183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
271923183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
272023183910SDouglas Gilbert 
272123183910SDouglas Gilbert /* Note: The following function creates attribute files in the
272223183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
272323183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
272423183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
272523183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
272623183910SDouglas Gilbert  */
27276ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
27281da177e4SLinus Torvalds {
27296ecaff7fSRandy Dunlap 	int ret;
27306ecaff7fSRandy Dunlap 
27316ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27326ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
27336ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27346ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27356ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
273623183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27376ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
273823183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27396ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
274023183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
27416ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27426ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
27436ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
274423183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
274523183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
27466ecaff7fSRandy Dunlap 	return ret;
27471da177e4SLinus Torvalds }
27481da177e4SLinus Torvalds 
27491da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
27501da177e4SLinus Torvalds {
275123183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
275223183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
27531da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
27541da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
27551da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
27561da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
275723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
275823183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
27591da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
276023183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
27611da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
27621da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
27631da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
27641da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
27651da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
27661da177e4SLinus Torvalds }
27671da177e4SLinus Torvalds 
27681da177e4SLinus Torvalds static int __init scsi_debug_init(void)
27691da177e4SLinus Torvalds {
2770c65b1445SDouglas Gilbert 	unsigned int sz;
27711da177e4SLinus Torvalds 	int host_to_add;
27721da177e4SLinus Torvalds 	int k;
27736ecaff7fSRandy Dunlap 	int ret;
27741da177e4SLinus Torvalds 
27751da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
27761da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
2777c65b1445SDouglas Gilbert 	sdebug_store_size = (unsigned int)scsi_debug_dev_size_mb * 1048576;
2778c65b1445SDouglas Gilbert 	sdebug_store_sectors = sdebug_store_size / SECT_SIZE;
2779c65b1445SDouglas Gilbert 	if (scsi_debug_virtual_gb > 0) {
2780c65b1445SDouglas Gilbert 		sdebug_capacity = 2048 * 1024;
2781c65b1445SDouglas Gilbert 		sdebug_capacity *= scsi_debug_virtual_gb;
2782c65b1445SDouglas Gilbert 	} else
2783c65b1445SDouglas Gilbert 		sdebug_capacity = sdebug_store_sectors;
27841da177e4SLinus Torvalds 
27851da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
27861da177e4SLinus Torvalds 	sdebug_heads = 8;
27871da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
27881da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
27891da177e4SLinus Torvalds 		sdebug_heads = 32;
27901da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
27911da177e4SLinus Torvalds 		sdebug_heads = 64;
27921da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
27931da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
27941da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
27951da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
27961da177e4SLinus Torvalds 		sdebug_heads = 255;
27971da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
27981da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
27991da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
28001da177e4SLinus Torvalds 	}
28011da177e4SLinus Torvalds 
28021da177e4SLinus Torvalds 	sz = sdebug_store_size;
28031da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
28041da177e4SLinus Torvalds 	if (NULL == fake_storep) {
28051da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
28061da177e4SLinus Torvalds 		return -ENOMEM;
28071da177e4SLinus Torvalds 	}
28081da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
28091da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
28101da177e4SLinus Torvalds 		sdebug_build_parts(fake_storep);
28111da177e4SLinus Torvalds 
28126ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
28136ecaff7fSRandy Dunlap 	if (ret < 0) {
28146ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
28156ecaff7fSRandy Dunlap 			ret);
28166ecaff7fSRandy Dunlap 		goto free_vm;
28176ecaff7fSRandy Dunlap 	}
28186ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
28196ecaff7fSRandy Dunlap 	if (ret < 0) {
28206ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
28216ecaff7fSRandy Dunlap 			ret);
28226ecaff7fSRandy Dunlap 		goto dev_unreg;
28236ecaff7fSRandy Dunlap 	}
28246ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
28256ecaff7fSRandy Dunlap 	if (ret < 0) {
28266ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
28276ecaff7fSRandy Dunlap 			ret);
28286ecaff7fSRandy Dunlap 		goto bus_unreg;
28296ecaff7fSRandy Dunlap 	}
28306ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
28316ecaff7fSRandy Dunlap 	if (ret < 0) {
28326ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
28336ecaff7fSRandy Dunlap 			ret);
28346ecaff7fSRandy Dunlap 		goto del_files;
28356ecaff7fSRandy Dunlap 	}
28361da177e4SLinus Torvalds 
28376ecaff7fSRandy Dunlap 	init_all_queued();
28381da177e4SLinus Torvalds 
28391da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
28401da177e4SLinus Torvalds         scsi_debug_add_host = 0;
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
28431da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
28441da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
28451da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
28461da177e4SLinus Torvalds                         break;
28471da177e4SLinus Torvalds                 }
28481da177e4SLinus Torvalds         }
28491da177e4SLinus Torvalds 
28501da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
28511da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
28521da177e4SLinus Torvalds 		       scsi_debug_add_host);
28531da177e4SLinus Torvalds 	}
28541da177e4SLinus Torvalds 	return 0;
28556ecaff7fSRandy Dunlap 
28566ecaff7fSRandy Dunlap del_files:
28576ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
28586ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
28596ecaff7fSRandy Dunlap bus_unreg:
28606ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
28616ecaff7fSRandy Dunlap dev_unreg:
28626ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
28636ecaff7fSRandy Dunlap free_vm:
28646ecaff7fSRandy Dunlap 	vfree(fake_storep);
28656ecaff7fSRandy Dunlap 
28666ecaff7fSRandy Dunlap 	return ret;
28671da177e4SLinus Torvalds }
28681da177e4SLinus Torvalds 
28691da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
28701da177e4SLinus Torvalds {
28711da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
28721da177e4SLinus Torvalds 
28731da177e4SLinus Torvalds 	stop_all_queued();
28741da177e4SLinus Torvalds 	for (; k; k--)
28751da177e4SLinus Torvalds 		sdebug_remove_adapter();
28761da177e4SLinus Torvalds 	do_remove_driverfs_files();
28771da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
28781da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
28791da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
28801da177e4SLinus Torvalds 
28811da177e4SLinus Torvalds 	vfree(fake_storep);
28821da177e4SLinus Torvalds }
28831da177e4SLinus Torvalds 
28841da177e4SLinus Torvalds device_initcall(scsi_debug_init);
28851da177e4SLinus Torvalds module_exit(scsi_debug_exit);
28861da177e4SLinus Torvalds 
288752c1da39SAdrian Bunk static void pseudo_0_release(struct device * dev)
28881da177e4SLinus Torvalds {
28891da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
28901da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
28911da177e4SLinus Torvalds }
28921da177e4SLinus Torvalds 
28931da177e4SLinus Torvalds static struct device pseudo_primary = {
28941da177e4SLinus Torvalds 	.bus_id		= "pseudo_0",
28951da177e4SLinus Torvalds 	.release	= pseudo_0_release,
28961da177e4SLinus Torvalds };
28971da177e4SLinus Torvalds 
28981da177e4SLinus Torvalds static int pseudo_lld_bus_match(struct device *dev,
28991da177e4SLinus Torvalds                           struct device_driver *dev_driver)
29001da177e4SLinus Torvalds {
29011da177e4SLinus Torvalds         return 1;
29021da177e4SLinus Torvalds }
29031da177e4SLinus Torvalds 
29041da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus = {
29051da177e4SLinus Torvalds         .name = "pseudo",
29061da177e4SLinus Torvalds         .match = pseudo_lld_bus_match,
2907bbbe3a41SRussell King 	.probe = sdebug_driver_probe,
2908bbbe3a41SRussell King 	.remove = sdebug_driver_remove,
29091da177e4SLinus Torvalds };
29101da177e4SLinus Torvalds 
29111da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
29121da177e4SLinus Torvalds {
29131da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29141da177e4SLinus Torvalds 
29151da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
29161da177e4SLinus Torvalds         kfree(sdbg_host);
29171da177e4SLinus Torvalds }
29181da177e4SLinus Torvalds 
29191da177e4SLinus Torvalds static int sdebug_add_adapter(void)
29201da177e4SLinus Torvalds {
29211da177e4SLinus Torvalds 	int k, devs_per_host;
29221da177e4SLinus Torvalds         int error = 0;
29231da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
29241da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
29251da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
29261da177e4SLinus Torvalds 
292724669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
29281da177e4SLinus Torvalds         if (NULL == sdbg_host) {
29291da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
29301da177e4SLinus Torvalds                        __FUNCTION__, __LINE__);
29311da177e4SLinus Torvalds                 return -ENOMEM;
29321da177e4SLinus Torvalds         }
29331da177e4SLinus Torvalds 
29341da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
29351da177e4SLinus Torvalds 
29361da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
29371da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
293824669f75SJes Sorensen                 sdbg_devinfo = kzalloc(sizeof(*sdbg_devinfo),GFP_KERNEL);
29391da177e4SLinus Torvalds                 if (NULL == sdbg_devinfo) {
29401da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
29411da177e4SLinus Torvalds                                __FUNCTION__, __LINE__);
29421da177e4SLinus Torvalds                         error = -ENOMEM;
29431da177e4SLinus Torvalds 			goto clean;
29441da177e4SLinus Torvalds                 }
29451da177e4SLinus Torvalds                 sdbg_devinfo->sdbg_host = sdbg_host;
29461da177e4SLinus Torvalds                 list_add_tail(&sdbg_devinfo->dev_list,
29471da177e4SLinus Torvalds                               &sdbg_host->dev_info_list);
29481da177e4SLinus Torvalds         }
29491da177e4SLinus Torvalds 
29501da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
29511da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
29521da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
29531da177e4SLinus Torvalds 
29541da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
29551da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
29561da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
29571da177e4SLinus Torvalds         sprintf(sdbg_host->dev.bus_id, "adapter%d", scsi_debug_add_host);
29581da177e4SLinus Torvalds 
29591da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
29601da177e4SLinus Torvalds 
29611da177e4SLinus Torvalds         if (error)
29621da177e4SLinus Torvalds 		goto clean;
29631da177e4SLinus Torvalds 
29641da177e4SLinus Torvalds 	++scsi_debug_add_host;
29651da177e4SLinus Torvalds         return error;
29661da177e4SLinus Torvalds 
29671da177e4SLinus Torvalds clean:
29681da177e4SLinus Torvalds 	list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
29691da177e4SLinus Torvalds 		sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
29701da177e4SLinus Torvalds 					  dev_list);
29711da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
29721da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
29731da177e4SLinus Torvalds 	}
29741da177e4SLinus Torvalds 
29751da177e4SLinus Torvalds 	kfree(sdbg_host);
29761da177e4SLinus Torvalds         return error;
29771da177e4SLinus Torvalds }
29781da177e4SLinus Torvalds 
29791da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
29801da177e4SLinus Torvalds {
29811da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
29821da177e4SLinus Torvalds 
29831da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
29841da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
29851da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
29861da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
29871da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
29881da177e4SLinus Torvalds 	}
29891da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
29901da177e4SLinus Torvalds 
29911da177e4SLinus Torvalds 	if (!sdbg_host)
29921da177e4SLinus Torvalds 		return;
29931da177e4SLinus Torvalds 
29941da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
29951da177e4SLinus Torvalds         --scsi_debug_add_host;
29961da177e4SLinus Torvalds }
29971da177e4SLinus Torvalds 
29989e603ca0SFUJITA Tomonori static struct scsi_host_template sdebug_driver_template = {
29999e603ca0SFUJITA Tomonori 	.proc_info =		scsi_debug_proc_info,
30009e603ca0SFUJITA Tomonori 	.proc_name =		sdebug_proc_name,
30019e603ca0SFUJITA Tomonori 	.name =			"SCSI DEBUG",
30029e603ca0SFUJITA Tomonori 	.info =			scsi_debug_info,
30039e603ca0SFUJITA Tomonori 	.slave_alloc =		scsi_debug_slave_alloc,
30049e603ca0SFUJITA Tomonori 	.slave_configure =	scsi_debug_slave_configure,
30059e603ca0SFUJITA Tomonori 	.slave_destroy =	scsi_debug_slave_destroy,
30069e603ca0SFUJITA Tomonori 	.ioctl =		scsi_debug_ioctl,
30079e603ca0SFUJITA Tomonori 	.queuecommand =		scsi_debug_queuecommand,
30089e603ca0SFUJITA Tomonori 	.eh_abort_handler =	scsi_debug_abort,
30099e603ca0SFUJITA Tomonori 	.eh_bus_reset_handler = scsi_debug_bus_reset,
30109e603ca0SFUJITA Tomonori 	.eh_device_reset_handler = scsi_debug_device_reset,
30119e603ca0SFUJITA Tomonori 	.eh_host_reset_handler = scsi_debug_host_reset,
30129e603ca0SFUJITA Tomonori 	.bios_param =		scsi_debug_biosparam,
30139e603ca0SFUJITA Tomonori 	.can_queue =		SCSI_DEBUG_CANQUEUE,
30149e603ca0SFUJITA Tomonori 	.this_id =		7,
30159e603ca0SFUJITA Tomonori 	.sg_tablesize =		256,
30169e603ca0SFUJITA Tomonori 	.cmd_per_lun =		16,
30179e603ca0SFUJITA Tomonori 	.max_sectors =		0xffff,
30189e603ca0SFUJITA Tomonori 	.use_clustering = 	DISABLE_CLUSTERING,
30199e603ca0SFUJITA Tomonori 	.module =		THIS_MODULE,
30209e603ca0SFUJITA Tomonori };
30219e603ca0SFUJITA Tomonori 
30221da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
30231da177e4SLinus Torvalds {
30241da177e4SLinus Torvalds         int error = 0;
30251da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30261da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
30271da177e4SLinus Torvalds 
30281da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30291da177e4SLinus Torvalds 
30301da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
30311da177e4SLinus Torvalds         if (NULL == hpnt) {
30321da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_register failed\n", __FUNCTION__);
30331da177e4SLinus Torvalds                 error = -ENODEV;
30341da177e4SLinus Torvalds 		return error;
30351da177e4SLinus Torvalds         }
30361da177e4SLinus Torvalds 
30371da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
30381da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
30391da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
30401da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
30411da177e4SLinus Torvalds 	else
30421da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3043c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
30441da177e4SLinus Torvalds 
30451da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
30461da177e4SLinus Torvalds         if (error) {
30471da177e4SLinus Torvalds                 printk(KERN_ERR "%s: scsi_add_host failed\n", __FUNCTION__);
30481da177e4SLinus Torvalds                 error = -ENODEV;
30491da177e4SLinus Torvalds 		scsi_host_put(hpnt);
30501da177e4SLinus Torvalds         } else
30511da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
30521da177e4SLinus Torvalds 
30531da177e4SLinus Torvalds 
30541da177e4SLinus Torvalds         return error;
30551da177e4SLinus Torvalds }
30561da177e4SLinus Torvalds 
30571da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
30581da177e4SLinus Torvalds {
30591da177e4SLinus Torvalds         struct list_head *lh, *lh_sf;
30601da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
30611da177e4SLinus Torvalds         struct sdebug_dev_info *sdbg_devinfo;
30621da177e4SLinus Torvalds 
30631da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
30641da177e4SLinus Torvalds 
30651da177e4SLinus Torvalds 	if (!sdbg_host) {
30661da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
30671da177e4SLinus Torvalds 		       __FUNCTION__);
30681da177e4SLinus Torvalds 		return -ENODEV;
30691da177e4SLinus Torvalds 	}
30701da177e4SLinus Torvalds 
30711da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
30721da177e4SLinus Torvalds 
30731da177e4SLinus Torvalds         list_for_each_safe(lh, lh_sf, &sdbg_host->dev_info_list) {
30741da177e4SLinus Torvalds                 sdbg_devinfo = list_entry(lh, struct sdebug_dev_info,
30751da177e4SLinus Torvalds                                           dev_list);
30761da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
30771da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
30781da177e4SLinus Torvalds         }
30791da177e4SLinus Torvalds 
30801da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
30811da177e4SLinus Torvalds         return 0;
30821da177e4SLinus Torvalds }
30831da177e4SLinus Torvalds 
30841da177e4SLinus Torvalds static void sdebug_max_tgts_luns(void)
30851da177e4SLinus Torvalds {
30861da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
30871da177e4SLinus Torvalds 	struct Scsi_Host *hpnt;
30881da177e4SLinus Torvalds 
30891da177e4SLinus Torvalds 	spin_lock(&sdebug_host_list_lock);
30901da177e4SLinus Torvalds 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
30911da177e4SLinus Torvalds 		hpnt = sdbg_host->shost;
30921da177e4SLinus Torvalds 		if ((hpnt->this_id >= 0) &&
30931da177e4SLinus Torvalds 		    (scsi_debug_num_tgts > hpnt->this_id))
30941da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts + 1;
30951da177e4SLinus Torvalds 		else
30961da177e4SLinus Torvalds 			hpnt->max_id = scsi_debug_num_tgts;
3097c65b1445SDouglas Gilbert 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS; /* scsi_debug_max_luns; */
30981da177e4SLinus Torvalds 	}
30991da177e4SLinus Torvalds 	spin_unlock(&sdebug_host_list_lock);
31001da177e4SLinus Torvalds }
3101