xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 5447ed6c)
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>
335a0e3ad6STejun Heo #include <linux/slab.h>
341da177e4SLinus Torvalds #include <linux/types.h>
351da177e4SLinus Torvalds #include <linux/string.h>
361da177e4SLinus Torvalds #include <linux/genhd.h>
371da177e4SLinus Torvalds #include <linux/fs.h>
381da177e4SLinus Torvalds #include <linux/init.h>
391da177e4SLinus Torvalds #include <linux/proc_fs.h>
401da177e4SLinus Torvalds #include <linux/vmalloc.h>
411da177e4SLinus Torvalds #include <linux/moduleparam.h>
42852e034dSJens Axboe #include <linux/scatterlist.h>
431da177e4SLinus Torvalds #include <linux/blkdev.h>
44c6a44287SMartin K. Petersen #include <linux/crc-t10dif.h>
45c6a44287SMartin K. Petersen 
46c6a44287SMartin K. Petersen #include <net/checksum.h>
479ff26eefSFUJITA Tomonori 
4844d92694SMartin K. Petersen #include <asm/unaligned.h>
4944d92694SMartin K. Petersen 
509ff26eefSFUJITA Tomonori #include <scsi/scsi.h>
519ff26eefSFUJITA Tomonori #include <scsi/scsi_cmnd.h>
529ff26eefSFUJITA Tomonori #include <scsi/scsi_device.h>
531da177e4SLinus Torvalds #include <scsi/scsi_host.h>
541da177e4SLinus Torvalds #include <scsi/scsicam.h>
55a34c4e98SFUJITA Tomonori #include <scsi/scsi_eh.h>
56395cef03SMartin K. Petersen #include <scsi/scsi_dbg.h>
571da177e4SLinus Torvalds 
58c6a44287SMartin K. Petersen #include "sd.h"
591da177e4SLinus Torvalds #include "scsi_logging.h"
601da177e4SLinus Torvalds 
616f3cbf55SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.81"
626f3cbf55SDouglas Gilbert static const char * scsi_debug_version_date = "20070104";
631da177e4SLinus Torvalds 
646f3cbf55SDouglas Gilbert /* Additional Sense Code (ASC) */
65c65b1445SDouglas Gilbert #define NO_ADDITIONAL_SENSE 0x0
66c65b1445SDouglas Gilbert #define LOGICAL_UNIT_NOT_READY 0x4
671da177e4SLinus Torvalds #define UNRECOVERED_READ_ERR 0x11
68c65b1445SDouglas Gilbert #define PARAMETER_LIST_LENGTH_ERR 0x1a
691da177e4SLinus Torvalds #define INVALID_OPCODE 0x20
701da177e4SLinus Torvalds #define ADDR_OUT_OF_RANGE 0x21
71395cef03SMartin K. Petersen #define INVALID_COMMAND_OPCODE 0x20
721da177e4SLinus Torvalds #define INVALID_FIELD_IN_CDB 0x24
73c65b1445SDouglas Gilbert #define INVALID_FIELD_IN_PARAM_LIST 0x26
741da177e4SLinus Torvalds #define POWERON_RESET 0x29
751da177e4SLinus Torvalds #define SAVING_PARAMS_UNSUP 0x39
766f3cbf55SDouglas Gilbert #define TRANSPORT_PROBLEM 0x4b
77c65b1445SDouglas Gilbert #define THRESHOLD_EXCEEDED 0x5d
78c65b1445SDouglas Gilbert #define LOW_POWER_COND_ON 0x5e
791da177e4SLinus Torvalds 
806f3cbf55SDouglas Gilbert /* Additional Sense Code Qualifier (ASCQ) */
816f3cbf55SDouglas Gilbert #define ACK_NAK_TO 0x3
826f3cbf55SDouglas Gilbert 
831da177e4SLinus Torvalds #define SDEBUG_TAGGED_QUEUING 0 /* 0 | MSG_SIMPLE_TAG | MSG_ORDERED_TAG */
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds /* Default values for driver parameters */
861da177e4SLinus Torvalds #define DEF_NUM_HOST   1
871da177e4SLinus Torvalds #define DEF_NUM_TGTS   1
881da177e4SLinus Torvalds #define DEF_MAX_LUNS   1
891da177e4SLinus Torvalds /* With these defaults, this driver will make 1 host with 1 target
901da177e4SLinus Torvalds  * (id 0) containing 1 logical unit (lun 0). That is 1 device.
911da177e4SLinus Torvalds  */
921da177e4SLinus Torvalds #define DEF_DELAY   1
931da177e4SLinus Torvalds #define DEF_DEV_SIZE_MB   8
941da177e4SLinus Torvalds #define DEF_EVERY_NTH   0
951da177e4SLinus Torvalds #define DEF_NUM_PARTS   0
961da177e4SLinus Torvalds #define DEF_OPTS   0
971da177e4SLinus Torvalds #define DEF_SCSI_LEVEL   5    /* INQUIRY, byte2 [5->SPC-3] */
981da177e4SLinus Torvalds #define DEF_PTYPE   0
991da177e4SLinus Torvalds #define DEF_D_SENSE   0
100c65b1445SDouglas Gilbert #define DEF_NO_LUN_0   0
101c65b1445SDouglas Gilbert #define DEF_VIRTUAL_GB   0
10223183910SDouglas Gilbert #define DEF_FAKE_RW	0
10323183910SDouglas Gilbert #define DEF_VPD_USE_HOSTNO 1
104597136abSMartin K. Petersen #define DEF_SECTOR_SIZE 512
105c6a44287SMartin K. Petersen #define DEF_DIX 0
106c6a44287SMartin K. Petersen #define DEF_DIF 0
107c6a44287SMartin K. Petersen #define DEF_GUARD 0
108c6a44287SMartin K. Petersen #define DEF_ATO 1
109ea61fca5SMartin K. Petersen #define DEF_PHYSBLK_EXP 0
110ea61fca5SMartin K. Petersen #define DEF_LOWEST_ALIGNED 0
11144d92694SMartin K. Petersen #define DEF_UNMAP_MAX_BLOCKS 0
11244d92694SMartin K. Petersen #define DEF_UNMAP_MAX_DESC 0
11344d92694SMartin K. Petersen #define DEF_UNMAP_GRANULARITY 0
11444d92694SMartin K. Petersen #define DEF_UNMAP_ALIGNMENT 0
1151da177e4SLinus Torvalds 
1161da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
1171da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
1181da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
1191da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
1201da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1216f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
122c6a44287SMartin K. Petersen #define SCSI_DEBUG_OPT_DIF_ERR   32
123c6a44287SMartin K. Petersen #define SCSI_DEBUG_OPT_DIX_ERR   64
1241da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1251da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1261da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1271da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1286f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1296f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1301da177e4SLinus Torvalds  *
1311da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1321da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1331da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1341da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1356f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1366f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1371da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1381da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1391da177e4SLinus Torvalds  */
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1421da177e4SLinus Torvalds  * sector on read commands: */
1431da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1461da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1471da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
148c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1491da177e4SLinus Torvalds 
1501da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1511da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1521da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1531da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1541da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
1551da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
1561da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1571da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1581da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1591da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1601da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
161c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
162c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
16323183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
16423183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
165597136abSMartin K. Petersen static int scsi_debug_sector_size = DEF_SECTOR_SIZE;
166c6a44287SMartin K. Petersen static int scsi_debug_dix = DEF_DIX;
167c6a44287SMartin K. Petersen static int scsi_debug_dif = DEF_DIF;
168c6a44287SMartin K. Petersen static int scsi_debug_guard = DEF_GUARD;
169c6a44287SMartin K. Petersen static int scsi_debug_ato = DEF_ATO;
170ea61fca5SMartin K. Petersen static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP;
171ea61fca5SMartin K. Petersen static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED;
17244d92694SMartin K. Petersen static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
17344d92694SMartin K. Petersen static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
17444d92694SMartin K. Petersen static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
17544d92694SMartin K. Petersen static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
1761da177e4SLinus Torvalds 
1771da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1801da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1811da177e4SLinus Torvalds 
182c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1831da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1861da177e4SLinus Torvalds    may still need them */
1871da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1881da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1891da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1901da177e4SLinus Torvalds 
1911da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
1941da177e4SLinus Torvalds 
1959e603ca0SFUJITA Tomonori #define SCSI_DEBUG_CANQUEUE  255
196395cef03SMartin K. Petersen #define SCSI_DEBUG_MAX_CMD_LEN 32
1979e603ca0SFUJITA Tomonori 
1981da177e4SLinus Torvalds struct sdebug_dev_info {
1991da177e4SLinus Torvalds 	struct list_head dev_list;
2001da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
2011da177e4SLinus Torvalds 	unsigned int channel;
2021da177e4SLinus Torvalds 	unsigned int target;
2031da177e4SLinus Torvalds 	unsigned int lun;
2041da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
205c65b1445SDouglas Gilbert 	unsigned int wlun;
2061da177e4SLinus Torvalds 	char reset;
207c65b1445SDouglas Gilbert 	char stopped;
2081da177e4SLinus Torvalds 	char used;
2091da177e4SLinus Torvalds };
2101da177e4SLinus Torvalds 
2111da177e4SLinus Torvalds struct sdebug_host_info {
2121da177e4SLinus Torvalds 	struct list_head host_list;
2131da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2141da177e4SLinus Torvalds 	struct device dev;
2151da177e4SLinus Torvalds 	struct list_head dev_info_list;
2161da177e4SLinus Torvalds };
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds #define to_sdebug_host(d)	\
2191da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
2221da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds struct sdebug_queued_cmd {
2271da177e4SLinus Torvalds 	int in_use;
2281da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
2291da177e4SLinus Torvalds 	done_funct_t done_funct;
2301da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2311da177e4SLinus Torvalds 	int scsi_result;
2321da177e4SLinus Torvalds };
2331da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2341da177e4SLinus Torvalds 
2351da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
236c6a44287SMartin K. Petersen static unsigned char *dif_storep;	/* protection info */
23744d92694SMartin K. Petersen static void *map_storep;		/* provisioning map */
2381da177e4SLinus Torvalds 
23944d92694SMartin K. Petersen static unsigned long map_size;
2401da177e4SLinus Torvalds static int num_aborts = 0;
2411da177e4SLinus Torvalds static int num_dev_resets = 0;
2421da177e4SLinus Torvalds static int num_bus_resets = 0;
2431da177e4SLinus Torvalds static int num_host_resets = 0;
244c6a44287SMartin K. Petersen static int dix_writes;
245c6a44287SMartin K. Petersen static int dix_reads;
246c6a44287SMartin K. Petersen static int dif_errors;
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2491da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2501da177e4SLinus Torvalds 
2511da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2521da177e4SLinus Torvalds 
2531da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2541da177e4SLinus Torvalds 
255c6a44287SMartin K. Petersen static inline sector_t dif_offset(sector_t sector)
256c6a44287SMartin K. Petersen {
257c6a44287SMartin K. Petersen 	return sector << 3;
258c6a44287SMartin K. Petersen }
259c6a44287SMartin K. Petersen 
2601da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2611da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2621da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2631da177e4SLinus Torvalds };
2641da177e4SLinus Torvalds 
2651da177e4SLinus Torvalds static const int check_condition_result =
2661da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2671da177e4SLinus Torvalds 
268c6a44287SMartin K. Petersen static const int illegal_condition_result =
269c6a44287SMartin K. Petersen 	(DRIVER_SENSE << 24) | (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION;
270c6a44287SMartin K. Petersen 
271c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
272c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
273c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
274c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
275c65b1445SDouglas Gilbert 
2761da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2771da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2781da177e4SLinus Torvalds 
2798dea0d02SFUJITA Tomonori static void sdebug_max_tgts_luns(void)
2808dea0d02SFUJITA Tomonori {
2818dea0d02SFUJITA Tomonori 	struct sdebug_host_info *sdbg_host;
2828dea0d02SFUJITA Tomonori 	struct Scsi_Host *hpnt;
2838dea0d02SFUJITA Tomonori 
2848dea0d02SFUJITA Tomonori 	spin_lock(&sdebug_host_list_lock);
2858dea0d02SFUJITA Tomonori 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
2868dea0d02SFUJITA Tomonori 		hpnt = sdbg_host->shost;
2878dea0d02SFUJITA Tomonori 		if ((hpnt->this_id >= 0) &&
2888dea0d02SFUJITA Tomonori 		    (scsi_debug_num_tgts > hpnt->this_id))
2898dea0d02SFUJITA Tomonori 			hpnt->max_id = scsi_debug_num_tgts + 1;
2908dea0d02SFUJITA Tomonori 		else
2918dea0d02SFUJITA Tomonori 			hpnt->max_id = scsi_debug_num_tgts;
2928dea0d02SFUJITA Tomonori 		/* scsi_debug_max_luns; */
2938dea0d02SFUJITA Tomonori 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;
2948dea0d02SFUJITA Tomonori 	}
2958dea0d02SFUJITA Tomonori 	spin_unlock(&sdebug_host_list_lock);
2968dea0d02SFUJITA Tomonori }
2978dea0d02SFUJITA Tomonori 
2988dea0d02SFUJITA Tomonori static void mk_sense_buffer(struct sdebug_dev_info *devip, int key,
2998dea0d02SFUJITA Tomonori 			    int asc, int asq)
3008dea0d02SFUJITA Tomonori {
3018dea0d02SFUJITA Tomonori 	unsigned char *sbuff;
3028dea0d02SFUJITA Tomonori 
3038dea0d02SFUJITA Tomonori 	sbuff = devip->sense_buff;
3048dea0d02SFUJITA Tomonori 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
3058dea0d02SFUJITA Tomonori 
3068dea0d02SFUJITA Tomonori 	scsi_build_sense_buffer(scsi_debug_dsense, sbuff, key, asc, asq);
3078dea0d02SFUJITA Tomonori 
3088dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3098dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
3108dea0d02SFUJITA Tomonori 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
3118dea0d02SFUJITA Tomonori }
3121da177e4SLinus Torvalds 
3133de9f944SFUJITA Tomonori static void get_data_transfer_info(unsigned char *cmd,
314395cef03SMartin K. Petersen 				   unsigned long long *lba, unsigned int *num,
315395cef03SMartin K. Petersen 				   u32 *ei_lba)
3163de9f944SFUJITA Tomonori {
317395cef03SMartin K. Petersen 	*ei_lba = 0;
318395cef03SMartin K. Petersen 
3193de9f944SFUJITA Tomonori 	switch (*cmd) {
320395cef03SMartin K. Petersen 	case VARIABLE_LENGTH_CMD:
321395cef03SMartin K. Petersen 		*lba = (u64)cmd[19] | (u64)cmd[18] << 8 |
322395cef03SMartin K. Petersen 			(u64)cmd[17] << 16 | (u64)cmd[16] << 24 |
323395cef03SMartin K. Petersen 			(u64)cmd[15] << 32 | (u64)cmd[14] << 40 |
324395cef03SMartin K. Petersen 			(u64)cmd[13] << 48 | (u64)cmd[12] << 56;
325395cef03SMartin K. Petersen 
326395cef03SMartin K. Petersen 		*ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 |
327395cef03SMartin K. Petersen 			(u32)cmd[21] << 16 | (u32)cmd[20] << 24;
328395cef03SMartin K. Petersen 
329395cef03SMartin K. Petersen 		*num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 |
330395cef03SMartin K. Petersen 			(u32)cmd[28] << 24;
331395cef03SMartin K. Petersen 		break;
332395cef03SMartin K. Petersen 
33344d92694SMartin K. Petersen 	case WRITE_SAME_16:
3343de9f944SFUJITA Tomonori 	case WRITE_16:
3353de9f944SFUJITA Tomonori 	case READ_16:
336d5cdc989SFUJITA Tomonori 		*lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
337d5cdc989SFUJITA Tomonori 			(u64)cmd[7] << 16 | (u64)cmd[6] << 24 |
338d5cdc989SFUJITA Tomonori 			(u64)cmd[5] << 32 | (u64)cmd[4] << 40 |
339d5cdc989SFUJITA Tomonori 			(u64)cmd[3] << 48 | (u64)cmd[2] << 56;
340d5cdc989SFUJITA Tomonori 
341d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 |
342d5cdc989SFUJITA Tomonori 			(u32)cmd[10] << 24;
3433de9f944SFUJITA Tomonori 		break;
3443de9f944SFUJITA Tomonori 	case WRITE_12:
3453de9f944SFUJITA Tomonori 	case READ_12:
346d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 |
347d5cdc989SFUJITA Tomonori 			(u32)cmd[2] << 24;
348d5cdc989SFUJITA Tomonori 
349d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
350d5cdc989SFUJITA Tomonori 			(u32)cmd[6] << 24;
3513de9f944SFUJITA Tomonori 		break;
35244d92694SMartin K. Petersen 	case WRITE_SAME:
3533de9f944SFUJITA Tomonori 	case WRITE_10:
3543de9f944SFUJITA Tomonori 	case READ_10:
355c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
356d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[5] | (u32)cmd[4] << 8 |	(u32)cmd[3] << 16 |
357d5cdc989SFUJITA Tomonori 			(u32)cmd[2] << 24;
358d5cdc989SFUJITA Tomonori 
359d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[8] | (u32)cmd[7] << 8;
3603de9f944SFUJITA Tomonori 		break;
3613de9f944SFUJITA Tomonori 	case WRITE_6:
3623de9f944SFUJITA Tomonori 	case READ_6:
363d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[3] | (u32)cmd[2] << 8 |
364d5cdc989SFUJITA Tomonori 			(u32)(cmd[1] & 0x1f) << 16;
3653de9f944SFUJITA Tomonori 		*num = (0 == cmd[4]) ? 256 : cmd[4];
3663de9f944SFUJITA Tomonori 		break;
3673de9f944SFUJITA Tomonori 	default:
3683de9f944SFUJITA Tomonori 		break;
3693de9f944SFUJITA Tomonori 	}
3703de9f944SFUJITA Tomonori }
3711da177e4SLinus Torvalds 
3721da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
3751da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
3761da177e4SLinus Torvalds 	}
3771da177e4SLinus Torvalds 	return -EINVAL;
3781da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
3791da177e4SLinus Torvalds }
3801da177e4SLinus Torvalds 
381c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
382c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
3831da177e4SLinus Torvalds {
3841da177e4SLinus Torvalds 	if (devip->reset) {
3851da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3861da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
3871da177e4SLinus Torvalds 			       "attention: power on reset\n");
3881da177e4SLinus Torvalds 		devip->reset = 0;
3891da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
3901da177e4SLinus Torvalds 		return check_condition_result;
3911da177e4SLinus Torvalds 	}
392c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
393c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
394c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
395c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
396c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
397c65b1445SDouglas Gilbert 				0x2);
398c65b1445SDouglas Gilbert 		return check_condition_result;
399c65b1445SDouglas Gilbert 	}
4001da177e4SLinus Torvalds 	return 0;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds 
4031da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
4041da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
4051da177e4SLinus Torvalds 				int arr_len)
4061da177e4SLinus Torvalds {
40721a61829SFUJITA Tomonori 	int act_len;
408072d0bb3SFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
4091da177e4SLinus Torvalds 
410072d0bb3SFUJITA Tomonori 	if (!sdb->length)
4111da177e4SLinus Torvalds 		return 0;
412072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
4131da177e4SLinus Torvalds 		return (DID_ERROR << 16);
41421a61829SFUJITA Tomonori 
41521a61829SFUJITA Tomonori 	act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
41621a61829SFUJITA Tomonori 				      arr, arr_len);
417072d0bb3SFUJITA Tomonori 	if (sdb->resid)
418072d0bb3SFUJITA Tomonori 		sdb->resid -= act_len;
419c65b1445SDouglas Gilbert 	else
42021a61829SFUJITA Tomonori 		sdb->resid = scsi_bufflen(scp) - act_len;
42121a61829SFUJITA Tomonori 
4221da177e4SLinus Torvalds 	return 0;
4231da177e4SLinus Torvalds }
4241da177e4SLinus Torvalds 
4251da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
4261da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
42721a61829SFUJITA Tomonori 			       int arr_len)
4281da177e4SLinus Torvalds {
42921a61829SFUJITA Tomonori 	if (!scsi_bufflen(scp))
4301da177e4SLinus Torvalds 		return 0;
431072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE))
4321da177e4SLinus Torvalds 		return -1;
43321a61829SFUJITA Tomonori 
43421a61829SFUJITA Tomonori 	return scsi_sg_copy_to_buffer(scp, arr, arr_len);
4351da177e4SLinus Torvalds }
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 
4381da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
4391da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
4401da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
4411da177e4SLinus Torvalds 
4425a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
4435a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
4445a09e398SHannes Reinecke 			   const char * dev_id_str,
445c65b1445SDouglas Gilbert 			   int dev_id_str_len)
4461da177e4SLinus Torvalds {
447c65b1445SDouglas Gilbert 	int num, port_a;
448c65b1445SDouglas Gilbert 	char b[32];
4491da177e4SLinus Torvalds 
450c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
4511da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
4521da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
4531da177e4SLinus Torvalds 	arr[1] = 0x1;
4541da177e4SLinus Torvalds 	arr[2] = 0x0;
4551da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
4561da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
4571da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
4581da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
4591da177e4SLinus Torvalds 	arr[3] = num;
4601da177e4SLinus Torvalds 	num += 4;
461c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
462c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
463c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
464c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
465c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
466c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
467c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
468c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
469c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
470c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
471c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
472c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
473c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
474c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
475c65b1445SDouglas Gilbert 		/* Target relative port number */
476c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
477c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
478c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
479c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
480c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
481c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
482c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
483c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
484c65b1445SDouglas Gilbert 	}
485c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
486c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
487c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
488c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
489c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
490c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
491c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
492c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
493c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
494c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
495c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
496c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
497c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
4985a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
4995a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
5005a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
5015a09e398SHannes Reinecke 	arr[num++] = 0x0;
5025a09e398SHannes Reinecke 	arr[num++] = 0x4;
5035a09e398SHannes Reinecke 	arr[num++] = 0;
5045a09e398SHannes Reinecke 	arr[num++] = 0;
5055a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
5065a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
507c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
508c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
509c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
510c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
511c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
512c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
513c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
514c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
515c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
516c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
517c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
518c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
519c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
520c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
521c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
522c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
523c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
524c65b1445SDouglas Gilbert 	arr[num++] = 24;
525c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
526c65b1445SDouglas Gilbert 	num += 12;
527c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
528c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
529c65b1445SDouglas Gilbert 	num += 8;
530c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
531c65b1445SDouglas Gilbert 	num += 4;
532c65b1445SDouglas Gilbert 	return num;
533c65b1445SDouglas Gilbert }
534c65b1445SDouglas Gilbert 
535c65b1445SDouglas Gilbert 
536c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
537c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
538c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
539c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
540c65b1445SDouglas Gilbert };
541c65b1445SDouglas Gilbert 
542c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
543c65b1445SDouglas Gilbert {
544c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
545c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
546c65b1445SDouglas Gilbert }
547c65b1445SDouglas Gilbert 
548c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
549c65b1445SDouglas Gilbert {
550c65b1445SDouglas Gilbert 	int num = 0;
551c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
552c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
553c65b1445SDouglas Gilbert 	int plen, olen;
554c65b1445SDouglas Gilbert 
555c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
556c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
557c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
558c65b1445SDouglas Gilbert 	olen = strlen(na1);
559c65b1445SDouglas Gilbert 	plen = olen + 1;
560c65b1445SDouglas Gilbert 	if (plen % 4)
561c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
562c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
563c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
564c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
565c65b1445SDouglas Gilbert 	num += plen;
566c65b1445SDouglas Gilbert 
567c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
568c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
569c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
570c65b1445SDouglas Gilbert 	olen = strlen(na2);
571c65b1445SDouglas Gilbert 	plen = olen + 1;
572c65b1445SDouglas Gilbert 	if (plen % 4)
573c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
574c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
575c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
576c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
577c65b1445SDouglas Gilbert 	num += plen;
578c65b1445SDouglas Gilbert 
579c65b1445SDouglas Gilbert 	return num;
580c65b1445SDouglas Gilbert }
581c65b1445SDouglas Gilbert 
582c65b1445SDouglas Gilbert /* SCSI ports VPD page */
583c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
584c65b1445SDouglas Gilbert {
585c65b1445SDouglas Gilbert 	int num = 0;
586c65b1445SDouglas Gilbert 	int port_a, port_b;
587c65b1445SDouglas Gilbert 
588c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
589c65b1445SDouglas Gilbert 	port_b = port_a + 1;
590c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
591c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
592c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
593c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
594c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
595c65b1445SDouglas Gilbert 	num += 6;
596c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
597c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
598c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
599c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
600c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
601c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
602c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
603c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
604c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
605c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
606c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
607c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
608c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
609c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
610c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
611c65b1445SDouglas Gilbert 
612c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
613c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
614c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
615c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
616c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
617c65b1445SDouglas Gilbert 	num += 6;
618c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
619c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
620c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
621c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
622c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
623c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
624c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
625c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
626c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
627c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
628c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
629c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
630c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
631c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
632c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
633c65b1445SDouglas Gilbert 
634c65b1445SDouglas Gilbert 	return num;
635c65b1445SDouglas Gilbert }
636c65b1445SDouglas Gilbert 
637c65b1445SDouglas Gilbert 
638c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
639c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
640c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
641c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
642c65b1445SDouglas Gilbert '1','2','3','4',
643c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
644c65b1445SDouglas Gilbert 0xec,0,0,0,
645c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
646c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
647c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
648c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
649c65b1445SDouglas Gilbert 0x53,0x41,
650c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
651c65b1445SDouglas Gilbert 0x20,0x20,
652c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
653c65b1445SDouglas Gilbert 0x10,0x80,
654c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
655c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
656c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
657c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
658c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
659c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
660c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,0,
661c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
662c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
663c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
664c65b1445SDouglas Gilbert 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
665c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
666c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
667c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,0,
668c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
669c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
670c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
671c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
672c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
673c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
674c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
675c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
676c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
677c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
678c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
679c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
680c65b1445SDouglas Gilbert };
681c65b1445SDouglas Gilbert 
682c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
683c65b1445SDouglas Gilbert {
684c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
685c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
686c65b1445SDouglas Gilbert }
687c65b1445SDouglas Gilbert 
688c65b1445SDouglas Gilbert 
6891e49f785SDouglas Gilbert /* Block limits VPD page (SBC-3) */
690c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
6911e49f785SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64,
6921e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6931e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
6941e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
695c65b1445SDouglas Gilbert };
696c65b1445SDouglas Gilbert 
697c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
698c65b1445SDouglas Gilbert {
699ea61fca5SMartin K. Petersen 	unsigned int gran;
700ea61fca5SMartin K. Petersen 
701c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
702ea61fca5SMartin K. Petersen 	gran = 1 << scsi_debug_physblk_exp;
703ea61fca5SMartin K. Petersen 	arr[2] = (gran >> 8) & 0xff;
704ea61fca5SMartin K. Petersen 	arr[3] = gran & 0xff;
705c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
706c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
707c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
708c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
709c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
710c65b1445SDouglas Gilbert 	}
71144d92694SMartin K. Petersen 
71244d92694SMartin K. Petersen 	if (scsi_debug_unmap_max_desc) {
71344d92694SMartin K. Petersen 		unsigned int blocks;
71444d92694SMartin K. Petersen 
71544d92694SMartin K. Petersen 		if (scsi_debug_unmap_max_blocks)
71644d92694SMartin K. Petersen 			blocks = scsi_debug_unmap_max_blocks;
71744d92694SMartin K. Petersen 		else
71844d92694SMartin K. Petersen 			blocks = 0xffffffff;
71944d92694SMartin K. Petersen 
72044d92694SMartin K. Petersen 		put_unaligned_be32(blocks, &arr[16]);
72144d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]);
72244d92694SMartin K. Petersen 	}
72344d92694SMartin K. Petersen 
72444d92694SMartin K. Petersen 	if (scsi_debug_unmap_alignment) {
72544d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]);
72644d92694SMartin K. Petersen 		arr[28] |= 0x80; /* UGAVALID */
72744d92694SMartin K. Petersen 	}
72844d92694SMartin K. Petersen 
72944d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity) {
73044d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]);
73144d92694SMartin K. Petersen 		return 0x3c; /* Mandatory page length for thin provisioning */
73244d92694SMartin K. Petersen 	}
73344d92694SMartin K. Petersen 
734c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
7351da177e4SLinus Torvalds }
7361da177e4SLinus Torvalds 
7371e49f785SDouglas Gilbert /* Block device characteristics VPD page (SBC-3) */
738eac6e8e4SMatthew Wilcox static int inquiry_evpd_b1(unsigned char *arr)
739eac6e8e4SMatthew Wilcox {
740eac6e8e4SMatthew Wilcox 	memset(arr, 0, 0x3c);
741eac6e8e4SMatthew Wilcox 	arr[0] = 0;
7421e49f785SDouglas Gilbert 	arr[1] = 1;	/* non rotating medium (e.g. solid state) */
7431e49f785SDouglas Gilbert 	arr[2] = 0;
7441e49f785SDouglas Gilbert 	arr[3] = 5;	/* less than 1.8" */
745eac6e8e4SMatthew Wilcox 
746eac6e8e4SMatthew Wilcox 	return 0x3c;
747eac6e8e4SMatthew Wilcox }
7481da177e4SLinus Torvalds 
7491da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
750c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
7531da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
7541da177e4SLinus Torvalds {
7551da177e4SLinus Torvalds 	unsigned char pq_pdt;
7565a09e398SHannes Reinecke 	unsigned char * arr;
7571da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
7585a09e398SHannes Reinecke 	int alloc_len, n, ret;
7591da177e4SLinus Torvalds 
7601da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
7616f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
7626f3cbf55SDouglas Gilbert 	if (! arr)
7636f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
764c65b1445SDouglas Gilbert 	if (devip->wlun)
765c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
766c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
767c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
768c65b1445SDouglas Gilbert 	else
7691da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
7701da177e4SLinus Torvalds 	arr[0] = pq_pdt;
7711da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
7721da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
7731da177e4SLinus Torvalds 			       	0);
7745a09e398SHannes Reinecke 		kfree(arr);
7751da177e4SLinus Torvalds 		return check_condition_result;
7761da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
7775a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
778c65b1445SDouglas Gilbert 		char lu_id_str[6];
779c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
7801da177e4SLinus Torvalds 
7815a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
7825a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
78323183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
78423183910SDouglas Gilbert 			host_no = 0;
785c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
786c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
787c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
788c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
789c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
7901da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
791c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
792c65b1445SDouglas Gilbert 			n = 4;
793c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
794c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
795c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
796c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
797c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
798c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
799c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
800c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
801c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
802c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
803eac6e8e4SMatthew Wilcox 			arr[n++] = 0xb1;  /* Block characteristics (SBC) */
804c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
8051da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
806c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
8071da177e4SLinus Torvalds 			arr[3] = len;
808c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
8091da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
810c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
8115a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
8125a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
8135a09e398SHannes Reinecke 						 lu_id_str, len);
814c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
815c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
816c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
817c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
818c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
819c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
820c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
821c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
822c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
823c6a44287SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE3_PROTECTION)
824c6a44287SMartin K. Petersen 				arr[4] = 0x4;	/* SPT: GRD_CHK:1 */
825c6a44287SMartin K. Petersen 			else if (scsi_debug_dif)
826c6a44287SMartin K. Petersen 				arr[4] = 0x5;   /* SPT: GRD_CHK:1, REF_CHK:1 */
827c6a44287SMartin K. Petersen 			else
828c65b1445SDouglas Gilbert 				arr[4] = 0x0;   /* no protection stuff */
829c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
830c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
831c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
832c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
833c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
834c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
835c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
836c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
837c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
838c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
839c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
840c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
841c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
842c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
843c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
844c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
845c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
846c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
847c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
848eac6e8e4SMatthew Wilcox 		} else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */
849eac6e8e4SMatthew Wilcox 			arr[1] = cmd[2];        /*sanity */
850eac6e8e4SMatthew Wilcox 			arr[3] = inquiry_evpd_b1(&arr[4]);
8511da177e4SLinus Torvalds 		} else {
8521da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
8531da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
8541da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
8555a09e398SHannes Reinecke 			kfree(arr);
8561da177e4SLinus Torvalds 			return check_condition_result;
8571da177e4SLinus Torvalds 		}
858c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
8595a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
860c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
8615a09e398SHannes Reinecke 		kfree(arr);
8625a09e398SHannes Reinecke 		return ret;
8631da177e4SLinus Torvalds 	}
8641da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
8651da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
8661da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
8671da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
8681da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
869c6a44287SMartin K. Petersen 	arr[5] = scsi_debug_dif ? 1 : 0; /* PROTECT bit */
8705a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
8715a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
872c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
8731da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
874c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
8751da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
8761da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
8771da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
8781da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
879c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
880c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
881c65b1445SDouglas Gilbert 	n = 62;
8821da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
883c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
8841da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
885c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
8861da177e4SLinus Torvalds 	}
887c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
8885a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
8891da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
8905a09e398SHannes Reinecke 	kfree(arr);
8915a09e398SHannes Reinecke 	return ret;
8921da177e4SLinus Torvalds }
8931da177e4SLinus Torvalds 
8941da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
8951da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
8961da177e4SLinus Torvalds {
8971da177e4SLinus Torvalds 	unsigned char * sbuff;
8981da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
8991da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
900c65b1445SDouglas Gilbert 	int want_dsense;
9011da177e4SLinus Torvalds 	int len = 18;
9021da177e4SLinus Torvalds 
903c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
9041da177e4SLinus Torvalds 	if (devip->reset == 1)
905c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
906c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
9071da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
908c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
909c65b1445SDouglas Gilbert 		if (want_dsense) {
910c65b1445SDouglas Gilbert 			arr[0] = 0x72;
911c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
912c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
913c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
914c65b1445SDouglas Gilbert 		} else {
915c65b1445SDouglas Gilbert 			arr[0] = 0x70;
916c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
917c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
918c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
919c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
920c65b1445SDouglas Gilbert 		}
921c65b1445SDouglas Gilbert 	} else {
922c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
9231da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
9241da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
925c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
9261da177e4SLinus Torvalds 			arr[0] = 0x72;
9271da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
9281da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
9291da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
9301da177e4SLinus Torvalds 			len = 8;
931c65b1445SDouglas Gilbert 		}
932c65b1445SDouglas Gilbert 	}
933c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
9341da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
9351da177e4SLinus Torvalds }
9361da177e4SLinus Torvalds 
937c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
938c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
939c65b1445SDouglas Gilbert {
940c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
941c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
942c65b1445SDouglas Gilbert 
943c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
944c65b1445SDouglas Gilbert 		return errsts;
945c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
946c65b1445SDouglas Gilbert 	if (power_cond) {
947c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
948c65b1445SDouglas Gilbert 			       	0);
949c65b1445SDouglas Gilbert 		return check_condition_result;
950c65b1445SDouglas Gilbert 	}
951c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
952c65b1445SDouglas Gilbert 	if (start == devip->stopped)
953c65b1445SDouglas Gilbert 		devip->stopped = !start;
954c65b1445SDouglas Gilbert 	return 0;
955c65b1445SDouglas Gilbert }
956c65b1445SDouglas Gilbert 
95728898873SFUJITA Tomonori static sector_t get_sdebug_capacity(void)
95828898873SFUJITA Tomonori {
95928898873SFUJITA Tomonori 	if (scsi_debug_virtual_gb > 0)
9605447ed6cSDouglas Gilbert 		return (sector_t)scsi_debug_virtual_gb *
9615447ed6cSDouglas Gilbert 			(1073741824 / scsi_debug_sector_size);
96228898873SFUJITA Tomonori 	else
96328898873SFUJITA Tomonori 		return sdebug_store_sectors;
96428898873SFUJITA Tomonori }
96528898873SFUJITA Tomonori 
9661da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
9671da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
9681da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9691da177e4SLinus Torvalds {
9701da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
971c65b1445SDouglas Gilbert 	unsigned int capac;
9721da177e4SLinus Torvalds 	int errsts;
9731da177e4SLinus Torvalds 
974c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
9751da177e4SLinus Torvalds 		return errsts;
976c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
97728898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
9781da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
979c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
980c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
9811da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
9821da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
9831da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
9841da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
985c65b1445SDouglas Gilbert 	} else {
986c65b1445SDouglas Gilbert 		arr[0] = 0xff;
987c65b1445SDouglas Gilbert 		arr[1] = 0xff;
988c65b1445SDouglas Gilbert 		arr[2] = 0xff;
989c65b1445SDouglas Gilbert 		arr[3] = 0xff;
990c65b1445SDouglas Gilbert 	}
991597136abSMartin K. Petersen 	arr[6] = (scsi_debug_sector_size >> 8) & 0xff;
992597136abSMartin K. Petersen 	arr[7] = scsi_debug_sector_size & 0xff;
9931da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
9941da177e4SLinus Torvalds }
9951da177e4SLinus Torvalds 
996c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
997c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
998c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
999c65b1445SDouglas Gilbert {
1000c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1001c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1002c65b1445SDouglas Gilbert 	unsigned long long capac;
1003c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1004c65b1445SDouglas Gilbert 
1005c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1006c65b1445SDouglas Gilbert 		return errsts;
1007c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1008c65b1445SDouglas Gilbert 		     + cmd[13]);
1009c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
101028898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
1011c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1012c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1013c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1014c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1015597136abSMartin K. Petersen 	arr[8] = (scsi_debug_sector_size >> 24) & 0xff;
1016597136abSMartin K. Petersen 	arr[9] = (scsi_debug_sector_size >> 16) & 0xff;
1017597136abSMartin K. Petersen 	arr[10] = (scsi_debug_sector_size >> 8) & 0xff;
1018597136abSMartin K. Petersen 	arr[11] = scsi_debug_sector_size & 0xff;
1019ea61fca5SMartin K. Petersen 	arr[13] = scsi_debug_physblk_exp & 0xf;
1020ea61fca5SMartin K. Petersen 	arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
102144d92694SMartin K. Petersen 
102244d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
102344d92694SMartin K. Petersen 		arr[14] |= 0x80; /* TPE */
102444d92694SMartin K. Petersen 
1025ea61fca5SMartin K. Petersen 	arr[15] = scsi_debug_lowest_aligned & 0xff;
1026c6a44287SMartin K. Petersen 
1027c6a44287SMartin K. Petersen 	if (scsi_debug_dif) {
1028c6a44287SMartin K. Petersen 		arr[12] = (scsi_debug_dif - 1) << 1; /* P_TYPE */
1029c6a44287SMartin K. Petersen 		arr[12] |= 1; /* PROT_EN */
1030c6a44287SMartin K. Petersen 	}
1031c6a44287SMartin K. Petersen 
1032c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1033c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1034c65b1445SDouglas Gilbert }
1035c65b1445SDouglas Gilbert 
10365a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
10375a09e398SHannes Reinecke 
10385a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
10395a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
10405a09e398SHannes Reinecke {
10415a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10425a09e398SHannes Reinecke 	unsigned char * arr;
10435a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
10445a09e398SHannes Reinecke 	int n, ret, alen, rlen;
10455a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
10465a09e398SHannes Reinecke 
10475a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
10485a09e398SHannes Reinecke 		+ cmd[9]);
10495a09e398SHannes Reinecke 
10506f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
10516f3cbf55SDouglas Gilbert 	if (! arr)
10526f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
10535a09e398SHannes Reinecke 	/*
10545a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
10555a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
10565a09e398SHannes Reinecke 	 * So we create two port groups with one port each
10575a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
10585a09e398SHannes Reinecke 	 */
10595a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
10605a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
10615a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
10625a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
10635a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
10645a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
10655a09e398SHannes Reinecke 
10665a09e398SHannes Reinecke 	/*
10675a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
10685a09e398SHannes Reinecke 	 */
10695a09e398SHannes Reinecke 	n = 4;
10705a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
10715a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
10725a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
10735a09e398SHannes Reinecke 	} else {
10745a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
10755a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
10765a09e398SHannes Reinecke 	}
10775a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
10785a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
10795a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10805a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
10815a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
10825a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
10835a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10845a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10855a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
10865a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
10875a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
10885a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
10895a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
10905a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
10915a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10925a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
10935a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
10945a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
10955a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10965a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10975a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
10985a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
10995a09e398SHannes Reinecke 
11005a09e398SHannes Reinecke 	rlen = n - 4;
11015a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
11025a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
11035a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
11045a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
11055a09e398SHannes Reinecke 
11065a09e398SHannes Reinecke 	/*
11075a09e398SHannes Reinecke 	 * Return the smallest value of either
11085a09e398SHannes Reinecke 	 * - The allocated length
11095a09e398SHannes Reinecke 	 * - The constructed command length
11105a09e398SHannes Reinecke 	 * - The maximum array size
11115a09e398SHannes Reinecke 	 */
11125a09e398SHannes Reinecke 	rlen = min(alen,n);
11135a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
11145a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
11155a09e398SHannes Reinecke 	kfree(arr);
11165a09e398SHannes Reinecke 	return ret;
11175a09e398SHannes Reinecke }
11185a09e398SHannes Reinecke 
11191da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
11201da177e4SLinus Torvalds 
11211da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
11221da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
11231da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
11241da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
11251da177e4SLinus Torvalds 
11261da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
11271da177e4SLinus Torvalds 	if (1 == pcontrol)
11281da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
11291da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
11301da177e4SLinus Torvalds }
11311da177e4SLinus Torvalds 
11321da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
11331da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
11341da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
11351da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
11381da177e4SLinus Torvalds 	if (1 == pcontrol)
11391da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
11401da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
11411da177e4SLinus Torvalds }
11421da177e4SLinus Torvalds 
11431da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
11441da177e4SLinus Torvalds {       /* Format device page for mode_sense */
11451da177e4SLinus Torvalds 	unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
11461da177e4SLinus Torvalds 				     0, 0, 0, 0, 0, 0, 0, 0,
11471da177e4SLinus Torvalds 				     0, 0, 0, 0, 0x40, 0, 0, 0};
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds 	memcpy(p, format_pg, sizeof(format_pg));
11501da177e4SLinus Torvalds 	p[10] = (sdebug_sectors_per >> 8) & 0xff;
11511da177e4SLinus Torvalds 	p[11] = sdebug_sectors_per & 0xff;
1152597136abSMartin K. Petersen 	p[12] = (scsi_debug_sector_size >> 8) & 0xff;
1153597136abSMartin K. Petersen 	p[13] = scsi_debug_sector_size & 0xff;
11541da177e4SLinus Torvalds 	if (DEV_REMOVEABLE(target))
11551da177e4SLinus Torvalds 		p[20] |= 0x20; /* should agree with INQUIRY */
11561da177e4SLinus Torvalds 	if (1 == pcontrol)
11571da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(format_pg) - 2);
11581da177e4SLinus Torvalds 	return sizeof(format_pg);
11591da177e4SLinus Torvalds }
11601da177e4SLinus Torvalds 
11611da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
11621da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
11631da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
11641da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
11651da177e4SLinus Torvalds 
11661da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
11671da177e4SLinus Torvalds 	if (1 == pcontrol)
11681da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
11691da177e4SLinus Torvalds 	return sizeof(caching_pg);
11701da177e4SLinus Torvalds }
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
11731da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1174c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1175c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1176c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
11771da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
11781da177e4SLinus Torvalds 
11791da177e4SLinus Torvalds 	if (scsi_debug_dsense)
11801da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1181c65b1445SDouglas Gilbert 	else
1182c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
1183c6a44287SMartin K. Petersen 
1184c6a44287SMartin K. Petersen 	if (scsi_debug_ato)
1185c6a44287SMartin K. Petersen 		ctrl_m_pg[5] |= 0x80; /* ATO=1 */
1186c6a44287SMartin K. Petersen 
11871da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
11881da177e4SLinus Torvalds 	if (1 == pcontrol)
1189c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1190c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1191c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
11921da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
11931da177e4SLinus Torvalds }
11941da177e4SLinus Torvalds 
1195c65b1445SDouglas Gilbert 
11961da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
11971da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1198c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
11991da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1200c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1201c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1202c65b1445SDouglas Gilbert 
12031da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
12041da177e4SLinus Torvalds 	if (1 == pcontrol)
1205c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1206c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1207c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
12081da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
12091da177e4SLinus Torvalds }
12101da177e4SLinus Torvalds 
1211c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1212c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1213c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1214c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1215c65b1445SDouglas Gilbert 
1216c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1217c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1218c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1219c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1220c65b1445SDouglas Gilbert }
1221c65b1445SDouglas Gilbert 
1222c65b1445SDouglas Gilbert 
1223c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1224c65b1445SDouglas Gilbert 			      int target_dev_id)
1225c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1226c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1227c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1228c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1229c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1230c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1231c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1232c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1233c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1234c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1235c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1236c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1237c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1238c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1239c65b1445SDouglas Gilbert 		};
1240c65b1445SDouglas Gilbert 	int port_a, port_b;
1241c65b1445SDouglas Gilbert 
1242c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1243c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1244c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1245c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1246c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1247c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1248c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1249c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1250c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1251c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1252c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1253c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1254c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1255c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1256c65b1445SDouglas Gilbert }
1257c65b1445SDouglas Gilbert 
1258c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1259c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1260c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1261c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1262c65b1445SDouglas Gilbert 		};
1263c65b1445SDouglas Gilbert 
1264c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1265c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1266c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1267c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1268c65b1445SDouglas Gilbert }
1269c65b1445SDouglas Gilbert 
12701da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
12711da177e4SLinus Torvalds 
12721da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
12731da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
12741da177e4SLinus Torvalds {
127523183910SDouglas Gilbert 	unsigned char dbd, llbaa;
127623183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
12771da177e4SLinus Torvalds 	unsigned char dev_spec;
127823183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
12791da177e4SLinus Torvalds 	unsigned char * ap;
12801da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
12811da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
12821da177e4SLinus Torvalds 
1283c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
12841da177e4SLinus Torvalds 		return errsts;
128523183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
12861da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
12871da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
12881da177e4SLinus Torvalds 	subpcode = cmd[3];
12891da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
129023183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
129123183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
129223183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
129323183910SDouglas Gilbert 	else
129423183910SDouglas Gilbert 		bd_len = 0;
12951da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
12961da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
12971da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
12981da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
12991da177e4SLinus Torvalds 			       	0);
13001da177e4SLinus Torvalds 		return check_condition_result;
13011da177e4SLinus Torvalds 	}
1302c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1303c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
130423183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
130523183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
130623183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
130723183910SDouglas Gilbert 	else
130823183910SDouglas Gilbert 		dev_spec = 0x0;
13091da177e4SLinus Torvalds 	if (msense_6) {
13101da177e4SLinus Torvalds 		arr[2] = dev_spec;
131123183910SDouglas Gilbert 		arr[3] = bd_len;
13121da177e4SLinus Torvalds 		offset = 4;
13131da177e4SLinus Torvalds 	} else {
13141da177e4SLinus Torvalds 		arr[3] = dev_spec;
131523183910SDouglas Gilbert 		if (16 == bd_len)
131623183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
131723183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
13181da177e4SLinus Torvalds 		offset = 8;
13191da177e4SLinus Torvalds 	}
13201da177e4SLinus Torvalds 	ap = arr + offset;
132128898873SFUJITA Tomonori 	if ((bd_len > 0) && (!sdebug_capacity))
132228898873SFUJITA Tomonori 		sdebug_capacity = get_sdebug_capacity();
132328898873SFUJITA Tomonori 
132423183910SDouglas Gilbert 	if (8 == bd_len) {
132523183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
132623183910SDouglas Gilbert 			ap[0] = 0xff;
132723183910SDouglas Gilbert 			ap[1] = 0xff;
132823183910SDouglas Gilbert 			ap[2] = 0xff;
132923183910SDouglas Gilbert 			ap[3] = 0xff;
133023183910SDouglas Gilbert 		} else {
133123183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
133223183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
133323183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
133423183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
133523183910SDouglas Gilbert 		}
1336597136abSMartin K. Petersen 		ap[6] = (scsi_debug_sector_size >> 8) & 0xff;
1337597136abSMartin K. Petersen 		ap[7] = scsi_debug_sector_size & 0xff;
133823183910SDouglas Gilbert 		offset += bd_len;
133923183910SDouglas Gilbert 		ap = arr + offset;
134023183910SDouglas Gilbert 	} else if (16 == bd_len) {
134123183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
134223183910SDouglas Gilbert 
134323183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
134423183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
1345597136abSMartin K. Petersen 		ap[12] = (scsi_debug_sector_size >> 24) & 0xff;
1346597136abSMartin K. Petersen 		ap[13] = (scsi_debug_sector_size >> 16) & 0xff;
1347597136abSMartin K. Petersen 		ap[14] = (scsi_debug_sector_size >> 8) & 0xff;
1348597136abSMartin K. Petersen 		ap[15] = scsi_debug_sector_size & 0xff;
134923183910SDouglas Gilbert 		offset += bd_len;
135023183910SDouglas Gilbert 		ap = arr + offset;
135123183910SDouglas Gilbert 	}
13521da177e4SLinus Torvalds 
1353c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1354c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
13551da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
13561da177e4SLinus Torvalds 			       	0);
13571da177e4SLinus Torvalds 		return check_condition_result;
13581da177e4SLinus Torvalds 	}
13591da177e4SLinus Torvalds 	switch (pcode) {
13601da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
13611da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
13621da177e4SLinus Torvalds 		offset += len;
13631da177e4SLinus Torvalds 		break;
13641da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
13651da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
13661da177e4SLinus Torvalds 		offset += len;
13671da177e4SLinus Torvalds 		break;
13681da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
13691da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
13701da177e4SLinus Torvalds                 offset += len;
13711da177e4SLinus Torvalds                 break;
13721da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
13731da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
13741da177e4SLinus Torvalds 		offset += len;
13751da177e4SLinus Torvalds 		break;
13761da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
13771da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
13781da177e4SLinus Torvalds 		offset += len;
13791da177e4SLinus Torvalds 		break;
1380c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1381c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1382c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1383c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1384c65b1445SDouglas Gilbert 			return check_condition_result;
1385c65b1445SDouglas Gilbert 	        }
1386c65b1445SDouglas Gilbert 		len = 0;
1387c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1388c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1389c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1390c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1391c65b1445SDouglas Gilbert 						  target_dev_id);
1392c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1393c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1394c65b1445SDouglas Gilbert 		offset += len;
1395c65b1445SDouglas Gilbert 		break;
13961da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
13971da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
13981da177e4SLinus Torvalds 		offset += len;
13991da177e4SLinus Torvalds 		break;
14001da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1401c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
14021da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
14031da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
14041da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
14051da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
14061da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1407c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1408c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1409c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1410c65b1445SDouglas Gilbert 						  target, target_dev_id);
1411c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1412c65b1445SDouglas Gilbert 			}
14131da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1414c65b1445SDouglas Gilbert 		} else {
1415c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1416c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1417c65b1445SDouglas Gilbert 			return check_condition_result;
1418c65b1445SDouglas Gilbert                 }
14191da177e4SLinus Torvalds 		offset += len;
14201da177e4SLinus Torvalds 		break;
14211da177e4SLinus Torvalds 	default:
14221da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14231da177e4SLinus Torvalds 			       	0);
14241da177e4SLinus Torvalds 		return check_condition_result;
14251da177e4SLinus Torvalds 	}
14261da177e4SLinus Torvalds 	if (msense_6)
14271da177e4SLinus Torvalds 		arr[0] = offset - 1;
14281da177e4SLinus Torvalds 	else {
14291da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
14301da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
14311da177e4SLinus Torvalds 	}
14321da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
14331da177e4SLinus Torvalds }
14341da177e4SLinus Torvalds 
1435c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1436c65b1445SDouglas Gilbert 
1437c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1438c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1439c65b1445SDouglas Gilbert {
1440c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1441c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1442c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1443c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1444c65b1445SDouglas Gilbert 
1445c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1446c65b1445SDouglas Gilbert 		return errsts;
1447c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1448c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1449c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1450c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1451c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1452c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1453c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1454c65b1445SDouglas Gilbert 		return check_condition_result;
1455c65b1445SDouglas Gilbert 	}
1456c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1457c65b1445SDouglas Gilbert         if (-1 == res)
1458c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1459c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1460c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1461c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1462c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1463c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1464c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
146523183910SDouglas Gilbert 	if (md_len > 2) {
1466c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1467c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1468c65b1445SDouglas Gilbert 		return check_condition_result;
1469c65b1445SDouglas Gilbert 	}
1470c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1471c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1472c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1473c65b1445SDouglas Gilbert 	if (ps) {
1474c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1475c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1476c65b1445SDouglas Gilbert 		return check_condition_result;
1477c65b1445SDouglas Gilbert 	}
1478c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1479c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1480c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1481c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1482c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1483c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1484c65b1445SDouglas Gilbert 		return check_condition_result;
1485c65b1445SDouglas Gilbert 	}
1486c65b1445SDouglas Gilbert 	switch (mpage) {
1487c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1488c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1489c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1490c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1491c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1492c65b1445SDouglas Gilbert 			return 0;
1493c65b1445SDouglas Gilbert 		}
1494c65b1445SDouglas Gilbert 		break;
1495c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1496c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1497c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1498c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1499c65b1445SDouglas Gilbert 			return 0;
1500c65b1445SDouglas Gilbert 		}
1501c65b1445SDouglas Gilbert 		break;
1502c65b1445SDouglas Gilbert 	default:
1503c65b1445SDouglas Gilbert 		break;
1504c65b1445SDouglas Gilbert 	}
1505c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1506c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1507c65b1445SDouglas Gilbert 	return check_condition_result;
1508c65b1445SDouglas Gilbert }
1509c65b1445SDouglas Gilbert 
1510c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1511c65b1445SDouglas Gilbert {
1512c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1513c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1514c65b1445SDouglas Gilbert 		};
1515c65b1445SDouglas Gilbert 
1516c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1517c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1518c65b1445SDouglas Gilbert }
1519c65b1445SDouglas Gilbert 
1520c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1521c65b1445SDouglas Gilbert {
1522c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1523c65b1445SDouglas Gilbert 		};
1524c65b1445SDouglas Gilbert 
1525c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1526c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1527c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1528c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1529c65b1445SDouglas Gilbert 	}
1530c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1531c65b1445SDouglas Gilbert }
1532c65b1445SDouglas Gilbert 
1533c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1534c65b1445SDouglas Gilbert 
1535c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1536c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1537c65b1445SDouglas Gilbert {
153823183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1539c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1540c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1541c65b1445SDouglas Gilbert 
1542c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1543c65b1445SDouglas Gilbert 		return errsts;
1544c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1545c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1546c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1547c65b1445SDouglas Gilbert 	if (ppc || sp) {
1548c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1549c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1550c65b1445SDouglas Gilbert 		return check_condition_result;
1551c65b1445SDouglas Gilbert 	}
1552c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1553c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
155423183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1555c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1556c65b1445SDouglas Gilbert 	arr[0] = pcode;
155723183910SDouglas Gilbert 	if (0 == subpcode) {
1558c65b1445SDouglas Gilbert 		switch (pcode) {
1559c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1560c65b1445SDouglas Gilbert 			n = 4;
1561c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1562c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1563c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1564c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1565c65b1445SDouglas Gilbert 			break;
1566c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1567c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1568c65b1445SDouglas Gilbert 			break;
1569c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1570c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1571c65b1445SDouglas Gilbert 			break;
1572c65b1445SDouglas Gilbert 		default:
1573c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1574c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1575c65b1445SDouglas Gilbert 			return check_condition_result;
1576c65b1445SDouglas Gilbert 		}
157723183910SDouglas Gilbert 	} else if (0xff == subpcode) {
157823183910SDouglas Gilbert 		arr[0] |= 0x40;
157923183910SDouglas Gilbert 		arr[1] = subpcode;
158023183910SDouglas Gilbert 		switch (pcode) {
158123183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
158223183910SDouglas Gilbert 			n = 4;
158323183910SDouglas Gilbert 			arr[n++] = 0x0;
158423183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
158523183910SDouglas Gilbert 			arr[n++] = 0x0;
158623183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
158723183910SDouglas Gilbert 			arr[n++] = 0xd;
158823183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
158923183910SDouglas Gilbert 			arr[n++] = 0x2f;
159023183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
159123183910SDouglas Gilbert 			arr[3] = n - 4;
159223183910SDouglas Gilbert 			break;
159323183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
159423183910SDouglas Gilbert 			n = 4;
159523183910SDouglas Gilbert 			arr[n++] = 0xd;
159623183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
159723183910SDouglas Gilbert 			arr[3] = n - 4;
159823183910SDouglas Gilbert 			break;
159923183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
160023183910SDouglas Gilbert 			n = 4;
160123183910SDouglas Gilbert 			arr[n++] = 0x2f;
160223183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
160323183910SDouglas Gilbert 			arr[3] = n - 4;
160423183910SDouglas Gilbert 			break;
160523183910SDouglas Gilbert 		default:
160623183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
160723183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
160823183910SDouglas Gilbert 			return check_condition_result;
160923183910SDouglas Gilbert 		}
161023183910SDouglas Gilbert 	} else {
161123183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
161223183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
161323183910SDouglas Gilbert 		return check_condition_result;
161423183910SDouglas Gilbert 	}
1615c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1616c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1617c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1618c65b1445SDouglas Gilbert }
1619c65b1445SDouglas Gilbert 
162019789100SFUJITA Tomonori static int check_device_access_params(struct sdebug_dev_info *devi,
162119789100SFUJITA Tomonori 				      unsigned long long lba, unsigned int num)
16221da177e4SLinus Torvalds {
1623c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
162419789100SFUJITA Tomonori 		mk_sense_buffer(devi, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, 0);
16251da177e4SLinus Torvalds 		return check_condition_result;
16261da177e4SLinus Torvalds 	}
1627c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1628c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
162919789100SFUJITA Tomonori 		mk_sense_buffer(devi, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
1630c65b1445SDouglas Gilbert 		return check_condition_result;
1631c65b1445SDouglas Gilbert 	}
163219789100SFUJITA Tomonori 	return 0;
163319789100SFUJITA Tomonori }
163419789100SFUJITA Tomonori 
163519789100SFUJITA Tomonori static int do_device_access(struct scsi_cmnd *scmd,
163619789100SFUJITA Tomonori 			    struct sdebug_dev_info *devi,
163719789100SFUJITA Tomonori 			    unsigned long long lba, unsigned int num, int write)
163819789100SFUJITA Tomonori {
163919789100SFUJITA Tomonori 	int ret;
164019789100SFUJITA Tomonori 	unsigned int block, rest = 0;
164119789100SFUJITA Tomonori 	int (*func)(struct scsi_cmnd *, unsigned char *, int);
164219789100SFUJITA Tomonori 
164319789100SFUJITA Tomonori 	func = write ? fetch_to_dev_buffer : fill_from_dev_buffer;
164419789100SFUJITA Tomonori 
164519789100SFUJITA Tomonori 	block = do_div(lba, sdebug_store_sectors);
164619789100SFUJITA Tomonori 	if (block + num > sdebug_store_sectors)
164719789100SFUJITA Tomonori 		rest = block + num - sdebug_store_sectors;
164819789100SFUJITA Tomonori 
1649597136abSMartin K. Petersen 	ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
1650597136abSMartin K. Petersen 		   (num - rest) * scsi_debug_sector_size);
165119789100SFUJITA Tomonori 	if (!ret && rest)
1652597136abSMartin K. Petersen 		ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
165319789100SFUJITA Tomonori 
165419789100SFUJITA Tomonori 	return ret;
165519789100SFUJITA Tomonori }
165619789100SFUJITA Tomonori 
1657c6a44287SMartin K. Petersen static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
1658395cef03SMartin K. Petersen 			    unsigned int sectors, u32 ei_lba)
1659c6a44287SMartin K. Petersen {
1660c6a44287SMartin K. Petersen 	unsigned int i, resid;
1661c6a44287SMartin K. Petersen 	struct scatterlist *psgl;
1662c6a44287SMartin K. Petersen 	struct sd_dif_tuple *sdt;
1663c6a44287SMartin K. Petersen 	sector_t sector;
1664c6a44287SMartin K. Petersen 	sector_t tmp_sec = start_sec;
1665c6a44287SMartin K. Petersen 	void *paddr;
1666c6a44287SMartin K. Petersen 
1667c6a44287SMartin K. Petersen 	start_sec = do_div(tmp_sec, sdebug_store_sectors);
1668c6a44287SMartin K. Petersen 
1669c6a44287SMartin K. Petersen 	sdt = (struct sd_dif_tuple *)(dif_storep + dif_offset(start_sec));
1670c6a44287SMartin K. Petersen 
1671c6a44287SMartin K. Petersen 	for (i = 0 ; i < sectors ; i++) {
1672c6a44287SMartin K. Petersen 		u16 csum;
1673c6a44287SMartin K. Petersen 
1674c6a44287SMartin K. Petersen 		if (sdt[i].app_tag == 0xffff)
1675c6a44287SMartin K. Petersen 			continue;
1676c6a44287SMartin K. Petersen 
1677c6a44287SMartin K. Petersen 		sector = start_sec + i;
1678c6a44287SMartin K. Petersen 
1679c6a44287SMartin K. Petersen 		switch (scsi_debug_guard) {
1680c6a44287SMartin K. Petersen 		case 1:
1681c6a44287SMartin K. Petersen 			csum = ip_compute_csum(fake_storep +
1682c6a44287SMartin K. Petersen 					       sector * scsi_debug_sector_size,
1683c6a44287SMartin K. Petersen 					       scsi_debug_sector_size);
1684c6a44287SMartin K. Petersen 			break;
1685c6a44287SMartin K. Petersen 		case 0:
1686c6a44287SMartin K. Petersen 			csum = crc_t10dif(fake_storep +
1687c6a44287SMartin K. Petersen 					  sector * scsi_debug_sector_size,
1688c6a44287SMartin K. Petersen 					  scsi_debug_sector_size);
1689c6a44287SMartin K. Petersen 			csum = cpu_to_be16(csum);
1690c6a44287SMartin K. Petersen 			break;
1691c6a44287SMartin K. Petersen 		default:
1692c6a44287SMartin K. Petersen 			BUG();
1693c6a44287SMartin K. Petersen 		}
1694c6a44287SMartin K. Petersen 
1695c6a44287SMartin K. Petersen 		if (sdt[i].guard_tag != csum) {
1696c6a44287SMartin K. Petersen 			printk(KERN_ERR "%s: GUARD check failed on sector %lu" \
1697c6a44287SMartin K. Petersen 			       " rcvd 0x%04x, data 0x%04x\n", __func__,
1698c6a44287SMartin K. Petersen 			       (unsigned long)sector,
1699c6a44287SMartin K. Petersen 			       be16_to_cpu(sdt[i].guard_tag),
1700c6a44287SMartin K. Petersen 			       be16_to_cpu(csum));
1701c6a44287SMartin K. Petersen 			dif_errors++;
1702c6a44287SMartin K. Petersen 			return 0x01;
1703c6a44287SMartin K. Petersen 		}
1704c6a44287SMartin K. Petersen 
1705395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
1706c6a44287SMartin K. Petersen 		    be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) {
1707c6a44287SMartin K. Petersen 			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
1708c6a44287SMartin K. Petersen 			       __func__, (unsigned long)sector);
1709c6a44287SMartin K. Petersen 			dif_errors++;
1710c6a44287SMartin K. Petersen 			return 0x03;
1711c6a44287SMartin K. Petersen 		}
1712395cef03SMartin K. Petersen 
1713395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
1714395cef03SMartin K. Petersen 		    be32_to_cpu(sdt[i].ref_tag) != ei_lba) {
1715395cef03SMartin K. Petersen 			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
1716395cef03SMartin K. Petersen 			       __func__, (unsigned long)sector);
1717395cef03SMartin K. Petersen 			dif_errors++;
1718395cef03SMartin K. Petersen 			return 0x03;
1719395cef03SMartin K. Petersen 		}
1720395cef03SMartin K. Petersen 
1721395cef03SMartin K. Petersen 		ei_lba++;
1722c6a44287SMartin K. Petersen 	}
1723c6a44287SMartin K. Petersen 
1724c6a44287SMartin K. Petersen 	resid = sectors * 8; /* Bytes of protection data to copy into sgl */
1725c6a44287SMartin K. Petersen 	sector = start_sec;
1726c6a44287SMartin K. Petersen 
1727c6a44287SMartin K. Petersen 	scsi_for_each_prot_sg(SCpnt, psgl, scsi_prot_sg_count(SCpnt), i) {
1728c6a44287SMartin K. Petersen 		int len = min(psgl->length, resid);
1729c6a44287SMartin K. Petersen 
1730c6a44287SMartin K. Petersen 		paddr = kmap_atomic(sg_page(psgl), KM_IRQ0) + psgl->offset;
1731c6a44287SMartin K. Petersen 		memcpy(paddr, dif_storep + dif_offset(sector), len);
1732c6a44287SMartin K. Petersen 
1733c6a44287SMartin K. Petersen 		sector += len >> 3;
1734c6a44287SMartin K. Petersen 		if (sector >= sdebug_store_sectors) {
1735c6a44287SMartin K. Petersen 			/* Force wrap */
1736c6a44287SMartin K. Petersen 			tmp_sec = sector;
1737c6a44287SMartin K. Petersen 			sector = do_div(tmp_sec, sdebug_store_sectors);
1738c6a44287SMartin K. Petersen 		}
1739c6a44287SMartin K. Petersen 		resid -= len;
1740c6a44287SMartin K. Petersen 		kunmap_atomic(paddr, KM_IRQ0);
1741c6a44287SMartin K. Petersen 	}
1742c6a44287SMartin K. Petersen 
1743c6a44287SMartin K. Petersen 	dix_reads++;
1744c6a44287SMartin K. Petersen 
1745c6a44287SMartin K. Petersen 	return 0;
1746c6a44287SMartin K. Petersen }
1747c6a44287SMartin K. Petersen 
174819789100SFUJITA Tomonori static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
1749395cef03SMartin K. Petersen 		     unsigned int num, struct sdebug_dev_info *devip,
1750395cef03SMartin K. Petersen 		     u32 ei_lba)
175119789100SFUJITA Tomonori {
175219789100SFUJITA Tomonori 	unsigned long iflags;
175319789100SFUJITA Tomonori 	int ret;
175419789100SFUJITA Tomonori 
175519789100SFUJITA Tomonori 	ret = check_device_access_params(devip, lba, num);
175619789100SFUJITA Tomonori 	if (ret)
175719789100SFUJITA Tomonori 		return ret;
175819789100SFUJITA Tomonori 
17591da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1760c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1761c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1762c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
17631da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
17641da177e4SLinus Torvalds 				0);
1765c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1766c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1767c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1768c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1769c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1770c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1771c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1772c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1773c65b1445SDouglas Gilbert 		}
17741da177e4SLinus Torvalds 		return check_condition_result;
17751da177e4SLinus Torvalds 	}
1776c6a44287SMartin K. Petersen 
1777c6a44287SMartin K. Petersen 	/* DIX + T10 DIF */
1778c6a44287SMartin K. Petersen 	if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
1779395cef03SMartin K. Petersen 		int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba);
1780c6a44287SMartin K. Petersen 
1781c6a44287SMartin K. Petersen 		if (prot_ret) {
1782c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, prot_ret);
1783c6a44287SMartin K. Petersen 			return illegal_condition_result;
1784c6a44287SMartin K. Petersen 		}
1785c6a44287SMartin K. Petersen 	}
1786c6a44287SMartin K. Petersen 
17871da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
178819789100SFUJITA Tomonori 	ret = do_device_access(SCpnt, devip, lba, num, 0);
17891da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
17901da177e4SLinus Torvalds 	return ret;
17911da177e4SLinus Torvalds }
17921da177e4SLinus Torvalds 
1793c6a44287SMartin K. Petersen void dump_sector(unsigned char *buf, int len)
1794c6a44287SMartin K. Petersen {
1795c6a44287SMartin K. Petersen 	int i, j;
1796c6a44287SMartin K. Petersen 
1797c6a44287SMartin K. Petersen 	printk(KERN_ERR ">>> Sector Dump <<<\n");
1798c6a44287SMartin K. Petersen 
1799c6a44287SMartin K. Petersen 	for (i = 0 ; i < len ; i += 16) {
1800c6a44287SMartin K. Petersen 		printk(KERN_ERR "%04d: ", i);
1801c6a44287SMartin K. Petersen 
1802c6a44287SMartin K. Petersen 		for (j = 0 ; j < 16 ; j++) {
1803c6a44287SMartin K. Petersen 			unsigned char c = buf[i+j];
1804c6a44287SMartin K. Petersen 			if (c >= 0x20 && c < 0x7e)
1805c6a44287SMartin K. Petersen 				printk(" %c ", buf[i+j]);
1806c6a44287SMartin K. Petersen 			else
1807c6a44287SMartin K. Petersen 				printk("%02x ", buf[i+j]);
1808c6a44287SMartin K. Petersen 		}
1809c6a44287SMartin K. Petersen 
1810c6a44287SMartin K. Petersen 		printk("\n");
1811c6a44287SMartin K. Petersen 	}
1812c6a44287SMartin K. Petersen }
1813c6a44287SMartin K. Petersen 
1814c6a44287SMartin K. Petersen static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
1815395cef03SMartin K. Petersen 			     unsigned int sectors, u32 ei_lba)
1816c6a44287SMartin K. Petersen {
1817c6a44287SMartin K. Petersen 	int i, j, ret;
1818c6a44287SMartin K. Petersen 	struct sd_dif_tuple *sdt;
1819c6a44287SMartin K. Petersen 	struct scatterlist *dsgl = scsi_sglist(SCpnt);
1820c6a44287SMartin K. Petersen 	struct scatterlist *psgl = scsi_prot_sglist(SCpnt);
1821c6a44287SMartin K. Petersen 	void *daddr, *paddr;
1822c6a44287SMartin K. Petersen 	sector_t tmp_sec = start_sec;
1823c6a44287SMartin K. Petersen 	sector_t sector;
1824c6a44287SMartin K. Petersen 	int ppage_offset;
1825c6a44287SMartin K. Petersen 	unsigned short csum;
1826c6a44287SMartin K. Petersen 
1827c6a44287SMartin K. Petersen 	sector = do_div(tmp_sec, sdebug_store_sectors);
1828c6a44287SMartin K. Petersen 
1829c6a44287SMartin K. Petersen 	BUG_ON(scsi_sg_count(SCpnt) == 0);
1830c6a44287SMartin K. Petersen 	BUG_ON(scsi_prot_sg_count(SCpnt) == 0);
1831c6a44287SMartin K. Petersen 
1832c6a44287SMartin K. Petersen 	paddr = kmap_atomic(sg_page(psgl), KM_IRQ1) + psgl->offset;
1833c6a44287SMartin K. Petersen 	ppage_offset = 0;
1834c6a44287SMartin K. Petersen 
1835c6a44287SMartin K. Petersen 	/* For each data page */
1836c6a44287SMartin K. Petersen 	scsi_for_each_sg(SCpnt, dsgl, scsi_sg_count(SCpnt), i) {
1837c6a44287SMartin K. Petersen 		daddr = kmap_atomic(sg_page(dsgl), KM_IRQ0) + dsgl->offset;
1838c6a44287SMartin K. Petersen 
1839c6a44287SMartin K. Petersen 		/* For each sector-sized chunk in data page */
1840c6a44287SMartin K. Petersen 		for (j = 0 ; j < dsgl->length ; j += scsi_debug_sector_size) {
1841c6a44287SMartin K. Petersen 
1842c6a44287SMartin K. Petersen 			/* If we're at the end of the current
1843c6a44287SMartin K. Petersen 			 * protection page advance to the next one
1844c6a44287SMartin K. Petersen 			 */
1845c6a44287SMartin K. Petersen 			if (ppage_offset >= psgl->length) {
1846c6a44287SMartin K. Petersen 				kunmap_atomic(paddr, KM_IRQ1);
1847c6a44287SMartin K. Petersen 				psgl = sg_next(psgl);
1848c6a44287SMartin K. Petersen 				BUG_ON(psgl == NULL);
1849c6a44287SMartin K. Petersen 				paddr = kmap_atomic(sg_page(psgl), KM_IRQ1)
1850c6a44287SMartin K. Petersen 					+ psgl->offset;
1851c6a44287SMartin K. Petersen 				ppage_offset = 0;
1852c6a44287SMartin K. Petersen 			}
1853c6a44287SMartin K. Petersen 
1854c6a44287SMartin K. Petersen 			sdt = paddr + ppage_offset;
1855c6a44287SMartin K. Petersen 
1856c6a44287SMartin K. Petersen 			switch (scsi_debug_guard) {
1857c6a44287SMartin K. Petersen 			case 1:
1858c6a44287SMartin K. Petersen 				csum = ip_compute_csum(daddr,
1859c6a44287SMartin K. Petersen 						       scsi_debug_sector_size);
1860c6a44287SMartin K. Petersen 				break;
1861c6a44287SMartin K. Petersen 			case 0:
1862c6a44287SMartin K. Petersen 				csum = cpu_to_be16(crc_t10dif(daddr,
1863c6a44287SMartin K. Petersen 						      scsi_debug_sector_size));
1864c6a44287SMartin K. Petersen 				break;
1865c6a44287SMartin K. Petersen 			default:
1866c6a44287SMartin K. Petersen 				BUG();
1867c6a44287SMartin K. Petersen 				ret = 0;
1868c6a44287SMartin K. Petersen 				goto out;
1869c6a44287SMartin K. Petersen 			}
1870c6a44287SMartin K. Petersen 
1871c6a44287SMartin K. Petersen 			if (sdt->guard_tag != csum) {
1872c6a44287SMartin K. Petersen 				printk(KERN_ERR
1873c6a44287SMartin K. Petersen 				       "%s: GUARD check failed on sector %lu " \
1874c6a44287SMartin K. Petersen 				       "rcvd 0x%04x, calculated 0x%04x\n",
1875c6a44287SMartin K. Petersen 				       __func__, (unsigned long)sector,
1876c6a44287SMartin K. Petersen 				       be16_to_cpu(sdt->guard_tag),
1877c6a44287SMartin K. Petersen 				       be16_to_cpu(csum));
1878c6a44287SMartin K. Petersen 				ret = 0x01;
1879c6a44287SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1880c6a44287SMartin K. Petersen 				goto out;
1881c6a44287SMartin K. Petersen 			}
1882c6a44287SMartin K. Petersen 
1883395cef03SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
1884c6a44287SMartin K. Petersen 			    be32_to_cpu(sdt->ref_tag)
1885c6a44287SMartin K. Petersen 			    != (start_sec & 0xffffffff)) {
1886c6a44287SMartin K. Petersen 				printk(KERN_ERR
1887c6a44287SMartin K. Petersen 				       "%s: REF check failed on sector %lu\n",
1888c6a44287SMartin K. Petersen 				       __func__, (unsigned long)sector);
1889c6a44287SMartin K. Petersen 				ret = 0x03;
1890c6a44287SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1891c6a44287SMartin K. Petersen 				goto out;
1892c6a44287SMartin K. Petersen 			}
1893c6a44287SMartin K. Petersen 
1894395cef03SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
1895395cef03SMartin K. Petersen 			    be32_to_cpu(sdt->ref_tag) != ei_lba) {
1896395cef03SMartin K. Petersen 				printk(KERN_ERR
1897395cef03SMartin K. Petersen 				       "%s: REF check failed on sector %lu\n",
1898395cef03SMartin K. Petersen 				       __func__, (unsigned long)sector);
1899395cef03SMartin K. Petersen 				ret = 0x03;
1900395cef03SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1901395cef03SMartin K. Petersen 				goto out;
1902395cef03SMartin K. Petersen 			}
1903395cef03SMartin K. Petersen 
1904c6a44287SMartin K. Petersen 			/* Would be great to copy this in bigger
1905c6a44287SMartin K. Petersen 			 * chunks.  However, for the sake of
1906c6a44287SMartin K. Petersen 			 * correctness we need to verify each sector
1907c6a44287SMartin K. Petersen 			 * before writing it to "stable" storage
1908c6a44287SMartin K. Petersen 			 */
1909c6a44287SMartin K. Petersen 			memcpy(dif_storep + dif_offset(sector), sdt, 8);
1910c6a44287SMartin K. Petersen 
1911c6a44287SMartin K. Petersen 			sector++;
1912c6a44287SMartin K. Petersen 
1913c6a44287SMartin K. Petersen 			if (sector == sdebug_store_sectors)
1914c6a44287SMartin K. Petersen 				sector = 0;	/* Force wrap */
1915c6a44287SMartin K. Petersen 
1916c6a44287SMartin K. Petersen 			start_sec++;
1917395cef03SMartin K. Petersen 			ei_lba++;
1918c6a44287SMartin K. Petersen 			daddr += scsi_debug_sector_size;
1919c6a44287SMartin K. Petersen 			ppage_offset += sizeof(struct sd_dif_tuple);
1920c6a44287SMartin K. Petersen 		}
1921c6a44287SMartin K. Petersen 
1922c6a44287SMartin K. Petersen 		kunmap_atomic(daddr, KM_IRQ0);
1923c6a44287SMartin K. Petersen 	}
1924c6a44287SMartin K. Petersen 
1925c6a44287SMartin K. Petersen 	kunmap_atomic(paddr, KM_IRQ1);
1926c6a44287SMartin K. Petersen 
1927c6a44287SMartin K. Petersen 	dix_writes++;
1928c6a44287SMartin K. Petersen 
1929c6a44287SMartin K. Petersen 	return 0;
1930c6a44287SMartin K. Petersen 
1931c6a44287SMartin K. Petersen out:
1932c6a44287SMartin K. Petersen 	dif_errors++;
1933c6a44287SMartin K. Petersen 	kunmap_atomic(daddr, KM_IRQ0);
1934c6a44287SMartin K. Petersen 	kunmap_atomic(paddr, KM_IRQ1);
1935c6a44287SMartin K. Petersen 	return ret;
1936c6a44287SMartin K. Petersen }
1937c6a44287SMartin K. Petersen 
193844d92694SMartin K. Petersen static unsigned int map_state(sector_t lba, unsigned int *num)
193944d92694SMartin K. Petersen {
194044d92694SMartin K. Petersen 	unsigned int granularity, alignment, mapped;
194144d92694SMartin K. Petersen 	sector_t block, next, end;
194244d92694SMartin K. Petersen 
194344d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
194444d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
194544d92694SMartin K. Petersen 	block = lba + alignment;
194644d92694SMartin K. Petersen 	do_div(block, granularity);
194744d92694SMartin K. Petersen 
194844d92694SMartin K. Petersen 	mapped = test_bit(block, map_storep);
194944d92694SMartin K. Petersen 
195044d92694SMartin K. Petersen 	if (mapped)
195144d92694SMartin K. Petersen 		next = find_next_zero_bit(map_storep, map_size, block);
195244d92694SMartin K. Petersen 	else
195344d92694SMartin K. Petersen 		next = find_next_bit(map_storep, map_size, block);
195444d92694SMartin K. Petersen 
195544d92694SMartin K. Petersen 	end = next * granularity - scsi_debug_unmap_alignment;
195644d92694SMartin K. Petersen 	*num = end - lba;
195744d92694SMartin K. Petersen 
195844d92694SMartin K. Petersen 	return mapped;
195944d92694SMartin K. Petersen }
196044d92694SMartin K. Petersen 
196144d92694SMartin K. Petersen static void map_region(sector_t lba, unsigned int len)
196244d92694SMartin K. Petersen {
196344d92694SMartin K. Petersen 	unsigned int granularity, alignment;
196444d92694SMartin K. Petersen 	sector_t end = lba + len;
196544d92694SMartin K. Petersen 
196644d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
196744d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
196844d92694SMartin K. Petersen 
196944d92694SMartin K. Petersen 	while (lba < end) {
197044d92694SMartin K. Petersen 		sector_t block, rem;
197144d92694SMartin K. Petersen 
197244d92694SMartin K. Petersen 		block = lba + alignment;
197344d92694SMartin K. Petersen 		rem = do_div(block, granularity);
197444d92694SMartin K. Petersen 
197544d92694SMartin K. Petersen 		set_bit(block, map_storep);
197644d92694SMartin K. Petersen 
197744d92694SMartin K. Petersen 		lba += granularity - rem;
197844d92694SMartin K. Petersen 	}
197944d92694SMartin K. Petersen }
198044d92694SMartin K. Petersen 
198144d92694SMartin K. Petersen static void unmap_region(sector_t lba, unsigned int len)
198244d92694SMartin K. Petersen {
198344d92694SMartin K. Petersen 	unsigned int granularity, alignment;
198444d92694SMartin K. Petersen 	sector_t end = lba + len;
198544d92694SMartin K. Petersen 
198644d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
198744d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
198844d92694SMartin K. Petersen 
198944d92694SMartin K. Petersen 	while (lba < end) {
199044d92694SMartin K. Petersen 		sector_t block, rem;
199144d92694SMartin K. Petersen 
199244d92694SMartin K. Petersen 		block = lba + alignment;
199344d92694SMartin K. Petersen 		rem = do_div(block, granularity);
199444d92694SMartin K. Petersen 
199544d92694SMartin K. Petersen 		if (rem == 0 && lba + granularity <= end)
199644d92694SMartin K. Petersen 			clear_bit(block, map_storep);
199744d92694SMartin K. Petersen 
199844d92694SMartin K. Petersen 		lba += granularity - rem;
199944d92694SMartin K. Petersen 	}
200044d92694SMartin K. Petersen }
200144d92694SMartin K. Petersen 
2002c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
2003395cef03SMartin K. Petersen 		      unsigned int num, struct sdebug_dev_info *devip,
2004395cef03SMartin K. Petersen 		      u32 ei_lba)
20051da177e4SLinus Torvalds {
20061da177e4SLinus Torvalds 	unsigned long iflags;
200719789100SFUJITA Tomonori 	int ret;
20081da177e4SLinus Torvalds 
200919789100SFUJITA Tomonori 	ret = check_device_access_params(devip, lba, num);
201019789100SFUJITA Tomonori 	if (ret)
201119789100SFUJITA Tomonori 		return ret;
20121da177e4SLinus Torvalds 
2013c6a44287SMartin K. Petersen 	/* DIX + T10 DIF */
2014c6a44287SMartin K. Petersen 	if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
2015395cef03SMartin K. Petersen 		int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba);
2016c6a44287SMartin K. Petersen 
2017c6a44287SMartin K. Petersen 		if (prot_ret) {
2018c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, prot_ret);
2019c6a44287SMartin K. Petersen 			return illegal_condition_result;
2020c6a44287SMartin K. Petersen 		}
2021c6a44287SMartin K. Petersen 	}
2022c6a44287SMartin K. Petersen 
20231da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
202419789100SFUJITA Tomonori 	ret = do_device_access(SCpnt, devip, lba, num, 1);
202544d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
202644d92694SMartin K. Petersen 		map_region(lba, num);
20271da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
202819789100SFUJITA Tomonori 	if (-1 == ret)
20291da177e4SLinus Torvalds 		return (DID_ERROR << 16);
2030597136abSMartin K. Petersen 	else if ((ret < (num * scsi_debug_sector_size)) &&
20311da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
2032c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
2033597136abSMartin K. Petersen 		       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
203444d92694SMartin K. Petersen 
20351da177e4SLinus Torvalds 	return 0;
20361da177e4SLinus Torvalds }
20371da177e4SLinus Torvalds 
203844d92694SMartin K. Petersen static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
203944d92694SMartin K. Petersen 		      unsigned int num, struct sdebug_dev_info *devip,
204044d92694SMartin K. Petersen 			   u32 ei_lba, unsigned int unmap)
204144d92694SMartin K. Petersen {
204244d92694SMartin K. Petersen 	unsigned long iflags;
204344d92694SMartin K. Petersen 	unsigned long long i;
204444d92694SMartin K. Petersen 	int ret;
204544d92694SMartin K. Petersen 
204644d92694SMartin K. Petersen 	ret = check_device_access_params(devip, lba, num);
204744d92694SMartin K. Petersen 	if (ret)
204844d92694SMartin K. Petersen 		return ret;
204944d92694SMartin K. Petersen 
205044d92694SMartin K. Petersen 	write_lock_irqsave(&atomic_rw, iflags);
205144d92694SMartin K. Petersen 
205244d92694SMartin K. Petersen 	if (unmap && scsi_debug_unmap_granularity) {
205344d92694SMartin K. Petersen 		unmap_region(lba, num);
205444d92694SMartin K. Petersen 		goto out;
205544d92694SMartin K. Petersen 	}
205644d92694SMartin K. Petersen 
205744d92694SMartin K. Petersen 	/* Else fetch one logical block */
205844d92694SMartin K. Petersen 	ret = fetch_to_dev_buffer(scmd,
205944d92694SMartin K. Petersen 				  fake_storep + (lba * scsi_debug_sector_size),
206044d92694SMartin K. Petersen 				  scsi_debug_sector_size);
206144d92694SMartin K. Petersen 
206244d92694SMartin K. Petersen 	if (-1 == ret) {
206344d92694SMartin K. Petersen 		write_unlock_irqrestore(&atomic_rw, iflags);
206444d92694SMartin K. Petersen 		return (DID_ERROR << 16);
206544d92694SMartin K. Petersen 	} else if ((ret < (num * scsi_debug_sector_size)) &&
206644d92694SMartin K. Petersen 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
206744d92694SMartin K. Petersen 		printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, "
206844d92694SMartin K. Petersen 		       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
206944d92694SMartin K. Petersen 
207044d92694SMartin K. Petersen 	/* Copy first sector to remaining blocks */
207144d92694SMartin K. Petersen 	for (i = 1 ; i < num ; i++)
207244d92694SMartin K. Petersen 		memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size),
207344d92694SMartin K. Petersen 		       fake_storep + (lba * scsi_debug_sector_size),
207444d92694SMartin K. Petersen 		       scsi_debug_sector_size);
207544d92694SMartin K. Petersen 
207644d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
207744d92694SMartin K. Petersen 		map_region(lba, num);
207844d92694SMartin K. Petersen out:
207944d92694SMartin K. Petersen 	write_unlock_irqrestore(&atomic_rw, iflags);
208044d92694SMartin K. Petersen 
208144d92694SMartin K. Petersen 	return 0;
208244d92694SMartin K. Petersen }
208344d92694SMartin K. Petersen 
208444d92694SMartin K. Petersen struct unmap_block_desc {
208544d92694SMartin K. Petersen 	__be64	lba;
208644d92694SMartin K. Petersen 	__be32	blocks;
208744d92694SMartin K. Petersen 	__be32	__reserved;
208844d92694SMartin K. Petersen };
208944d92694SMartin K. Petersen 
209044d92694SMartin K. Petersen static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
209144d92694SMartin K. Petersen {
209244d92694SMartin K. Petersen 	unsigned char *buf;
209344d92694SMartin K. Petersen 	struct unmap_block_desc *desc;
209444d92694SMartin K. Petersen 	unsigned int i, payload_len, descriptors;
209544d92694SMartin K. Petersen 	int ret;
209644d92694SMartin K. Petersen 
209744d92694SMartin K. Petersen 	ret = check_readiness(scmd, 1, devip);
209844d92694SMartin K. Petersen 	if (ret)
209944d92694SMartin K. Petersen 		return ret;
210044d92694SMartin K. Petersen 
210144d92694SMartin K. Petersen 	payload_len = get_unaligned_be16(&scmd->cmnd[7]);
210244d92694SMartin K. Petersen 	BUG_ON(scsi_bufflen(scmd) != payload_len);
210344d92694SMartin K. Petersen 
210444d92694SMartin K. Petersen 	descriptors = (payload_len - 8) / 16;
210544d92694SMartin K. Petersen 
210644d92694SMartin K. Petersen 	buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
210744d92694SMartin K. Petersen 	if (!buf)
210844d92694SMartin K. Petersen 		return check_condition_result;
210944d92694SMartin K. Petersen 
211044d92694SMartin K. Petersen 	scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
211144d92694SMartin K. Petersen 
211244d92694SMartin K. Petersen 	BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
211344d92694SMartin K. Petersen 	BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
211444d92694SMartin K. Petersen 
211544d92694SMartin K. Petersen 	desc = (void *)&buf[8];
211644d92694SMartin K. Petersen 
211744d92694SMartin K. Petersen 	for (i = 0 ; i < descriptors ; i++) {
211844d92694SMartin K. Petersen 		unsigned long long lba = get_unaligned_be64(&desc[i].lba);
211944d92694SMartin K. Petersen 		unsigned int num = get_unaligned_be32(&desc[i].blocks);
212044d92694SMartin K. Petersen 
212144d92694SMartin K. Petersen 		ret = check_device_access_params(devip, lba, num);
212244d92694SMartin K. Petersen 		if (ret)
212344d92694SMartin K. Petersen 			goto out;
212444d92694SMartin K. Petersen 
212544d92694SMartin K. Petersen 		unmap_region(lba, num);
212644d92694SMartin K. Petersen 	}
212744d92694SMartin K. Petersen 
212844d92694SMartin K. Petersen 	ret = 0;
212944d92694SMartin K. Petersen 
213044d92694SMartin K. Petersen out:
213144d92694SMartin K. Petersen 	kfree(buf);
213244d92694SMartin K. Petersen 
213344d92694SMartin K. Petersen 	return ret;
213444d92694SMartin K. Petersen }
213544d92694SMartin K. Petersen 
213644d92694SMartin K. Petersen #define SDEBUG_GET_LBA_STATUS_LEN 32
213744d92694SMartin K. Petersen 
213844d92694SMartin K. Petersen static int resp_get_lba_status(struct scsi_cmnd * scmd,
213944d92694SMartin K. Petersen 			       struct sdebug_dev_info * devip)
214044d92694SMartin K. Petersen {
214144d92694SMartin K. Petersen 	unsigned long long lba;
214244d92694SMartin K. Petersen 	unsigned int alloc_len, mapped, num;
214344d92694SMartin K. Petersen 	unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
214444d92694SMartin K. Petersen 	int ret;
214544d92694SMartin K. Petersen 
214644d92694SMartin K. Petersen 	ret = check_readiness(scmd, 1, devip);
214744d92694SMartin K. Petersen 	if (ret)
214844d92694SMartin K. Petersen 		return ret;
214944d92694SMartin K. Petersen 
215044d92694SMartin K. Petersen 	lba = get_unaligned_be64(&scmd->cmnd[2]);
215144d92694SMartin K. Petersen 	alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
215244d92694SMartin K. Petersen 
215344d92694SMartin K. Petersen 	if (alloc_len < 24)
215444d92694SMartin K. Petersen 		return 0;
215544d92694SMartin K. Petersen 
215644d92694SMartin K. Petersen 	ret = check_device_access_params(devip, lba, 1);
215744d92694SMartin K. Petersen 	if (ret)
215844d92694SMartin K. Petersen 		return ret;
215944d92694SMartin K. Petersen 
216044d92694SMartin K. Petersen 	mapped = map_state(lba, &num);
216144d92694SMartin K. Petersen 
216244d92694SMartin K. Petersen 	memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
216344d92694SMartin K. Petersen 	put_unaligned_be32(16, &arr[0]);	/* Parameter Data Length */
216444d92694SMartin K. Petersen 	put_unaligned_be64(lba, &arr[8]);	/* LBA */
216544d92694SMartin K. Petersen 	put_unaligned_be32(num, &arr[16]);	/* Number of blocks */
216644d92694SMartin K. Petersen 	arr[20] = !mapped;			/* mapped = 0, unmapped = 1 */
216744d92694SMartin K. Petersen 
216844d92694SMartin K. Petersen 	return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
216944d92694SMartin K. Petersen }
217044d92694SMartin K. Petersen 
2171c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
21721da177e4SLinus Torvalds 
21731da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
21741da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
21751da177e4SLinus Torvalds {
21761da177e4SLinus Torvalds 	unsigned int alloc_len;
2177c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
21781da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
21791da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
21801da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
21811da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
2182c65b1445SDouglas Gilbert 	unsigned char * max_addr;
21831da177e4SLinus Torvalds 
21841da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
2185c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
21861da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
21871da177e4SLinus Torvalds 			       	0);
21881da177e4SLinus Torvalds 		return check_condition_result;
21891da177e4SLinus Torvalds 	}
21901da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
21911da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
21921da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
2193c65b1445SDouglas Gilbert 	if (1 == select_report)
2194c65b1445SDouglas Gilbert 		lun_cnt = 0;
2195c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
2196c65b1445SDouglas Gilbert 		--lun_cnt;
2197c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
2198c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
2199c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
2200c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
2201c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
2202c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
2203c65b1445SDouglas Gilbert 	if (n < num) {
2204c65b1445SDouglas Gilbert 		wlun = 0;
2205c65b1445SDouglas Gilbert 		lun_cnt = n;
2206c65b1445SDouglas Gilbert 	}
22071da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
2208c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
2209c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
2210c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
2211c65b1445SDouglas Gilbert 	     i++, lun++) {
2212c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
22131da177e4SLinus Torvalds 		if (upper)
22141da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
22151da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
2216c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
22171da177e4SLinus Torvalds 	}
2218c65b1445SDouglas Gilbert 	if (wlun) {
2219c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
2220c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
2221c65b1445SDouglas Gilbert 		i++;
2222c65b1445SDouglas Gilbert 	}
2223c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
22241da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
22251da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
22261da177e4SLinus Torvalds }
22271da177e4SLinus Torvalds 
2228c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
2229c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip)
2230c639d14eSFUJITA Tomonori {
2231c639d14eSFUJITA Tomonori 	int i, j, ret = -1;
2232c639d14eSFUJITA Tomonori 	unsigned char *kaddr, *buf;
2233c639d14eSFUJITA Tomonori 	unsigned int offset;
2234c639d14eSFUJITA Tomonori 	struct scatterlist *sg;
2235c639d14eSFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
2236c639d14eSFUJITA Tomonori 
2237c639d14eSFUJITA Tomonori 	/* better not to use temporary buffer. */
2238c639d14eSFUJITA Tomonori 	buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
2239c639d14eSFUJITA Tomonori 	if (!buf)
2240c639d14eSFUJITA Tomonori 		return ret;
2241c639d14eSFUJITA Tomonori 
224221a61829SFUJITA Tomonori 	scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp));
2243c639d14eSFUJITA Tomonori 
2244c639d14eSFUJITA Tomonori 	offset = 0;
2245c639d14eSFUJITA Tomonori 	for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
2246c639d14eSFUJITA Tomonori 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
2247c639d14eSFUJITA Tomonori 		if (!kaddr)
2248c639d14eSFUJITA Tomonori 			goto out;
2249c639d14eSFUJITA Tomonori 
2250c639d14eSFUJITA Tomonori 		for (j = 0; j < sg->length; j++)
2251c639d14eSFUJITA Tomonori 			*(kaddr + sg->offset + j) ^= *(buf + offset + j);
2252c639d14eSFUJITA Tomonori 
2253c639d14eSFUJITA Tomonori 		offset += sg->length;
2254c639d14eSFUJITA Tomonori 		kunmap_atomic(kaddr, KM_USER0);
2255c639d14eSFUJITA Tomonori 	}
2256c639d14eSFUJITA Tomonori 	ret = 0;
2257c639d14eSFUJITA Tomonori out:
2258c639d14eSFUJITA Tomonori 	kfree(buf);
2259c639d14eSFUJITA Tomonori 
2260c639d14eSFUJITA Tomonori 	return ret;
2261c639d14eSFUJITA Tomonori }
2262c639d14eSFUJITA Tomonori 
22631da177e4SLinus Torvalds /* When timer goes off this function is called. */
22641da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
22651da177e4SLinus Torvalds {
22661da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22671da177e4SLinus Torvalds 	unsigned long iflags;
22681da177e4SLinus Torvalds 
22691da177e4SLinus Torvalds 	if (indx >= SCSI_DEBUG_CANQUEUE) {
22701da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
22711da177e4SLinus Torvalds 		       "large\n");
22721da177e4SLinus Torvalds 		return;
22731da177e4SLinus Torvalds 	}
22741da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22751da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
22761da177e4SLinus Torvalds 	if (! sqcp->in_use) {
22771da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
22781da177e4SLinus Torvalds 		       "interrupt\n");
22791da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
22801da177e4SLinus Torvalds 		return;
22811da177e4SLinus Torvalds 	}
22821da177e4SLinus Torvalds 	sqcp->in_use = 0;
22831da177e4SLinus Torvalds 	if (sqcp->done_funct) {
22841da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
22851da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
22861da177e4SLinus Torvalds 	}
22871da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
22881da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
22891da177e4SLinus Torvalds }
22901da177e4SLinus Torvalds 
22911da177e4SLinus Torvalds 
22928dea0d02SFUJITA Tomonori static struct sdebug_dev_info *
22938dea0d02SFUJITA Tomonori sdebug_device_create(struct sdebug_host_info *sdbg_host, gfp_t flags)
22945cb2fc06SFUJITA Tomonori {
22955cb2fc06SFUJITA Tomonori 	struct sdebug_dev_info *devip;
22965cb2fc06SFUJITA Tomonori 
22975cb2fc06SFUJITA Tomonori 	devip = kzalloc(sizeof(*devip), flags);
22985cb2fc06SFUJITA Tomonori 	if (devip) {
22995cb2fc06SFUJITA Tomonori 		devip->sdbg_host = sdbg_host;
23005cb2fc06SFUJITA Tomonori 		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
23015cb2fc06SFUJITA Tomonori 	}
23025cb2fc06SFUJITA Tomonori 	return devip;
23035cb2fc06SFUJITA Tomonori }
23045cb2fc06SFUJITA Tomonori 
23051da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
23061da177e4SLinus Torvalds {
23071da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
23081da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
23091da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
23101da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
23111da177e4SLinus Torvalds 
23121da177e4SLinus Torvalds 	if (devip)
23131da177e4SLinus Torvalds 		return devip;
2314d1e4c9c5SFUJITA Tomonori 	sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host);
23151da177e4SLinus Torvalds 	if (!sdbg_host) {
23161da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
23171da177e4SLinus Torvalds 		return NULL;
23181da177e4SLinus Torvalds         }
23191da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
23201da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
23211da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
23221da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
23231da177e4SLinus Torvalds                         return devip;
23241da177e4SLinus Torvalds 		else {
23251da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
23261da177e4SLinus Torvalds 				open_devip = devip;
23271da177e4SLinus Torvalds 		}
23281da177e4SLinus Torvalds 	}
23295cb2fc06SFUJITA Tomonori 	if (!open_devip) { /* try and make a new one */
23305cb2fc06SFUJITA Tomonori 		open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC);
23315cb2fc06SFUJITA Tomonori 		if (!open_devip) {
23321da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
2333cadbd4a5SHarvey Harrison 				__func__, __LINE__);
23341da177e4SLinus Torvalds 			return NULL;
23351da177e4SLinus Torvalds 		}
23361da177e4SLinus Torvalds 	}
2337a75869d1SFUJITA Tomonori 
23381da177e4SLinus Torvalds 	open_devip->channel = sdev->channel;
23391da177e4SLinus Torvalds 	open_devip->target = sdev->id;
23401da177e4SLinus Torvalds 	open_devip->lun = sdev->lun;
23411da177e4SLinus Torvalds 	open_devip->sdbg_host = sdbg_host;
23421da177e4SLinus Torvalds 	open_devip->reset = 1;
23431da177e4SLinus Torvalds 	open_devip->used = 1;
23441da177e4SLinus Torvalds 	memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
23451da177e4SLinus Torvalds 	if (scsi_debug_dsense)
23461da177e4SLinus Torvalds 		open_devip->sense_buff[0] = 0x72;
23471da177e4SLinus Torvalds 	else {
23481da177e4SLinus Torvalds 		open_devip->sense_buff[0] = 0x70;
23491da177e4SLinus Torvalds 		open_devip->sense_buff[7] = 0xa;
23501da177e4SLinus Torvalds 	}
2351c65b1445SDouglas Gilbert 	if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2352c65b1445SDouglas Gilbert 		open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
2353a75869d1SFUJITA Tomonori 
23541da177e4SLinus Torvalds 	return open_devip;
23551da177e4SLinus Torvalds }
23561da177e4SLinus Torvalds 
23578dea0d02SFUJITA Tomonori static int scsi_debug_slave_alloc(struct scsi_device *sdp)
23581da177e4SLinus Torvalds {
23598dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
23608dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
23618dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
236275ad23bcSNick Piggin 	queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue);
23638dea0d02SFUJITA Tomonori 	return 0;
23648dea0d02SFUJITA Tomonori }
23651da177e4SLinus Torvalds 
23668dea0d02SFUJITA Tomonori static int scsi_debug_slave_configure(struct scsi_device *sdp)
23678dea0d02SFUJITA Tomonori {
23688dea0d02SFUJITA Tomonori 	struct sdebug_dev_info *devip;
2369a34c4e98SFUJITA Tomonori 
23701da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
23718dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
23728dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
23738dea0d02SFUJITA Tomonori 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
23748dea0d02SFUJITA Tomonori 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
23758dea0d02SFUJITA Tomonori 	devip = devInfoReg(sdp);
23768dea0d02SFUJITA Tomonori 	if (NULL == devip)
23778dea0d02SFUJITA Tomonori 		return 1;	/* no resources, will be marked offline */
23788dea0d02SFUJITA Tomonori 	sdp->hostdata = devip;
23798dea0d02SFUJITA Tomonori 	if (sdp->host->cmd_per_lun)
23808dea0d02SFUJITA Tomonori 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
23818dea0d02SFUJITA Tomonori 					sdp->host->cmd_per_lun);
23828dea0d02SFUJITA Tomonori 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
23838dea0d02SFUJITA Tomonori 	return 0;
23848dea0d02SFUJITA Tomonori }
23858dea0d02SFUJITA Tomonori 
23868dea0d02SFUJITA Tomonori static void scsi_debug_slave_destroy(struct scsi_device *sdp)
23878dea0d02SFUJITA Tomonori {
23888dea0d02SFUJITA Tomonori 	struct sdebug_dev_info *devip =
23898dea0d02SFUJITA Tomonori 		(struct sdebug_dev_info *)sdp->hostdata;
23908dea0d02SFUJITA Tomonori 
23918dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
23928dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
23938dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
23948dea0d02SFUJITA Tomonori 	if (devip) {
23958dea0d02SFUJITA Tomonori 		/* make this slot avaliable for re-use */
23968dea0d02SFUJITA Tomonori 		devip->used = 0;
23978dea0d02SFUJITA Tomonori 		sdp->hostdata = NULL;
23988dea0d02SFUJITA Tomonori 	}
23998dea0d02SFUJITA Tomonori }
24008dea0d02SFUJITA Tomonori 
24018dea0d02SFUJITA Tomonori /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
24028dea0d02SFUJITA Tomonori static int stop_queued_cmnd(struct scsi_cmnd *cmnd)
24038dea0d02SFUJITA Tomonori {
24048dea0d02SFUJITA Tomonori 	unsigned long iflags;
24058dea0d02SFUJITA Tomonori 	int k;
24068dea0d02SFUJITA Tomonori 	struct sdebug_queued_cmd *sqcp;
24078dea0d02SFUJITA Tomonori 
24088dea0d02SFUJITA Tomonori 	spin_lock_irqsave(&queued_arr_lock, iflags);
24098dea0d02SFUJITA Tomonori 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
24108dea0d02SFUJITA Tomonori 		sqcp = &queued_arr[k];
24118dea0d02SFUJITA Tomonori 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
24128dea0d02SFUJITA Tomonori 			del_timer_sync(&sqcp->cmnd_timer);
24138dea0d02SFUJITA Tomonori 			sqcp->in_use = 0;
24148dea0d02SFUJITA Tomonori 			sqcp->a_cmnd = NULL;
24158dea0d02SFUJITA Tomonori 			break;
24168dea0d02SFUJITA Tomonori 		}
24178dea0d02SFUJITA Tomonori 	}
24188dea0d02SFUJITA Tomonori 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
24198dea0d02SFUJITA Tomonori 	return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0;
24208dea0d02SFUJITA Tomonori }
24218dea0d02SFUJITA Tomonori 
24228dea0d02SFUJITA Tomonori /* Deletes (stops) timers of all queued commands */
24238dea0d02SFUJITA Tomonori static void stop_all_queued(void)
24248dea0d02SFUJITA Tomonori {
24258dea0d02SFUJITA Tomonori 	unsigned long iflags;
24268dea0d02SFUJITA Tomonori 	int k;
24278dea0d02SFUJITA Tomonori 	struct sdebug_queued_cmd *sqcp;
24288dea0d02SFUJITA Tomonori 
24298dea0d02SFUJITA Tomonori 	spin_lock_irqsave(&queued_arr_lock, iflags);
24308dea0d02SFUJITA Tomonori 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
24318dea0d02SFUJITA Tomonori 		sqcp = &queued_arr[k];
24328dea0d02SFUJITA Tomonori 		if (sqcp->in_use && sqcp->a_cmnd) {
24338dea0d02SFUJITA Tomonori 			del_timer_sync(&sqcp->cmnd_timer);
24348dea0d02SFUJITA Tomonori 			sqcp->in_use = 0;
24358dea0d02SFUJITA Tomonori 			sqcp->a_cmnd = NULL;
24368dea0d02SFUJITA Tomonori 		}
24378dea0d02SFUJITA Tomonori 	}
24388dea0d02SFUJITA Tomonori 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
24391da177e4SLinus Torvalds }
24401da177e4SLinus Torvalds 
24411da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
24421da177e4SLinus Torvalds {
24431da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24441da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
24451da177e4SLinus Torvalds 	++num_aborts;
24461da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
24471da177e4SLinus Torvalds 	return SUCCESS;
24481da177e4SLinus Torvalds }
24491da177e4SLinus Torvalds 
24501da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
24511da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
24521da177e4SLinus Torvalds {
24531da177e4SLinus Torvalds 	int res;
24541da177e4SLinus Torvalds 	unsigned char *buf;
24551da177e4SLinus Torvalds 
24561da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24571da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
24581da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
24591da177e4SLinus Torvalds 	if (buf) {
24601da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
24611da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
24621da177e4SLinus Torvalds 		kfree(buf);
24631da177e4SLinus Torvalds 		if (! res)
24641da177e4SLinus Torvalds 			return res;
24651da177e4SLinus Torvalds 	}
24661da177e4SLinus Torvalds 	info[0] = sdebug_heads;
24671da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
24681da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
24691da177e4SLinus Torvalds 	return 0;
24701da177e4SLinus Torvalds }
24711da177e4SLinus Torvalds 
24721da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
24731da177e4SLinus Torvalds {
24741da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
24751da177e4SLinus Torvalds 
24761da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24771da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
24781da177e4SLinus Torvalds 	++num_dev_resets;
24791da177e4SLinus Torvalds 	if (SCpnt) {
24801da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
24811da177e4SLinus Torvalds 		if (devip)
24821da177e4SLinus Torvalds 			devip->reset = 1;
24831da177e4SLinus Torvalds 	}
24841da177e4SLinus Torvalds 	return SUCCESS;
24851da177e4SLinus Torvalds }
24861da177e4SLinus Torvalds 
24871da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
24881da177e4SLinus Torvalds {
24891da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
24901da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
24911da177e4SLinus Torvalds         struct scsi_device * sdp;
24921da177e4SLinus Torvalds         struct Scsi_Host * hp;
24931da177e4SLinus Torvalds 
24941da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24951da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
24961da177e4SLinus Torvalds 	++num_bus_resets;
24971da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
2498d1e4c9c5SFUJITA Tomonori 		sdbg_host = *(struct sdebug_host_info **)shost_priv(hp);
24991da177e4SLinus Torvalds 		if (sdbg_host) {
25001da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
25011da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
25021da177e4SLinus Torvalds                                             dev_list)
25031da177e4SLinus Torvalds 				dev_info->reset = 1;
25041da177e4SLinus Torvalds 		}
25051da177e4SLinus Torvalds 	}
25061da177e4SLinus Torvalds 	return SUCCESS;
25071da177e4SLinus Torvalds }
25081da177e4SLinus Torvalds 
25091da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
25101da177e4SLinus Torvalds {
25111da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
25121da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
25131da177e4SLinus Torvalds 
25141da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
25151da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
25161da177e4SLinus Torvalds 	++num_host_resets;
25171da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
25181da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
25191da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
25201da177e4SLinus Torvalds                                     dev_list)
25211da177e4SLinus Torvalds                         dev_info->reset = 1;
25221da177e4SLinus Torvalds         }
25231da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
25241da177e4SLinus Torvalds 	stop_all_queued();
25251da177e4SLinus Torvalds 	return SUCCESS;
25261da177e4SLinus Torvalds }
25271da177e4SLinus Torvalds 
25281da177e4SLinus Torvalds /* Initializes timers in queued array */
25291da177e4SLinus Torvalds static void __init init_all_queued(void)
25301da177e4SLinus Torvalds {
25311da177e4SLinus Torvalds 	unsigned long iflags;
25321da177e4SLinus Torvalds 	int k;
25331da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
25341da177e4SLinus Torvalds 
25351da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
25361da177e4SLinus Torvalds 	for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
25371da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
25381da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
25391da177e4SLinus Torvalds 		sqcp->in_use = 0;
25401da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
25411da177e4SLinus Torvalds 	}
25421da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
25431da177e4SLinus Torvalds }
25441da177e4SLinus Torvalds 
2545f58b0efbSFUJITA Tomonori static void __init sdebug_build_parts(unsigned char *ramp,
25465f2578e5SFUJITA Tomonori 				      unsigned long store_size)
25471da177e4SLinus Torvalds {
25481da177e4SLinus Torvalds 	struct partition * pp;
25491da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
25501da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
25511da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
25521da177e4SLinus Torvalds 
25531da177e4SLinus Torvalds 	/* assume partition table already zeroed */
2554f58b0efbSFUJITA Tomonori 	if ((scsi_debug_num_parts < 1) || (store_size < 1048576))
25551da177e4SLinus Torvalds 		return;
25561da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
25571da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
25581da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
25591da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
25601da177e4SLinus Torvalds 	}
2561c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
25621da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
25631da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
25641da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
25651da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
25661da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
25671da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
25681da177e4SLinus Torvalds 			    * heads_by_sects;
25691da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
25701da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
25711da177e4SLinus Torvalds 
25721da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
25731da177e4SLinus Torvalds 	ramp[511] = 0xAA;
25741da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
25751da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
25761da177e4SLinus Torvalds 		start_sec = starts[k];
25771da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
25781da177e4SLinus Torvalds 		pp->boot_ind = 0;
25791da177e4SLinus Torvalds 
25801da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
25811da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
25821da177e4SLinus Torvalds 			   / sdebug_sectors_per;
25831da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
25841da177e4SLinus Torvalds 
25851da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
25861da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
25871da177e4SLinus Torvalds 			       / sdebug_sectors_per;
25881da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
25891da177e4SLinus Torvalds 
25901da177e4SLinus Torvalds 		pp->start_sect = start_sec;
25911da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
25921da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
25931da177e4SLinus Torvalds 	}
25941da177e4SLinus Torvalds }
25951da177e4SLinus Torvalds 
25961da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
25971da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
25981da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
25991da177e4SLinus Torvalds {
26001da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
26011da177e4SLinus Torvalds 		if (scsi_result) {
26021da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
26031da177e4SLinus Torvalds 
2604c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2605c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2606c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
26071da177e4SLinus Torvalds 		}
26081da177e4SLinus Torvalds 	}
26091da177e4SLinus Torvalds 	if (cmnd && devip) {
26101da177e4SLinus Torvalds 		/* simulate autosense by this driver */
26111da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
26121da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
26131da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
26141da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
26151da177e4SLinus Torvalds 	}
26161da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
26171da177e4SLinus Torvalds 		if (cmnd)
26181da177e4SLinus Torvalds 			cmnd->result = scsi_result;
26191da177e4SLinus Torvalds 		if (done)
26201da177e4SLinus Torvalds 			done(cmnd);
26211da177e4SLinus Torvalds 		return 0;
26221da177e4SLinus Torvalds 	} else {
26231da177e4SLinus Torvalds 		unsigned long iflags;
26241da177e4SLinus Torvalds 		int k;
26251da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
26261da177e4SLinus Torvalds 
26271da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
26281da177e4SLinus Torvalds 		for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) {
26291da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
26301da177e4SLinus Torvalds 			if (! sqcp->in_use)
26311da177e4SLinus Torvalds 				break;
26321da177e4SLinus Torvalds 		}
26331da177e4SLinus Torvalds 		if (k >= SCSI_DEBUG_CANQUEUE) {
26341da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
26351da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
26361da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
26371da177e4SLinus Torvalds 		}
26381da177e4SLinus Torvalds 		sqcp->in_use = 1;
26391da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
26401da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
26411da177e4SLinus Torvalds 		sqcp->done_funct = done;
26421da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
26431da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
26441da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
26451da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
26461da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
26471da177e4SLinus Torvalds 		if (cmnd)
26481da177e4SLinus Torvalds 			cmnd->result = 0;
26491da177e4SLinus Torvalds 		return 0;
26501da177e4SLinus Torvalds 	}
26511da177e4SLinus Torvalds }
265223183910SDouglas Gilbert /* Note: The following macros create attribute files in the
265323183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
265423183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
265523183910SDouglas Gilbert    as it can when the corresponding attribute in the
265623183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
265723183910SDouglas Gilbert  */
2658c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2659c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2660c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2661c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2662c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
266323183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2664c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
2665c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
2666c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2667c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2668c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2669c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2670c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2671c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
267223183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
267323183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
2674597136abSMartin K. Petersen module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
2675c6a44287SMartin K. Petersen module_param_named(dix, scsi_debug_dix, int, S_IRUGO);
2676c6a44287SMartin K. Petersen module_param_named(dif, scsi_debug_dif, int, S_IRUGO);
2677c6a44287SMartin K. Petersen module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
2678c6a44287SMartin K. Petersen module_param_named(ato, scsi_debug_ato, int, S_IRUGO);
2679ea61fca5SMartin K. Petersen module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO);
2680ea61fca5SMartin K. Petersen module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
268144d92694SMartin K. Petersen module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
268244d92694SMartin K. Petersen module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO);
268344d92694SMartin K. Petersen module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
268444d92694SMartin K. Petersen module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
26851da177e4SLinus Torvalds 
26861da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
26871da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
26881da177e4SLinus Torvalds MODULE_LICENSE("GPL");
26891da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
26901da177e4SLinus Torvalds 
26911da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
26921da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2693c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2694c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2695beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
269623183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2697c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
2698c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
26991da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2700c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
27016f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
27021da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
27031da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2704c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
270523183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
2706ea61fca5SMartin K. Petersen MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)");
2707ea61fca5SMartin K. Petersen MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)");
2708ea61fca5SMartin K. Petersen MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)");
2709c6a44287SMartin K. Petersen MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
2710c6a44287SMartin K. Petersen MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
2711c6a44287SMartin K. Petersen MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
2712c6a44287SMartin K. Petersen MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
271344d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)");
271444d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)");
271544d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)");
271644d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
27171da177e4SLinus Torvalds 
27181da177e4SLinus Torvalds static char sdebug_info[256];
27191da177e4SLinus Torvalds 
27201da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
27211da177e4SLinus Torvalds {
27221da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
27231da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
27241da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
27251da177e4SLinus Torvalds 		scsi_debug_opts);
27261da177e4SLinus Torvalds 	return sdebug_info;
27271da177e4SLinus Torvalds }
27281da177e4SLinus Torvalds 
27291da177e4SLinus Torvalds /* scsi_debug_proc_info
27301da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
27311da177e4SLinus Torvalds  */
27321da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
27331da177e4SLinus Torvalds 				int length, int inout)
27341da177e4SLinus Torvalds {
27351da177e4SLinus Torvalds 	int len, pos, begin;
27361da177e4SLinus Torvalds 	int orig_length;
27371da177e4SLinus Torvalds 
27381da177e4SLinus Torvalds 	orig_length = length;
27391da177e4SLinus Torvalds 
27401da177e4SLinus Torvalds 	if (inout == 1) {
27411da177e4SLinus Torvalds 		char arr[16];
27421da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
27431da177e4SLinus Torvalds 
27441da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
27451da177e4SLinus Torvalds 			return -EACCES;
27461da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
27471da177e4SLinus Torvalds 		arr[minLen] = '\0';
27481da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
27491da177e4SLinus Torvalds 			return -EINVAL;
27501da177e4SLinus Torvalds 		scsi_debug_opts = pos;
27511da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
27521da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
27531da177e4SLinus Torvalds 		return length;
27541da177e4SLinus Torvalds 	}
27551da177e4SLinus Torvalds 	begin = 0;
27561da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
27571da177e4SLinus Torvalds 	    "%s [%s]\n"
27581da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
27591da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
27601da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
27611da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
27621da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
2763c6a44287SMartin K. Petersen 	    "host_resets=%d\ndix_reads=%d dix_writes=%d dif_errors=%d\n",
27641da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
27651da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
27661da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
27671da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
2768597136abSMartin K. Petersen 	    scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads,
2769597136abSMartin K. Petersen 	    sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets,
2770c6a44287SMartin K. Petersen 	    num_host_resets, dix_reads, dix_writes, dif_errors);
27711da177e4SLinus Torvalds 	if (pos < offset) {
27721da177e4SLinus Torvalds 		len = 0;
27731da177e4SLinus Torvalds 		begin = pos;
27741da177e4SLinus Torvalds 	}
27751da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
27761da177e4SLinus Torvalds 	len -= (offset - begin);
27771da177e4SLinus Torvalds 	if (len > length)
27781da177e4SLinus Torvalds 		len = length;
27791da177e4SLinus Torvalds 	return len;
27801da177e4SLinus Torvalds }
27811da177e4SLinus Torvalds 
27821da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
27831da177e4SLinus Torvalds {
27841da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
27851da177e4SLinus Torvalds }
27861da177e4SLinus Torvalds 
27871da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
27881da177e4SLinus Torvalds 				  const char * buf, size_t count)
27891da177e4SLinus Torvalds {
27901da177e4SLinus Torvalds         int delay;
27911da177e4SLinus Torvalds 	char work[20];
27921da177e4SLinus Torvalds 
27931da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
27941da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
27951da177e4SLinus Torvalds 			scsi_debug_delay = delay;
27961da177e4SLinus Torvalds 			return count;
27971da177e4SLinus Torvalds 		}
27981da177e4SLinus Torvalds 	}
27991da177e4SLinus Torvalds 	return -EINVAL;
28001da177e4SLinus Torvalds }
28011da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
28021da177e4SLinus Torvalds 	    sdebug_delay_store);
28031da177e4SLinus Torvalds 
28041da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
28051da177e4SLinus Torvalds {
28061da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
28071da177e4SLinus Torvalds }
28081da177e4SLinus Torvalds 
28091da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
28101da177e4SLinus Torvalds 				 const char * buf, size_t count)
28111da177e4SLinus Torvalds {
28121da177e4SLinus Torvalds         int opts;
28131da177e4SLinus Torvalds 	char work[20];
28141da177e4SLinus Torvalds 
28151da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
28161da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
28171da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
28181da177e4SLinus Torvalds 				goto opts_done;
28191da177e4SLinus Torvalds 		} else {
28201da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
28211da177e4SLinus Torvalds 				goto opts_done;
28221da177e4SLinus Torvalds 		}
28231da177e4SLinus Torvalds 	}
28241da177e4SLinus Torvalds 	return -EINVAL;
28251da177e4SLinus Torvalds opts_done:
28261da177e4SLinus Torvalds 	scsi_debug_opts = opts;
28271da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
28281da177e4SLinus Torvalds 	return count;
28291da177e4SLinus Torvalds }
28301da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
28311da177e4SLinus Torvalds 	    sdebug_opts_store);
28321da177e4SLinus Torvalds 
28331da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
28341da177e4SLinus Torvalds {
28351da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
28361da177e4SLinus Torvalds }
28371da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
28381da177e4SLinus Torvalds 				  const char * buf, size_t count)
28391da177e4SLinus Torvalds {
28401da177e4SLinus Torvalds         int n;
28411da177e4SLinus Torvalds 
28421da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
28431da177e4SLinus Torvalds 		scsi_debug_ptype = n;
28441da177e4SLinus Torvalds 		return count;
28451da177e4SLinus Torvalds 	}
28461da177e4SLinus Torvalds 	return -EINVAL;
28471da177e4SLinus Torvalds }
28481da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
28491da177e4SLinus Torvalds 
28501da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
28511da177e4SLinus Torvalds {
28521da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
28531da177e4SLinus Torvalds }
28541da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
28551da177e4SLinus Torvalds 				  const char * buf, size_t count)
28561da177e4SLinus Torvalds {
28571da177e4SLinus Torvalds         int n;
28581da177e4SLinus Torvalds 
28591da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
28601da177e4SLinus Torvalds 		scsi_debug_dsense = n;
28611da177e4SLinus Torvalds 		return count;
28621da177e4SLinus Torvalds 	}
28631da177e4SLinus Torvalds 	return -EINVAL;
28641da177e4SLinus Torvalds }
28651da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
28661da177e4SLinus Torvalds 	    sdebug_dsense_store);
28671da177e4SLinus Torvalds 
286823183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
286923183910SDouglas Gilbert {
287023183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
287123183910SDouglas Gilbert }
287223183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
287323183910SDouglas Gilbert 				    const char * buf, size_t count)
287423183910SDouglas Gilbert {
287523183910SDouglas Gilbert         int n;
287623183910SDouglas Gilbert 
287723183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
287823183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
287923183910SDouglas Gilbert 		return count;
288023183910SDouglas Gilbert 	}
288123183910SDouglas Gilbert 	return -EINVAL;
288223183910SDouglas Gilbert }
288323183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
288423183910SDouglas Gilbert 	    sdebug_fake_rw_store);
288523183910SDouglas Gilbert 
2886c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2887c65b1445SDouglas Gilbert {
2888c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2889c65b1445SDouglas Gilbert }
2890c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2891c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2892c65b1445SDouglas Gilbert {
2893c65b1445SDouglas Gilbert         int n;
2894c65b1445SDouglas Gilbert 
2895c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2896c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2897c65b1445SDouglas Gilbert 		return count;
2898c65b1445SDouglas Gilbert 	}
2899c65b1445SDouglas Gilbert 	return -EINVAL;
2900c65b1445SDouglas Gilbert }
2901c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2902c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2903c65b1445SDouglas Gilbert 
29041da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
29051da177e4SLinus Torvalds {
29061da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
29071da177e4SLinus Torvalds }
29081da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
29091da177e4SLinus Torvalds 				     const char * buf, size_t count)
29101da177e4SLinus Torvalds {
29111da177e4SLinus Torvalds         int n;
29121da177e4SLinus Torvalds 
29131da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
29141da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
29151da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
29161da177e4SLinus Torvalds 		return count;
29171da177e4SLinus Torvalds 	}
29181da177e4SLinus Torvalds 	return -EINVAL;
29191da177e4SLinus Torvalds }
29201da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
29211da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
29221da177e4SLinus Torvalds 
29231da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
29241da177e4SLinus Torvalds {
29251da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
29261da177e4SLinus Torvalds }
29271da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
29281da177e4SLinus Torvalds 
29291da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
29301da177e4SLinus Torvalds {
29311da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
29321da177e4SLinus Torvalds }
29331da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
29341da177e4SLinus Torvalds 
29351da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
29361da177e4SLinus Torvalds {
29371da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
29381da177e4SLinus Torvalds }
29391da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
29401da177e4SLinus Torvalds 				      const char * buf, size_t count)
29411da177e4SLinus Torvalds {
29421da177e4SLinus Torvalds         int nth;
29431da177e4SLinus Torvalds 
29441da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
29451da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
29461da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
29471da177e4SLinus Torvalds 		return count;
29481da177e4SLinus Torvalds 	}
29491da177e4SLinus Torvalds 	return -EINVAL;
29501da177e4SLinus Torvalds }
29511da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
29521da177e4SLinus Torvalds 	    sdebug_every_nth_store);
29531da177e4SLinus Torvalds 
29541da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
29551da177e4SLinus Torvalds {
29561da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
29571da177e4SLinus Torvalds }
29581da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
29591da177e4SLinus Torvalds 				     const char * buf, size_t count)
29601da177e4SLinus Torvalds {
29611da177e4SLinus Torvalds         int n;
29621da177e4SLinus Torvalds 
29631da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
29641da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
29651da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
29661da177e4SLinus Torvalds 		return count;
29671da177e4SLinus Torvalds 	}
29681da177e4SLinus Torvalds 	return -EINVAL;
29691da177e4SLinus Torvalds }
29701da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
29711da177e4SLinus Torvalds 	    sdebug_max_luns_store);
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
29741da177e4SLinus Torvalds {
29751da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
29761da177e4SLinus Torvalds }
29771da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
29781da177e4SLinus Torvalds 
2979c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
2980c65b1445SDouglas Gilbert {
2981c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
2982c65b1445SDouglas Gilbert }
2983c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
2984c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
2985c65b1445SDouglas Gilbert {
2986c65b1445SDouglas Gilbert         int n;
2987c65b1445SDouglas Gilbert 
2988c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2989c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
299028898873SFUJITA Tomonori 
299128898873SFUJITA Tomonori 		sdebug_capacity = get_sdebug_capacity();
299228898873SFUJITA Tomonori 
2993c65b1445SDouglas Gilbert 		return count;
2994c65b1445SDouglas Gilbert 	}
2995c65b1445SDouglas Gilbert 	return -EINVAL;
2996c65b1445SDouglas Gilbert }
2997c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
2998c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
2999c65b1445SDouglas Gilbert 
30001da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
30011da177e4SLinus Torvalds {
30021da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
30031da177e4SLinus Torvalds }
30041da177e4SLinus Torvalds 
30051da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
30061da177e4SLinus Torvalds 				     const char * buf, size_t count)
30071da177e4SLinus Torvalds {
30081da177e4SLinus Torvalds 	int delta_hosts;
30091da177e4SLinus Torvalds 
3010f3df41cfSFUJITA Tomonori 	if (sscanf(buf, "%d", &delta_hosts) != 1)
30111da177e4SLinus Torvalds 		return -EINVAL;
30121da177e4SLinus Torvalds 	if (delta_hosts > 0) {
30131da177e4SLinus Torvalds 		do {
30141da177e4SLinus Torvalds 			sdebug_add_adapter();
30151da177e4SLinus Torvalds 		} while (--delta_hosts);
30161da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
30171da177e4SLinus Torvalds 		do {
30181da177e4SLinus Torvalds 			sdebug_remove_adapter();
30191da177e4SLinus Torvalds 		} while (++delta_hosts);
30201da177e4SLinus Torvalds 	}
30211da177e4SLinus Torvalds 	return count;
30221da177e4SLinus Torvalds }
30231da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
30241da177e4SLinus Torvalds 	    sdebug_add_host_store);
30251da177e4SLinus Torvalds 
302623183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
302723183910SDouglas Gilbert 					  char * buf)
302823183910SDouglas Gilbert {
302923183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
303023183910SDouglas Gilbert }
303123183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
303223183910SDouglas Gilbert 					   const char * buf, size_t count)
303323183910SDouglas Gilbert {
303423183910SDouglas Gilbert 	int n;
303523183910SDouglas Gilbert 
303623183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
303723183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
303823183910SDouglas Gilbert 		return count;
303923183910SDouglas Gilbert 	}
304023183910SDouglas Gilbert 	return -EINVAL;
304123183910SDouglas Gilbert }
304223183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
304323183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
304423183910SDouglas Gilbert 
3045597136abSMartin K. Petersen static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf)
3046597136abSMartin K. Petersen {
3047597136abSMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size);
3048597136abSMartin K. Petersen }
3049597136abSMartin K. Petersen DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL);
3050597136abSMartin K. Petersen 
3051c6a44287SMartin K. Petersen static ssize_t sdebug_dix_show(struct device_driver *ddp, char *buf)
3052c6a44287SMartin K. Petersen {
3053c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dix);
3054c6a44287SMartin K. Petersen }
3055c6a44287SMartin K. Petersen DRIVER_ATTR(dix, S_IRUGO, sdebug_dix_show, NULL);
3056c6a44287SMartin K. Petersen 
3057c6a44287SMartin K. Petersen static ssize_t sdebug_dif_show(struct device_driver *ddp, char *buf)
3058c6a44287SMartin K. Petersen {
3059c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dif);
3060c6a44287SMartin K. Petersen }
3061c6a44287SMartin K. Petersen DRIVER_ATTR(dif, S_IRUGO, sdebug_dif_show, NULL);
3062c6a44287SMartin K. Petersen 
3063c6a44287SMartin K. Petersen static ssize_t sdebug_guard_show(struct device_driver *ddp, char *buf)
3064c6a44287SMartin K. Petersen {
3065c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_guard);
3066c6a44287SMartin K. Petersen }
3067c6a44287SMartin K. Petersen DRIVER_ATTR(guard, S_IRUGO, sdebug_guard_show, NULL);
3068c6a44287SMartin K. Petersen 
3069c6a44287SMartin K. Petersen static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf)
3070c6a44287SMartin K. Petersen {
3071c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ato);
3072c6a44287SMartin K. Petersen }
3073c6a44287SMartin K. Petersen DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL);
3074c6a44287SMartin K. Petersen 
307544d92694SMartin K. Petersen static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf)
307644d92694SMartin K. Petersen {
307744d92694SMartin K. Petersen 	ssize_t count;
307844d92694SMartin K. Petersen 
307944d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity == 0)
308044d92694SMartin K. Petersen 		return scnprintf(buf, PAGE_SIZE, "0-%u\n",
308144d92694SMartin K. Petersen 				 sdebug_store_sectors);
308244d92694SMartin K. Petersen 
308344d92694SMartin K. Petersen 	count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
308444d92694SMartin K. Petersen 
308544d92694SMartin K. Petersen 	buf[count++] = '\n';
308644d92694SMartin K. Petersen 	buf[count++] = 0;
308744d92694SMartin K. Petersen 
308844d92694SMartin K. Petersen 	return count;
308944d92694SMartin K. Petersen }
309044d92694SMartin K. Petersen DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL);
309144d92694SMartin K. Petersen 
3092c6a44287SMartin K. Petersen 
309323183910SDouglas Gilbert /* Note: The following function creates attribute files in the
309423183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
309523183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
309623183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
309723183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
309823183910SDouglas Gilbert  */
30996ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
31001da177e4SLinus Torvalds {
31016ecaff7fSRandy Dunlap 	int ret;
31026ecaff7fSRandy Dunlap 
31036ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
31046ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
31056ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
31066ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
31076ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
310823183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
31096ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
311023183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
31116ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
311223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
31136ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
31146ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
31156ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
311623183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
311723183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
3118597136abSMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
3119c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dix);
3120c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif);
3121c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard);
3122c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato);
312344d92694SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map);
31246ecaff7fSRandy Dunlap 	return ret;
31251da177e4SLinus Torvalds }
31261da177e4SLinus Torvalds 
31271da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
31281da177e4SLinus Torvalds {
312944d92694SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map);
3130c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato);
3131c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard);
3132c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif);
3133c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dix);
3134597136abSMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
313523183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
313623183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
31371da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
31381da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
31391da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
31401da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
314123183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
314223183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
31431da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
314423183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
31451da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
31461da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
31471da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
31481da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
31491da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
31501da177e4SLinus Torvalds }
31511da177e4SLinus Torvalds 
31528dea0d02SFUJITA Tomonori static void pseudo_0_release(struct device *dev)
31538dea0d02SFUJITA Tomonori {
31548dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
31558dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
31568dea0d02SFUJITA Tomonori }
31578dea0d02SFUJITA Tomonori 
31588dea0d02SFUJITA Tomonori static struct device pseudo_primary = {
315971610f55SKay Sievers 	.init_name	= "pseudo_0",
31608dea0d02SFUJITA Tomonori 	.release	= pseudo_0_release,
31618dea0d02SFUJITA Tomonori };
31628dea0d02SFUJITA Tomonori 
31631da177e4SLinus Torvalds static int __init scsi_debug_init(void)
31641da177e4SLinus Torvalds {
31655f2578e5SFUJITA Tomonori 	unsigned long sz;
31661da177e4SLinus Torvalds 	int host_to_add;
31671da177e4SLinus Torvalds 	int k;
31686ecaff7fSRandy Dunlap 	int ret;
31691da177e4SLinus Torvalds 
3170597136abSMartin K. Petersen 	switch (scsi_debug_sector_size) {
3171597136abSMartin K. Petersen 	case  512:
3172597136abSMartin K. Petersen 	case 1024:
3173597136abSMartin K. Petersen 	case 2048:
3174597136abSMartin K. Petersen 	case 4096:
3175597136abSMartin K. Petersen 		break;
3176597136abSMartin K. Petersen 	default:
3177c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: invalid sector_size %d\n",
3178597136abSMartin K. Petersen 		       scsi_debug_sector_size);
3179597136abSMartin K. Petersen 		return -EINVAL;
3180597136abSMartin K. Petersen 	}
3181597136abSMartin K. Petersen 
3182c6a44287SMartin K. Petersen 	switch (scsi_debug_dif) {
3183c6a44287SMartin K. Petersen 
3184c6a44287SMartin K. Petersen 	case SD_DIF_TYPE0_PROTECTION:
3185c6a44287SMartin K. Petersen 	case SD_DIF_TYPE1_PROTECTION:
3186395cef03SMartin K. Petersen 	case SD_DIF_TYPE2_PROTECTION:
3187c6a44287SMartin K. Petersen 	case SD_DIF_TYPE3_PROTECTION:
3188c6a44287SMartin K. Petersen 		break;
3189c6a44287SMartin K. Petersen 
3190c6a44287SMartin K. Petersen 	default:
3191395cef03SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: dif must be 0, 1, 2 or 3\n");
3192c6a44287SMartin K. Petersen 		return -EINVAL;
3193c6a44287SMartin K. Petersen 	}
3194c6a44287SMartin K. Petersen 
3195c6a44287SMartin K. Petersen 	if (scsi_debug_guard > 1) {
3196c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: guard must be 0 or 1\n");
3197c6a44287SMartin K. Petersen 		return -EINVAL;
3198c6a44287SMartin K. Petersen 	}
3199c6a44287SMartin K. Petersen 
3200c6a44287SMartin K. Petersen 	if (scsi_debug_ato > 1) {
3201c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: ato must be 0 or 1\n");
3202c6a44287SMartin K. Petersen 		return -EINVAL;
3203c6a44287SMartin K. Petersen 	}
3204c6a44287SMartin K. Petersen 
3205ea61fca5SMartin K. Petersen 	if (scsi_debug_physblk_exp > 15) {
3206ea61fca5SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: invalid physblk_exp %u\n",
3207ea61fca5SMartin K. Petersen 		       scsi_debug_physblk_exp);
3208ea61fca5SMartin K. Petersen 		return -EINVAL;
3209ea61fca5SMartin K. Petersen 	}
3210ea61fca5SMartin K. Petersen 
3211ea61fca5SMartin K. Petersen 	if (scsi_debug_lowest_aligned > 0x3fff) {
3212ea61fca5SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: lowest_aligned too big: %u\n",
3213ea61fca5SMartin K. Petersen 		       scsi_debug_lowest_aligned);
3214ea61fca5SMartin K. Petersen 		return -EINVAL;
3215ea61fca5SMartin K. Petersen 	}
3216ea61fca5SMartin K. Petersen 
32171da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
32181da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
32195f2578e5SFUJITA Tomonori 	sz = (unsigned long)scsi_debug_dev_size_mb * 1048576;
3220597136abSMartin K. Petersen 	sdebug_store_sectors = sz / scsi_debug_sector_size;
322128898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
32221da177e4SLinus Torvalds 
32231da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
32241da177e4SLinus Torvalds 	sdebug_heads = 8;
32251da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
32261da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
32271da177e4SLinus Torvalds 		sdebug_heads = 32;
32281da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
32291da177e4SLinus Torvalds 		sdebug_heads = 64;
32301da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
32311da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
32321da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
32331da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
32341da177e4SLinus Torvalds 		sdebug_heads = 255;
32351da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
32361da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
32371da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
32381da177e4SLinus Torvalds 	}
32391da177e4SLinus Torvalds 
32401da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
32411da177e4SLinus Torvalds 	if (NULL == fake_storep) {
32421da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
32431da177e4SLinus Torvalds 		return -ENOMEM;
32441da177e4SLinus Torvalds 	}
32451da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
32461da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
3247f58b0efbSFUJITA Tomonori 		sdebug_build_parts(fake_storep, sz);
32481da177e4SLinus Torvalds 
3249c6a44287SMartin K. Petersen 	if (scsi_debug_dif) {
3250c6a44287SMartin K. Petersen 		int dif_size;
3251c6a44287SMartin K. Petersen 
3252c6a44287SMartin K. Petersen 		dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple);
3253c6a44287SMartin K. Petersen 		dif_storep = vmalloc(dif_size);
3254c6a44287SMartin K. Petersen 
3255c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: dif_storep %u bytes @ %p\n",
3256c6a44287SMartin K. Petersen 		       dif_size, dif_storep);
3257c6a44287SMartin K. Petersen 
3258c6a44287SMartin K. Petersen 		if (dif_storep == NULL) {
3259c6a44287SMartin K. Petersen 			printk(KERN_ERR "scsi_debug_init: out of mem. (DIX)\n");
3260c6a44287SMartin K. Petersen 			ret = -ENOMEM;
3261c6a44287SMartin K. Petersen 			goto free_vm;
3262c6a44287SMartin K. Petersen 		}
3263c6a44287SMartin K. Petersen 
3264c6a44287SMartin K. Petersen 		memset(dif_storep, 0xff, dif_size);
3265c6a44287SMartin K. Petersen 	}
3266c6a44287SMartin K. Petersen 
326744d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity) {
326844d92694SMartin K. Petersen 		unsigned int map_bytes;
326944d92694SMartin K. Petersen 
327044d92694SMartin K. Petersen 		if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
327144d92694SMartin K. Petersen 			printk(KERN_ERR
327244d92694SMartin K. Petersen 			       "%s: ERR: unmap_granularity < unmap_alignment\n",
327344d92694SMartin K. Petersen 			       __func__);
327444d92694SMartin K. Petersen 			return -EINVAL;
327544d92694SMartin K. Petersen 		}
327644d92694SMartin K. Petersen 
327744d92694SMartin K. Petersen 		map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
327844d92694SMartin K. Petersen 		map_bytes = map_size >> 3;
327944d92694SMartin K. Petersen 		map_storep = vmalloc(map_bytes);
328044d92694SMartin K. Petersen 
328144d92694SMartin K. Petersen 		printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
328244d92694SMartin K. Petersen 		       map_size);
328344d92694SMartin K. Petersen 
328444d92694SMartin K. Petersen 		if (map_storep == NULL) {
328544d92694SMartin K. Petersen 			printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n");
328644d92694SMartin K. Petersen 			ret = -ENOMEM;
328744d92694SMartin K. Petersen 			goto free_vm;
328844d92694SMartin K. Petersen 		}
328944d92694SMartin K. Petersen 
329044d92694SMartin K. Petersen 		memset(map_storep, 0x0, map_bytes);
329144d92694SMartin K. Petersen 
329244d92694SMartin K. Petersen 		/* Map first 1KB for partition table */
329344d92694SMartin K. Petersen 		if (scsi_debug_num_parts)
329444d92694SMartin K. Petersen 			map_region(0, 2);
329544d92694SMartin K. Petersen 	}
329644d92694SMartin K. Petersen 
32976ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
32986ecaff7fSRandy Dunlap 	if (ret < 0) {
32996ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
33006ecaff7fSRandy Dunlap 			ret);
33016ecaff7fSRandy Dunlap 		goto free_vm;
33026ecaff7fSRandy Dunlap 	}
33036ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
33046ecaff7fSRandy Dunlap 	if (ret < 0) {
33056ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
33066ecaff7fSRandy Dunlap 			ret);
33076ecaff7fSRandy Dunlap 		goto dev_unreg;
33086ecaff7fSRandy Dunlap 	}
33096ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
33106ecaff7fSRandy Dunlap 	if (ret < 0) {
33116ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
33126ecaff7fSRandy Dunlap 			ret);
33136ecaff7fSRandy Dunlap 		goto bus_unreg;
33146ecaff7fSRandy Dunlap 	}
33156ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
33166ecaff7fSRandy Dunlap 	if (ret < 0) {
33176ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
33186ecaff7fSRandy Dunlap 			ret);
33196ecaff7fSRandy Dunlap 		goto del_files;
33206ecaff7fSRandy Dunlap 	}
33211da177e4SLinus Torvalds 
33226ecaff7fSRandy Dunlap 	init_all_queued();
33231da177e4SLinus Torvalds 
33241da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
33251da177e4SLinus Torvalds         scsi_debug_add_host = 0;
33261da177e4SLinus Torvalds 
33271da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
33281da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
33291da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
33301da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
33311da177e4SLinus Torvalds                         break;
33321da177e4SLinus Torvalds                 }
33331da177e4SLinus Torvalds         }
33341da177e4SLinus Torvalds 
33351da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
33361da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
33371da177e4SLinus Torvalds 		       scsi_debug_add_host);
33381da177e4SLinus Torvalds 	}
33391da177e4SLinus Torvalds 	return 0;
33406ecaff7fSRandy Dunlap 
33416ecaff7fSRandy Dunlap del_files:
33426ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
33436ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
33446ecaff7fSRandy Dunlap bus_unreg:
33456ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
33466ecaff7fSRandy Dunlap dev_unreg:
33476ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
33486ecaff7fSRandy Dunlap free_vm:
334944d92694SMartin K. Petersen 	if (map_storep)
335044d92694SMartin K. Petersen 		vfree(map_storep);
3351c6a44287SMartin K. Petersen 	if (dif_storep)
3352c6a44287SMartin K. Petersen 		vfree(dif_storep);
33536ecaff7fSRandy Dunlap 	vfree(fake_storep);
33546ecaff7fSRandy Dunlap 
33556ecaff7fSRandy Dunlap 	return ret;
33561da177e4SLinus Torvalds }
33571da177e4SLinus Torvalds 
33581da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
33591da177e4SLinus Torvalds {
33601da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
33611da177e4SLinus Torvalds 
33621da177e4SLinus Torvalds 	stop_all_queued();
33631da177e4SLinus Torvalds 	for (; k; k--)
33641da177e4SLinus Torvalds 		sdebug_remove_adapter();
33651da177e4SLinus Torvalds 	do_remove_driverfs_files();
33661da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
33671da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
33681da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
33691da177e4SLinus Torvalds 
3370c6a44287SMartin K. Petersen 	if (dif_storep)
3371c6a44287SMartin K. Petersen 		vfree(dif_storep);
3372c6a44287SMartin K. Petersen 
33731da177e4SLinus Torvalds 	vfree(fake_storep);
33741da177e4SLinus Torvalds }
33751da177e4SLinus Torvalds 
33761da177e4SLinus Torvalds device_initcall(scsi_debug_init);
33771da177e4SLinus Torvalds module_exit(scsi_debug_exit);
33781da177e4SLinus Torvalds 
33791da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
33801da177e4SLinus Torvalds {
33811da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
33821da177e4SLinus Torvalds 
33831da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
33841da177e4SLinus Torvalds         kfree(sdbg_host);
33851da177e4SLinus Torvalds }
33861da177e4SLinus Torvalds 
33871da177e4SLinus Torvalds static int sdebug_add_adapter(void)
33881da177e4SLinus Torvalds {
33891da177e4SLinus Torvalds 	int k, devs_per_host;
33901da177e4SLinus Torvalds         int error = 0;
33911da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
33928b40228fSFUJITA Tomonori 	struct sdebug_dev_info *sdbg_devinfo, *tmp;
33931da177e4SLinus Torvalds 
339424669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
33951da177e4SLinus Torvalds         if (NULL == sdbg_host) {
33961da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
3397cadbd4a5SHarvey Harrison                        __func__, __LINE__);
33981da177e4SLinus Torvalds                 return -ENOMEM;
33991da177e4SLinus Torvalds         }
34001da177e4SLinus Torvalds 
34011da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
34021da177e4SLinus Torvalds 
34031da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
34041da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
34055cb2fc06SFUJITA Tomonori 		sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL);
34065cb2fc06SFUJITA Tomonori 		if (!sdbg_devinfo) {
34071da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
3408cadbd4a5SHarvey Harrison                                __func__, __LINE__);
34091da177e4SLinus Torvalds                         error = -ENOMEM;
34101da177e4SLinus Torvalds 			goto clean;
34111da177e4SLinus Torvalds                 }
34121da177e4SLinus Torvalds         }
34131da177e4SLinus Torvalds 
34141da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
34151da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
34161da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
34171da177e4SLinus Torvalds 
34181da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
34191da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
34201da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
342171610f55SKay Sievers         dev_set_name(&sdbg_host->dev, "adapter%d", scsi_debug_add_host);
34221da177e4SLinus Torvalds 
34231da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
34241da177e4SLinus Torvalds 
34251da177e4SLinus Torvalds         if (error)
34261da177e4SLinus Torvalds 		goto clean;
34271da177e4SLinus Torvalds 
34281da177e4SLinus Torvalds 	++scsi_debug_add_host;
34291da177e4SLinus Torvalds         return error;
34301da177e4SLinus Torvalds 
34311da177e4SLinus Torvalds clean:
34328b40228fSFUJITA Tomonori 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
34338b40228fSFUJITA Tomonori 				 dev_list) {
34341da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
34351da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
34361da177e4SLinus Torvalds 	}
34371da177e4SLinus Torvalds 
34381da177e4SLinus Torvalds 	kfree(sdbg_host);
34391da177e4SLinus Torvalds         return error;
34401da177e4SLinus Torvalds }
34411da177e4SLinus Torvalds 
34421da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
34431da177e4SLinus Torvalds {
34441da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
34451da177e4SLinus Torvalds 
34461da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
34471da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
34481da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
34491da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
34501da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
34511da177e4SLinus Torvalds 	}
34521da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
34531da177e4SLinus Torvalds 
34541da177e4SLinus Torvalds 	if (!sdbg_host)
34551da177e4SLinus Torvalds 		return;
34561da177e4SLinus Torvalds 
34571da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
34581da177e4SLinus Torvalds         --scsi_debug_add_host;
34591da177e4SLinus Torvalds }
34601da177e4SLinus Torvalds 
3461639db475SFUJITA Tomonori static
3462639db475SFUJITA Tomonori int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
3463639db475SFUJITA Tomonori {
3464639db475SFUJITA Tomonori 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
3465639db475SFUJITA Tomonori 	int len, k;
3466639db475SFUJITA Tomonori 	unsigned int num;
3467639db475SFUJITA Tomonori 	unsigned long long lba;
3468395cef03SMartin K. Petersen 	u32 ei_lba;
3469639db475SFUJITA Tomonori 	int errsts = 0;
3470639db475SFUJITA Tomonori 	int target = SCpnt->device->id;
3471639db475SFUJITA Tomonori 	struct sdebug_dev_info *devip = NULL;
3472639db475SFUJITA Tomonori 	int inj_recovered = 0;
3473639db475SFUJITA Tomonori 	int inj_transport = 0;
3474c6a44287SMartin K. Petersen 	int inj_dif = 0;
3475c6a44287SMartin K. Petersen 	int inj_dix = 0;
3476639db475SFUJITA Tomonori 	int delay_override = 0;
347744d92694SMartin K. Petersen 	int unmap = 0;
3478639db475SFUJITA Tomonori 
3479639db475SFUJITA Tomonori 	scsi_set_resid(SCpnt, 0);
3480639db475SFUJITA Tomonori 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3481639db475SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: cmd ");
3482639db475SFUJITA Tomonori 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3483639db475SFUJITA Tomonori 			printk("%02x ", (int)cmd[k]);
3484639db475SFUJITA Tomonori 		printk("\n");
3485639db475SFUJITA Tomonori 	}
3486639db475SFUJITA Tomonori 
3487639db475SFUJITA Tomonori 	if (target == SCpnt->device->host->hostt->this_id) {
3488639db475SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3489639db475SFUJITA Tomonori 		       "target!\n");
3490639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3491639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3492639db475SFUJITA Tomonori 	}
3493639db475SFUJITA Tomonori 
3494639db475SFUJITA Tomonori 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
3495639db475SFUJITA Tomonori 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3496639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3497639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3498639db475SFUJITA Tomonori 	devip = devInfoReg(SCpnt->device);
3499639db475SFUJITA Tomonori 	if (NULL == devip)
3500639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3501639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3502639db475SFUJITA Tomonori 
3503639db475SFUJITA Tomonori 	if ((scsi_debug_every_nth != 0) &&
3504639db475SFUJITA Tomonori 	    (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3505639db475SFUJITA Tomonori 		scsi_debug_cmnd_count = 0;
3506639db475SFUJITA Tomonori 		if (scsi_debug_every_nth < -1)
3507639db475SFUJITA Tomonori 			scsi_debug_every_nth = -1;
3508639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3509639db475SFUJITA Tomonori 			return 0; /* ignore command causing timeout */
3510639db475SFUJITA Tomonori 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3511639db475SFUJITA Tomonori 			inj_recovered = 1; /* to reads and writes below */
3512639db475SFUJITA Tomonori 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
3513639db475SFUJITA Tomonori 			inj_transport = 1; /* to reads and writes below */
3514c6a44287SMartin K. Petersen 		else if (SCSI_DEBUG_OPT_DIF_ERR & scsi_debug_opts)
3515c6a44287SMartin K. Petersen 			inj_dif = 1; /* to reads and writes below */
3516c6a44287SMartin K. Petersen 		else if (SCSI_DEBUG_OPT_DIX_ERR & scsi_debug_opts)
3517c6a44287SMartin K. Petersen 			inj_dix = 1; /* to reads and writes below */
3518639db475SFUJITA Tomonori 	}
3519639db475SFUJITA Tomonori 
3520639db475SFUJITA Tomonori 	if (devip->wlun) {
3521639db475SFUJITA Tomonori 		switch (*cmd) {
3522639db475SFUJITA Tomonori 		case INQUIRY:
3523639db475SFUJITA Tomonori 		case REQUEST_SENSE:
3524639db475SFUJITA Tomonori 		case TEST_UNIT_READY:
3525639db475SFUJITA Tomonori 		case REPORT_LUNS:
3526639db475SFUJITA Tomonori 			break;  /* only allowable wlun commands */
3527639db475SFUJITA Tomonori 		default:
3528639db475SFUJITA Tomonori 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3529639db475SFUJITA Tomonori 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
3530639db475SFUJITA Tomonori 				       "not supported for wlun\n", *cmd);
3531639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3532639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3533639db475SFUJITA Tomonori 			errsts = check_condition_result;
3534639db475SFUJITA Tomonori 			return schedule_resp(SCpnt, devip, done, errsts,
3535639db475SFUJITA Tomonori 					     0);
3536639db475SFUJITA Tomonori 		}
3537639db475SFUJITA Tomonori 	}
3538639db475SFUJITA Tomonori 
3539639db475SFUJITA Tomonori 	switch (*cmd) {
3540639db475SFUJITA Tomonori 	case INQUIRY:     /* mandatory, ignore unit attention */
3541639db475SFUJITA Tomonori 		delay_override = 1;
3542639db475SFUJITA Tomonori 		errsts = resp_inquiry(SCpnt, target, devip);
3543639db475SFUJITA Tomonori 		break;
3544639db475SFUJITA Tomonori 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
3545639db475SFUJITA Tomonori 		delay_override = 1;
3546639db475SFUJITA Tomonori 		errsts = resp_requests(SCpnt, devip);
3547639db475SFUJITA Tomonori 		break;
3548639db475SFUJITA Tomonori 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3549639db475SFUJITA Tomonori 	case START_STOP:
3550639db475SFUJITA Tomonori 		errsts = resp_start_stop(SCpnt, devip);
3551639db475SFUJITA Tomonori 		break;
3552639db475SFUJITA Tomonori 	case ALLOW_MEDIUM_REMOVAL:
3553639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3554639db475SFUJITA Tomonori 		if (errsts)
3555639db475SFUJITA Tomonori 			break;
3556639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3557639db475SFUJITA Tomonori 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
3558639db475SFUJITA Tomonori 			       cmd[4] ? "inhibited" : "enabled");
3559639db475SFUJITA Tomonori 		break;
3560639db475SFUJITA Tomonori 	case SEND_DIAGNOSTIC:     /* mandatory */
3561639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3562639db475SFUJITA Tomonori 		break;
3563639db475SFUJITA Tomonori 	case TEST_UNIT_READY:     /* mandatory */
3564639db475SFUJITA Tomonori 		delay_override = 1;
3565639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3566639db475SFUJITA Tomonori 		break;
3567639db475SFUJITA Tomonori 	case RESERVE:
3568639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3569639db475SFUJITA Tomonori 		break;
3570639db475SFUJITA Tomonori 	case RESERVE_10:
3571639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3572639db475SFUJITA Tomonori 		break;
3573639db475SFUJITA Tomonori 	case RELEASE:
3574639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3575639db475SFUJITA Tomonori 		break;
3576639db475SFUJITA Tomonori 	case RELEASE_10:
3577639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3578639db475SFUJITA Tomonori 		break;
3579639db475SFUJITA Tomonori 	case READ_CAPACITY:
3580639db475SFUJITA Tomonori 		errsts = resp_readcap(SCpnt, devip);
3581639db475SFUJITA Tomonori 		break;
3582639db475SFUJITA Tomonori 	case SERVICE_ACTION_IN:
358344d92694SMartin K. Petersen 		if (cmd[1] == SAI_READ_CAPACITY_16)
358444d92694SMartin K. Petersen 			errsts = resp_readcap16(SCpnt, devip);
358544d92694SMartin K. Petersen 		else if (cmd[1] == SAI_GET_LBA_STATUS) {
358644d92694SMartin K. Petersen 
358744d92694SMartin K. Petersen 			if (scsi_debug_unmap_max_desc == 0) {
358844d92694SMartin K. Petersen 				mk_sense_buffer(devip, ILLEGAL_REQUEST,
358944d92694SMartin K. Petersen 						INVALID_COMMAND_OPCODE, 0);
359044d92694SMartin K. Petersen 				errsts = check_condition_result;
359144d92694SMartin K. Petersen 			} else
359244d92694SMartin K. Petersen 				errsts = resp_get_lba_status(SCpnt, devip);
359344d92694SMartin K. Petersen 		} else {
3594639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3595639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3596639db475SFUJITA Tomonori 			errsts = check_condition_result;
3597639db475SFUJITA Tomonori 		}
3598639db475SFUJITA Tomonori 		break;
3599639db475SFUJITA Tomonori 	case MAINTENANCE_IN:
3600639db475SFUJITA Tomonori 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
3601639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3602639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3603639db475SFUJITA Tomonori 			errsts = check_condition_result;
3604639db475SFUJITA Tomonori 			break;
3605639db475SFUJITA Tomonori 		}
3606639db475SFUJITA Tomonori 		errsts = resp_report_tgtpgs(SCpnt, devip);
3607639db475SFUJITA Tomonori 		break;
3608639db475SFUJITA Tomonori 	case READ_16:
3609639db475SFUJITA Tomonori 	case READ_12:
3610639db475SFUJITA Tomonori 	case READ_10:
3611395cef03SMartin K. Petersen 		/* READ{10,12,16} and DIF Type 2 are natural enemies */
3612395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
3613395cef03SMartin K. Petersen 		    cmd[1] & 0xe0) {
3614395cef03SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3615395cef03SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
3616395cef03SMartin K. Petersen 			errsts = check_condition_result;
3617395cef03SMartin K. Petersen 			break;
3618395cef03SMartin K. Petersen 		}
3619395cef03SMartin K. Petersen 
3620395cef03SMartin K. Petersen 		if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
3621395cef03SMartin K. Petersen 		     scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
3622395cef03SMartin K. Petersen 		    (cmd[1] & 0xe0) == 0)
3623395cef03SMartin K. Petersen 			printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
3624395cef03SMartin K. Petersen 
3625395cef03SMartin K. Petersen 		/* fall through */
3626639db475SFUJITA Tomonori 	case READ_6:
3627395cef03SMartin K. Petersen read:
3628639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3629639db475SFUJITA Tomonori 		if (errsts)
3630639db475SFUJITA Tomonori 			break;
3631639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3632639db475SFUJITA Tomonori 			break;
3633395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3634395cef03SMartin K. Petersen 		errsts = resp_read(SCpnt, lba, num, devip, ei_lba);
3635639db475SFUJITA Tomonori 		if (inj_recovered && (0 == errsts)) {
3636639db475SFUJITA Tomonori 			mk_sense_buffer(devip, RECOVERED_ERROR,
3637639db475SFUJITA Tomonori 					THRESHOLD_EXCEEDED, 0);
3638639db475SFUJITA Tomonori 			errsts = check_condition_result;
3639639db475SFUJITA Tomonori 		} else if (inj_transport && (0 == errsts)) {
3640639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ABORTED_COMMAND,
3641639db475SFUJITA Tomonori 					TRANSPORT_PROBLEM, ACK_NAK_TO);
3642639db475SFUJITA Tomonori 			errsts = check_condition_result;
3643c6a44287SMartin K. Petersen 		} else if (inj_dif && (0 == errsts)) {
3644c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
3645c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3646c6a44287SMartin K. Petersen 		} else if (inj_dix && (0 == errsts)) {
3647c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, 1);
3648c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3649639db475SFUJITA Tomonori 		}
3650639db475SFUJITA Tomonori 		break;
3651639db475SFUJITA Tomonori 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
3652639db475SFUJITA Tomonori 		delay_override = 1;
3653639db475SFUJITA Tomonori 		errsts = resp_report_luns(SCpnt, devip);
3654639db475SFUJITA Tomonori 		break;
3655639db475SFUJITA Tomonori 	case VERIFY:		/* 10 byte SBC-2 command */
3656639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3657639db475SFUJITA Tomonori 		break;
3658639db475SFUJITA Tomonori 	case WRITE_16:
3659639db475SFUJITA Tomonori 	case WRITE_12:
3660639db475SFUJITA Tomonori 	case WRITE_10:
3661395cef03SMartin K. Petersen 		/* WRITE{10,12,16} and DIF Type 2 are natural enemies */
3662395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
3663395cef03SMartin K. Petersen 		    cmd[1] & 0xe0) {
3664395cef03SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3665395cef03SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
3666395cef03SMartin K. Petersen 			errsts = check_condition_result;
3667395cef03SMartin K. Petersen 			break;
3668395cef03SMartin K. Petersen 		}
3669395cef03SMartin K. Petersen 
3670395cef03SMartin K. Petersen 		if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
3671395cef03SMartin K. Petersen 		     scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
3672395cef03SMartin K. Petersen 		    (cmd[1] & 0xe0) == 0)
3673395cef03SMartin K. Petersen 			printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
3674395cef03SMartin K. Petersen 
3675395cef03SMartin K. Petersen 		/* fall through */
3676639db475SFUJITA Tomonori 	case WRITE_6:
3677395cef03SMartin K. Petersen write:
3678639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3679639db475SFUJITA Tomonori 		if (errsts)
3680639db475SFUJITA Tomonori 			break;
3681639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3682639db475SFUJITA Tomonori 			break;
3683395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3684395cef03SMartin K. Petersen 		errsts = resp_write(SCpnt, lba, num, devip, ei_lba);
3685639db475SFUJITA Tomonori 		if (inj_recovered && (0 == errsts)) {
3686639db475SFUJITA Tomonori 			mk_sense_buffer(devip, RECOVERED_ERROR,
3687639db475SFUJITA Tomonori 					THRESHOLD_EXCEEDED, 0);
3688639db475SFUJITA Tomonori 			errsts = check_condition_result;
3689c6a44287SMartin K. Petersen 		} else if (inj_dif && (0 == errsts)) {
3690c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
3691c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3692c6a44287SMartin K. Petersen 		} else if (inj_dix && (0 == errsts)) {
3693c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, 1);
3694c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3695639db475SFUJITA Tomonori 		}
3696639db475SFUJITA Tomonori 		break;
369744d92694SMartin K. Petersen 	case WRITE_SAME_16:
369844d92694SMartin K. Petersen 		if (cmd[1] & 0x8)
369944d92694SMartin K. Petersen 			unmap = 1;
370044d92694SMartin K. Petersen 		/* fall through */
370144d92694SMartin K. Petersen 	case WRITE_SAME:
370244d92694SMartin K. Petersen 		errsts = check_readiness(SCpnt, 0, devip);
370344d92694SMartin K. Petersen 		if (errsts)
370444d92694SMartin K. Petersen 			break;
370544d92694SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
370644d92694SMartin K. Petersen 		errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap);
370744d92694SMartin K. Petersen 		break;
370844d92694SMartin K. Petersen 	case UNMAP:
370944d92694SMartin K. Petersen 		errsts = check_readiness(SCpnt, 0, devip);
371044d92694SMartin K. Petersen 		if (errsts)
371144d92694SMartin K. Petersen 			break;
371244d92694SMartin K. Petersen 
371344d92694SMartin K. Petersen 		if (scsi_debug_unmap_max_desc == 0) {
371444d92694SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
371544d92694SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
371644d92694SMartin K. Petersen 			errsts = check_condition_result;
371744d92694SMartin K. Petersen 		} else
371844d92694SMartin K. Petersen 			errsts = resp_unmap(SCpnt, devip);
371944d92694SMartin K. Petersen 		break;
3720639db475SFUJITA Tomonori 	case MODE_SENSE:
3721639db475SFUJITA Tomonori 	case MODE_SENSE_10:
3722639db475SFUJITA Tomonori 		errsts = resp_mode_sense(SCpnt, target, devip);
3723639db475SFUJITA Tomonori 		break;
3724639db475SFUJITA Tomonori 	case MODE_SELECT:
3725639db475SFUJITA Tomonori 		errsts = resp_mode_select(SCpnt, 1, devip);
3726639db475SFUJITA Tomonori 		break;
3727639db475SFUJITA Tomonori 	case MODE_SELECT_10:
3728639db475SFUJITA Tomonori 		errsts = resp_mode_select(SCpnt, 0, devip);
3729639db475SFUJITA Tomonori 		break;
3730639db475SFUJITA Tomonori 	case LOG_SENSE:
3731639db475SFUJITA Tomonori 		errsts = resp_log_sense(SCpnt, devip);
3732639db475SFUJITA Tomonori 		break;
3733639db475SFUJITA Tomonori 	case SYNCHRONIZE_CACHE:
3734639db475SFUJITA Tomonori 		delay_override = 1;
3735639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3736639db475SFUJITA Tomonori 		break;
3737639db475SFUJITA Tomonori 	case WRITE_BUFFER:
3738639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3739639db475SFUJITA Tomonori 		break;
3740639db475SFUJITA Tomonori 	case XDWRITEREAD_10:
3741639db475SFUJITA Tomonori 		if (!scsi_bidi_cmnd(SCpnt)) {
3742639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3743639db475SFUJITA Tomonori 					INVALID_FIELD_IN_CDB, 0);
3744639db475SFUJITA Tomonori 			errsts = check_condition_result;
3745639db475SFUJITA Tomonori 			break;
3746639db475SFUJITA Tomonori 		}
3747639db475SFUJITA Tomonori 
3748639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3749639db475SFUJITA Tomonori 		if (errsts)
3750639db475SFUJITA Tomonori 			break;
3751639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3752639db475SFUJITA Tomonori 			break;
3753395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3754395cef03SMartin K. Petersen 		errsts = resp_read(SCpnt, lba, num, devip, ei_lba);
3755639db475SFUJITA Tomonori 		if (errsts)
3756639db475SFUJITA Tomonori 			break;
3757395cef03SMartin K. Petersen 		errsts = resp_write(SCpnt, lba, num, devip, ei_lba);
3758639db475SFUJITA Tomonori 		if (errsts)
3759639db475SFUJITA Tomonori 			break;
3760639db475SFUJITA Tomonori 		errsts = resp_xdwriteread(SCpnt, lba, num, devip);
3761639db475SFUJITA Tomonori 		break;
3762395cef03SMartin K. Petersen 	case VARIABLE_LENGTH_CMD:
3763395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) {
3764395cef03SMartin K. Petersen 
3765395cef03SMartin K. Petersen 			if ((cmd[10] & 0xe0) == 0)
3766395cef03SMartin K. Petersen 				printk(KERN_ERR
3767395cef03SMartin K. Petersen 				       "Unprotected RD/WR to DIF device\n");
3768395cef03SMartin K. Petersen 
3769395cef03SMartin K. Petersen 			if (cmd[9] == READ_32) {
3770395cef03SMartin K. Petersen 				BUG_ON(SCpnt->cmd_len < 32);
3771395cef03SMartin K. Petersen 				goto read;
3772395cef03SMartin K. Petersen 			}
3773395cef03SMartin K. Petersen 
3774395cef03SMartin K. Petersen 			if (cmd[9] == WRITE_32) {
3775395cef03SMartin K. Petersen 				BUG_ON(SCpnt->cmd_len < 32);
3776395cef03SMartin K. Petersen 				goto write;
3777395cef03SMartin K. Petersen 			}
3778395cef03SMartin K. Petersen 		}
3779395cef03SMartin K. Petersen 
3780395cef03SMartin K. Petersen 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
3781395cef03SMartin K. Petersen 				INVALID_FIELD_IN_CDB, 0);
3782395cef03SMartin K. Petersen 		errsts = check_condition_result;
3783395cef03SMartin K. Petersen 		break;
3784395cef03SMartin K. Petersen 
3785639db475SFUJITA Tomonori 	default:
3786639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3787639db475SFUJITA Tomonori 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
3788639db475SFUJITA Tomonori 			       "supported\n", *cmd);
3789639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3790639db475SFUJITA Tomonori 		if (errsts)
3791639db475SFUJITA Tomonori 			break;	/* Unit attention takes precedence */
3792639db475SFUJITA Tomonori 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
3793639db475SFUJITA Tomonori 		errsts = check_condition_result;
3794639db475SFUJITA Tomonori 		break;
3795639db475SFUJITA Tomonori 	}
3796639db475SFUJITA Tomonori 	return schedule_resp(SCpnt, devip, done, errsts,
3797639db475SFUJITA Tomonori 			     (delay_override ? 0 : scsi_debug_delay));
3798639db475SFUJITA Tomonori }
3799639db475SFUJITA Tomonori 
38009e603ca0SFUJITA Tomonori static struct scsi_host_template sdebug_driver_template = {
38019e603ca0SFUJITA Tomonori 	.proc_info =		scsi_debug_proc_info,
38029e603ca0SFUJITA Tomonori 	.proc_name =		sdebug_proc_name,
38039e603ca0SFUJITA Tomonori 	.name =			"SCSI DEBUG",
38049e603ca0SFUJITA Tomonori 	.info =			scsi_debug_info,
38059e603ca0SFUJITA Tomonori 	.slave_alloc =		scsi_debug_slave_alloc,
38069e603ca0SFUJITA Tomonori 	.slave_configure =	scsi_debug_slave_configure,
38079e603ca0SFUJITA Tomonori 	.slave_destroy =	scsi_debug_slave_destroy,
38089e603ca0SFUJITA Tomonori 	.ioctl =		scsi_debug_ioctl,
38099e603ca0SFUJITA Tomonori 	.queuecommand =		scsi_debug_queuecommand,
38109e603ca0SFUJITA Tomonori 	.eh_abort_handler =	scsi_debug_abort,
38119e603ca0SFUJITA Tomonori 	.eh_bus_reset_handler = scsi_debug_bus_reset,
38129e603ca0SFUJITA Tomonori 	.eh_device_reset_handler = scsi_debug_device_reset,
38139e603ca0SFUJITA Tomonori 	.eh_host_reset_handler = scsi_debug_host_reset,
38149e603ca0SFUJITA Tomonori 	.bios_param =		scsi_debug_biosparam,
38159e603ca0SFUJITA Tomonori 	.can_queue =		SCSI_DEBUG_CANQUEUE,
38169e603ca0SFUJITA Tomonori 	.this_id =		7,
38179e603ca0SFUJITA Tomonori 	.sg_tablesize =		256,
38189e603ca0SFUJITA Tomonori 	.cmd_per_lun =		16,
38199e603ca0SFUJITA Tomonori 	.max_sectors =		0xffff,
38209e603ca0SFUJITA Tomonori 	.use_clustering = 	DISABLE_CLUSTERING,
38219e603ca0SFUJITA Tomonori 	.module =		THIS_MODULE,
38229e603ca0SFUJITA Tomonori };
38239e603ca0SFUJITA Tomonori 
38241da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
38251da177e4SLinus Torvalds {
38261da177e4SLinus Torvalds         int error = 0;
38271da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
38281da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
3829c6a44287SMartin K. Petersen 	int host_prot;
38301da177e4SLinus Torvalds 
38311da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
38321da177e4SLinus Torvalds 
38331da177e4SLinus Torvalds         hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
38341da177e4SLinus Torvalds         if (NULL == hpnt) {
3835cadbd4a5SHarvey Harrison                 printk(KERN_ERR "%s: scsi_register failed\n", __func__);
38361da177e4SLinus Torvalds                 error = -ENODEV;
38371da177e4SLinus Torvalds 		return error;
38381da177e4SLinus Torvalds         }
38391da177e4SLinus Torvalds 
38401da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
38411da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
38421da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
38431da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
38441da177e4SLinus Torvalds 	else
38451da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3846c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
38471da177e4SLinus Torvalds 
3848c6a44287SMartin K. Petersen 	host_prot = 0;
3849c6a44287SMartin K. Petersen 
3850c6a44287SMartin K. Petersen 	switch (scsi_debug_dif) {
3851c6a44287SMartin K. Petersen 
3852c6a44287SMartin K. Petersen 	case SD_DIF_TYPE1_PROTECTION:
3853c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE1_PROTECTION;
3854c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3855c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE1_PROTECTION;
3856c6a44287SMartin K. Petersen 		break;
3857c6a44287SMartin K. Petersen 
3858c6a44287SMartin K. Petersen 	case SD_DIF_TYPE2_PROTECTION:
3859c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE2_PROTECTION;
3860c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3861c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE2_PROTECTION;
3862c6a44287SMartin K. Petersen 		break;
3863c6a44287SMartin K. Petersen 
3864c6a44287SMartin K. Petersen 	case SD_DIF_TYPE3_PROTECTION:
3865c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE3_PROTECTION;
3866c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3867c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE3_PROTECTION;
3868c6a44287SMartin K. Petersen 		break;
3869c6a44287SMartin K. Petersen 
3870c6a44287SMartin K. Petersen 	default:
3871c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3872c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE0_PROTECTION;
3873c6a44287SMartin K. Petersen 		break;
3874c6a44287SMartin K. Petersen 	}
3875c6a44287SMartin K. Petersen 
3876c6a44287SMartin K. Petersen 	scsi_host_set_prot(hpnt, host_prot);
3877c6a44287SMartin K. Petersen 
3878c6a44287SMartin K. Petersen 	printk(KERN_INFO "scsi_debug: host protection%s%s%s%s%s%s%s\n",
3879c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "",
3880c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "",
3881c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "",
3882c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE0_PROTECTION) ? " DIX0" : "",
3883c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE1_PROTECTION) ? " DIX1" : "",
3884c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE2_PROTECTION) ? " DIX2" : "",
3885c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE3_PROTECTION) ? " DIX3" : "");
3886c6a44287SMartin K. Petersen 
3887c6a44287SMartin K. Petersen 	if (scsi_debug_guard == 1)
3888c6a44287SMartin K. Petersen 		scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_IP);
3889c6a44287SMartin K. Petersen 	else
3890c6a44287SMartin K. Petersen 		scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_CRC);
3891c6a44287SMartin K. Petersen 
38921da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
38931da177e4SLinus Torvalds         if (error) {
3894cadbd4a5SHarvey Harrison                 printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
38951da177e4SLinus Torvalds                 error = -ENODEV;
38961da177e4SLinus Torvalds 		scsi_host_put(hpnt);
38971da177e4SLinus Torvalds         } else
38981da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
38991da177e4SLinus Torvalds 
39001da177e4SLinus Torvalds 
39011da177e4SLinus Torvalds         return error;
39021da177e4SLinus Torvalds }
39031da177e4SLinus Torvalds 
39041da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
39051da177e4SLinus Torvalds {
39061da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
39078b40228fSFUJITA Tomonori 	struct sdebug_dev_info *sdbg_devinfo, *tmp;
39081da177e4SLinus Torvalds 
39091da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
39101da177e4SLinus Torvalds 
39111da177e4SLinus Torvalds 	if (!sdbg_host) {
39121da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
3913cadbd4a5SHarvey Harrison 		       __func__);
39141da177e4SLinus Torvalds 		return -ENODEV;
39151da177e4SLinus Torvalds 	}
39161da177e4SLinus Torvalds 
39171da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
39181da177e4SLinus Torvalds 
39198b40228fSFUJITA Tomonori 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
39208b40228fSFUJITA Tomonori 				 dev_list) {
39211da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
39221da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
39231da177e4SLinus Torvalds         }
39241da177e4SLinus Torvalds 
39251da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
39261da177e4SLinus Torvalds         return 0;
39271da177e4SLinus Torvalds }
39281da177e4SLinus Torvalds 
39298dea0d02SFUJITA Tomonori static int pseudo_lld_bus_match(struct device *dev,
39308dea0d02SFUJITA Tomonori 				struct device_driver *dev_driver)
39311da177e4SLinus Torvalds {
39328dea0d02SFUJITA Tomonori 	return 1;
39338dea0d02SFUJITA Tomonori }
39341da177e4SLinus Torvalds 
39358dea0d02SFUJITA Tomonori static struct bus_type pseudo_lld_bus = {
39368dea0d02SFUJITA Tomonori 	.name = "pseudo",
39378dea0d02SFUJITA Tomonori 	.match = pseudo_lld_bus_match,
39388dea0d02SFUJITA Tomonori 	.probe = sdebug_driver_probe,
39398dea0d02SFUJITA Tomonori 	.remove = sdebug_driver_remove,
39408dea0d02SFUJITA Tomonori };
3941