xref: /openbmc/linux/drivers/scsi/scsi_debug.c (revision 9ab98f57)
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  *
1578d4e5a0SDouglas Gilbert  *  For documentation see http://sg.danny.cz/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 
6178d4e5a0SDouglas Gilbert #define SCSI_DEBUG_VERSION "1.82"
6278d4e5a0SDouglas Gilbert static const char * scsi_debug_version_date = "20100324";
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
111e308b3d1SMartin K. Petersen #define DEF_OPT_BLKS 64
11244d92694SMartin K. Petersen #define DEF_UNMAP_MAX_BLOCKS 0
11344d92694SMartin K. Petersen #define DEF_UNMAP_MAX_DESC 0
11444d92694SMartin K. Petersen #define DEF_UNMAP_GRANULARITY 0
11544d92694SMartin K. Petersen #define DEF_UNMAP_ALIGNMENT 0
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds /* bit mask values for scsi_debug_opts */
1181da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_NOISE   1
1191da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_MEDIUM_ERR   2
1201da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_TIMEOUT   4
1211da177e4SLinus Torvalds #define SCSI_DEBUG_OPT_RECOVERED_ERR   8
1226f3cbf55SDouglas Gilbert #define SCSI_DEBUG_OPT_TRANSPORT_ERR   16
123c6a44287SMartin K. Petersen #define SCSI_DEBUG_OPT_DIF_ERR   32
124c6a44287SMartin K. Petersen #define SCSI_DEBUG_OPT_DIX_ERR   64
1251da177e4SLinus Torvalds /* When "every_nth" > 0 then modulo "every_nth" commands:
1261da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1271da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1281da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1296f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1306f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1311da177e4SLinus Torvalds  *
1321da177e4SLinus Torvalds  * When "every_nth" < 0 then after "- every_nth" commands:
1331da177e4SLinus Torvalds  *   - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
1341da177e4SLinus Torvalds  *   - a RECOVERED_ERROR is simulated on successful read and write
1351da177e4SLinus Torvalds  *     commands if SCSI_DEBUG_OPT_RECOVERED_ERR is set.
1366f3cbf55SDouglas Gilbert  *   - a TRANSPORT_ERROR is simulated on successful read and write
1376f3cbf55SDouglas Gilbert  *     commands if SCSI_DEBUG_OPT_TRANSPORT_ERR is set.
1381da177e4SLinus Torvalds  * This will continue until some other action occurs (e.g. the user
1391da177e4SLinus Torvalds  * writing a new value (other than -1 or 1) to every_nth via sysfs).
1401da177e4SLinus Torvalds  */
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds /* when 1==SCSI_DEBUG_OPT_MEDIUM_ERR, a medium error is simulated at this
1431da177e4SLinus Torvalds  * sector on read commands: */
1441da177e4SLinus Torvalds #define OPT_MEDIUM_ERR_ADDR   0x1234 /* that's sector 4660 in decimal */
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds /* If REPORT LUNS has luns >= 256 it can choose "flat space" (value 1)
1471da177e4SLinus Torvalds  * or "peripheral device" addressing (value 0) */
1481da177e4SLinus Torvalds #define SAM2_LUN_ADDRESS_METHOD 0
149c65b1445SDouglas Gilbert #define SAM2_WLUN_REPORT_LUNS 0xc101
1501da177e4SLinus Torvalds 
15178d4e5a0SDouglas Gilbert /* Can queue up to this number of commands. Typically commands that
15278d4e5a0SDouglas Gilbert  * that have a non-zero delay are queued. */
15378d4e5a0SDouglas Gilbert #define SCSI_DEBUG_CANQUEUE  255
15478d4e5a0SDouglas Gilbert 
1551da177e4SLinus Torvalds static int scsi_debug_add_host = DEF_NUM_HOST;
1561da177e4SLinus Torvalds static int scsi_debug_delay = DEF_DELAY;
1571da177e4SLinus Torvalds static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
1581da177e4SLinus Torvalds static int scsi_debug_every_nth = DEF_EVERY_NTH;
1591da177e4SLinus Torvalds static int scsi_debug_max_luns = DEF_MAX_LUNS;
16078d4e5a0SDouglas Gilbert static int scsi_debug_max_queue = SCSI_DEBUG_CANQUEUE;
1611da177e4SLinus Torvalds static int scsi_debug_num_parts = DEF_NUM_PARTS;
16278d4e5a0SDouglas Gilbert static int scsi_debug_no_uld = 0;
1631da177e4SLinus Torvalds static int scsi_debug_num_tgts = DEF_NUM_TGTS; /* targets per host */
1641da177e4SLinus Torvalds static int scsi_debug_opts = DEF_OPTS;
1651da177e4SLinus Torvalds static int scsi_debug_scsi_level = DEF_SCSI_LEVEL;
1661da177e4SLinus Torvalds static int scsi_debug_ptype = DEF_PTYPE; /* SCSI peripheral type (0==disk) */
1671da177e4SLinus Torvalds static int scsi_debug_dsense = DEF_D_SENSE;
168c65b1445SDouglas Gilbert static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
169c65b1445SDouglas Gilbert static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
17023183910SDouglas Gilbert static int scsi_debug_fake_rw = DEF_FAKE_RW;
17123183910SDouglas Gilbert static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
172597136abSMartin K. Petersen static int scsi_debug_sector_size = DEF_SECTOR_SIZE;
173c6a44287SMartin K. Petersen static int scsi_debug_dix = DEF_DIX;
174c6a44287SMartin K. Petersen static int scsi_debug_dif = DEF_DIF;
175c6a44287SMartin K. Petersen static int scsi_debug_guard = DEF_GUARD;
176c6a44287SMartin K. Petersen static int scsi_debug_ato = DEF_ATO;
177ea61fca5SMartin K. Petersen static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP;
178ea61fca5SMartin K. Petersen static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED;
179e308b3d1SMartin K. Petersen static int scsi_debug_opt_blks = DEF_OPT_BLKS;
18044d92694SMartin K. Petersen static int scsi_debug_unmap_max_desc = DEF_UNMAP_MAX_DESC;
18144d92694SMartin K. Petersen static int scsi_debug_unmap_max_blocks = DEF_UNMAP_MAX_BLOCKS;
18244d92694SMartin K. Petersen static int scsi_debug_unmap_granularity = DEF_UNMAP_GRANULARITY;
18344d92694SMartin K. Petersen static int scsi_debug_unmap_alignment = DEF_UNMAP_ALIGNMENT;
1841da177e4SLinus Torvalds 
1851da177e4SLinus Torvalds static int scsi_debug_cmnd_count = 0;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds #define DEV_READONLY(TGT)      (0)
1881da177e4SLinus Torvalds #define DEV_REMOVEABLE(TGT)    (0)
1891da177e4SLinus Torvalds 
190c65b1445SDouglas Gilbert static unsigned int sdebug_store_sectors;
1911da177e4SLinus Torvalds static sector_t sdebug_capacity;	/* in sectors */
1921da177e4SLinus Torvalds 
1931da177e4SLinus Torvalds /* old BIOS stuff, kernel may get rid of them but some mode sense pages
1941da177e4SLinus Torvalds    may still need them */
1951da177e4SLinus Torvalds static int sdebug_heads;		/* heads per disk */
1961da177e4SLinus Torvalds static int sdebug_cylinders_per;	/* cylinders per surface */
1971da177e4SLinus Torvalds static int sdebug_sectors_per;		/* sectors per cylinder */
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds #define SDEBUG_MAX_PARTS 4
2001da177e4SLinus Torvalds 
2011da177e4SLinus Torvalds #define SDEBUG_SENSE_LEN 32
2021da177e4SLinus Torvalds 
203395cef03SMartin K. Petersen #define SCSI_DEBUG_MAX_CMD_LEN 32
2049e603ca0SFUJITA Tomonori 
2051da177e4SLinus Torvalds struct sdebug_dev_info {
2061da177e4SLinus Torvalds 	struct list_head dev_list;
2071da177e4SLinus Torvalds 	unsigned char sense_buff[SDEBUG_SENSE_LEN];	/* weak nexus */
2081da177e4SLinus Torvalds 	unsigned int channel;
2091da177e4SLinus Torvalds 	unsigned int target;
2101da177e4SLinus Torvalds 	unsigned int lun;
2111da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
212c65b1445SDouglas Gilbert 	unsigned int wlun;
2131da177e4SLinus Torvalds 	char reset;
214c65b1445SDouglas Gilbert 	char stopped;
2151da177e4SLinus Torvalds 	char used;
2161da177e4SLinus Torvalds };
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds struct sdebug_host_info {
2191da177e4SLinus Torvalds 	struct list_head host_list;
2201da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2211da177e4SLinus Torvalds 	struct device dev;
2221da177e4SLinus Torvalds 	struct list_head dev_info_list;
2231da177e4SLinus Torvalds };
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds #define to_sdebug_host(d)	\
2261da177e4SLinus Torvalds 	container_of(d, struct sdebug_host_info, dev)
2271da177e4SLinus Torvalds 
2281da177e4SLinus Torvalds static LIST_HEAD(sdebug_host_list);
2291da177e4SLinus Torvalds static DEFINE_SPINLOCK(sdebug_host_list_lock);
2301da177e4SLinus Torvalds 
2311da177e4SLinus Torvalds typedef void (* done_funct_t) (struct scsi_cmnd *);
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds struct sdebug_queued_cmd {
2341da177e4SLinus Torvalds 	int in_use;
2351da177e4SLinus Torvalds 	struct timer_list cmnd_timer;
2361da177e4SLinus Torvalds 	done_funct_t done_funct;
2371da177e4SLinus Torvalds 	struct scsi_cmnd * a_cmnd;
2381da177e4SLinus Torvalds 	int scsi_result;
2391da177e4SLinus Torvalds };
2401da177e4SLinus Torvalds static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE];
2411da177e4SLinus Torvalds 
2421da177e4SLinus Torvalds static unsigned char * fake_storep;	/* ramdisk storage */
243c6a44287SMartin K. Petersen static unsigned char *dif_storep;	/* protection info */
24444d92694SMartin K. Petersen static void *map_storep;		/* provisioning map */
2451da177e4SLinus Torvalds 
24644d92694SMartin K. Petersen static unsigned long map_size;
2471da177e4SLinus Torvalds static int num_aborts = 0;
2481da177e4SLinus Torvalds static int num_dev_resets = 0;
2491da177e4SLinus Torvalds static int num_bus_resets = 0;
2501da177e4SLinus Torvalds static int num_host_resets = 0;
251c6a44287SMartin K. Petersen static int dix_writes;
252c6a44287SMartin K. Petersen static int dix_reads;
253c6a44287SMartin K. Petersen static int dif_errors;
2541da177e4SLinus Torvalds 
2551da177e4SLinus Torvalds static DEFINE_SPINLOCK(queued_arr_lock);
2561da177e4SLinus Torvalds static DEFINE_RWLOCK(atomic_rw);
2571da177e4SLinus Torvalds 
2581da177e4SLinus Torvalds static char sdebug_proc_name[] = "scsi_debug";
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds static struct bus_type pseudo_lld_bus;
2611da177e4SLinus Torvalds 
262c6a44287SMartin K. Petersen static inline sector_t dif_offset(sector_t sector)
263c6a44287SMartin K. Petersen {
264c6a44287SMartin K. Petersen 	return sector << 3;
265c6a44287SMartin K. Petersen }
266c6a44287SMartin K. Petersen 
2671da177e4SLinus Torvalds static struct device_driver sdebug_driverfs_driver = {
2681da177e4SLinus Torvalds 	.name 		= sdebug_proc_name,
2691da177e4SLinus Torvalds 	.bus		= &pseudo_lld_bus,
2701da177e4SLinus Torvalds };
2711da177e4SLinus Torvalds 
2721da177e4SLinus Torvalds static const int check_condition_result =
2731da177e4SLinus Torvalds 		(DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
2741da177e4SLinus Torvalds 
275c6a44287SMartin K. Petersen static const int illegal_condition_result =
276c6a44287SMartin K. Petersen 	(DRIVER_SENSE << 24) | (DID_ABORT << 16) | SAM_STAT_CHECK_CONDITION;
277c6a44287SMartin K. Petersen 
278c65b1445SDouglas Gilbert static unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
279c65b1445SDouglas Gilbert 				    0, 0, 0x2, 0x4b};
280c65b1445SDouglas Gilbert static unsigned char iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
281c65b1445SDouglas Gilbert 			           0, 0, 0x0, 0x0};
282c65b1445SDouglas Gilbert 
2831da177e4SLinus Torvalds static int sdebug_add_adapter(void);
2841da177e4SLinus Torvalds static void sdebug_remove_adapter(void);
2851da177e4SLinus Torvalds 
2868dea0d02SFUJITA Tomonori static void sdebug_max_tgts_luns(void)
2878dea0d02SFUJITA Tomonori {
2888dea0d02SFUJITA Tomonori 	struct sdebug_host_info *sdbg_host;
2898dea0d02SFUJITA Tomonori 	struct Scsi_Host *hpnt;
2908dea0d02SFUJITA Tomonori 
2918dea0d02SFUJITA Tomonori 	spin_lock(&sdebug_host_list_lock);
2928dea0d02SFUJITA Tomonori 	list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
2938dea0d02SFUJITA Tomonori 		hpnt = sdbg_host->shost;
2948dea0d02SFUJITA Tomonori 		if ((hpnt->this_id >= 0) &&
2958dea0d02SFUJITA Tomonori 		    (scsi_debug_num_tgts > hpnt->this_id))
2968dea0d02SFUJITA Tomonori 			hpnt->max_id = scsi_debug_num_tgts + 1;
2978dea0d02SFUJITA Tomonori 		else
2988dea0d02SFUJITA Tomonori 			hpnt->max_id = scsi_debug_num_tgts;
2998dea0d02SFUJITA Tomonori 		/* scsi_debug_max_luns; */
3008dea0d02SFUJITA Tomonori 		hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;
3018dea0d02SFUJITA Tomonori 	}
3028dea0d02SFUJITA Tomonori 	spin_unlock(&sdebug_host_list_lock);
3038dea0d02SFUJITA Tomonori }
3048dea0d02SFUJITA Tomonori 
3058dea0d02SFUJITA Tomonori static void mk_sense_buffer(struct sdebug_dev_info *devip, int key,
3068dea0d02SFUJITA Tomonori 			    int asc, int asq)
3078dea0d02SFUJITA Tomonori {
3088dea0d02SFUJITA Tomonori 	unsigned char *sbuff;
3098dea0d02SFUJITA Tomonori 
3108dea0d02SFUJITA Tomonori 	sbuff = devip->sense_buff;
3118dea0d02SFUJITA Tomonori 	memset(sbuff, 0, SDEBUG_SENSE_LEN);
3128dea0d02SFUJITA Tomonori 
3138dea0d02SFUJITA Tomonori 	scsi_build_sense_buffer(scsi_debug_dsense, sbuff, key, asc, asq);
3148dea0d02SFUJITA Tomonori 
3158dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3168dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug:    [sense_key,asc,ascq]: "
3178dea0d02SFUJITA Tomonori 		      "[0x%x,0x%x,0x%x]\n", key, asc, asq);
3188dea0d02SFUJITA Tomonori }
3191da177e4SLinus Torvalds 
3203de9f944SFUJITA Tomonori static void get_data_transfer_info(unsigned char *cmd,
321395cef03SMartin K. Petersen 				   unsigned long long *lba, unsigned int *num,
322395cef03SMartin K. Petersen 				   u32 *ei_lba)
3233de9f944SFUJITA Tomonori {
324395cef03SMartin K. Petersen 	*ei_lba = 0;
325395cef03SMartin K. Petersen 
3263de9f944SFUJITA Tomonori 	switch (*cmd) {
327395cef03SMartin K. Petersen 	case VARIABLE_LENGTH_CMD:
328395cef03SMartin K. Petersen 		*lba = (u64)cmd[19] | (u64)cmd[18] << 8 |
329395cef03SMartin K. Petersen 			(u64)cmd[17] << 16 | (u64)cmd[16] << 24 |
330395cef03SMartin K. Petersen 			(u64)cmd[15] << 32 | (u64)cmd[14] << 40 |
331395cef03SMartin K. Petersen 			(u64)cmd[13] << 48 | (u64)cmd[12] << 56;
332395cef03SMartin K. Petersen 
333395cef03SMartin K. Petersen 		*ei_lba = (u32)cmd[23] | (u32)cmd[22] << 8 |
334395cef03SMartin K. Petersen 			(u32)cmd[21] << 16 | (u32)cmd[20] << 24;
335395cef03SMartin K. Petersen 
336395cef03SMartin K. Petersen 		*num = (u32)cmd[31] | (u32)cmd[30] << 8 | (u32)cmd[29] << 16 |
337395cef03SMartin K. Petersen 			(u32)cmd[28] << 24;
338395cef03SMartin K. Petersen 		break;
339395cef03SMartin K. Petersen 
34044d92694SMartin K. Petersen 	case WRITE_SAME_16:
3413de9f944SFUJITA Tomonori 	case WRITE_16:
3423de9f944SFUJITA Tomonori 	case READ_16:
343d5cdc989SFUJITA Tomonori 		*lba = (u64)cmd[9] | (u64)cmd[8] << 8 |
344d5cdc989SFUJITA Tomonori 			(u64)cmd[7] << 16 | (u64)cmd[6] << 24 |
345d5cdc989SFUJITA Tomonori 			(u64)cmd[5] << 32 | (u64)cmd[4] << 40 |
346d5cdc989SFUJITA Tomonori 			(u64)cmd[3] << 48 | (u64)cmd[2] << 56;
347d5cdc989SFUJITA Tomonori 
348d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[13] | (u32)cmd[12] << 8 | (u32)cmd[11] << 16 |
349d5cdc989SFUJITA Tomonori 			(u32)cmd[10] << 24;
3503de9f944SFUJITA Tomonori 		break;
3513de9f944SFUJITA Tomonori 	case WRITE_12:
3523de9f944SFUJITA Tomonori 	case READ_12:
353d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[5] | (u32)cmd[4] << 8 | (u32)cmd[3] << 16 |
354d5cdc989SFUJITA Tomonori 			(u32)cmd[2] << 24;
355d5cdc989SFUJITA Tomonori 
356d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[9] | (u32)cmd[8] << 8 | (u32)cmd[7] << 16 |
357d5cdc989SFUJITA Tomonori 			(u32)cmd[6] << 24;
3583de9f944SFUJITA Tomonori 		break;
35944d92694SMartin K. Petersen 	case WRITE_SAME:
3603de9f944SFUJITA Tomonori 	case WRITE_10:
3613de9f944SFUJITA Tomonori 	case READ_10:
362c639d14eSFUJITA Tomonori 	case XDWRITEREAD_10:
363d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[5] | (u32)cmd[4] << 8 |	(u32)cmd[3] << 16 |
364d5cdc989SFUJITA Tomonori 			(u32)cmd[2] << 24;
365d5cdc989SFUJITA Tomonori 
366d5cdc989SFUJITA Tomonori 		*num = (u32)cmd[8] | (u32)cmd[7] << 8;
3673de9f944SFUJITA Tomonori 		break;
3683de9f944SFUJITA Tomonori 	case WRITE_6:
3693de9f944SFUJITA Tomonori 	case READ_6:
370d5cdc989SFUJITA Tomonori 		*lba = (u32)cmd[3] | (u32)cmd[2] << 8 |
371d5cdc989SFUJITA Tomonori 			(u32)(cmd[1] & 0x1f) << 16;
3723de9f944SFUJITA Tomonori 		*num = (0 == cmd[4]) ? 256 : cmd[4];
3733de9f944SFUJITA Tomonori 		break;
3743de9f944SFUJITA Tomonori 	default:
3753de9f944SFUJITA Tomonori 		break;
3763de9f944SFUJITA Tomonori 	}
3773de9f944SFUJITA Tomonori }
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds static int scsi_debug_ioctl(struct scsi_device *dev, int cmd, void __user *arg)
3801da177e4SLinus Torvalds {
3811da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
3821da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd);
3831da177e4SLinus Torvalds 	}
3841da177e4SLinus Torvalds 	return -EINVAL;
3851da177e4SLinus Torvalds 	/* return -ENOTTY; // correct return but upsets fdisk */
3861da177e4SLinus Torvalds }
3871da177e4SLinus Torvalds 
388c65b1445SDouglas Gilbert static int check_readiness(struct scsi_cmnd * SCpnt, int reset_only,
389c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
3901da177e4SLinus Torvalds {
3911da177e4SLinus Torvalds 	if (devip->reset) {
3921da177e4SLinus Torvalds 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3931da177e4SLinus Torvalds 			printk(KERN_INFO "scsi_debug: Reporting Unit "
3941da177e4SLinus Torvalds 			       "attention: power on reset\n");
3951da177e4SLinus Torvalds 		devip->reset = 0;
3961da177e4SLinus Torvalds 		mk_sense_buffer(devip, UNIT_ATTENTION, POWERON_RESET, 0);
3971da177e4SLinus Torvalds 		return check_condition_result;
3981da177e4SLinus Torvalds 	}
399c65b1445SDouglas Gilbert 	if ((0 == reset_only) && devip->stopped) {
400c65b1445SDouglas Gilbert 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
401c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug: Reporting Not "
402c65b1445SDouglas Gilbert 			       "ready: initializing command required\n");
403c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, NOT_READY, LOGICAL_UNIT_NOT_READY,
404c65b1445SDouglas Gilbert 				0x2);
405c65b1445SDouglas Gilbert 		return check_condition_result;
406c65b1445SDouglas Gilbert 	}
4071da177e4SLinus Torvalds 	return 0;
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds /* Returns 0 if ok else (DID_ERROR << 16). Sets scp->resid . */
4111da177e4SLinus Torvalds static int fill_from_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
4121da177e4SLinus Torvalds 				int arr_len)
4131da177e4SLinus Torvalds {
41421a61829SFUJITA Tomonori 	int act_len;
415072d0bb3SFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
4161da177e4SLinus Torvalds 
417072d0bb3SFUJITA Tomonori 	if (!sdb->length)
4181da177e4SLinus Torvalds 		return 0;
419072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_FROM_DEVICE))
4201da177e4SLinus Torvalds 		return (DID_ERROR << 16);
42121a61829SFUJITA Tomonori 
42221a61829SFUJITA Tomonori 	act_len = sg_copy_from_buffer(sdb->table.sgl, sdb->table.nents,
42321a61829SFUJITA Tomonori 				      arr, arr_len);
424072d0bb3SFUJITA Tomonori 	if (sdb->resid)
425072d0bb3SFUJITA Tomonori 		sdb->resid -= act_len;
426c65b1445SDouglas Gilbert 	else
42721a61829SFUJITA Tomonori 		sdb->resid = scsi_bufflen(scp) - act_len;
42821a61829SFUJITA Tomonori 
4291da177e4SLinus Torvalds 	return 0;
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds /* Returns number of bytes fetched into 'arr' or -1 if error. */
4331da177e4SLinus Torvalds static int fetch_to_dev_buffer(struct scsi_cmnd *scp, unsigned char *arr,
43421a61829SFUJITA Tomonori 			       int arr_len)
4351da177e4SLinus Torvalds {
43621a61829SFUJITA Tomonori 	if (!scsi_bufflen(scp))
4371da177e4SLinus Torvalds 		return 0;
438072d0bb3SFUJITA Tomonori 	if (!(scsi_bidi_cmnd(scp) || scp->sc_data_direction == DMA_TO_DEVICE))
4391da177e4SLinus Torvalds 		return -1;
44021a61829SFUJITA Tomonori 
44121a61829SFUJITA Tomonori 	return scsi_sg_copy_to_buffer(scp, arr, arr_len);
4421da177e4SLinus Torvalds }
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds static const char * inq_vendor_id = "Linux   ";
4461da177e4SLinus Torvalds static const char * inq_product_id = "scsi_debug      ";
4471da177e4SLinus Torvalds static const char * inq_product_rev = "0004";
4481da177e4SLinus Torvalds 
4495a09e398SHannes Reinecke static int inquiry_evpd_83(unsigned char * arr, int port_group_id,
4505a09e398SHannes Reinecke 			   int target_dev_id, int dev_id_num,
4515a09e398SHannes Reinecke 			   const char * dev_id_str,
452c65b1445SDouglas Gilbert 			   int dev_id_str_len)
4531da177e4SLinus Torvalds {
454c65b1445SDouglas Gilbert 	int num, port_a;
455c65b1445SDouglas Gilbert 	char b[32];
4561da177e4SLinus Torvalds 
457c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
4581da177e4SLinus Torvalds 	/* T10 vendor identifier field format (faked) */
4591da177e4SLinus Torvalds 	arr[0] = 0x2;	/* ASCII */
4601da177e4SLinus Torvalds 	arr[1] = 0x1;
4611da177e4SLinus Torvalds 	arr[2] = 0x0;
4621da177e4SLinus Torvalds 	memcpy(&arr[4], inq_vendor_id, 8);
4631da177e4SLinus Torvalds 	memcpy(&arr[12], inq_product_id, 16);
4641da177e4SLinus Torvalds 	memcpy(&arr[28], dev_id_str, dev_id_str_len);
4651da177e4SLinus Torvalds 	num = 8 + 16 + dev_id_str_len;
4661da177e4SLinus Torvalds 	arr[3] = num;
4671da177e4SLinus Torvalds 	num += 4;
468c65b1445SDouglas Gilbert 	if (dev_id_num >= 0) {
469c65b1445SDouglas Gilbert 		/* NAA-5, Logical unit identifier (binary) */
470c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* binary (not necessarily sas) */
471c65b1445SDouglas Gilbert 		arr[num++] = 0x3;	/* PIV=0, lu, naa */
472c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
473c65b1445SDouglas Gilbert 		arr[num++] = 0x8;
474c65b1445SDouglas Gilbert 		arr[num++] = 0x53;  /* naa-5 ieee company id=0x333333 (fake) */
475c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
476c65b1445SDouglas Gilbert 		arr[num++] = 0x33;
477c65b1445SDouglas Gilbert 		arr[num++] = 0x30;
478c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 24);
479c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 16) & 0xff;
480c65b1445SDouglas Gilbert 		arr[num++] = (dev_id_num >> 8) & 0xff;
481c65b1445SDouglas Gilbert 		arr[num++] = dev_id_num & 0xff;
482c65b1445SDouglas Gilbert 		/* Target relative port number */
483c65b1445SDouglas Gilbert 		arr[num++] = 0x61;	/* proto=sas, binary */
484c65b1445SDouglas Gilbert 		arr[num++] = 0x94;	/* PIV=1, target port, rel port */
485c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
486c65b1445SDouglas Gilbert 		arr[num++] = 0x4;	/* length */
487c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
488c65b1445SDouglas Gilbert 		arr[num++] = 0x0;	/* reserved */
489c65b1445SDouglas Gilbert 		arr[num++] = 0x0;
490c65b1445SDouglas Gilbert 		arr[num++] = 0x1;	/* relative port A */
491c65b1445SDouglas Gilbert 	}
492c65b1445SDouglas Gilbert 	/* NAA-5, Target port identifier */
493c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
494c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* piv=1, target port, naa */
495c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
496c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
497c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
498c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
499c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
500c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
501c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
502c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
503c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
504c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
5055a09e398SHannes Reinecke 	/* NAA-5, Target port group identifier */
5065a09e398SHannes Reinecke 	arr[num++] = 0x61;	/* proto=sas, binary */
5075a09e398SHannes Reinecke 	arr[num++] = 0x95;	/* piv=1, target port group id */
5085a09e398SHannes Reinecke 	arr[num++] = 0x0;
5095a09e398SHannes Reinecke 	arr[num++] = 0x4;
5105a09e398SHannes Reinecke 	arr[num++] = 0;
5115a09e398SHannes Reinecke 	arr[num++] = 0;
5125a09e398SHannes Reinecke 	arr[num++] = (port_group_id >> 8) & 0xff;
5135a09e398SHannes Reinecke 	arr[num++] = port_group_id & 0xff;
514c65b1445SDouglas Gilbert 	/* NAA-5, Target device identifier */
515c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
516c65b1445SDouglas Gilbert 	arr[num++] = 0xa3;	/* piv=1, target device, naa */
517c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
518c65b1445SDouglas Gilbert 	arr[num++] = 0x8;
519c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* naa-5, company id=0x222222 (fake) */
520c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
521c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
522c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
523c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 24);
524c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 16) & 0xff;
525c65b1445SDouglas Gilbert 	arr[num++] = (target_dev_id >> 8) & 0xff;
526c65b1445SDouglas Gilbert 	arr[num++] = target_dev_id & 0xff;
527c65b1445SDouglas Gilbert 	/* SCSI name string: Target device identifier */
528c65b1445SDouglas Gilbert 	arr[num++] = 0x63;	/* proto=sas, UTF-8 */
529c65b1445SDouglas Gilbert 	arr[num++] = 0xa8;	/* piv=1, target device, SCSI name string */
530c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
531c65b1445SDouglas Gilbert 	arr[num++] = 24;
532c65b1445SDouglas Gilbert 	memcpy(arr + num, "naa.52222220", 12);
533c65b1445SDouglas Gilbert 	num += 12;
534c65b1445SDouglas Gilbert 	snprintf(b, sizeof(b), "%08X", target_dev_id);
535c65b1445SDouglas Gilbert 	memcpy(arr + num, b, 8);
536c65b1445SDouglas Gilbert 	num += 8;
537c65b1445SDouglas Gilbert 	memset(arr + num, 0, 4);
538c65b1445SDouglas Gilbert 	num += 4;
539c65b1445SDouglas Gilbert 	return num;
540c65b1445SDouglas Gilbert }
541c65b1445SDouglas Gilbert 
542c65b1445SDouglas Gilbert 
543c65b1445SDouglas Gilbert static unsigned char vpd84_data[] = {
544c65b1445SDouglas Gilbert /* from 4th byte */ 0x22,0x22,0x22,0x0,0xbb,0x0,
545c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x1,
546c65b1445SDouglas Gilbert     0x22,0x22,0x22,0x0,0xbb,0x2,
547c65b1445SDouglas Gilbert };
548c65b1445SDouglas Gilbert 
549c65b1445SDouglas Gilbert static int inquiry_evpd_84(unsigned char * arr)
550c65b1445SDouglas Gilbert {
551c65b1445SDouglas Gilbert 	memcpy(arr, vpd84_data, sizeof(vpd84_data));
552c65b1445SDouglas Gilbert 	return sizeof(vpd84_data);
553c65b1445SDouglas Gilbert }
554c65b1445SDouglas Gilbert 
555c65b1445SDouglas Gilbert static int inquiry_evpd_85(unsigned char * arr)
556c65b1445SDouglas Gilbert {
557c65b1445SDouglas Gilbert 	int num = 0;
558c65b1445SDouglas Gilbert 	const char * na1 = "https://www.kernel.org/config";
559c65b1445SDouglas Gilbert 	const char * na2 = "http://www.kernel.org/log";
560c65b1445SDouglas Gilbert 	int plen, olen;
561c65b1445SDouglas Gilbert 
562c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* lu, storage config */
563c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
564c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
565c65b1445SDouglas Gilbert 	olen = strlen(na1);
566c65b1445SDouglas Gilbert 	plen = olen + 1;
567c65b1445SDouglas Gilbert 	if (plen % 4)
568c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
569c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null termianted, padded */
570c65b1445SDouglas Gilbert 	memcpy(arr + num, na1, olen);
571c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
572c65b1445SDouglas Gilbert 	num += plen;
573c65b1445SDouglas Gilbert 
574c65b1445SDouglas Gilbert 	arr[num++] = 0x4;	/* lu, logging */
575c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
576c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
577c65b1445SDouglas Gilbert 	olen = strlen(na2);
578c65b1445SDouglas Gilbert 	plen = olen + 1;
579c65b1445SDouglas Gilbert 	if (plen % 4)
580c65b1445SDouglas Gilbert 		plen = ((plen / 4) + 1) * 4;
581c65b1445SDouglas Gilbert 	arr[num++] = plen;	/* length, null terminated, padded */
582c65b1445SDouglas Gilbert 	memcpy(arr + num, na2, olen);
583c65b1445SDouglas Gilbert 	memset(arr + num + olen, 0, plen - olen);
584c65b1445SDouglas Gilbert 	num += plen;
585c65b1445SDouglas Gilbert 
586c65b1445SDouglas Gilbert 	return num;
587c65b1445SDouglas Gilbert }
588c65b1445SDouglas Gilbert 
589c65b1445SDouglas Gilbert /* SCSI ports VPD page */
590c65b1445SDouglas Gilbert static int inquiry_evpd_88(unsigned char * arr, int target_dev_id)
591c65b1445SDouglas Gilbert {
592c65b1445SDouglas Gilbert 	int num = 0;
593c65b1445SDouglas Gilbert 	int port_a, port_b;
594c65b1445SDouglas Gilbert 
595c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
596c65b1445SDouglas Gilbert 	port_b = port_a + 1;
597c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
598c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
599c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
600c65b1445SDouglas Gilbert 	arr[num++] = 0x1;	/* relative port 1 (primary) */
601c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
602c65b1445SDouglas Gilbert 	num += 6;
603c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
604c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
605c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (A) */
606c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
607c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
608c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
609c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
610c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
611c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
612c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
613c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
614c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 24);
615c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 16) & 0xff;
616c65b1445SDouglas Gilbert 	arr[num++] = (port_a >> 8) & 0xff;
617c65b1445SDouglas Gilbert 	arr[num++] = port_a & 0xff;
618c65b1445SDouglas Gilbert 
619c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
620c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
621c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
622c65b1445SDouglas Gilbert 	arr[num++] = 0x2;	/* relative port 2 (secondary) */
623c65b1445SDouglas Gilbert 	memset(arr + num, 0, 6);
624c65b1445SDouglas Gilbert 	num += 6;
625c65b1445SDouglas Gilbert 	arr[num++] = 0x0;
626c65b1445SDouglas Gilbert 	arr[num++] = 12;	/* length tp descriptor */
627c65b1445SDouglas Gilbert 	/* naa-5 target port identifier (B) */
628c65b1445SDouglas Gilbert 	arr[num++] = 0x61;	/* proto=sas, binary */
629c65b1445SDouglas Gilbert 	arr[num++] = 0x93;	/* PIV=1, target port, NAA */
630c65b1445SDouglas Gilbert 	arr[num++] = 0x0;	/* reserved */
631c65b1445SDouglas Gilbert 	arr[num++] = 0x8;	/* length */
632c65b1445SDouglas Gilbert 	arr[num++] = 0x52;	/* NAA-5, company_id=0x222222 (fake) */
633c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
634c65b1445SDouglas Gilbert 	arr[num++] = 0x22;
635c65b1445SDouglas Gilbert 	arr[num++] = 0x20;
636c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 24);
637c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 16) & 0xff;
638c65b1445SDouglas Gilbert 	arr[num++] = (port_b >> 8) & 0xff;
639c65b1445SDouglas Gilbert 	arr[num++] = port_b & 0xff;
640c65b1445SDouglas Gilbert 
641c65b1445SDouglas Gilbert 	return num;
642c65b1445SDouglas Gilbert }
643c65b1445SDouglas Gilbert 
644c65b1445SDouglas Gilbert 
645c65b1445SDouglas Gilbert static unsigned char vpd89_data[] = {
646c65b1445SDouglas Gilbert /* from 4th byte */ 0,0,0,0,
647c65b1445SDouglas Gilbert 'l','i','n','u','x',' ',' ',' ',
648c65b1445SDouglas Gilbert 'S','A','T',' ','s','c','s','i','_','d','e','b','u','g',' ',' ',
649c65b1445SDouglas Gilbert '1','2','3','4',
650c65b1445SDouglas Gilbert 0x34,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,
651c65b1445SDouglas Gilbert 0xec,0,0,0,
652c65b1445SDouglas Gilbert 0x5a,0xc,0xff,0x3f,0x37,0xc8,0x10,0,0,0,0,0,0x3f,0,0,0,
653c65b1445SDouglas Gilbert 0,0,0,0,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x20,0x20,0x20,0x20,
654c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0,0,0,0x40,0x4,0,0x2e,0x33,
655c65b1445SDouglas Gilbert 0x38,0x31,0x20,0x20,0x20,0x20,0x54,0x53,0x38,0x33,0x30,0x30,0x33,0x31,
656c65b1445SDouglas Gilbert 0x53,0x41,
657c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
658c65b1445SDouglas Gilbert 0x20,0x20,
659c65b1445SDouglas Gilbert 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
660c65b1445SDouglas Gilbert 0x10,0x80,
661c65b1445SDouglas Gilbert 0,0,0,0x2f,0,0,0,0x2,0,0x2,0x7,0,0xff,0xff,0x1,0,
662c65b1445SDouglas Gilbert 0x3f,0,0xc1,0xff,0x3e,0,0x10,0x1,0xb0,0xf8,0x50,0x9,0,0,0x7,0,
663c65b1445SDouglas Gilbert 0x3,0,0x78,0,0x78,0,0xf0,0,0x78,0,0,0,0,0,0,0,
664c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0x2,0,0,0,0,0,0,0,
665c65b1445SDouglas Gilbert 0x7e,0,0x1b,0,0x6b,0x34,0x1,0x7d,0x3,0x40,0x69,0x34,0x1,0x3c,0x3,0x40,
666c65b1445SDouglas Gilbert 0x7f,0x40,0,0,0,0,0xfe,0xfe,0,0,0,0,0,0xfe,0,0,
667c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0xb0,0xf8,0x50,0x9,0,0,0,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 0x1,0,0xb0,0xf8,0x50,0x9,0xb0,0xf8,0x50,0x9,0x20,0x20,0x2,0,0xb6,0x42,
672c65b1445SDouglas Gilbert 0,0x80,0x8a,0,0x6,0x3c,0xa,0x3c,0xff,0xff,0xc6,0x7,0,0x1,0,0x8,
673c65b1445SDouglas Gilbert 0xf0,0xf,0,0x10,0x2,0,0x30,0,0,0,0,0,0,0,0x6,0xfe,
674c65b1445SDouglas Gilbert 0,0,0x2,0,0x50,0,0x8a,0,0x4f,0x95,0,0,0x21,0,0xb,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,0,0,
680c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
681c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
682c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
683c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
684c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
685c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
686c65b1445SDouglas Gilbert 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0xa5,0x51,
687c65b1445SDouglas Gilbert };
688c65b1445SDouglas Gilbert 
689c65b1445SDouglas Gilbert static int inquiry_evpd_89(unsigned char * arr)
690c65b1445SDouglas Gilbert {
691c65b1445SDouglas Gilbert 	memcpy(arr, vpd89_data, sizeof(vpd89_data));
692c65b1445SDouglas Gilbert 	return sizeof(vpd89_data);
693c65b1445SDouglas Gilbert }
694c65b1445SDouglas Gilbert 
695c65b1445SDouglas Gilbert 
6961e49f785SDouglas Gilbert /* Block limits VPD page (SBC-3) */
697c65b1445SDouglas Gilbert static unsigned char vpdb0_data[] = {
6981e49f785SDouglas Gilbert 	/* from 4th byte */ 0,0,0,4, 0,0,0x4,0, 0,0,0,64,
6991e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7001e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
7011e49f785SDouglas Gilbert 	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
702c65b1445SDouglas Gilbert };
703c65b1445SDouglas Gilbert 
704c65b1445SDouglas Gilbert static int inquiry_evpd_b0(unsigned char * arr)
705c65b1445SDouglas Gilbert {
706ea61fca5SMartin K. Petersen 	unsigned int gran;
707ea61fca5SMartin K. Petersen 
708c65b1445SDouglas Gilbert 	memcpy(arr, vpdb0_data, sizeof(vpdb0_data));
709e308b3d1SMartin K. Petersen 
710e308b3d1SMartin K. Petersen 	/* Optimal transfer length granularity */
711ea61fca5SMartin K. Petersen 	gran = 1 << scsi_debug_physblk_exp;
712ea61fca5SMartin K. Petersen 	arr[2] = (gran >> 8) & 0xff;
713ea61fca5SMartin K. Petersen 	arr[3] = gran & 0xff;
714e308b3d1SMartin K. Petersen 
715e308b3d1SMartin K. Petersen 	/* Maximum Transfer Length */
716c65b1445SDouglas Gilbert 	if (sdebug_store_sectors > 0x400) {
717c65b1445SDouglas Gilbert 		arr[4] = (sdebug_store_sectors >> 24) & 0xff;
718c65b1445SDouglas Gilbert 		arr[5] = (sdebug_store_sectors >> 16) & 0xff;
719c65b1445SDouglas Gilbert 		arr[6] = (sdebug_store_sectors >> 8) & 0xff;
720c65b1445SDouglas Gilbert 		arr[7] = sdebug_store_sectors & 0xff;
721c65b1445SDouglas Gilbert 	}
72244d92694SMartin K. Petersen 
723e308b3d1SMartin K. Petersen 	/* Optimal Transfer Length */
724e308b3d1SMartin K. Petersen 	put_unaligned_be32(scsi_debug_opt_blks, &arr[8]);
725e308b3d1SMartin K. Petersen 
72644d92694SMartin K. Petersen 	if (scsi_debug_unmap_max_desc) {
72744d92694SMartin K. Petersen 		unsigned int blocks;
72844d92694SMartin K. Petersen 
72944d92694SMartin K. Petersen 		if (scsi_debug_unmap_max_blocks)
73044d92694SMartin K. Petersen 			blocks = scsi_debug_unmap_max_blocks;
73144d92694SMartin K. Petersen 		else
73244d92694SMartin K. Petersen 			blocks = 0xffffffff;
73344d92694SMartin K. Petersen 
734e308b3d1SMartin K. Petersen 		/* Maximum Unmap LBA Count */
73544d92694SMartin K. Petersen 		put_unaligned_be32(blocks, &arr[16]);
736e308b3d1SMartin K. Petersen 
737e308b3d1SMartin K. Petersen 		/* Maximum Unmap Block Descriptor Count */
73844d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_max_desc, &arr[20]);
73944d92694SMartin K. Petersen 	}
74044d92694SMartin K. Petersen 
741e308b3d1SMartin K. Petersen 	/* Unmap Granularity Alignment */
74244d92694SMartin K. Petersen 	if (scsi_debug_unmap_alignment) {
74344d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_alignment, &arr[28]);
74444d92694SMartin K. Petersen 		arr[28] |= 0x80; /* UGAVALID */
74544d92694SMartin K. Petersen 	}
74644d92694SMartin K. Petersen 
747e308b3d1SMartin K. Petersen 	/* Optimal Unmap Granularity */
74844d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity) {
74944d92694SMartin K. Petersen 		put_unaligned_be32(scsi_debug_unmap_granularity, &arr[24]);
75044d92694SMartin K. Petersen 		return 0x3c; /* Mandatory page length for thin provisioning */
75144d92694SMartin K. Petersen 	}
75244d92694SMartin K. Petersen 
753c65b1445SDouglas Gilbert 	return sizeof(vpdb0_data);
7541da177e4SLinus Torvalds }
7551da177e4SLinus Torvalds 
7561e49f785SDouglas Gilbert /* Block device characteristics VPD page (SBC-3) */
757eac6e8e4SMatthew Wilcox static int inquiry_evpd_b1(unsigned char *arr)
758eac6e8e4SMatthew Wilcox {
759eac6e8e4SMatthew Wilcox 	memset(arr, 0, 0x3c);
760eac6e8e4SMatthew Wilcox 	arr[0] = 0;
7611e49f785SDouglas Gilbert 	arr[1] = 1;	/* non rotating medium (e.g. solid state) */
7621e49f785SDouglas Gilbert 	arr[2] = 0;
7631e49f785SDouglas Gilbert 	arr[3] = 5;	/* less than 1.8" */
764eac6e8e4SMatthew Wilcox 
765eac6e8e4SMatthew Wilcox 	return 0x3c;
766eac6e8e4SMatthew Wilcox }
7671da177e4SLinus Torvalds 
7681da177e4SLinus Torvalds #define SDEBUG_LONG_INQ_SZ 96
769c65b1445SDouglas Gilbert #define SDEBUG_MAX_INQ_ARR_SZ 584
7701da177e4SLinus Torvalds 
7711da177e4SLinus Torvalds static int resp_inquiry(struct scsi_cmnd * scp, int target,
7721da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
7731da177e4SLinus Torvalds {
7741da177e4SLinus Torvalds 	unsigned char pq_pdt;
7755a09e398SHannes Reinecke 	unsigned char * arr;
7761da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
7775a09e398SHannes Reinecke 	int alloc_len, n, ret;
7781da177e4SLinus Torvalds 
7791da177e4SLinus Torvalds 	alloc_len = (cmd[3] << 8) + cmd[4];
7806f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_INQ_ARR_SZ, GFP_ATOMIC);
7816f3cbf55SDouglas Gilbert 	if (! arr)
7826f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
783c65b1445SDouglas Gilbert 	if (devip->wlun)
784c65b1445SDouglas Gilbert 		pq_pdt = 0x1e;	/* present, wlun */
785c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (0 == devip->lun))
786c65b1445SDouglas Gilbert 		pq_pdt = 0x7f;	/* not present, no device type */
787c65b1445SDouglas Gilbert 	else
7881da177e4SLinus Torvalds 		pq_pdt = (scsi_debug_ptype & 0x1f);
7891da177e4SLinus Torvalds 	arr[0] = pq_pdt;
7901da177e4SLinus Torvalds 	if (0x2 & cmd[1]) {  /* CMDDT bit set */
7911da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
7921da177e4SLinus Torvalds 			       	0);
7935a09e398SHannes Reinecke 		kfree(arr);
7941da177e4SLinus Torvalds 		return check_condition_result;
7951da177e4SLinus Torvalds 	} else if (0x1 & cmd[1]) {  /* EVPD bit set */
7965a09e398SHannes Reinecke 		int lu_id_num, port_group_id, target_dev_id, len;
797c65b1445SDouglas Gilbert 		char lu_id_str[6];
798c65b1445SDouglas Gilbert 		int host_no = devip->sdbg_host->shost->host_no;
7991da177e4SLinus Torvalds 
8005a09e398SHannes Reinecke 		port_group_id = (((host_no + 1) & 0x7f) << 8) +
8015a09e398SHannes Reinecke 		    (devip->channel & 0x7f);
80223183910SDouglas Gilbert 		if (0 == scsi_debug_vpd_use_hostno)
80323183910SDouglas Gilbert 			host_no = 0;
804c65b1445SDouglas Gilbert 		lu_id_num = devip->wlun ? -1 : (((host_no + 1) * 2000) +
805c65b1445SDouglas Gilbert 			    (devip->target * 1000) + devip->lun);
806c65b1445SDouglas Gilbert 		target_dev_id = ((host_no + 1) * 2000) +
807c65b1445SDouglas Gilbert 				 (devip->target * 1000) - 3;
808c65b1445SDouglas Gilbert 		len = scnprintf(lu_id_str, 6, "%d", lu_id_num);
8091da177e4SLinus Torvalds 		if (0 == cmd[2]) { /* supported vital product data pages */
810c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
811c65b1445SDouglas Gilbert 			n = 4;
812c65b1445SDouglas Gilbert 			arr[n++] = 0x0;   /* this page */
813c65b1445SDouglas Gilbert 			arr[n++] = 0x80;  /* unit serial number */
814c65b1445SDouglas Gilbert 			arr[n++] = 0x83;  /* device identification */
815c65b1445SDouglas Gilbert 			arr[n++] = 0x84;  /* software interface ident. */
816c65b1445SDouglas Gilbert 			arr[n++] = 0x85;  /* management network addresses */
817c65b1445SDouglas Gilbert 			arr[n++] = 0x86;  /* extended inquiry */
818c65b1445SDouglas Gilbert 			arr[n++] = 0x87;  /* mode page policy */
819c65b1445SDouglas Gilbert 			arr[n++] = 0x88;  /* SCSI ports */
820c65b1445SDouglas Gilbert 			arr[n++] = 0x89;  /* ATA information */
821c65b1445SDouglas Gilbert 			arr[n++] = 0xb0;  /* Block limits (SBC) */
822eac6e8e4SMatthew Wilcox 			arr[n++] = 0xb1;  /* Block characteristics (SBC) */
823c65b1445SDouglas Gilbert 			arr[3] = n - 4;	  /* number of supported VPD pages */
8241da177e4SLinus Torvalds 		} else if (0x80 == cmd[2]) { /* unit serial number */
825c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
8261da177e4SLinus Torvalds 			arr[3] = len;
827c65b1445SDouglas Gilbert 			memcpy(&arr[4], lu_id_str, len);
8281da177e4SLinus Torvalds 		} else if (0x83 == cmd[2]) { /* device identification */
829c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
8305a09e398SHannes Reinecke 			arr[3] = inquiry_evpd_83(&arr[4], port_group_id,
8315a09e398SHannes Reinecke 						 target_dev_id, lu_id_num,
8325a09e398SHannes Reinecke 						 lu_id_str, len);
833c65b1445SDouglas Gilbert 		} else if (0x84 == cmd[2]) { /* Software interface ident. */
834c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
835c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_84(&arr[4]);
836c65b1445SDouglas Gilbert 		} else if (0x85 == cmd[2]) { /* Management network addresses */
837c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
838c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_85(&arr[4]);
839c65b1445SDouglas Gilbert 		} else if (0x86 == cmd[2]) { /* extended inquiry */
840c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
841c65b1445SDouglas Gilbert 			arr[3] = 0x3c;	/* number of following entries */
842c6a44287SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE3_PROTECTION)
843c6a44287SMartin K. Petersen 				arr[4] = 0x4;	/* SPT: GRD_CHK:1 */
844c6a44287SMartin K. Petersen 			else if (scsi_debug_dif)
845c6a44287SMartin K. Petersen 				arr[4] = 0x5;   /* SPT: GRD_CHK:1, REF_CHK:1 */
846c6a44287SMartin K. Petersen 			else
847c65b1445SDouglas Gilbert 				arr[4] = 0x0;   /* no protection stuff */
848c65b1445SDouglas Gilbert 			arr[5] = 0x7;   /* head of q, ordered + simple q's */
849c65b1445SDouglas Gilbert 		} else if (0x87 == cmd[2]) { /* mode page policy */
850c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
851c65b1445SDouglas Gilbert 			arr[3] = 0x8;	/* number of following entries */
852c65b1445SDouglas Gilbert 			arr[4] = 0x2;	/* disconnect-reconnect mp */
853c65b1445SDouglas Gilbert 			arr[6] = 0x80;	/* mlus, shared */
854c65b1445SDouglas Gilbert 			arr[8] = 0x18;	 /* protocol specific lu */
855c65b1445SDouglas Gilbert 			arr[10] = 0x82;	 /* mlus, per initiator port */
856c65b1445SDouglas Gilbert 		} else if (0x88 == cmd[2]) { /* SCSI Ports */
857c65b1445SDouglas Gilbert 			arr[1] = cmd[2];	/*sanity */
858c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_88(&arr[4], target_dev_id);
859c65b1445SDouglas Gilbert 		} else if (0x89 == cmd[2]) { /* ATA information */
860c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
861c65b1445SDouglas Gilbert 			n = inquiry_evpd_89(&arr[4]);
862c65b1445SDouglas Gilbert 			arr[2] = (n >> 8);
863c65b1445SDouglas Gilbert 			arr[3] = (n & 0xff);
864c65b1445SDouglas Gilbert 		} else if (0xb0 == cmd[2]) { /* Block limits (SBC) */
865c65b1445SDouglas Gilbert 			arr[1] = cmd[2];        /*sanity */
866c65b1445SDouglas Gilbert 			arr[3] = inquiry_evpd_b0(&arr[4]);
867eac6e8e4SMatthew Wilcox 		} else if (0xb1 == cmd[2]) { /* Block characteristics (SBC) */
868eac6e8e4SMatthew Wilcox 			arr[1] = cmd[2];        /*sanity */
869eac6e8e4SMatthew Wilcox 			arr[3] = inquiry_evpd_b1(&arr[4]);
8701da177e4SLinus Torvalds 		} else {
8711da177e4SLinus Torvalds 			/* Illegal request, invalid field in cdb */
8721da177e4SLinus Torvalds 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
8731da177e4SLinus Torvalds 					INVALID_FIELD_IN_CDB, 0);
8745a09e398SHannes Reinecke 			kfree(arr);
8751da177e4SLinus Torvalds 			return check_condition_result;
8761da177e4SLinus Torvalds 		}
877c65b1445SDouglas Gilbert 		len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
8785a09e398SHannes Reinecke 		ret = fill_from_dev_buffer(scp, arr,
879c65b1445SDouglas Gilbert 			    min(len, SDEBUG_MAX_INQ_ARR_SZ));
8805a09e398SHannes Reinecke 		kfree(arr);
8815a09e398SHannes Reinecke 		return ret;
8821da177e4SLinus Torvalds 	}
8831da177e4SLinus Torvalds 	/* drops through here for a standard inquiry */
8841da177e4SLinus Torvalds 	arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
8851da177e4SLinus Torvalds 	arr[2] = scsi_debug_scsi_level;
8861da177e4SLinus Torvalds 	arr[3] = 2;    /* response_data_format==2 */
8871da177e4SLinus Torvalds 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
888c6a44287SMartin K. Petersen 	arr[5] = scsi_debug_dif ? 1 : 0; /* PROTECT bit */
8895a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno)
8905a09e398SHannes Reinecke 		arr[5] = 0x10; /* claim: implicit TGPS */
891c65b1445SDouglas Gilbert 	arr[6] = 0x10; /* claim: MultiP */
8921da177e4SLinus Torvalds 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
893c65b1445SDouglas Gilbert 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
8941da177e4SLinus Torvalds 	memcpy(&arr[8], inq_vendor_id, 8);
8951da177e4SLinus Torvalds 	memcpy(&arr[16], inq_product_id, 16);
8961da177e4SLinus Torvalds 	memcpy(&arr[32], inq_product_rev, 4);
8971da177e4SLinus Torvalds 	/* version descriptors (2 bytes each) follow */
898c65b1445SDouglas Gilbert 	arr[58] = 0x0; arr[59] = 0x77; /* SAM-3 ANSI */
899c65b1445SDouglas Gilbert 	arr[60] = 0x3; arr[61] = 0x14;  /* SPC-3 ANSI */
900c65b1445SDouglas Gilbert 	n = 62;
9011da177e4SLinus Torvalds 	if (scsi_debug_ptype == 0) {
902c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x3d; /* SBC-2 ANSI */
9031da177e4SLinus Torvalds 	} else if (scsi_debug_ptype == 1) {
904c65b1445SDouglas Gilbert 		arr[n++] = 0x3; arr[n++] = 0x60; /* SSC-2 no version */
9051da177e4SLinus Torvalds 	}
906c65b1445SDouglas Gilbert 	arr[n++] = 0xc; arr[n++] = 0xf;  /* SAS-1.1 rev 10 */
9075a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
9081da177e4SLinus Torvalds 			    min(alloc_len, SDEBUG_LONG_INQ_SZ));
9095a09e398SHannes Reinecke 	kfree(arr);
9105a09e398SHannes Reinecke 	return ret;
9111da177e4SLinus Torvalds }
9121da177e4SLinus Torvalds 
9131da177e4SLinus Torvalds static int resp_requests(struct scsi_cmnd * scp,
9141da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip)
9151da177e4SLinus Torvalds {
9161da177e4SLinus Torvalds 	unsigned char * sbuff;
9171da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
9181da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_SENSE_LEN];
919c65b1445SDouglas Gilbert 	int want_dsense;
9201da177e4SLinus Torvalds 	int len = 18;
9211da177e4SLinus Torvalds 
922c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
9231da177e4SLinus Torvalds 	if (devip->reset == 1)
924c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
925c65b1445SDouglas Gilbert 	want_dsense = !!(cmd[1] & 1) || scsi_debug_dsense;
9261da177e4SLinus Torvalds 	sbuff = devip->sense_buff;
927c65b1445SDouglas Gilbert 	if ((iec_m_pg[2] & 0x4) && (6 == (iec_m_pg[3] & 0xf))) {
928c65b1445SDouglas Gilbert 		if (want_dsense) {
929c65b1445SDouglas Gilbert 			arr[0] = 0x72;
930c65b1445SDouglas Gilbert 			arr[1] = 0x0;		/* NO_SENSE in sense_key */
931c65b1445SDouglas Gilbert 			arr[2] = THRESHOLD_EXCEEDED;
932c65b1445SDouglas Gilbert 			arr[3] = 0xff;		/* TEST set and MRIE==6 */
933c65b1445SDouglas Gilbert 		} else {
934c65b1445SDouglas Gilbert 			arr[0] = 0x70;
935c65b1445SDouglas Gilbert 			arr[2] = 0x0;		/* NO_SENSE in sense_key */
936c65b1445SDouglas Gilbert 			arr[7] = 0xa;   	/* 18 byte sense buffer */
937c65b1445SDouglas Gilbert 			arr[12] = THRESHOLD_EXCEEDED;
938c65b1445SDouglas Gilbert 			arr[13] = 0xff;		/* TEST set and MRIE==6 */
939c65b1445SDouglas Gilbert 		}
940c65b1445SDouglas Gilbert 	} else {
941c65b1445SDouglas Gilbert 		memcpy(arr, sbuff, SDEBUG_SENSE_LEN);
9421da177e4SLinus Torvalds 		if ((cmd[1] & 1) && (! scsi_debug_dsense)) {
9431da177e4SLinus Torvalds 			/* DESC bit set and sense_buff in fixed format */
944c65b1445SDouglas Gilbert 			memset(arr, 0, sizeof(arr));
9451da177e4SLinus Torvalds 			arr[0] = 0x72;
9461da177e4SLinus Torvalds 			arr[1] = sbuff[2];     /* sense key */
9471da177e4SLinus Torvalds 			arr[2] = sbuff[12];    /* asc */
9481da177e4SLinus Torvalds 			arr[3] = sbuff[13];    /* ascq */
9491da177e4SLinus Torvalds 			len = 8;
950c65b1445SDouglas Gilbert 		}
951c65b1445SDouglas Gilbert 	}
952c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, 0, NO_ADDITIONAL_SENSE, 0);
9531da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, len);
9541da177e4SLinus Torvalds }
9551da177e4SLinus Torvalds 
956c65b1445SDouglas Gilbert static int resp_start_stop(struct scsi_cmnd * scp,
957c65b1445SDouglas Gilbert 			   struct sdebug_dev_info * devip)
958c65b1445SDouglas Gilbert {
959c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
960c65b1445SDouglas Gilbert 	int power_cond, errsts, start;
961c65b1445SDouglas Gilbert 
962c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
963c65b1445SDouglas Gilbert 		return errsts;
964c65b1445SDouglas Gilbert 	power_cond = (cmd[4] & 0xf0) >> 4;
965c65b1445SDouglas Gilbert 	if (power_cond) {
966c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
967c65b1445SDouglas Gilbert 			       	0);
968c65b1445SDouglas Gilbert 		return check_condition_result;
969c65b1445SDouglas Gilbert 	}
970c65b1445SDouglas Gilbert 	start = cmd[4] & 1;
971c65b1445SDouglas Gilbert 	if (start == devip->stopped)
972c65b1445SDouglas Gilbert 		devip->stopped = !start;
973c65b1445SDouglas Gilbert 	return 0;
974c65b1445SDouglas Gilbert }
975c65b1445SDouglas Gilbert 
97628898873SFUJITA Tomonori static sector_t get_sdebug_capacity(void)
97728898873SFUJITA Tomonori {
97828898873SFUJITA Tomonori 	if (scsi_debug_virtual_gb > 0)
9795447ed6cSDouglas Gilbert 		return (sector_t)scsi_debug_virtual_gb *
9805447ed6cSDouglas Gilbert 			(1073741824 / scsi_debug_sector_size);
98128898873SFUJITA Tomonori 	else
98228898873SFUJITA Tomonori 		return sdebug_store_sectors;
98328898873SFUJITA Tomonori }
98428898873SFUJITA Tomonori 
9851da177e4SLinus Torvalds #define SDEBUG_READCAP_ARR_SZ 8
9861da177e4SLinus Torvalds static int resp_readcap(struct scsi_cmnd * scp,
9871da177e4SLinus Torvalds 			struct sdebug_dev_info * devip)
9881da177e4SLinus Torvalds {
9891da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_READCAP_ARR_SZ];
990c65b1445SDouglas Gilbert 	unsigned int capac;
9911da177e4SLinus Torvalds 	int errsts;
9921da177e4SLinus Torvalds 
993c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
9941da177e4SLinus Torvalds 		return errsts;
995c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
99628898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
9971da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_READCAP_ARR_SZ);
998c65b1445SDouglas Gilbert 	if (sdebug_capacity < 0xffffffff) {
999c65b1445SDouglas Gilbert 		capac = (unsigned int)sdebug_capacity - 1;
10001da177e4SLinus Torvalds 		arr[0] = (capac >> 24);
10011da177e4SLinus Torvalds 		arr[1] = (capac >> 16) & 0xff;
10021da177e4SLinus Torvalds 		arr[2] = (capac >> 8) & 0xff;
10031da177e4SLinus Torvalds 		arr[3] = capac & 0xff;
1004c65b1445SDouglas Gilbert 	} else {
1005c65b1445SDouglas Gilbert 		arr[0] = 0xff;
1006c65b1445SDouglas Gilbert 		arr[1] = 0xff;
1007c65b1445SDouglas Gilbert 		arr[2] = 0xff;
1008c65b1445SDouglas Gilbert 		arr[3] = 0xff;
1009c65b1445SDouglas Gilbert 	}
1010597136abSMartin K. Petersen 	arr[6] = (scsi_debug_sector_size >> 8) & 0xff;
1011597136abSMartin K. Petersen 	arr[7] = scsi_debug_sector_size & 0xff;
10121da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
10131da177e4SLinus Torvalds }
10141da177e4SLinus Torvalds 
1015c65b1445SDouglas Gilbert #define SDEBUG_READCAP16_ARR_SZ 32
1016c65b1445SDouglas Gilbert static int resp_readcap16(struct scsi_cmnd * scp,
1017c65b1445SDouglas Gilbert 			  struct sdebug_dev_info * devip)
1018c65b1445SDouglas Gilbert {
1019c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1020c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_READCAP16_ARR_SZ];
1021c65b1445SDouglas Gilbert 	unsigned long long capac;
1022c65b1445SDouglas Gilbert 	int errsts, k, alloc_len;
1023c65b1445SDouglas Gilbert 
1024c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1025c65b1445SDouglas Gilbert 		return errsts;
1026c65b1445SDouglas Gilbert 	alloc_len = ((cmd[10] << 24) + (cmd[11] << 16) + (cmd[12] << 8)
1027c65b1445SDouglas Gilbert 		     + cmd[13]);
1028c65b1445SDouglas Gilbert 	/* following just in case virtual_gb changed */
102928898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
1030c65b1445SDouglas Gilbert 	memset(arr, 0, SDEBUG_READCAP16_ARR_SZ);
1031c65b1445SDouglas Gilbert 	capac = sdebug_capacity - 1;
1032c65b1445SDouglas Gilbert 	for (k = 0; k < 8; ++k, capac >>= 8)
1033c65b1445SDouglas Gilbert 		arr[7 - k] = capac & 0xff;
1034597136abSMartin K. Petersen 	arr[8] = (scsi_debug_sector_size >> 24) & 0xff;
1035597136abSMartin K. Petersen 	arr[9] = (scsi_debug_sector_size >> 16) & 0xff;
1036597136abSMartin K. Petersen 	arr[10] = (scsi_debug_sector_size >> 8) & 0xff;
1037597136abSMartin K. Petersen 	arr[11] = scsi_debug_sector_size & 0xff;
1038ea61fca5SMartin K. Petersen 	arr[13] = scsi_debug_physblk_exp & 0xf;
1039ea61fca5SMartin K. Petersen 	arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f;
104044d92694SMartin K. Petersen 
104144d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
104244d92694SMartin K. Petersen 		arr[14] |= 0x80; /* TPE */
104344d92694SMartin K. Petersen 
1044ea61fca5SMartin K. Petersen 	arr[15] = scsi_debug_lowest_aligned & 0xff;
1045c6a44287SMartin K. Petersen 
1046c6a44287SMartin K. Petersen 	if (scsi_debug_dif) {
1047c6a44287SMartin K. Petersen 		arr[12] = (scsi_debug_dif - 1) << 1; /* P_TYPE */
1048c6a44287SMartin K. Petersen 		arr[12] |= 1; /* PROT_EN */
1049c6a44287SMartin K. Petersen 	}
1050c6a44287SMartin K. Petersen 
1051c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1052c65b1445SDouglas Gilbert 				    min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
1053c65b1445SDouglas Gilbert }
1054c65b1445SDouglas Gilbert 
10555a09e398SHannes Reinecke #define SDEBUG_MAX_TGTPGS_ARR_SZ 1412
10565a09e398SHannes Reinecke 
10575a09e398SHannes Reinecke static int resp_report_tgtpgs(struct scsi_cmnd * scp,
10585a09e398SHannes Reinecke 			      struct sdebug_dev_info * devip)
10595a09e398SHannes Reinecke {
10605a09e398SHannes Reinecke 	unsigned char *cmd = (unsigned char *)scp->cmnd;
10615a09e398SHannes Reinecke 	unsigned char * arr;
10625a09e398SHannes Reinecke 	int host_no = devip->sdbg_host->shost->host_no;
10635a09e398SHannes Reinecke 	int n, ret, alen, rlen;
10645a09e398SHannes Reinecke 	int port_group_a, port_group_b, port_a, port_b;
10655a09e398SHannes Reinecke 
10665a09e398SHannes Reinecke 	alen = ((cmd[6] << 24) + (cmd[7] << 16) + (cmd[8] << 8)
10675a09e398SHannes Reinecke 		+ cmd[9]);
10685a09e398SHannes Reinecke 
10696f3cbf55SDouglas Gilbert 	arr = kzalloc(SDEBUG_MAX_TGTPGS_ARR_SZ, GFP_ATOMIC);
10706f3cbf55SDouglas Gilbert 	if (! arr)
10716f3cbf55SDouglas Gilbert 		return DID_REQUEUE << 16;
10725a09e398SHannes Reinecke 	/*
10735a09e398SHannes Reinecke 	 * EVPD page 0x88 states we have two ports, one
10745a09e398SHannes Reinecke 	 * real and a fake port with no device connected.
10755a09e398SHannes Reinecke 	 * So we create two port groups with one port each
10765a09e398SHannes Reinecke 	 * and set the group with port B to unavailable.
10775a09e398SHannes Reinecke 	 */
10785a09e398SHannes Reinecke 	port_a = 0x1; /* relative port A */
10795a09e398SHannes Reinecke 	port_b = 0x2; /* relative port B */
10805a09e398SHannes Reinecke 	port_group_a = (((host_no + 1) & 0x7f) << 8) +
10815a09e398SHannes Reinecke 	    (devip->channel & 0x7f);
10825a09e398SHannes Reinecke 	port_group_b = (((host_no + 1) & 0x7f) << 8) +
10835a09e398SHannes Reinecke 	    (devip->channel & 0x7f) + 0x80;
10845a09e398SHannes Reinecke 
10855a09e398SHannes Reinecke 	/*
10865a09e398SHannes Reinecke 	 * The asymmetric access state is cycled according to the host_id.
10875a09e398SHannes Reinecke 	 */
10885a09e398SHannes Reinecke 	n = 4;
10895a09e398SHannes Reinecke 	if (0 == scsi_debug_vpd_use_hostno) {
10905a09e398SHannes Reinecke 	    arr[n++] = host_no % 3; /* Asymm access state */
10915a09e398SHannes Reinecke 	    arr[n++] = 0x0F; /* claim: all states are supported */
10925a09e398SHannes Reinecke 	} else {
10935a09e398SHannes Reinecke 	    arr[n++] = 0x0; /* Active/Optimized path */
10945a09e398SHannes Reinecke 	    arr[n++] = 0x01; /* claim: only support active/optimized paths */
10955a09e398SHannes Reinecke 	}
10965a09e398SHannes Reinecke 	arr[n++] = (port_group_a >> 8) & 0xff;
10975a09e398SHannes Reinecke 	arr[n++] = port_group_a & 0xff;
10985a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
10995a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
11005a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
11015a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
11025a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
11035a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
11045a09e398SHannes Reinecke 	arr[n++] = (port_a >> 8) & 0xff;
11055a09e398SHannes Reinecke 	arr[n++] = port_a & 0xff;
11065a09e398SHannes Reinecke 	arr[n++] = 3;    /* Port unavailable */
11075a09e398SHannes Reinecke 	arr[n++] = 0x08; /* claim: only unavailalbe paths are supported */
11085a09e398SHannes Reinecke 	arr[n++] = (port_group_b >> 8) & 0xff;
11095a09e398SHannes Reinecke 	arr[n++] = port_group_b & 0xff;
11105a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
11115a09e398SHannes Reinecke 	arr[n++] = 0;    /* Status code */
11125a09e398SHannes Reinecke 	arr[n++] = 0;    /* Vendor unique */
11135a09e398SHannes Reinecke 	arr[n++] = 0x1;  /* One port per group */
11145a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
11155a09e398SHannes Reinecke 	arr[n++] = 0;    /* Reserved */
11165a09e398SHannes Reinecke 	arr[n++] = (port_b >> 8) & 0xff;
11175a09e398SHannes Reinecke 	arr[n++] = port_b & 0xff;
11185a09e398SHannes Reinecke 
11195a09e398SHannes Reinecke 	rlen = n - 4;
11205a09e398SHannes Reinecke 	arr[0] = (rlen >> 24) & 0xff;
11215a09e398SHannes Reinecke 	arr[1] = (rlen >> 16) & 0xff;
11225a09e398SHannes Reinecke 	arr[2] = (rlen >> 8) & 0xff;
11235a09e398SHannes Reinecke 	arr[3] = rlen & 0xff;
11245a09e398SHannes Reinecke 
11255a09e398SHannes Reinecke 	/*
11265a09e398SHannes Reinecke 	 * Return the smallest value of either
11275a09e398SHannes Reinecke 	 * - The allocated length
11285a09e398SHannes Reinecke 	 * - The constructed command length
11295a09e398SHannes Reinecke 	 * - The maximum array size
11305a09e398SHannes Reinecke 	 */
11315a09e398SHannes Reinecke 	rlen = min(alen,n);
11325a09e398SHannes Reinecke 	ret = fill_from_dev_buffer(scp, arr,
11335a09e398SHannes Reinecke 				   min(rlen, SDEBUG_MAX_TGTPGS_ARR_SZ));
11345a09e398SHannes Reinecke 	kfree(arr);
11355a09e398SHannes Reinecke 	return ret;
11365a09e398SHannes Reinecke }
11375a09e398SHannes Reinecke 
11381da177e4SLinus Torvalds /* <<Following mode page info copied from ST318451LW>> */
11391da177e4SLinus Torvalds 
11401da177e4SLinus Torvalds static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target)
11411da177e4SLinus Torvalds {	/* Read-Write Error Recovery page for mode_sense */
11421da177e4SLinus Torvalds 	unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0,
11431da177e4SLinus Torvalds 					5, 0, 0xff, 0xff};
11441da177e4SLinus Torvalds 
11451da177e4SLinus Torvalds 	memcpy(p, err_recov_pg, sizeof(err_recov_pg));
11461da177e4SLinus Torvalds 	if (1 == pcontrol)
11471da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(err_recov_pg) - 2);
11481da177e4SLinus Torvalds 	return sizeof(err_recov_pg);
11491da177e4SLinus Torvalds }
11501da177e4SLinus Torvalds 
11511da177e4SLinus Torvalds static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
11521da177e4SLinus Torvalds { 	/* Disconnect-Reconnect page for mode_sense */
11531da177e4SLinus Torvalds 	unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0,
11541da177e4SLinus Torvalds 					 0, 0, 0, 0, 0, 0, 0, 0};
11551da177e4SLinus Torvalds 
11561da177e4SLinus Torvalds 	memcpy(p, disconnect_pg, sizeof(disconnect_pg));
11571da177e4SLinus Torvalds 	if (1 == pcontrol)
11581da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(disconnect_pg) - 2);
11591da177e4SLinus Torvalds 	return sizeof(disconnect_pg);
11601da177e4SLinus Torvalds }
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds static int resp_format_pg(unsigned char * p, int pcontrol, int target)
11631da177e4SLinus Torvalds {       /* Format device page for mode_sense */
11641da177e4SLinus Torvalds 	unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
11651da177e4SLinus Torvalds 				     0, 0, 0, 0, 0, 0, 0, 0,
11661da177e4SLinus Torvalds 				     0, 0, 0, 0, 0x40, 0, 0, 0};
11671da177e4SLinus Torvalds 
11681da177e4SLinus Torvalds 	memcpy(p, format_pg, sizeof(format_pg));
11691da177e4SLinus Torvalds 	p[10] = (sdebug_sectors_per >> 8) & 0xff;
11701da177e4SLinus Torvalds 	p[11] = sdebug_sectors_per & 0xff;
1171597136abSMartin K. Petersen 	p[12] = (scsi_debug_sector_size >> 8) & 0xff;
1172597136abSMartin K. Petersen 	p[13] = scsi_debug_sector_size & 0xff;
11731da177e4SLinus Torvalds 	if (DEV_REMOVEABLE(target))
11741da177e4SLinus Torvalds 		p[20] |= 0x20; /* should agree with INQUIRY */
11751da177e4SLinus Torvalds 	if (1 == pcontrol)
11761da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(format_pg) - 2);
11771da177e4SLinus Torvalds 	return sizeof(format_pg);
11781da177e4SLinus Torvalds }
11791da177e4SLinus Torvalds 
11801da177e4SLinus Torvalds static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
11811da177e4SLinus Torvalds { 	/* Caching page for mode_sense */
11821da177e4SLinus Torvalds 	unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0,
11831da177e4SLinus Torvalds 		0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0,     0, 0, 0, 0};
11841da177e4SLinus Torvalds 
11851da177e4SLinus Torvalds 	memcpy(p, caching_pg, sizeof(caching_pg));
11861da177e4SLinus Torvalds 	if (1 == pcontrol)
11871da177e4SLinus Torvalds 		memset(p + 2, 0, sizeof(caching_pg) - 2);
11881da177e4SLinus Torvalds 	return sizeof(caching_pg);
11891da177e4SLinus Torvalds }
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target)
11921da177e4SLinus Torvalds { 	/* Control mode page for mode_sense */
1193c65b1445SDouglas Gilbert 	unsigned char ch_ctrl_m_pg[] = {/* 0xa, 10, */ 0x6, 0, 0, 0, 0, 0,
1194c65b1445SDouglas Gilbert 				        0, 0, 0, 0};
1195c65b1445SDouglas Gilbert 	unsigned char d_ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0,
11961da177e4SLinus Torvalds 				     0, 0, 0x2, 0x4b};
11971da177e4SLinus Torvalds 
11981da177e4SLinus Torvalds 	if (scsi_debug_dsense)
11991da177e4SLinus Torvalds 		ctrl_m_pg[2] |= 0x4;
1200c65b1445SDouglas Gilbert 	else
1201c65b1445SDouglas Gilbert 		ctrl_m_pg[2] &= ~0x4;
1202c6a44287SMartin K. Petersen 
1203c6a44287SMartin K. Petersen 	if (scsi_debug_ato)
1204c6a44287SMartin K. Petersen 		ctrl_m_pg[5] |= 0x80; /* ATO=1 */
1205c6a44287SMartin K. Petersen 
12061da177e4SLinus Torvalds 	memcpy(p, ctrl_m_pg, sizeof(ctrl_m_pg));
12071da177e4SLinus Torvalds 	if (1 == pcontrol)
1208c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_ctrl_m_pg, sizeof(ch_ctrl_m_pg));
1209c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1210c65b1445SDouglas Gilbert 		memcpy(p, d_ctrl_m_pg, sizeof(d_ctrl_m_pg));
12111da177e4SLinus Torvalds 	return sizeof(ctrl_m_pg);
12121da177e4SLinus Torvalds }
12131da177e4SLinus Torvalds 
1214c65b1445SDouglas Gilbert 
12151da177e4SLinus Torvalds static int resp_iec_m_pg(unsigned char * p, int pcontrol, int target)
12161da177e4SLinus Torvalds {	/* Informational Exceptions control mode page for mode_sense */
1217c65b1445SDouglas Gilbert 	unsigned char ch_iec_m_pg[] = {/* 0x1c, 0xa, */ 0x4, 0xf, 0, 0, 0, 0,
12181da177e4SLinus Torvalds 				       0, 0, 0x0, 0x0};
1219c65b1445SDouglas Gilbert 	unsigned char d_iec_m_pg[] = {0x1c, 0xa, 0x08, 0, 0, 0, 0, 0,
1220c65b1445SDouglas Gilbert 				      0, 0, 0x0, 0x0};
1221c65b1445SDouglas Gilbert 
12221da177e4SLinus Torvalds 	memcpy(p, iec_m_pg, sizeof(iec_m_pg));
12231da177e4SLinus Torvalds 	if (1 == pcontrol)
1224c65b1445SDouglas Gilbert 		memcpy(p + 2, ch_iec_m_pg, sizeof(ch_iec_m_pg));
1225c65b1445SDouglas Gilbert 	else if (2 == pcontrol)
1226c65b1445SDouglas Gilbert 		memcpy(p, d_iec_m_pg, sizeof(d_iec_m_pg));
12271da177e4SLinus Torvalds 	return sizeof(iec_m_pg);
12281da177e4SLinus Torvalds }
12291da177e4SLinus Torvalds 
1230c65b1445SDouglas Gilbert static int resp_sas_sf_m_pg(unsigned char * p, int pcontrol, int target)
1231c65b1445SDouglas Gilbert {	/* SAS SSP mode page - short format for mode_sense */
1232c65b1445SDouglas Gilbert 	unsigned char sas_sf_m_pg[] = {0x19, 0x6,
1233c65b1445SDouglas Gilbert 		0x6, 0x0, 0x7, 0xd0, 0x0, 0x0};
1234c65b1445SDouglas Gilbert 
1235c65b1445SDouglas Gilbert 	memcpy(p, sas_sf_m_pg, sizeof(sas_sf_m_pg));
1236c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1237c65b1445SDouglas Gilbert 		memset(p + 2, 0, sizeof(sas_sf_m_pg) - 2);
1238c65b1445SDouglas Gilbert 	return sizeof(sas_sf_m_pg);
1239c65b1445SDouglas Gilbert }
1240c65b1445SDouglas Gilbert 
1241c65b1445SDouglas Gilbert 
1242c65b1445SDouglas Gilbert static int resp_sas_pcd_m_spg(unsigned char * p, int pcontrol, int target,
1243c65b1445SDouglas Gilbert 			      int target_dev_id)
1244c65b1445SDouglas Gilbert {	/* SAS phy control and discover mode page for mode_sense */
1245c65b1445SDouglas Gilbert 	unsigned char sas_pcd_m_pg[] = {0x59, 0x1, 0, 0x64, 0, 0x6, 0, 2,
1246c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0x10, 0x9, 0x8, 0x0,
1247c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1248c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1249c65b1445SDouglas Gilbert 		    0x2, 0, 0, 0, 0, 0, 0, 0,
1250c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1251c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1252c65b1445SDouglas Gilbert 		    0, 1, 0, 0, 0x10, 0x9, 0x8, 0x0,
1253c65b1445SDouglas Gilbert 		    0x52, 0x22, 0x22, 0x20, 0x0, 0x0, 0x0, 0x0,
1254c65b1445SDouglas Gilbert 		    0x51, 0x11, 0x11, 0x10, 0x0, 0x0, 0x0, 0x1,
1255c65b1445SDouglas Gilbert 		    0x3, 0, 0, 0, 0, 0, 0, 0,
1256c65b1445SDouglas Gilbert 		    0x88, 0x99, 0, 0, 0, 0, 0, 0,
1257c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1258c65b1445SDouglas Gilbert 		};
1259c65b1445SDouglas Gilbert 	int port_a, port_b;
1260c65b1445SDouglas Gilbert 
1261c65b1445SDouglas Gilbert 	port_a = target_dev_id + 1;
1262c65b1445SDouglas Gilbert 	port_b = port_a + 1;
1263c65b1445SDouglas Gilbert 	memcpy(p, sas_pcd_m_pg, sizeof(sas_pcd_m_pg));
1264c65b1445SDouglas Gilbert 	p[20] = (port_a >> 24);
1265c65b1445SDouglas Gilbert 	p[21] = (port_a >> 16) & 0xff;
1266c65b1445SDouglas Gilbert 	p[22] = (port_a >> 8) & 0xff;
1267c65b1445SDouglas Gilbert 	p[23] = port_a & 0xff;
1268c65b1445SDouglas Gilbert 	p[48 + 20] = (port_b >> 24);
1269c65b1445SDouglas Gilbert 	p[48 + 21] = (port_b >> 16) & 0xff;
1270c65b1445SDouglas Gilbert 	p[48 + 22] = (port_b >> 8) & 0xff;
1271c65b1445SDouglas Gilbert 	p[48 + 23] = port_b & 0xff;
1272c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1273c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_pcd_m_pg) - 4);
1274c65b1445SDouglas Gilbert 	return sizeof(sas_pcd_m_pg);
1275c65b1445SDouglas Gilbert }
1276c65b1445SDouglas Gilbert 
1277c65b1445SDouglas Gilbert static int resp_sas_sha_m_spg(unsigned char * p, int pcontrol)
1278c65b1445SDouglas Gilbert {	/* SAS SSP shared protocol specific port mode subpage */
1279c65b1445SDouglas Gilbert 	unsigned char sas_sha_m_pg[] = {0x59, 0x2, 0, 0xc, 0, 0x6, 0x10, 0,
1280c65b1445SDouglas Gilbert 		    0, 0, 0, 0, 0, 0, 0, 0,
1281c65b1445SDouglas Gilbert 		};
1282c65b1445SDouglas Gilbert 
1283c65b1445SDouglas Gilbert 	memcpy(p, sas_sha_m_pg, sizeof(sas_sha_m_pg));
1284c65b1445SDouglas Gilbert 	if (1 == pcontrol)
1285c65b1445SDouglas Gilbert 		memset(p + 4, 0, sizeof(sas_sha_m_pg) - 4);
1286c65b1445SDouglas Gilbert 	return sizeof(sas_sha_m_pg);
1287c65b1445SDouglas Gilbert }
1288c65b1445SDouglas Gilbert 
12891da177e4SLinus Torvalds #define SDEBUG_MAX_MSENSE_SZ 256
12901da177e4SLinus Torvalds 
12911da177e4SLinus Torvalds static int resp_mode_sense(struct scsi_cmnd * scp, int target,
12921da177e4SLinus Torvalds 			   struct sdebug_dev_info * devip)
12931da177e4SLinus Torvalds {
129423183910SDouglas Gilbert 	unsigned char dbd, llbaa;
129523183910SDouglas Gilbert 	int pcontrol, pcode, subpcode, bd_len;
12961da177e4SLinus Torvalds 	unsigned char dev_spec;
129723183910SDouglas Gilbert 	int k, alloc_len, msense_6, offset, len, errsts, target_dev_id;
12981da177e4SLinus Torvalds 	unsigned char * ap;
12991da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_MAX_MSENSE_SZ];
13001da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
13011da177e4SLinus Torvalds 
1302c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
13031da177e4SLinus Torvalds 		return errsts;
130423183910SDouglas Gilbert 	dbd = !!(cmd[1] & 0x8);
13051da177e4SLinus Torvalds 	pcontrol = (cmd[2] & 0xc0) >> 6;
13061da177e4SLinus Torvalds 	pcode = cmd[2] & 0x3f;
13071da177e4SLinus Torvalds 	subpcode = cmd[3];
13081da177e4SLinus Torvalds 	msense_6 = (MODE_SENSE == cmd[0]);
130923183910SDouglas Gilbert 	llbaa = msense_6 ? 0 : !!(cmd[1] & 0x10);
131023183910SDouglas Gilbert 	if ((0 == scsi_debug_ptype) && (0 == dbd))
131123183910SDouglas Gilbert 		bd_len = llbaa ? 16 : 8;
131223183910SDouglas Gilbert 	else
131323183910SDouglas Gilbert 		bd_len = 0;
13141da177e4SLinus Torvalds 	alloc_len = msense_6 ? cmd[4] : ((cmd[7] << 8) | cmd[8]);
13151da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_MAX_MSENSE_SZ);
13161da177e4SLinus Torvalds 	if (0x3 == pcontrol) {  /* Saving values not supported */
13171da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, SAVING_PARAMS_UNSUP,
13181da177e4SLinus Torvalds 			       	0);
13191da177e4SLinus Torvalds 		return check_condition_result;
13201da177e4SLinus Torvalds 	}
1321c65b1445SDouglas Gilbert 	target_dev_id = ((devip->sdbg_host->shost->host_no + 1) * 2000) +
1322c65b1445SDouglas Gilbert 			(devip->target * 1000) - 3;
132323183910SDouglas Gilbert 	/* set DPOFUA bit for disks */
132423183910SDouglas Gilbert 	if (0 == scsi_debug_ptype)
132523183910SDouglas Gilbert 		dev_spec = (DEV_READONLY(target) ? 0x80 : 0x0) | 0x10;
132623183910SDouglas Gilbert 	else
132723183910SDouglas Gilbert 		dev_spec = 0x0;
13281da177e4SLinus Torvalds 	if (msense_6) {
13291da177e4SLinus Torvalds 		arr[2] = dev_spec;
133023183910SDouglas Gilbert 		arr[3] = bd_len;
13311da177e4SLinus Torvalds 		offset = 4;
13321da177e4SLinus Torvalds 	} else {
13331da177e4SLinus Torvalds 		arr[3] = dev_spec;
133423183910SDouglas Gilbert 		if (16 == bd_len)
133523183910SDouglas Gilbert 			arr[4] = 0x1;	/* set LONGLBA bit */
133623183910SDouglas Gilbert 		arr[7] = bd_len;	/* assume 255 or less */
13371da177e4SLinus Torvalds 		offset = 8;
13381da177e4SLinus Torvalds 	}
13391da177e4SLinus Torvalds 	ap = arr + offset;
134028898873SFUJITA Tomonori 	if ((bd_len > 0) && (!sdebug_capacity))
134128898873SFUJITA Tomonori 		sdebug_capacity = get_sdebug_capacity();
134228898873SFUJITA Tomonori 
134323183910SDouglas Gilbert 	if (8 == bd_len) {
134423183910SDouglas Gilbert 		if (sdebug_capacity > 0xfffffffe) {
134523183910SDouglas Gilbert 			ap[0] = 0xff;
134623183910SDouglas Gilbert 			ap[1] = 0xff;
134723183910SDouglas Gilbert 			ap[2] = 0xff;
134823183910SDouglas Gilbert 			ap[3] = 0xff;
134923183910SDouglas Gilbert 		} else {
135023183910SDouglas Gilbert 			ap[0] = (sdebug_capacity >> 24) & 0xff;
135123183910SDouglas Gilbert 			ap[1] = (sdebug_capacity >> 16) & 0xff;
135223183910SDouglas Gilbert 			ap[2] = (sdebug_capacity >> 8) & 0xff;
135323183910SDouglas Gilbert 			ap[3] = sdebug_capacity & 0xff;
135423183910SDouglas Gilbert 		}
1355597136abSMartin K. Petersen 		ap[6] = (scsi_debug_sector_size >> 8) & 0xff;
1356597136abSMartin K. Petersen 		ap[7] = scsi_debug_sector_size & 0xff;
135723183910SDouglas Gilbert 		offset += bd_len;
135823183910SDouglas Gilbert 		ap = arr + offset;
135923183910SDouglas Gilbert 	} else if (16 == bd_len) {
136023183910SDouglas Gilbert 		unsigned long long capac = sdebug_capacity;
136123183910SDouglas Gilbert 
136223183910SDouglas Gilbert         	for (k = 0; k < 8; ++k, capac >>= 8)
136323183910SDouglas Gilbert                 	ap[7 - k] = capac & 0xff;
1364597136abSMartin K. Petersen 		ap[12] = (scsi_debug_sector_size >> 24) & 0xff;
1365597136abSMartin K. Petersen 		ap[13] = (scsi_debug_sector_size >> 16) & 0xff;
1366597136abSMartin K. Petersen 		ap[14] = (scsi_debug_sector_size >> 8) & 0xff;
1367597136abSMartin K. Petersen 		ap[15] = scsi_debug_sector_size & 0xff;
136823183910SDouglas Gilbert 		offset += bd_len;
136923183910SDouglas Gilbert 		ap = arr + offset;
137023183910SDouglas Gilbert 	}
13711da177e4SLinus Torvalds 
1372c65b1445SDouglas Gilbert 	if ((subpcode > 0x0) && (subpcode < 0xff) && (0x19 != pcode)) {
1373c65b1445SDouglas Gilbert 		/* TODO: Control Extension page */
13741da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
13751da177e4SLinus Torvalds 			       	0);
13761da177e4SLinus Torvalds 		return check_condition_result;
13771da177e4SLinus Torvalds 	}
13781da177e4SLinus Torvalds 	switch (pcode) {
13791da177e4SLinus Torvalds 	case 0x1:	/* Read-Write error recovery page, direct access */
13801da177e4SLinus Torvalds 		len = resp_err_recov_pg(ap, pcontrol, target);
13811da177e4SLinus Torvalds 		offset += len;
13821da177e4SLinus Torvalds 		break;
13831da177e4SLinus Torvalds 	case 0x2:	/* Disconnect-Reconnect page, all devices */
13841da177e4SLinus Torvalds 		len = resp_disconnect_pg(ap, pcontrol, target);
13851da177e4SLinus Torvalds 		offset += len;
13861da177e4SLinus Torvalds 		break;
13871da177e4SLinus Torvalds         case 0x3:       /* Format device page, direct access */
13881da177e4SLinus Torvalds                 len = resp_format_pg(ap, pcontrol, target);
13891da177e4SLinus Torvalds                 offset += len;
13901da177e4SLinus Torvalds                 break;
13911da177e4SLinus Torvalds 	case 0x8:	/* Caching page, direct access */
13921da177e4SLinus Torvalds 		len = resp_caching_pg(ap, pcontrol, target);
13931da177e4SLinus Torvalds 		offset += len;
13941da177e4SLinus Torvalds 		break;
13951da177e4SLinus Torvalds 	case 0xa:	/* Control Mode page, all devices */
13961da177e4SLinus Torvalds 		len = resp_ctrl_m_pg(ap, pcontrol, target);
13971da177e4SLinus Torvalds 		offset += len;
13981da177e4SLinus Torvalds 		break;
1399c65b1445SDouglas Gilbert 	case 0x19:	/* if spc==1 then sas phy, control+discover */
1400c65b1445SDouglas Gilbert 		if ((subpcode > 0x2) && (subpcode < 0xff)) {
1401c65b1445SDouglas Gilbert 		        mk_sense_buffer(devip, ILLEGAL_REQUEST,
1402c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1403c65b1445SDouglas Gilbert 			return check_condition_result;
1404c65b1445SDouglas Gilbert 	        }
1405c65b1445SDouglas Gilbert 		len = 0;
1406c65b1445SDouglas Gilbert 		if ((0x0 == subpcode) || (0xff == subpcode))
1407c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1408c65b1445SDouglas Gilbert 		if ((0x1 == subpcode) || (0xff == subpcode))
1409c65b1445SDouglas Gilbert 			len += resp_sas_pcd_m_spg(ap + len, pcontrol, target,
1410c65b1445SDouglas Gilbert 						  target_dev_id);
1411c65b1445SDouglas Gilbert 		if ((0x2 == subpcode) || (0xff == subpcode))
1412c65b1445SDouglas Gilbert 			len += resp_sas_sha_m_spg(ap + len, pcontrol);
1413c65b1445SDouglas Gilbert 		offset += len;
1414c65b1445SDouglas Gilbert 		break;
14151da177e4SLinus Torvalds 	case 0x1c:	/* Informational Exceptions Mode page, all devices */
14161da177e4SLinus Torvalds 		len = resp_iec_m_pg(ap, pcontrol, target);
14171da177e4SLinus Torvalds 		offset += len;
14181da177e4SLinus Torvalds 		break;
14191da177e4SLinus Torvalds 	case 0x3f:	/* Read all Mode pages */
1420c65b1445SDouglas Gilbert 		if ((0 == subpcode) || (0xff == subpcode)) {
14211da177e4SLinus Torvalds 			len = resp_err_recov_pg(ap, pcontrol, target);
14221da177e4SLinus Torvalds 			len += resp_disconnect_pg(ap + len, pcontrol, target);
14231da177e4SLinus Torvalds 			len += resp_format_pg(ap + len, pcontrol, target);
14241da177e4SLinus Torvalds 			len += resp_caching_pg(ap + len, pcontrol, target);
14251da177e4SLinus Torvalds 			len += resp_ctrl_m_pg(ap + len, pcontrol, target);
1426c65b1445SDouglas Gilbert 			len += resp_sas_sf_m_pg(ap + len, pcontrol, target);
1427c65b1445SDouglas Gilbert 			if (0xff == subpcode) {
1428c65b1445SDouglas Gilbert 				len += resp_sas_pcd_m_spg(ap + len, pcontrol,
1429c65b1445SDouglas Gilbert 						  target, target_dev_id);
1430c65b1445SDouglas Gilbert 				len += resp_sas_sha_m_spg(ap + len, pcontrol);
1431c65b1445SDouglas Gilbert 			}
14321da177e4SLinus Torvalds 			len += resp_iec_m_pg(ap + len, pcontrol, target);
1433c65b1445SDouglas Gilbert 		} else {
1434c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1435c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1436c65b1445SDouglas Gilbert 			return check_condition_result;
1437c65b1445SDouglas Gilbert                 }
14381da177e4SLinus Torvalds 		offset += len;
14391da177e4SLinus Torvalds 		break;
14401da177e4SLinus Torvalds 	default:
14411da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
14421da177e4SLinus Torvalds 			       	0);
14431da177e4SLinus Torvalds 		return check_condition_result;
14441da177e4SLinus Torvalds 	}
14451da177e4SLinus Torvalds 	if (msense_6)
14461da177e4SLinus Torvalds 		arr[0] = offset - 1;
14471da177e4SLinus Torvalds 	else {
14481da177e4SLinus Torvalds 		arr[0] = ((offset - 2) >> 8) & 0xff;
14491da177e4SLinus Torvalds 		arr[1] = (offset - 2) & 0xff;
14501da177e4SLinus Torvalds 	}
14511da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr, min(alloc_len, offset));
14521da177e4SLinus Torvalds }
14531da177e4SLinus Torvalds 
1454c65b1445SDouglas Gilbert #define SDEBUG_MAX_MSELECT_SZ 512
1455c65b1445SDouglas Gilbert 
1456c65b1445SDouglas Gilbert static int resp_mode_select(struct scsi_cmnd * scp, int mselect6,
1457c65b1445SDouglas Gilbert 			    struct sdebug_dev_info * devip)
1458c65b1445SDouglas Gilbert {
1459c65b1445SDouglas Gilbert 	int pf, sp, ps, md_len, bd_len, off, spf, pg_len;
1460c65b1445SDouglas Gilbert 	int param_len, res, errsts, mpage;
1461c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_MSELECT_SZ];
1462c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1463c65b1445SDouglas Gilbert 
1464c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1465c65b1445SDouglas Gilbert 		return errsts;
1466c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1467c65b1445SDouglas Gilbert 	pf = cmd[1] & 0x10;
1468c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1469c65b1445SDouglas Gilbert 	param_len = mselect6 ? cmd[4] : ((cmd[7] << 8) + cmd[8]);
1470c65b1445SDouglas Gilbert 	if ((0 == pf) || sp || (param_len > SDEBUG_MAX_MSELECT_SZ)) {
1471c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1472c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1473c65b1445SDouglas Gilbert 		return check_condition_result;
1474c65b1445SDouglas Gilbert 	}
1475c65b1445SDouglas Gilbert         res = fetch_to_dev_buffer(scp, arr, param_len);
1476c65b1445SDouglas Gilbert         if (-1 == res)
1477c65b1445SDouglas Gilbert                 return (DID_ERROR << 16);
1478c65b1445SDouglas Gilbert         else if ((res < param_len) &&
1479c65b1445SDouglas Gilbert                  (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
1480c65b1445SDouglas Gilbert                 printk(KERN_INFO "scsi_debug: mode_select: cdb indicated=%d, "
1481c65b1445SDouglas Gilbert                        " IO sent=%d bytes\n", param_len, res);
1482c65b1445SDouglas Gilbert 	md_len = mselect6 ? (arr[0] + 1) : ((arr[0] << 8) + arr[1] + 2);
1483c65b1445SDouglas Gilbert 	bd_len = mselect6 ? arr[3] : ((arr[6] << 8) + arr[7]);
148423183910SDouglas Gilbert 	if (md_len > 2) {
1485c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1486c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1487c65b1445SDouglas Gilbert 		return check_condition_result;
1488c65b1445SDouglas Gilbert 	}
1489c65b1445SDouglas Gilbert 	off = bd_len + (mselect6 ? 4 : 8);
1490c65b1445SDouglas Gilbert 	mpage = arr[off] & 0x3f;
1491c65b1445SDouglas Gilbert 	ps = !!(arr[off] & 0x80);
1492c65b1445SDouglas Gilbert 	if (ps) {
1493c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1494c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_PARAM_LIST, 0);
1495c65b1445SDouglas Gilbert 		return check_condition_result;
1496c65b1445SDouglas Gilbert 	}
1497c65b1445SDouglas Gilbert 	spf = !!(arr[off] & 0x40);
1498c65b1445SDouglas Gilbert 	pg_len = spf ? ((arr[off + 2] << 8) + arr[off + 3] + 4) :
1499c65b1445SDouglas Gilbert 		       (arr[off + 1] + 2);
1500c65b1445SDouglas Gilbert 	if ((pg_len + off) > param_len) {
1501c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1502c65b1445SDouglas Gilbert 				PARAMETER_LIST_LENGTH_ERR, 0);
1503c65b1445SDouglas Gilbert 		return check_condition_result;
1504c65b1445SDouglas Gilbert 	}
1505c65b1445SDouglas Gilbert 	switch (mpage) {
1506c65b1445SDouglas Gilbert 	case 0xa:      /* Control Mode page */
1507c65b1445SDouglas Gilbert 		if (ctrl_m_pg[1] == arr[off + 1]) {
1508c65b1445SDouglas Gilbert 			memcpy(ctrl_m_pg + 2, arr + off + 2,
1509c65b1445SDouglas Gilbert 			       sizeof(ctrl_m_pg) - 2);
1510c65b1445SDouglas Gilbert 			scsi_debug_dsense = !!(ctrl_m_pg[2] & 0x4);
1511c65b1445SDouglas Gilbert 			return 0;
1512c65b1445SDouglas Gilbert 		}
1513c65b1445SDouglas Gilbert 		break;
1514c65b1445SDouglas Gilbert 	case 0x1c:      /* Informational Exceptions Mode page */
1515c65b1445SDouglas Gilbert 		if (iec_m_pg[1] == arr[off + 1]) {
1516c65b1445SDouglas Gilbert 			memcpy(iec_m_pg + 2, arr + off + 2,
1517c65b1445SDouglas Gilbert 			       sizeof(iec_m_pg) - 2);
1518c65b1445SDouglas Gilbert 			return 0;
1519c65b1445SDouglas Gilbert 		}
1520c65b1445SDouglas Gilbert 		break;
1521c65b1445SDouglas Gilbert 	default:
1522c65b1445SDouglas Gilbert 		break;
1523c65b1445SDouglas Gilbert 	}
1524c65b1445SDouglas Gilbert 	mk_sense_buffer(devip, ILLEGAL_REQUEST,
1525c65b1445SDouglas Gilbert 			INVALID_FIELD_IN_PARAM_LIST, 0);
1526c65b1445SDouglas Gilbert 	return check_condition_result;
1527c65b1445SDouglas Gilbert }
1528c65b1445SDouglas Gilbert 
1529c65b1445SDouglas Gilbert static int resp_temp_l_pg(unsigned char * arr)
1530c65b1445SDouglas Gilbert {
1531c65b1445SDouglas Gilbert 	unsigned char temp_l_pg[] = {0x0, 0x0, 0x3, 0x2, 0x0, 38,
1532c65b1445SDouglas Gilbert 				     0x0, 0x1, 0x3, 0x2, 0x0, 65,
1533c65b1445SDouglas Gilbert 		};
1534c65b1445SDouglas Gilbert 
1535c65b1445SDouglas Gilbert         memcpy(arr, temp_l_pg, sizeof(temp_l_pg));
1536c65b1445SDouglas Gilbert         return sizeof(temp_l_pg);
1537c65b1445SDouglas Gilbert }
1538c65b1445SDouglas Gilbert 
1539c65b1445SDouglas Gilbert static int resp_ie_l_pg(unsigned char * arr)
1540c65b1445SDouglas Gilbert {
1541c65b1445SDouglas Gilbert 	unsigned char ie_l_pg[] = {0x0, 0x0, 0x3, 0x3, 0x0, 0x0, 38,
1542c65b1445SDouglas Gilbert 		};
1543c65b1445SDouglas Gilbert 
1544c65b1445SDouglas Gilbert         memcpy(arr, ie_l_pg, sizeof(ie_l_pg));
1545c65b1445SDouglas Gilbert 	if (iec_m_pg[2] & 0x4) {	/* TEST bit set */
1546c65b1445SDouglas Gilbert 		arr[4] = THRESHOLD_EXCEEDED;
1547c65b1445SDouglas Gilbert 		arr[5] = 0xff;
1548c65b1445SDouglas Gilbert 	}
1549c65b1445SDouglas Gilbert         return sizeof(ie_l_pg);
1550c65b1445SDouglas Gilbert }
1551c65b1445SDouglas Gilbert 
1552c65b1445SDouglas Gilbert #define SDEBUG_MAX_LSENSE_SZ 512
1553c65b1445SDouglas Gilbert 
1554c65b1445SDouglas Gilbert static int resp_log_sense(struct scsi_cmnd * scp,
1555c65b1445SDouglas Gilbert                           struct sdebug_dev_info * devip)
1556c65b1445SDouglas Gilbert {
155723183910SDouglas Gilbert 	int ppc, sp, pcontrol, pcode, subpcode, alloc_len, errsts, len, n;
1558c65b1445SDouglas Gilbert 	unsigned char arr[SDEBUG_MAX_LSENSE_SZ];
1559c65b1445SDouglas Gilbert 	unsigned char *cmd = (unsigned char *)scp->cmnd;
1560c65b1445SDouglas Gilbert 
1561c65b1445SDouglas Gilbert 	if ((errsts = check_readiness(scp, 1, devip)))
1562c65b1445SDouglas Gilbert 		return errsts;
1563c65b1445SDouglas Gilbert 	memset(arr, 0, sizeof(arr));
1564c65b1445SDouglas Gilbert 	ppc = cmd[1] & 0x2;
1565c65b1445SDouglas Gilbert 	sp = cmd[1] & 0x1;
1566c65b1445SDouglas Gilbert 	if (ppc || sp) {
1567c65b1445SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
1568c65b1445SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
1569c65b1445SDouglas Gilbert 		return check_condition_result;
1570c65b1445SDouglas Gilbert 	}
1571c65b1445SDouglas Gilbert 	pcontrol = (cmd[2] & 0xc0) >> 6;
1572c65b1445SDouglas Gilbert 	pcode = cmd[2] & 0x3f;
157323183910SDouglas Gilbert 	subpcode = cmd[3] & 0xff;
1574c65b1445SDouglas Gilbert 	alloc_len = (cmd[7] << 8) + cmd[8];
1575c65b1445SDouglas Gilbert 	arr[0] = pcode;
157623183910SDouglas Gilbert 	if (0 == subpcode) {
1577c65b1445SDouglas Gilbert 		switch (pcode) {
1578c65b1445SDouglas Gilbert 		case 0x0:	/* Supported log pages log page */
1579c65b1445SDouglas Gilbert 			n = 4;
1580c65b1445SDouglas Gilbert 			arr[n++] = 0x0;		/* this page */
1581c65b1445SDouglas Gilbert 			arr[n++] = 0xd;		/* Temperature */
1582c65b1445SDouglas Gilbert 			arr[n++] = 0x2f;	/* Informational exceptions */
1583c65b1445SDouglas Gilbert 			arr[3] = n - 4;
1584c65b1445SDouglas Gilbert 			break;
1585c65b1445SDouglas Gilbert 		case 0xd:	/* Temperature log page */
1586c65b1445SDouglas Gilbert 			arr[3] = resp_temp_l_pg(arr + 4);
1587c65b1445SDouglas Gilbert 			break;
1588c65b1445SDouglas Gilbert 		case 0x2f:	/* Informational exceptions log page */
1589c65b1445SDouglas Gilbert 			arr[3] = resp_ie_l_pg(arr + 4);
1590c65b1445SDouglas Gilbert 			break;
1591c65b1445SDouglas Gilbert 		default:
1592c65b1445SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
1593c65b1445SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
1594c65b1445SDouglas Gilbert 			return check_condition_result;
1595c65b1445SDouglas Gilbert 		}
159623183910SDouglas Gilbert 	} else if (0xff == subpcode) {
159723183910SDouglas Gilbert 		arr[0] |= 0x40;
159823183910SDouglas Gilbert 		arr[1] = subpcode;
159923183910SDouglas Gilbert 		switch (pcode) {
160023183910SDouglas Gilbert 		case 0x0:	/* Supported log pages and subpages log page */
160123183910SDouglas Gilbert 			n = 4;
160223183910SDouglas Gilbert 			arr[n++] = 0x0;
160323183910SDouglas Gilbert 			arr[n++] = 0x0;		/* 0,0 page */
160423183910SDouglas Gilbert 			arr[n++] = 0x0;
160523183910SDouglas Gilbert 			arr[n++] = 0xff;	/* this page */
160623183910SDouglas Gilbert 			arr[n++] = 0xd;
160723183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
160823183910SDouglas Gilbert 			arr[n++] = 0x2f;
160923183910SDouglas Gilbert 			arr[n++] = 0x0;	/* Informational exceptions */
161023183910SDouglas Gilbert 			arr[3] = n - 4;
161123183910SDouglas Gilbert 			break;
161223183910SDouglas Gilbert 		case 0xd:	/* Temperature subpages */
161323183910SDouglas Gilbert 			n = 4;
161423183910SDouglas Gilbert 			arr[n++] = 0xd;
161523183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Temperature */
161623183910SDouglas Gilbert 			arr[3] = n - 4;
161723183910SDouglas Gilbert 			break;
161823183910SDouglas Gilbert 		case 0x2f:	/* Informational exceptions subpages */
161923183910SDouglas Gilbert 			n = 4;
162023183910SDouglas Gilbert 			arr[n++] = 0x2f;
162123183910SDouglas Gilbert 			arr[n++] = 0x0;		/* Informational exceptions */
162223183910SDouglas Gilbert 			arr[3] = n - 4;
162323183910SDouglas Gilbert 			break;
162423183910SDouglas Gilbert 		default:
162523183910SDouglas Gilbert 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
162623183910SDouglas Gilbert 					INVALID_FIELD_IN_CDB, 0);
162723183910SDouglas Gilbert 			return check_condition_result;
162823183910SDouglas Gilbert 		}
162923183910SDouglas Gilbert 	} else {
163023183910SDouglas Gilbert 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
163123183910SDouglas Gilbert 				INVALID_FIELD_IN_CDB, 0);
163223183910SDouglas Gilbert 		return check_condition_result;
163323183910SDouglas Gilbert 	}
1634c65b1445SDouglas Gilbert 	len = min(((arr[2] << 8) + arr[3]) + 4, alloc_len);
1635c65b1445SDouglas Gilbert 	return fill_from_dev_buffer(scp, arr,
1636c65b1445SDouglas Gilbert 		    min(len, SDEBUG_MAX_INQ_ARR_SZ));
1637c65b1445SDouglas Gilbert }
1638c65b1445SDouglas Gilbert 
163919789100SFUJITA Tomonori static int check_device_access_params(struct sdebug_dev_info *devi,
164019789100SFUJITA Tomonori 				      unsigned long long lba, unsigned int num)
16411da177e4SLinus Torvalds {
1642c65b1445SDouglas Gilbert 	if (lba + num > sdebug_capacity) {
164319789100SFUJITA Tomonori 		mk_sense_buffer(devi, ILLEGAL_REQUEST, ADDR_OUT_OF_RANGE, 0);
16441da177e4SLinus Torvalds 		return check_condition_result;
16451da177e4SLinus Torvalds 	}
1646c65b1445SDouglas Gilbert 	/* transfer length excessive (tie in to block limits VPD page) */
1647c65b1445SDouglas Gilbert 	if (num > sdebug_store_sectors) {
164819789100SFUJITA Tomonori 		mk_sense_buffer(devi, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB, 0);
1649c65b1445SDouglas Gilbert 		return check_condition_result;
1650c65b1445SDouglas Gilbert 	}
165119789100SFUJITA Tomonori 	return 0;
165219789100SFUJITA Tomonori }
165319789100SFUJITA Tomonori 
165419789100SFUJITA Tomonori static int do_device_access(struct scsi_cmnd *scmd,
165519789100SFUJITA Tomonori 			    struct sdebug_dev_info *devi,
165619789100SFUJITA Tomonori 			    unsigned long long lba, unsigned int num, int write)
165719789100SFUJITA Tomonori {
165819789100SFUJITA Tomonori 	int ret;
165919789100SFUJITA Tomonori 	unsigned int block, rest = 0;
166019789100SFUJITA Tomonori 	int (*func)(struct scsi_cmnd *, unsigned char *, int);
166119789100SFUJITA Tomonori 
166219789100SFUJITA Tomonori 	func = write ? fetch_to_dev_buffer : fill_from_dev_buffer;
166319789100SFUJITA Tomonori 
166419789100SFUJITA Tomonori 	block = do_div(lba, sdebug_store_sectors);
166519789100SFUJITA Tomonori 	if (block + num > sdebug_store_sectors)
166619789100SFUJITA Tomonori 		rest = block + num - sdebug_store_sectors;
166719789100SFUJITA Tomonori 
1668597136abSMartin K. Petersen 	ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
1669597136abSMartin K. Petersen 		   (num - rest) * scsi_debug_sector_size);
167019789100SFUJITA Tomonori 	if (!ret && rest)
1671597136abSMartin K. Petersen 		ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
167219789100SFUJITA Tomonori 
167319789100SFUJITA Tomonori 	return ret;
167419789100SFUJITA Tomonori }
167519789100SFUJITA Tomonori 
1676c6a44287SMartin K. Petersen static int prot_verify_read(struct scsi_cmnd *SCpnt, sector_t start_sec,
1677395cef03SMartin K. Petersen 			    unsigned int sectors, u32 ei_lba)
1678c6a44287SMartin K. Petersen {
1679c6a44287SMartin K. Petersen 	unsigned int i, resid;
1680c6a44287SMartin K. Petersen 	struct scatterlist *psgl;
1681c6a44287SMartin K. Petersen 	struct sd_dif_tuple *sdt;
1682c6a44287SMartin K. Petersen 	sector_t sector;
1683c6a44287SMartin K. Petersen 	sector_t tmp_sec = start_sec;
1684c6a44287SMartin K. Petersen 	void *paddr;
1685c6a44287SMartin K. Petersen 
1686c6a44287SMartin K. Petersen 	start_sec = do_div(tmp_sec, sdebug_store_sectors);
1687c6a44287SMartin K. Petersen 
1688c6a44287SMartin K. Petersen 	sdt = (struct sd_dif_tuple *)(dif_storep + dif_offset(start_sec));
1689c6a44287SMartin K. Petersen 
1690c6a44287SMartin K. Petersen 	for (i = 0 ; i < sectors ; i++) {
1691c6a44287SMartin K. Petersen 		u16 csum;
1692c6a44287SMartin K. Petersen 
1693c6a44287SMartin K. Petersen 		if (sdt[i].app_tag == 0xffff)
1694c6a44287SMartin K. Petersen 			continue;
1695c6a44287SMartin K. Petersen 
1696c6a44287SMartin K. Petersen 		sector = start_sec + i;
1697c6a44287SMartin K. Petersen 
1698c6a44287SMartin K. Petersen 		switch (scsi_debug_guard) {
1699c6a44287SMartin K. Petersen 		case 1:
1700c6a44287SMartin K. Petersen 			csum = ip_compute_csum(fake_storep +
1701c6a44287SMartin K. Petersen 					       sector * scsi_debug_sector_size,
1702c6a44287SMartin K. Petersen 					       scsi_debug_sector_size);
1703c6a44287SMartin K. Petersen 			break;
1704c6a44287SMartin K. Petersen 		case 0:
1705c6a44287SMartin K. Petersen 			csum = crc_t10dif(fake_storep +
1706c6a44287SMartin K. Petersen 					  sector * scsi_debug_sector_size,
1707c6a44287SMartin K. Petersen 					  scsi_debug_sector_size);
1708c6a44287SMartin K. Petersen 			csum = cpu_to_be16(csum);
1709c6a44287SMartin K. Petersen 			break;
1710c6a44287SMartin K. Petersen 		default:
1711c6a44287SMartin K. Petersen 			BUG();
1712c6a44287SMartin K. Petersen 		}
1713c6a44287SMartin K. Petersen 
1714c6a44287SMartin K. Petersen 		if (sdt[i].guard_tag != csum) {
1715c6a44287SMartin K. Petersen 			printk(KERN_ERR "%s: GUARD check failed on sector %lu" \
1716c6a44287SMartin K. Petersen 			       " rcvd 0x%04x, data 0x%04x\n", __func__,
1717c6a44287SMartin K. Petersen 			       (unsigned long)sector,
1718c6a44287SMartin K. Petersen 			       be16_to_cpu(sdt[i].guard_tag),
1719c6a44287SMartin K. Petersen 			       be16_to_cpu(csum));
1720c6a44287SMartin K. Petersen 			dif_errors++;
1721c6a44287SMartin K. Petersen 			return 0x01;
1722c6a44287SMartin K. Petersen 		}
1723c6a44287SMartin K. Petersen 
1724395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
1725c6a44287SMartin K. Petersen 		    be32_to_cpu(sdt[i].ref_tag) != (sector & 0xffffffff)) {
1726c6a44287SMartin K. Petersen 			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
1727c6a44287SMartin K. Petersen 			       __func__, (unsigned long)sector);
1728c6a44287SMartin K. Petersen 			dif_errors++;
1729c6a44287SMartin K. Petersen 			return 0x03;
1730c6a44287SMartin K. Petersen 		}
1731395cef03SMartin K. Petersen 
1732395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
1733395cef03SMartin K. Petersen 		    be32_to_cpu(sdt[i].ref_tag) != ei_lba) {
1734395cef03SMartin K. Petersen 			printk(KERN_ERR "%s: REF check failed on sector %lu\n",
1735395cef03SMartin K. Petersen 			       __func__, (unsigned long)sector);
1736395cef03SMartin K. Petersen 			dif_errors++;
1737395cef03SMartin K. Petersen 			return 0x03;
1738395cef03SMartin K. Petersen 		}
1739395cef03SMartin K. Petersen 
1740395cef03SMartin K. Petersen 		ei_lba++;
1741c6a44287SMartin K. Petersen 	}
1742c6a44287SMartin K. Petersen 
1743c6a44287SMartin K. Petersen 	resid = sectors * 8; /* Bytes of protection data to copy into sgl */
1744c6a44287SMartin K. Petersen 	sector = start_sec;
1745c6a44287SMartin K. Petersen 
1746c6a44287SMartin K. Petersen 	scsi_for_each_prot_sg(SCpnt, psgl, scsi_prot_sg_count(SCpnt), i) {
1747c6a44287SMartin K. Petersen 		int len = min(psgl->length, resid);
1748c6a44287SMartin K. Petersen 
1749c6a44287SMartin K. Petersen 		paddr = kmap_atomic(sg_page(psgl), KM_IRQ0) + psgl->offset;
1750c6a44287SMartin K. Petersen 		memcpy(paddr, dif_storep + dif_offset(sector), len);
1751c6a44287SMartin K. Petersen 
1752c6a44287SMartin K. Petersen 		sector += len >> 3;
1753c6a44287SMartin K. Petersen 		if (sector >= sdebug_store_sectors) {
1754c6a44287SMartin K. Petersen 			/* Force wrap */
1755c6a44287SMartin K. Petersen 			tmp_sec = sector;
1756c6a44287SMartin K. Petersen 			sector = do_div(tmp_sec, sdebug_store_sectors);
1757c6a44287SMartin K. Petersen 		}
1758c6a44287SMartin K. Petersen 		resid -= len;
1759c6a44287SMartin K. Petersen 		kunmap_atomic(paddr, KM_IRQ0);
1760c6a44287SMartin K. Petersen 	}
1761c6a44287SMartin K. Petersen 
1762c6a44287SMartin K. Petersen 	dix_reads++;
1763c6a44287SMartin K. Petersen 
1764c6a44287SMartin K. Petersen 	return 0;
1765c6a44287SMartin K. Petersen }
1766c6a44287SMartin K. Petersen 
176719789100SFUJITA Tomonori static int resp_read(struct scsi_cmnd *SCpnt, unsigned long long lba,
1768395cef03SMartin K. Petersen 		     unsigned int num, struct sdebug_dev_info *devip,
1769395cef03SMartin K. Petersen 		     u32 ei_lba)
177019789100SFUJITA Tomonori {
177119789100SFUJITA Tomonori 	unsigned long iflags;
177219789100SFUJITA Tomonori 	int ret;
177319789100SFUJITA Tomonori 
177419789100SFUJITA Tomonori 	ret = check_device_access_params(devip, lba, num);
177519789100SFUJITA Tomonori 	if (ret)
177619789100SFUJITA Tomonori 		return ret;
177719789100SFUJITA Tomonori 
17781da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) &&
1779c65b1445SDouglas Gilbert 	    (lba <= OPT_MEDIUM_ERR_ADDR) &&
1780c65b1445SDouglas Gilbert 	    ((lba + num) > OPT_MEDIUM_ERR_ADDR)) {
1781c65b1445SDouglas Gilbert 		/* claim unrecoverable read error */
17821da177e4SLinus Torvalds 		mk_sense_buffer(devip, MEDIUM_ERROR, UNRECOVERED_READ_ERR,
17831da177e4SLinus Torvalds 				0);
1784c65b1445SDouglas Gilbert 		/* set info field and valid bit for fixed descriptor */
1785c65b1445SDouglas Gilbert 		if (0x70 == (devip->sense_buff[0] & 0x7f)) {
1786c65b1445SDouglas Gilbert 			devip->sense_buff[0] |= 0x80;	/* Valid bit */
1787c65b1445SDouglas Gilbert 			ret = OPT_MEDIUM_ERR_ADDR;
1788c65b1445SDouglas Gilbert 			devip->sense_buff[3] = (ret >> 24) & 0xff;
1789c65b1445SDouglas Gilbert 			devip->sense_buff[4] = (ret >> 16) & 0xff;
1790c65b1445SDouglas Gilbert 			devip->sense_buff[5] = (ret >> 8) & 0xff;
1791c65b1445SDouglas Gilbert 			devip->sense_buff[6] = ret & 0xff;
1792c65b1445SDouglas Gilbert 		}
17931da177e4SLinus Torvalds 		return check_condition_result;
17941da177e4SLinus Torvalds 	}
1795c6a44287SMartin K. Petersen 
1796c6a44287SMartin K. Petersen 	/* DIX + T10 DIF */
1797c6a44287SMartin K. Petersen 	if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
1798395cef03SMartin K. Petersen 		int prot_ret = prot_verify_read(SCpnt, lba, num, ei_lba);
1799c6a44287SMartin K. Petersen 
1800c6a44287SMartin K. Petersen 		if (prot_ret) {
1801c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, prot_ret);
1802c6a44287SMartin K. Petersen 			return illegal_condition_result;
1803c6a44287SMartin K. Petersen 		}
1804c6a44287SMartin K. Petersen 	}
1805c6a44287SMartin K. Petersen 
18061da177e4SLinus Torvalds 	read_lock_irqsave(&atomic_rw, iflags);
180719789100SFUJITA Tomonori 	ret = do_device_access(SCpnt, devip, lba, num, 0);
18081da177e4SLinus Torvalds 	read_unlock_irqrestore(&atomic_rw, iflags);
18091da177e4SLinus Torvalds 	return ret;
18101da177e4SLinus Torvalds }
18111da177e4SLinus Torvalds 
1812c6a44287SMartin K. Petersen void dump_sector(unsigned char *buf, int len)
1813c6a44287SMartin K. Petersen {
1814c6a44287SMartin K. Petersen 	int i, j;
1815c6a44287SMartin K. Petersen 
1816c6a44287SMartin K. Petersen 	printk(KERN_ERR ">>> Sector Dump <<<\n");
1817c6a44287SMartin K. Petersen 
1818c6a44287SMartin K. Petersen 	for (i = 0 ; i < len ; i += 16) {
1819c6a44287SMartin K. Petersen 		printk(KERN_ERR "%04d: ", i);
1820c6a44287SMartin K. Petersen 
1821c6a44287SMartin K. Petersen 		for (j = 0 ; j < 16 ; j++) {
1822c6a44287SMartin K. Petersen 			unsigned char c = buf[i+j];
1823c6a44287SMartin K. Petersen 			if (c >= 0x20 && c < 0x7e)
1824c6a44287SMartin K. Petersen 				printk(" %c ", buf[i+j]);
1825c6a44287SMartin K. Petersen 			else
1826c6a44287SMartin K. Petersen 				printk("%02x ", buf[i+j]);
1827c6a44287SMartin K. Petersen 		}
1828c6a44287SMartin K. Petersen 
1829c6a44287SMartin K. Petersen 		printk("\n");
1830c6a44287SMartin K. Petersen 	}
1831c6a44287SMartin K. Petersen }
1832c6a44287SMartin K. Petersen 
1833c6a44287SMartin K. Petersen static int prot_verify_write(struct scsi_cmnd *SCpnt, sector_t start_sec,
1834395cef03SMartin K. Petersen 			     unsigned int sectors, u32 ei_lba)
1835c6a44287SMartin K. Petersen {
1836c6a44287SMartin K. Petersen 	int i, j, ret;
1837c6a44287SMartin K. Petersen 	struct sd_dif_tuple *sdt;
1838c6a44287SMartin K. Petersen 	struct scatterlist *dsgl = scsi_sglist(SCpnt);
1839c6a44287SMartin K. Petersen 	struct scatterlist *psgl = scsi_prot_sglist(SCpnt);
1840c6a44287SMartin K. Petersen 	void *daddr, *paddr;
1841c6a44287SMartin K. Petersen 	sector_t tmp_sec = start_sec;
1842c6a44287SMartin K. Petersen 	sector_t sector;
1843c6a44287SMartin K. Petersen 	int ppage_offset;
1844c6a44287SMartin K. Petersen 	unsigned short csum;
1845c6a44287SMartin K. Petersen 
1846c6a44287SMartin K. Petersen 	sector = do_div(tmp_sec, sdebug_store_sectors);
1847c6a44287SMartin K. Petersen 
1848c6a44287SMartin K. Petersen 	BUG_ON(scsi_sg_count(SCpnt) == 0);
1849c6a44287SMartin K. Petersen 	BUG_ON(scsi_prot_sg_count(SCpnt) == 0);
1850c6a44287SMartin K. Petersen 
1851c6a44287SMartin K. Petersen 	paddr = kmap_atomic(sg_page(psgl), KM_IRQ1) + psgl->offset;
1852c6a44287SMartin K. Petersen 	ppage_offset = 0;
1853c6a44287SMartin K. Petersen 
1854c6a44287SMartin K. Petersen 	/* For each data page */
1855c6a44287SMartin K. Petersen 	scsi_for_each_sg(SCpnt, dsgl, scsi_sg_count(SCpnt), i) {
1856c6a44287SMartin K. Petersen 		daddr = kmap_atomic(sg_page(dsgl), KM_IRQ0) + dsgl->offset;
1857c6a44287SMartin K. Petersen 
1858c6a44287SMartin K. Petersen 		/* For each sector-sized chunk in data page */
1859c6a44287SMartin K. Petersen 		for (j = 0 ; j < dsgl->length ; j += scsi_debug_sector_size) {
1860c6a44287SMartin K. Petersen 
1861c6a44287SMartin K. Petersen 			/* If we're at the end of the current
1862c6a44287SMartin K. Petersen 			 * protection page advance to the next one
1863c6a44287SMartin K. Petersen 			 */
1864c6a44287SMartin K. Petersen 			if (ppage_offset >= psgl->length) {
1865c6a44287SMartin K. Petersen 				kunmap_atomic(paddr, KM_IRQ1);
1866c6a44287SMartin K. Petersen 				psgl = sg_next(psgl);
1867c6a44287SMartin K. Petersen 				BUG_ON(psgl == NULL);
1868c6a44287SMartin K. Petersen 				paddr = kmap_atomic(sg_page(psgl), KM_IRQ1)
1869c6a44287SMartin K. Petersen 					+ psgl->offset;
1870c6a44287SMartin K. Petersen 				ppage_offset = 0;
1871c6a44287SMartin K. Petersen 			}
1872c6a44287SMartin K. Petersen 
1873c6a44287SMartin K. Petersen 			sdt = paddr + ppage_offset;
1874c6a44287SMartin K. Petersen 
1875c6a44287SMartin K. Petersen 			switch (scsi_debug_guard) {
1876c6a44287SMartin K. Petersen 			case 1:
1877c6a44287SMartin K. Petersen 				csum = ip_compute_csum(daddr,
1878c6a44287SMartin K. Petersen 						       scsi_debug_sector_size);
1879c6a44287SMartin K. Petersen 				break;
1880c6a44287SMartin K. Petersen 			case 0:
1881c6a44287SMartin K. Petersen 				csum = cpu_to_be16(crc_t10dif(daddr,
1882c6a44287SMartin K. Petersen 						      scsi_debug_sector_size));
1883c6a44287SMartin K. Petersen 				break;
1884c6a44287SMartin K. Petersen 			default:
1885c6a44287SMartin K. Petersen 				BUG();
1886c6a44287SMartin K. Petersen 				ret = 0;
1887c6a44287SMartin K. Petersen 				goto out;
1888c6a44287SMartin K. Petersen 			}
1889c6a44287SMartin K. Petersen 
1890c6a44287SMartin K. Petersen 			if (sdt->guard_tag != csum) {
1891c6a44287SMartin K. Petersen 				printk(KERN_ERR
1892c6a44287SMartin K. Petersen 				       "%s: GUARD check failed on sector %lu " \
1893c6a44287SMartin K. Petersen 				       "rcvd 0x%04x, calculated 0x%04x\n",
1894c6a44287SMartin K. Petersen 				       __func__, (unsigned long)sector,
1895c6a44287SMartin K. Petersen 				       be16_to_cpu(sdt->guard_tag),
1896c6a44287SMartin K. Petersen 				       be16_to_cpu(csum));
1897c6a44287SMartin K. Petersen 				ret = 0x01;
1898c6a44287SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1899c6a44287SMartin K. Petersen 				goto out;
1900c6a44287SMartin K. Petersen 			}
1901c6a44287SMartin K. Petersen 
1902395cef03SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE1_PROTECTION &&
1903c6a44287SMartin K. Petersen 			    be32_to_cpu(sdt->ref_tag)
1904c6a44287SMartin K. Petersen 			    != (start_sec & 0xffffffff)) {
1905c6a44287SMartin K. Petersen 				printk(KERN_ERR
1906c6a44287SMartin K. Petersen 				       "%s: REF check failed on sector %lu\n",
1907c6a44287SMartin K. Petersen 				       __func__, (unsigned long)sector);
1908c6a44287SMartin K. Petersen 				ret = 0x03;
1909c6a44287SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1910c6a44287SMartin K. Petersen 				goto out;
1911c6a44287SMartin K. Petersen 			}
1912c6a44287SMartin K. Petersen 
1913395cef03SMartin K. Petersen 			if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
1914395cef03SMartin K. Petersen 			    be32_to_cpu(sdt->ref_tag) != ei_lba) {
1915395cef03SMartin K. Petersen 				printk(KERN_ERR
1916395cef03SMartin K. Petersen 				       "%s: REF check failed on sector %lu\n",
1917395cef03SMartin K. Petersen 				       __func__, (unsigned long)sector);
1918395cef03SMartin K. Petersen 				ret = 0x03;
1919395cef03SMartin K. Petersen 				dump_sector(daddr, scsi_debug_sector_size);
1920395cef03SMartin K. Petersen 				goto out;
1921395cef03SMartin K. Petersen 			}
1922395cef03SMartin K. Petersen 
1923c6a44287SMartin K. Petersen 			/* Would be great to copy this in bigger
1924c6a44287SMartin K. Petersen 			 * chunks.  However, for the sake of
1925c6a44287SMartin K. Petersen 			 * correctness we need to verify each sector
1926c6a44287SMartin K. Petersen 			 * before writing it to "stable" storage
1927c6a44287SMartin K. Petersen 			 */
1928c6a44287SMartin K. Petersen 			memcpy(dif_storep + dif_offset(sector), sdt, 8);
1929c6a44287SMartin K. Petersen 
1930c6a44287SMartin K. Petersen 			sector++;
1931c6a44287SMartin K. Petersen 
1932c6a44287SMartin K. Petersen 			if (sector == sdebug_store_sectors)
1933c6a44287SMartin K. Petersen 				sector = 0;	/* Force wrap */
1934c6a44287SMartin K. Petersen 
1935c6a44287SMartin K. Petersen 			start_sec++;
1936395cef03SMartin K. Petersen 			ei_lba++;
1937c6a44287SMartin K. Petersen 			daddr += scsi_debug_sector_size;
1938c6a44287SMartin K. Petersen 			ppage_offset += sizeof(struct sd_dif_tuple);
1939c6a44287SMartin K. Petersen 		}
1940c6a44287SMartin K. Petersen 
1941c6a44287SMartin K. Petersen 		kunmap_atomic(daddr, KM_IRQ0);
1942c6a44287SMartin K. Petersen 	}
1943c6a44287SMartin K. Petersen 
1944c6a44287SMartin K. Petersen 	kunmap_atomic(paddr, KM_IRQ1);
1945c6a44287SMartin K. Petersen 
1946c6a44287SMartin K. Petersen 	dix_writes++;
1947c6a44287SMartin K. Petersen 
1948c6a44287SMartin K. Petersen 	return 0;
1949c6a44287SMartin K. Petersen 
1950c6a44287SMartin K. Petersen out:
1951c6a44287SMartin K. Petersen 	dif_errors++;
1952c6a44287SMartin K. Petersen 	kunmap_atomic(daddr, KM_IRQ0);
1953c6a44287SMartin K. Petersen 	kunmap_atomic(paddr, KM_IRQ1);
1954c6a44287SMartin K. Petersen 	return ret;
1955c6a44287SMartin K. Petersen }
1956c6a44287SMartin K. Petersen 
195744d92694SMartin K. Petersen static unsigned int map_state(sector_t lba, unsigned int *num)
195844d92694SMartin K. Petersen {
195944d92694SMartin K. Petersen 	unsigned int granularity, alignment, mapped;
196044d92694SMartin K. Petersen 	sector_t block, next, end;
196144d92694SMartin K. Petersen 
196244d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
196344d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
196444d92694SMartin K. Petersen 	block = lba + alignment;
196544d92694SMartin K. Petersen 	do_div(block, granularity);
196644d92694SMartin K. Petersen 
196744d92694SMartin K. Petersen 	mapped = test_bit(block, map_storep);
196844d92694SMartin K. Petersen 
196944d92694SMartin K. Petersen 	if (mapped)
197044d92694SMartin K. Petersen 		next = find_next_zero_bit(map_storep, map_size, block);
197144d92694SMartin K. Petersen 	else
197244d92694SMartin K. Petersen 		next = find_next_bit(map_storep, map_size, block);
197344d92694SMartin K. Petersen 
197444d92694SMartin K. Petersen 	end = next * granularity - scsi_debug_unmap_alignment;
197544d92694SMartin K. Petersen 	*num = end - lba;
197644d92694SMartin K. Petersen 
197744d92694SMartin K. Petersen 	return mapped;
197844d92694SMartin K. Petersen }
197944d92694SMartin K. Petersen 
198044d92694SMartin K. Petersen static void map_region(sector_t lba, unsigned int len)
198144d92694SMartin K. Petersen {
198244d92694SMartin K. Petersen 	unsigned int granularity, alignment;
198344d92694SMartin K. Petersen 	sector_t end = lba + len;
198444d92694SMartin K. Petersen 
198544d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
198644d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
198744d92694SMartin K. Petersen 
198844d92694SMartin K. Petersen 	while (lba < end) {
198944d92694SMartin K. Petersen 		sector_t block, rem;
199044d92694SMartin K. Petersen 
199144d92694SMartin K. Petersen 		block = lba + alignment;
199244d92694SMartin K. Petersen 		rem = do_div(block, granularity);
199344d92694SMartin K. Petersen 
19949ab98f57SFUJITA Tomonori 		if (block < map_size)
199544d92694SMartin K. Petersen 			set_bit(block, map_storep);
199644d92694SMartin K. Petersen 
199744d92694SMartin K. Petersen 		lba += granularity - rem;
199844d92694SMartin K. Petersen 	}
199944d92694SMartin K. Petersen }
200044d92694SMartin K. Petersen 
200144d92694SMartin K. Petersen static void unmap_region(sector_t lba, unsigned int len)
200244d92694SMartin K. Petersen {
200344d92694SMartin K. Petersen 	unsigned int granularity, alignment;
200444d92694SMartin K. Petersen 	sector_t end = lba + len;
200544d92694SMartin K. Petersen 
200644d92694SMartin K. Petersen 	granularity = scsi_debug_unmap_granularity;
200744d92694SMartin K. Petersen 	alignment = granularity - scsi_debug_unmap_alignment;
200844d92694SMartin K. Petersen 
200944d92694SMartin K. Petersen 	while (lba < end) {
201044d92694SMartin K. Petersen 		sector_t block, rem;
201144d92694SMartin K. Petersen 
201244d92694SMartin K. Petersen 		block = lba + alignment;
201344d92694SMartin K. Petersen 		rem = do_div(block, granularity);
201444d92694SMartin K. Petersen 
20159ab98f57SFUJITA Tomonori 		if (rem == 0 && lba + granularity <= end &&
20169ab98f57SFUJITA Tomonori 		    block < map_size)
201744d92694SMartin K. Petersen 			clear_bit(block, map_storep);
201844d92694SMartin K. Petersen 
201944d92694SMartin K. Petersen 		lba += granularity - rem;
202044d92694SMartin K. Petersen 	}
202144d92694SMartin K. Petersen }
202244d92694SMartin K. Petersen 
2023c65b1445SDouglas Gilbert static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
2024395cef03SMartin K. Petersen 		      unsigned int num, struct sdebug_dev_info *devip,
2025395cef03SMartin K. Petersen 		      u32 ei_lba)
20261da177e4SLinus Torvalds {
20271da177e4SLinus Torvalds 	unsigned long iflags;
202819789100SFUJITA Tomonori 	int ret;
20291da177e4SLinus Torvalds 
203019789100SFUJITA Tomonori 	ret = check_device_access_params(devip, lba, num);
203119789100SFUJITA Tomonori 	if (ret)
203219789100SFUJITA Tomonori 		return ret;
20331da177e4SLinus Torvalds 
2034c6a44287SMartin K. Petersen 	/* DIX + T10 DIF */
2035c6a44287SMartin K. Petersen 	if (scsi_debug_dix && scsi_prot_sg_count(SCpnt)) {
2036395cef03SMartin K. Petersen 		int prot_ret = prot_verify_write(SCpnt, lba, num, ei_lba);
2037c6a44287SMartin K. Petersen 
2038c6a44287SMartin K. Petersen 		if (prot_ret) {
2039c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, prot_ret);
2040c6a44287SMartin K. Petersen 			return illegal_condition_result;
2041c6a44287SMartin K. Petersen 		}
2042c6a44287SMartin K. Petersen 	}
2043c6a44287SMartin K. Petersen 
20441da177e4SLinus Torvalds 	write_lock_irqsave(&atomic_rw, iflags);
204519789100SFUJITA Tomonori 	ret = do_device_access(SCpnt, devip, lba, num, 1);
204644d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
204744d92694SMartin K. Petersen 		map_region(lba, num);
20481da177e4SLinus Torvalds 	write_unlock_irqrestore(&atomic_rw, iflags);
204919789100SFUJITA Tomonori 	if (-1 == ret)
20501da177e4SLinus Torvalds 		return (DID_ERROR << 16);
2051597136abSMartin K. Petersen 	else if ((ret < (num * scsi_debug_sector_size)) &&
20521da177e4SLinus Torvalds 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
2053c65b1445SDouglas Gilbert 		printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
2054597136abSMartin K. Petersen 		       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
205544d92694SMartin K. Petersen 
20561da177e4SLinus Torvalds 	return 0;
20571da177e4SLinus Torvalds }
20581da177e4SLinus Torvalds 
205944d92694SMartin K. Petersen static int resp_write_same(struct scsi_cmnd *scmd, unsigned long long lba,
206044d92694SMartin K. Petersen 		      unsigned int num, struct sdebug_dev_info *devip,
206144d92694SMartin K. Petersen 			   u32 ei_lba, unsigned int unmap)
206244d92694SMartin K. Petersen {
206344d92694SMartin K. Petersen 	unsigned long iflags;
206444d92694SMartin K. Petersen 	unsigned long long i;
206544d92694SMartin K. Petersen 	int ret;
206644d92694SMartin K. Petersen 
206744d92694SMartin K. Petersen 	ret = check_device_access_params(devip, lba, num);
206844d92694SMartin K. Petersen 	if (ret)
206944d92694SMartin K. Petersen 		return ret;
207044d92694SMartin K. Petersen 
207144d92694SMartin K. Petersen 	write_lock_irqsave(&atomic_rw, iflags);
207244d92694SMartin K. Petersen 
207344d92694SMartin K. Petersen 	if (unmap && scsi_debug_unmap_granularity) {
207444d92694SMartin K. Petersen 		unmap_region(lba, num);
207544d92694SMartin K. Petersen 		goto out;
207644d92694SMartin K. Petersen 	}
207744d92694SMartin K. Petersen 
207844d92694SMartin K. Petersen 	/* Else fetch one logical block */
207944d92694SMartin K. Petersen 	ret = fetch_to_dev_buffer(scmd,
208044d92694SMartin K. Petersen 				  fake_storep + (lba * scsi_debug_sector_size),
208144d92694SMartin K. Petersen 				  scsi_debug_sector_size);
208244d92694SMartin K. Petersen 
208344d92694SMartin K. Petersen 	if (-1 == ret) {
208444d92694SMartin K. Petersen 		write_unlock_irqrestore(&atomic_rw, iflags);
208544d92694SMartin K. Petersen 		return (DID_ERROR << 16);
208644d92694SMartin K. Petersen 	} else if ((ret < (num * scsi_debug_sector_size)) &&
208744d92694SMartin K. Petersen 		 (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
208844d92694SMartin K. Petersen 		printk(KERN_INFO "scsi_debug: write same: cdb indicated=%u, "
208944d92694SMartin K. Petersen 		       " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
209044d92694SMartin K. Petersen 
209144d92694SMartin K. Petersen 	/* Copy first sector to remaining blocks */
209244d92694SMartin K. Petersen 	for (i = 1 ; i < num ; i++)
209344d92694SMartin K. Petersen 		memcpy(fake_storep + ((lba + i) * scsi_debug_sector_size),
209444d92694SMartin K. Petersen 		       fake_storep + (lba * scsi_debug_sector_size),
209544d92694SMartin K. Petersen 		       scsi_debug_sector_size);
209644d92694SMartin K. Petersen 
209744d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity)
209844d92694SMartin K. Petersen 		map_region(lba, num);
209944d92694SMartin K. Petersen out:
210044d92694SMartin K. Petersen 	write_unlock_irqrestore(&atomic_rw, iflags);
210144d92694SMartin K. Petersen 
210244d92694SMartin K. Petersen 	return 0;
210344d92694SMartin K. Petersen }
210444d92694SMartin K. Petersen 
210544d92694SMartin K. Petersen struct unmap_block_desc {
210644d92694SMartin K. Petersen 	__be64	lba;
210744d92694SMartin K. Petersen 	__be32	blocks;
210844d92694SMartin K. Petersen 	__be32	__reserved;
210944d92694SMartin K. Petersen };
211044d92694SMartin K. Petersen 
211144d92694SMartin K. Petersen static int resp_unmap(struct scsi_cmnd * scmd, struct sdebug_dev_info * devip)
211244d92694SMartin K. Petersen {
211344d92694SMartin K. Petersen 	unsigned char *buf;
211444d92694SMartin K. Petersen 	struct unmap_block_desc *desc;
211544d92694SMartin K. Petersen 	unsigned int i, payload_len, descriptors;
211644d92694SMartin K. Petersen 	int ret;
211744d92694SMartin K. Petersen 
211844d92694SMartin K. Petersen 	ret = check_readiness(scmd, 1, devip);
211944d92694SMartin K. Petersen 	if (ret)
212044d92694SMartin K. Petersen 		return ret;
212144d92694SMartin K. Petersen 
212244d92694SMartin K. Petersen 	payload_len = get_unaligned_be16(&scmd->cmnd[7]);
212344d92694SMartin K. Petersen 	BUG_ON(scsi_bufflen(scmd) != payload_len);
212444d92694SMartin K. Petersen 
212544d92694SMartin K. Petersen 	descriptors = (payload_len - 8) / 16;
212644d92694SMartin K. Petersen 
212744d92694SMartin K. Petersen 	buf = kmalloc(scsi_bufflen(scmd), GFP_ATOMIC);
212844d92694SMartin K. Petersen 	if (!buf)
212944d92694SMartin K. Petersen 		return check_condition_result;
213044d92694SMartin K. Petersen 
213144d92694SMartin K. Petersen 	scsi_sg_copy_to_buffer(scmd, buf, scsi_bufflen(scmd));
213244d92694SMartin K. Petersen 
213344d92694SMartin K. Petersen 	BUG_ON(get_unaligned_be16(&buf[0]) != payload_len - 2);
213444d92694SMartin K. Petersen 	BUG_ON(get_unaligned_be16(&buf[2]) != descriptors * 16);
213544d92694SMartin K. Petersen 
213644d92694SMartin K. Petersen 	desc = (void *)&buf[8];
213744d92694SMartin K. Petersen 
213844d92694SMartin K. Petersen 	for (i = 0 ; i < descriptors ; i++) {
213944d92694SMartin K. Petersen 		unsigned long long lba = get_unaligned_be64(&desc[i].lba);
214044d92694SMartin K. Petersen 		unsigned int num = get_unaligned_be32(&desc[i].blocks);
214144d92694SMartin K. Petersen 
214244d92694SMartin K. Petersen 		ret = check_device_access_params(devip, lba, num);
214344d92694SMartin K. Petersen 		if (ret)
214444d92694SMartin K. Petersen 			goto out;
214544d92694SMartin K. Petersen 
214644d92694SMartin K. Petersen 		unmap_region(lba, num);
214744d92694SMartin K. Petersen 	}
214844d92694SMartin K. Petersen 
214944d92694SMartin K. Petersen 	ret = 0;
215044d92694SMartin K. Petersen 
215144d92694SMartin K. Petersen out:
215244d92694SMartin K. Petersen 	kfree(buf);
215344d92694SMartin K. Petersen 
215444d92694SMartin K. Petersen 	return ret;
215544d92694SMartin K. Petersen }
215644d92694SMartin K. Petersen 
215744d92694SMartin K. Petersen #define SDEBUG_GET_LBA_STATUS_LEN 32
215844d92694SMartin K. Petersen 
215944d92694SMartin K. Petersen static int resp_get_lba_status(struct scsi_cmnd * scmd,
216044d92694SMartin K. Petersen 			       struct sdebug_dev_info * devip)
216144d92694SMartin K. Petersen {
216244d92694SMartin K. Petersen 	unsigned long long lba;
216344d92694SMartin K. Petersen 	unsigned int alloc_len, mapped, num;
216444d92694SMartin K. Petersen 	unsigned char arr[SDEBUG_GET_LBA_STATUS_LEN];
216544d92694SMartin K. Petersen 	int ret;
216644d92694SMartin K. Petersen 
216744d92694SMartin K. Petersen 	ret = check_readiness(scmd, 1, devip);
216844d92694SMartin K. Petersen 	if (ret)
216944d92694SMartin K. Petersen 		return ret;
217044d92694SMartin K. Petersen 
217144d92694SMartin K. Petersen 	lba = get_unaligned_be64(&scmd->cmnd[2]);
217244d92694SMartin K. Petersen 	alloc_len = get_unaligned_be32(&scmd->cmnd[10]);
217344d92694SMartin K. Petersen 
217444d92694SMartin K. Petersen 	if (alloc_len < 24)
217544d92694SMartin K. Petersen 		return 0;
217644d92694SMartin K. Petersen 
217744d92694SMartin K. Petersen 	ret = check_device_access_params(devip, lba, 1);
217844d92694SMartin K. Petersen 	if (ret)
217944d92694SMartin K. Petersen 		return ret;
218044d92694SMartin K. Petersen 
218144d92694SMartin K. Petersen 	mapped = map_state(lba, &num);
218244d92694SMartin K. Petersen 
218344d92694SMartin K. Petersen 	memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
218444d92694SMartin K. Petersen 	put_unaligned_be32(16, &arr[0]);	/* Parameter Data Length */
218544d92694SMartin K. Petersen 	put_unaligned_be64(lba, &arr[8]);	/* LBA */
218644d92694SMartin K. Petersen 	put_unaligned_be32(num, &arr[16]);	/* Number of blocks */
218744d92694SMartin K. Petersen 	arr[20] = !mapped;			/* mapped = 0, unmapped = 1 */
218844d92694SMartin K. Petersen 
218944d92694SMartin K. Petersen 	return fill_from_dev_buffer(scmd, arr, SDEBUG_GET_LBA_STATUS_LEN);
219044d92694SMartin K. Petersen }
219144d92694SMartin K. Petersen 
2192c65b1445SDouglas Gilbert #define SDEBUG_RLUN_ARR_SZ 256
21931da177e4SLinus Torvalds 
21941da177e4SLinus Torvalds static int resp_report_luns(struct scsi_cmnd * scp,
21951da177e4SLinus Torvalds 			    struct sdebug_dev_info * devip)
21961da177e4SLinus Torvalds {
21971da177e4SLinus Torvalds 	unsigned int alloc_len;
2198c65b1445SDouglas Gilbert 	int lun_cnt, i, upper, num, n, wlun, lun;
21991da177e4SLinus Torvalds 	unsigned char *cmd = (unsigned char *)scp->cmnd;
22001da177e4SLinus Torvalds 	int select_report = (int)cmd[2];
22011da177e4SLinus Torvalds 	struct scsi_lun *one_lun;
22021da177e4SLinus Torvalds 	unsigned char arr[SDEBUG_RLUN_ARR_SZ];
2203c65b1445SDouglas Gilbert 	unsigned char * max_addr;
22041da177e4SLinus Torvalds 
22051da177e4SLinus Torvalds 	alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24);
2206c65b1445SDouglas Gilbert 	if ((alloc_len < 4) || (select_report > 2)) {
22071da177e4SLinus Torvalds 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_FIELD_IN_CDB,
22081da177e4SLinus Torvalds 			       	0);
22091da177e4SLinus Torvalds 		return check_condition_result;
22101da177e4SLinus Torvalds 	}
22111da177e4SLinus Torvalds 	/* can produce response with up to 16k luns (lun 0 to lun 16383) */
22121da177e4SLinus Torvalds 	memset(arr, 0, SDEBUG_RLUN_ARR_SZ);
22131da177e4SLinus Torvalds 	lun_cnt = scsi_debug_max_luns;
2214c65b1445SDouglas Gilbert 	if (1 == select_report)
2215c65b1445SDouglas Gilbert 		lun_cnt = 0;
2216c65b1445SDouglas Gilbert 	else if (scsi_debug_no_lun_0 && (lun_cnt > 0))
2217c65b1445SDouglas Gilbert 		--lun_cnt;
2218c65b1445SDouglas Gilbert 	wlun = (select_report > 0) ? 1 : 0;
2219c65b1445SDouglas Gilbert 	num = lun_cnt + wlun;
2220c65b1445SDouglas Gilbert 	arr[2] = ((sizeof(struct scsi_lun) * num) >> 8) & 0xff;
2221c65b1445SDouglas Gilbert 	arr[3] = (sizeof(struct scsi_lun) * num) & 0xff;
2222c65b1445SDouglas Gilbert 	n = min((int)((SDEBUG_RLUN_ARR_SZ - 8) /
2223c65b1445SDouglas Gilbert 			    sizeof(struct scsi_lun)), num);
2224c65b1445SDouglas Gilbert 	if (n < num) {
2225c65b1445SDouglas Gilbert 		wlun = 0;
2226c65b1445SDouglas Gilbert 		lun_cnt = n;
2227c65b1445SDouglas Gilbert 	}
22281da177e4SLinus Torvalds 	one_lun = (struct scsi_lun *) &arr[8];
2229c65b1445SDouglas Gilbert 	max_addr = arr + SDEBUG_RLUN_ARR_SZ;
2230c65b1445SDouglas Gilbert 	for (i = 0, lun = (scsi_debug_no_lun_0 ? 1 : 0);
2231c65b1445SDouglas Gilbert              ((i < lun_cnt) && ((unsigned char *)(one_lun + i) < max_addr));
2232c65b1445SDouglas Gilbert 	     i++, lun++) {
2233c65b1445SDouglas Gilbert 		upper = (lun >> 8) & 0x3f;
22341da177e4SLinus Torvalds 		if (upper)
22351da177e4SLinus Torvalds 			one_lun[i].scsi_lun[0] =
22361da177e4SLinus Torvalds 			    (upper | (SAM2_LUN_ADDRESS_METHOD << 6));
2237c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = lun & 0xff;
22381da177e4SLinus Torvalds 	}
2239c65b1445SDouglas Gilbert 	if (wlun) {
2240c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[0] = (SAM2_WLUN_REPORT_LUNS >> 8) & 0xff;
2241c65b1445SDouglas Gilbert 		one_lun[i].scsi_lun[1] = SAM2_WLUN_REPORT_LUNS & 0xff;
2242c65b1445SDouglas Gilbert 		i++;
2243c65b1445SDouglas Gilbert 	}
2244c65b1445SDouglas Gilbert 	alloc_len = (unsigned char *)(one_lun + i) - arr;
22451da177e4SLinus Torvalds 	return fill_from_dev_buffer(scp, arr,
22461da177e4SLinus Torvalds 				    min((int)alloc_len, SDEBUG_RLUN_ARR_SZ));
22471da177e4SLinus Torvalds }
22481da177e4SLinus Torvalds 
2249c639d14eSFUJITA Tomonori static int resp_xdwriteread(struct scsi_cmnd *scp, unsigned long long lba,
2250c639d14eSFUJITA Tomonori 			    unsigned int num, struct sdebug_dev_info *devip)
2251c639d14eSFUJITA Tomonori {
2252c639d14eSFUJITA Tomonori 	int i, j, ret = -1;
2253c639d14eSFUJITA Tomonori 	unsigned char *kaddr, *buf;
2254c639d14eSFUJITA Tomonori 	unsigned int offset;
2255c639d14eSFUJITA Tomonori 	struct scatterlist *sg;
2256c639d14eSFUJITA Tomonori 	struct scsi_data_buffer *sdb = scsi_in(scp);
2257c639d14eSFUJITA Tomonori 
2258c639d14eSFUJITA Tomonori 	/* better not to use temporary buffer. */
2259c639d14eSFUJITA Tomonori 	buf = kmalloc(scsi_bufflen(scp), GFP_ATOMIC);
2260c639d14eSFUJITA Tomonori 	if (!buf)
2261c639d14eSFUJITA Tomonori 		return ret;
2262c639d14eSFUJITA Tomonori 
226321a61829SFUJITA Tomonori 	scsi_sg_copy_to_buffer(scp, buf, scsi_bufflen(scp));
2264c639d14eSFUJITA Tomonori 
2265c639d14eSFUJITA Tomonori 	offset = 0;
2266c639d14eSFUJITA Tomonori 	for_each_sg(sdb->table.sgl, sg, sdb->table.nents, i) {
2267c639d14eSFUJITA Tomonori 		kaddr = (unsigned char *)kmap_atomic(sg_page(sg), KM_USER0);
2268c639d14eSFUJITA Tomonori 		if (!kaddr)
2269c639d14eSFUJITA Tomonori 			goto out;
2270c639d14eSFUJITA Tomonori 
2271c639d14eSFUJITA Tomonori 		for (j = 0; j < sg->length; j++)
2272c639d14eSFUJITA Tomonori 			*(kaddr + sg->offset + j) ^= *(buf + offset + j);
2273c639d14eSFUJITA Tomonori 
2274c639d14eSFUJITA Tomonori 		offset += sg->length;
2275c639d14eSFUJITA Tomonori 		kunmap_atomic(kaddr, KM_USER0);
2276c639d14eSFUJITA Tomonori 	}
2277c639d14eSFUJITA Tomonori 	ret = 0;
2278c639d14eSFUJITA Tomonori out:
2279c639d14eSFUJITA Tomonori 	kfree(buf);
2280c639d14eSFUJITA Tomonori 
2281c639d14eSFUJITA Tomonori 	return ret;
2282c639d14eSFUJITA Tomonori }
2283c639d14eSFUJITA Tomonori 
22841da177e4SLinus Torvalds /* When timer goes off this function is called. */
22851da177e4SLinus Torvalds static void timer_intr_handler(unsigned long indx)
22861da177e4SLinus Torvalds {
22871da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
22881da177e4SLinus Torvalds 	unsigned long iflags;
22891da177e4SLinus Torvalds 
229078d4e5a0SDouglas Gilbert 	if (indx >= scsi_debug_max_queue) {
22911da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too "
22921da177e4SLinus Torvalds 		       "large\n");
22931da177e4SLinus Torvalds 		return;
22941da177e4SLinus Torvalds 	}
22951da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
22961da177e4SLinus Torvalds 	sqcp = &queued_arr[(int)indx];
22971da177e4SLinus Torvalds 	if (! sqcp->in_use) {
22981da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected "
22991da177e4SLinus Torvalds 		       "interrupt\n");
23001da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
23011da177e4SLinus Torvalds 		return;
23021da177e4SLinus Torvalds 	}
23031da177e4SLinus Torvalds 	sqcp->in_use = 0;
23041da177e4SLinus Torvalds 	if (sqcp->done_funct) {
23051da177e4SLinus Torvalds 		sqcp->a_cmnd->result = sqcp->scsi_result;
23061da177e4SLinus Torvalds 		sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */
23071da177e4SLinus Torvalds 	}
23081da177e4SLinus Torvalds 	sqcp->done_funct = NULL;
23091da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
23101da177e4SLinus Torvalds }
23111da177e4SLinus Torvalds 
23121da177e4SLinus Torvalds 
23138dea0d02SFUJITA Tomonori static struct sdebug_dev_info *
23148dea0d02SFUJITA Tomonori sdebug_device_create(struct sdebug_host_info *sdbg_host, gfp_t flags)
23155cb2fc06SFUJITA Tomonori {
23165cb2fc06SFUJITA Tomonori 	struct sdebug_dev_info *devip;
23175cb2fc06SFUJITA Tomonori 
23185cb2fc06SFUJITA Tomonori 	devip = kzalloc(sizeof(*devip), flags);
23195cb2fc06SFUJITA Tomonori 	if (devip) {
23205cb2fc06SFUJITA Tomonori 		devip->sdbg_host = sdbg_host;
23215cb2fc06SFUJITA Tomonori 		list_add_tail(&devip->dev_list, &sdbg_host->dev_info_list);
23225cb2fc06SFUJITA Tomonori 	}
23235cb2fc06SFUJITA Tomonori 	return devip;
23245cb2fc06SFUJITA Tomonori }
23255cb2fc06SFUJITA Tomonori 
23261da177e4SLinus Torvalds static struct sdebug_dev_info * devInfoReg(struct scsi_device * sdev)
23271da177e4SLinus Torvalds {
23281da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
23291da177e4SLinus Torvalds 	struct sdebug_dev_info * open_devip = NULL;
23301da177e4SLinus Torvalds 	struct sdebug_dev_info * devip =
23311da177e4SLinus Torvalds 			(struct sdebug_dev_info *)sdev->hostdata;
23321da177e4SLinus Torvalds 
23331da177e4SLinus Torvalds 	if (devip)
23341da177e4SLinus Torvalds 		return devip;
2335d1e4c9c5SFUJITA Tomonori 	sdbg_host = *(struct sdebug_host_info **)shost_priv(sdev->host);
23361da177e4SLinus Torvalds 	if (!sdbg_host) {
23371da177e4SLinus Torvalds                 printk(KERN_ERR "Host info NULL\n");
23381da177e4SLinus Torvalds 		return NULL;
23391da177e4SLinus Torvalds         }
23401da177e4SLinus Torvalds 	list_for_each_entry(devip, &sdbg_host->dev_info_list, dev_list) {
23411da177e4SLinus Torvalds 		if ((devip->used) && (devip->channel == sdev->channel) &&
23421da177e4SLinus Torvalds                     (devip->target == sdev->id) &&
23431da177e4SLinus Torvalds                     (devip->lun == sdev->lun))
23441da177e4SLinus Torvalds                         return devip;
23451da177e4SLinus Torvalds 		else {
23461da177e4SLinus Torvalds 			if ((!devip->used) && (!open_devip))
23471da177e4SLinus Torvalds 				open_devip = devip;
23481da177e4SLinus Torvalds 		}
23491da177e4SLinus Torvalds 	}
23505cb2fc06SFUJITA Tomonori 	if (!open_devip) { /* try and make a new one */
23515cb2fc06SFUJITA Tomonori 		open_devip = sdebug_device_create(sdbg_host, GFP_ATOMIC);
23525cb2fc06SFUJITA Tomonori 		if (!open_devip) {
23531da177e4SLinus Torvalds 			printk(KERN_ERR "%s: out of memory at line %d\n",
2354cadbd4a5SHarvey Harrison 				__func__, __LINE__);
23551da177e4SLinus Torvalds 			return NULL;
23561da177e4SLinus Torvalds 		}
23571da177e4SLinus Torvalds 	}
2358a75869d1SFUJITA Tomonori 
23591da177e4SLinus Torvalds 	open_devip->channel = sdev->channel;
23601da177e4SLinus Torvalds 	open_devip->target = sdev->id;
23611da177e4SLinus Torvalds 	open_devip->lun = sdev->lun;
23621da177e4SLinus Torvalds 	open_devip->sdbg_host = sdbg_host;
23631da177e4SLinus Torvalds 	open_devip->reset = 1;
23641da177e4SLinus Torvalds 	open_devip->used = 1;
23651da177e4SLinus Torvalds 	memset(open_devip->sense_buff, 0, SDEBUG_SENSE_LEN);
23661da177e4SLinus Torvalds 	if (scsi_debug_dsense)
23671da177e4SLinus Torvalds 		open_devip->sense_buff[0] = 0x72;
23681da177e4SLinus Torvalds 	else {
23691da177e4SLinus Torvalds 		open_devip->sense_buff[0] = 0x70;
23701da177e4SLinus Torvalds 		open_devip->sense_buff[7] = 0xa;
23711da177e4SLinus Torvalds 	}
2372c65b1445SDouglas Gilbert 	if (sdev->lun == SAM2_WLUN_REPORT_LUNS)
2373c65b1445SDouglas Gilbert 		open_devip->wlun = SAM2_WLUN_REPORT_LUNS & 0xff;
2374a75869d1SFUJITA Tomonori 
23751da177e4SLinus Torvalds 	return open_devip;
23761da177e4SLinus Torvalds }
23771da177e4SLinus Torvalds 
23788dea0d02SFUJITA Tomonori static int scsi_debug_slave_alloc(struct scsi_device *sdp)
23791da177e4SLinus Torvalds {
23808dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
23818dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_alloc <%u %u %u %u>\n",
23828dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
238375ad23bcSNick Piggin 	queue_flag_set_unlocked(QUEUE_FLAG_BIDI, sdp->request_queue);
23848dea0d02SFUJITA Tomonori 	return 0;
23858dea0d02SFUJITA Tomonori }
23861da177e4SLinus Torvalds 
23878dea0d02SFUJITA Tomonori static int scsi_debug_slave_configure(struct scsi_device *sdp)
23888dea0d02SFUJITA Tomonori {
23898dea0d02SFUJITA Tomonori 	struct sdebug_dev_info *devip;
2390a34c4e98SFUJITA Tomonori 
23911da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
23928dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_configure <%u %u %u %u>\n",
23938dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
23948dea0d02SFUJITA Tomonori 	if (sdp->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
23958dea0d02SFUJITA Tomonori 		sdp->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
23968dea0d02SFUJITA Tomonori 	devip = devInfoReg(sdp);
23978dea0d02SFUJITA Tomonori 	if (NULL == devip)
23988dea0d02SFUJITA Tomonori 		return 1;	/* no resources, will be marked offline */
23998dea0d02SFUJITA Tomonori 	sdp->hostdata = devip;
24008dea0d02SFUJITA Tomonori 	if (sdp->host->cmd_per_lun)
24018dea0d02SFUJITA Tomonori 		scsi_adjust_queue_depth(sdp, SDEBUG_TAGGED_QUEUING,
24028dea0d02SFUJITA Tomonori 					sdp->host->cmd_per_lun);
24038dea0d02SFUJITA Tomonori 	blk_queue_max_segment_size(sdp->request_queue, 256 * 1024);
240478d4e5a0SDouglas Gilbert 	if (scsi_debug_no_uld)
240578d4e5a0SDouglas Gilbert 		sdp->no_uld_attach = 1;
24068dea0d02SFUJITA Tomonori 	return 0;
24078dea0d02SFUJITA Tomonori }
24088dea0d02SFUJITA Tomonori 
24098dea0d02SFUJITA Tomonori static void scsi_debug_slave_destroy(struct scsi_device *sdp)
24108dea0d02SFUJITA Tomonori {
24118dea0d02SFUJITA Tomonori 	struct sdebug_dev_info *devip =
24128dea0d02SFUJITA Tomonori 		(struct sdebug_dev_info *)sdp->hostdata;
24138dea0d02SFUJITA Tomonori 
24148dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24158dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: slave_destroy <%u %u %u %u>\n",
24168dea0d02SFUJITA Tomonori 		       sdp->host->host_no, sdp->channel, sdp->id, sdp->lun);
24178dea0d02SFUJITA Tomonori 	if (devip) {
24188dea0d02SFUJITA Tomonori 		/* make this slot avaliable for re-use */
24198dea0d02SFUJITA Tomonori 		devip->used = 0;
24208dea0d02SFUJITA Tomonori 		sdp->hostdata = NULL;
24218dea0d02SFUJITA Tomonori 	}
24228dea0d02SFUJITA Tomonori }
24238dea0d02SFUJITA Tomonori 
24248dea0d02SFUJITA Tomonori /* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */
24258dea0d02SFUJITA Tomonori static int stop_queued_cmnd(struct scsi_cmnd *cmnd)
24268dea0d02SFUJITA Tomonori {
24278dea0d02SFUJITA Tomonori 	unsigned long iflags;
24288dea0d02SFUJITA Tomonori 	int k;
24298dea0d02SFUJITA Tomonori 	struct sdebug_queued_cmd *sqcp;
24308dea0d02SFUJITA Tomonori 
24318dea0d02SFUJITA Tomonori 	spin_lock_irqsave(&queued_arr_lock, iflags);
243278d4e5a0SDouglas Gilbert 	for (k = 0; k < scsi_debug_max_queue; ++k) {
24338dea0d02SFUJITA Tomonori 		sqcp = &queued_arr[k];
24348dea0d02SFUJITA Tomonori 		if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) {
24358dea0d02SFUJITA Tomonori 			del_timer_sync(&sqcp->cmnd_timer);
24368dea0d02SFUJITA Tomonori 			sqcp->in_use = 0;
24378dea0d02SFUJITA Tomonori 			sqcp->a_cmnd = NULL;
24388dea0d02SFUJITA Tomonori 			break;
24398dea0d02SFUJITA Tomonori 		}
24408dea0d02SFUJITA Tomonori 	}
24418dea0d02SFUJITA Tomonori 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
244278d4e5a0SDouglas Gilbert 	return (k < scsi_debug_max_queue) ? 1 : 0;
24438dea0d02SFUJITA Tomonori }
24448dea0d02SFUJITA Tomonori 
24458dea0d02SFUJITA Tomonori /* Deletes (stops) timers of all queued commands */
24468dea0d02SFUJITA Tomonori static void stop_all_queued(void)
24478dea0d02SFUJITA Tomonori {
24488dea0d02SFUJITA Tomonori 	unsigned long iflags;
24498dea0d02SFUJITA Tomonori 	int k;
24508dea0d02SFUJITA Tomonori 	struct sdebug_queued_cmd *sqcp;
24518dea0d02SFUJITA Tomonori 
24528dea0d02SFUJITA Tomonori 	spin_lock_irqsave(&queued_arr_lock, iflags);
245378d4e5a0SDouglas Gilbert 	for (k = 0; k < scsi_debug_max_queue; ++k) {
24548dea0d02SFUJITA Tomonori 		sqcp = &queued_arr[k];
24558dea0d02SFUJITA Tomonori 		if (sqcp->in_use && sqcp->a_cmnd) {
24568dea0d02SFUJITA Tomonori 			del_timer_sync(&sqcp->cmnd_timer);
24578dea0d02SFUJITA Tomonori 			sqcp->in_use = 0;
24588dea0d02SFUJITA Tomonori 			sqcp->a_cmnd = NULL;
24598dea0d02SFUJITA Tomonori 		}
24608dea0d02SFUJITA Tomonori 	}
24618dea0d02SFUJITA Tomonori 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
24621da177e4SLinus Torvalds }
24631da177e4SLinus Torvalds 
24641da177e4SLinus Torvalds static int scsi_debug_abort(struct scsi_cmnd * SCpnt)
24651da177e4SLinus Torvalds {
24661da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24671da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: abort\n");
24681da177e4SLinus Torvalds 	++num_aborts;
24691da177e4SLinus Torvalds 	stop_queued_cmnd(SCpnt);
24701da177e4SLinus Torvalds 	return SUCCESS;
24711da177e4SLinus Torvalds }
24721da177e4SLinus Torvalds 
24731da177e4SLinus Torvalds static int scsi_debug_biosparam(struct scsi_device *sdev,
24741da177e4SLinus Torvalds 		struct block_device * bdev, sector_t capacity, int *info)
24751da177e4SLinus Torvalds {
24761da177e4SLinus Torvalds 	int res;
24771da177e4SLinus Torvalds 	unsigned char *buf;
24781da177e4SLinus Torvalds 
24791da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
24801da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: biosparam\n");
24811da177e4SLinus Torvalds 	buf = scsi_bios_ptable(bdev);
24821da177e4SLinus Torvalds 	if (buf) {
24831da177e4SLinus Torvalds 		res = scsi_partsize(buf, capacity,
24841da177e4SLinus Torvalds 				    &info[2], &info[0], &info[1]);
24851da177e4SLinus Torvalds 		kfree(buf);
24861da177e4SLinus Torvalds 		if (! res)
24871da177e4SLinus Torvalds 			return res;
24881da177e4SLinus Torvalds 	}
24891da177e4SLinus Torvalds 	info[0] = sdebug_heads;
24901da177e4SLinus Torvalds 	info[1] = sdebug_sectors_per;
24911da177e4SLinus Torvalds 	info[2] = sdebug_cylinders_per;
24921da177e4SLinus Torvalds 	return 0;
24931da177e4SLinus Torvalds }
24941da177e4SLinus Torvalds 
24951da177e4SLinus Torvalds static int scsi_debug_device_reset(struct scsi_cmnd * SCpnt)
24961da177e4SLinus Torvalds {
24971da177e4SLinus Torvalds 	struct sdebug_dev_info * devip;
24981da177e4SLinus Torvalds 
24991da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
25001da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: device_reset\n");
25011da177e4SLinus Torvalds 	++num_dev_resets;
25021da177e4SLinus Torvalds 	if (SCpnt) {
25031da177e4SLinus Torvalds 		devip = devInfoReg(SCpnt->device);
25041da177e4SLinus Torvalds 		if (devip)
25051da177e4SLinus Torvalds 			devip->reset = 1;
25061da177e4SLinus Torvalds 	}
25071da177e4SLinus Torvalds 	return SUCCESS;
25081da177e4SLinus Torvalds }
25091da177e4SLinus Torvalds 
25101da177e4SLinus Torvalds static int scsi_debug_bus_reset(struct scsi_cmnd * SCpnt)
25111da177e4SLinus Torvalds {
25121da177e4SLinus Torvalds 	struct sdebug_host_info *sdbg_host;
25131da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
25141da177e4SLinus Torvalds         struct scsi_device * sdp;
25151da177e4SLinus Torvalds         struct Scsi_Host * hp;
25161da177e4SLinus Torvalds 
25171da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
25181da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: bus_reset\n");
25191da177e4SLinus Torvalds 	++num_bus_resets;
25201da177e4SLinus Torvalds 	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
2521d1e4c9c5SFUJITA Tomonori 		sdbg_host = *(struct sdebug_host_info **)shost_priv(hp);
25221da177e4SLinus Torvalds 		if (sdbg_host) {
25231da177e4SLinus Torvalds 			list_for_each_entry(dev_info,
25241da177e4SLinus Torvalds                                             &sdbg_host->dev_info_list,
25251da177e4SLinus Torvalds                                             dev_list)
25261da177e4SLinus Torvalds 				dev_info->reset = 1;
25271da177e4SLinus Torvalds 		}
25281da177e4SLinus Torvalds 	}
25291da177e4SLinus Torvalds 	return SUCCESS;
25301da177e4SLinus Torvalds }
25311da177e4SLinus Torvalds 
25321da177e4SLinus Torvalds static int scsi_debug_host_reset(struct scsi_cmnd * SCpnt)
25331da177e4SLinus Torvalds {
25341da177e4SLinus Torvalds 	struct sdebug_host_info * sdbg_host;
25351da177e4SLinus Torvalds         struct sdebug_dev_info * dev_info;
25361da177e4SLinus Torvalds 
25371da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
25381da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug: host_reset\n");
25391da177e4SLinus Torvalds 	++num_host_resets;
25401da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
25411da177e4SLinus Torvalds         list_for_each_entry(sdbg_host, &sdebug_host_list, host_list) {
25421da177e4SLinus Torvalds                 list_for_each_entry(dev_info, &sdbg_host->dev_info_list,
25431da177e4SLinus Torvalds                                     dev_list)
25441da177e4SLinus Torvalds                         dev_info->reset = 1;
25451da177e4SLinus Torvalds         }
25461da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
25471da177e4SLinus Torvalds 	stop_all_queued();
25481da177e4SLinus Torvalds 	return SUCCESS;
25491da177e4SLinus Torvalds }
25501da177e4SLinus Torvalds 
25511da177e4SLinus Torvalds /* Initializes timers in queued array */
25521da177e4SLinus Torvalds static void __init init_all_queued(void)
25531da177e4SLinus Torvalds {
25541da177e4SLinus Torvalds 	unsigned long iflags;
25551da177e4SLinus Torvalds 	int k;
25561da177e4SLinus Torvalds 	struct sdebug_queued_cmd * sqcp;
25571da177e4SLinus Torvalds 
25581da177e4SLinus Torvalds 	spin_lock_irqsave(&queued_arr_lock, iflags);
255978d4e5a0SDouglas Gilbert 	for (k = 0; k < scsi_debug_max_queue; ++k) {
25601da177e4SLinus Torvalds 		sqcp = &queued_arr[k];
25611da177e4SLinus Torvalds 		init_timer(&sqcp->cmnd_timer);
25621da177e4SLinus Torvalds 		sqcp->in_use = 0;
25631da177e4SLinus Torvalds 		sqcp->a_cmnd = NULL;
25641da177e4SLinus Torvalds 	}
25651da177e4SLinus Torvalds 	spin_unlock_irqrestore(&queued_arr_lock, iflags);
25661da177e4SLinus Torvalds }
25671da177e4SLinus Torvalds 
2568f58b0efbSFUJITA Tomonori static void __init sdebug_build_parts(unsigned char *ramp,
25695f2578e5SFUJITA Tomonori 				      unsigned long store_size)
25701da177e4SLinus Torvalds {
25711da177e4SLinus Torvalds 	struct partition * pp;
25721da177e4SLinus Torvalds 	int starts[SDEBUG_MAX_PARTS + 2];
25731da177e4SLinus Torvalds 	int sectors_per_part, num_sectors, k;
25741da177e4SLinus Torvalds 	int heads_by_sects, start_sec, end_sec;
25751da177e4SLinus Torvalds 
25761da177e4SLinus Torvalds 	/* assume partition table already zeroed */
2577f58b0efbSFUJITA Tomonori 	if ((scsi_debug_num_parts < 1) || (store_size < 1048576))
25781da177e4SLinus Torvalds 		return;
25791da177e4SLinus Torvalds 	if (scsi_debug_num_parts > SDEBUG_MAX_PARTS) {
25801da177e4SLinus Torvalds 		scsi_debug_num_parts = SDEBUG_MAX_PARTS;
25811da177e4SLinus Torvalds 		printk(KERN_WARNING "scsi_debug:build_parts: reducing "
25821da177e4SLinus Torvalds 				    "partitions to %d\n", SDEBUG_MAX_PARTS);
25831da177e4SLinus Torvalds 	}
2584c65b1445SDouglas Gilbert 	num_sectors = (int)sdebug_store_sectors;
25851da177e4SLinus Torvalds 	sectors_per_part = (num_sectors - sdebug_sectors_per)
25861da177e4SLinus Torvalds 			   / scsi_debug_num_parts;
25871da177e4SLinus Torvalds 	heads_by_sects = sdebug_heads * sdebug_sectors_per;
25881da177e4SLinus Torvalds         starts[0] = sdebug_sectors_per;
25891da177e4SLinus Torvalds 	for (k = 1; k < scsi_debug_num_parts; ++k)
25901da177e4SLinus Torvalds 		starts[k] = ((k * sectors_per_part) / heads_by_sects)
25911da177e4SLinus Torvalds 			    * heads_by_sects;
25921da177e4SLinus Torvalds 	starts[scsi_debug_num_parts] = num_sectors;
25931da177e4SLinus Torvalds 	starts[scsi_debug_num_parts + 1] = 0;
25941da177e4SLinus Torvalds 
25951da177e4SLinus Torvalds 	ramp[510] = 0x55;	/* magic partition markings */
25961da177e4SLinus Torvalds 	ramp[511] = 0xAA;
25971da177e4SLinus Torvalds 	pp = (struct partition *)(ramp + 0x1be);
25981da177e4SLinus Torvalds 	for (k = 0; starts[k + 1]; ++k, ++pp) {
25991da177e4SLinus Torvalds 		start_sec = starts[k];
26001da177e4SLinus Torvalds 		end_sec = starts[k + 1] - 1;
26011da177e4SLinus Torvalds 		pp->boot_ind = 0;
26021da177e4SLinus Torvalds 
26031da177e4SLinus Torvalds 		pp->cyl = start_sec / heads_by_sects;
26041da177e4SLinus Torvalds 		pp->head = (start_sec - (pp->cyl * heads_by_sects))
26051da177e4SLinus Torvalds 			   / sdebug_sectors_per;
26061da177e4SLinus Torvalds 		pp->sector = (start_sec % sdebug_sectors_per) + 1;
26071da177e4SLinus Torvalds 
26081da177e4SLinus Torvalds 		pp->end_cyl = end_sec / heads_by_sects;
26091da177e4SLinus Torvalds 		pp->end_head = (end_sec - (pp->end_cyl * heads_by_sects))
26101da177e4SLinus Torvalds 			       / sdebug_sectors_per;
26111da177e4SLinus Torvalds 		pp->end_sector = (end_sec % sdebug_sectors_per) + 1;
26121da177e4SLinus Torvalds 
26131da177e4SLinus Torvalds 		pp->start_sect = start_sec;
26141da177e4SLinus Torvalds 		pp->nr_sects = end_sec - start_sec + 1;
26151da177e4SLinus Torvalds 		pp->sys_ind = 0x83;	/* plain Linux partition */
26161da177e4SLinus Torvalds 	}
26171da177e4SLinus Torvalds }
26181da177e4SLinus Torvalds 
26191da177e4SLinus Torvalds static int schedule_resp(struct scsi_cmnd * cmnd,
26201da177e4SLinus Torvalds 			 struct sdebug_dev_info * devip,
26211da177e4SLinus Torvalds 			 done_funct_t done, int scsi_result, int delta_jiff)
26221da177e4SLinus Torvalds {
26231da177e4SLinus Torvalds 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmnd) {
26241da177e4SLinus Torvalds 		if (scsi_result) {
26251da177e4SLinus Torvalds 			struct scsi_device * sdp = cmnd->device;
26261da177e4SLinus Torvalds 
2627c65b1445SDouglas Gilbert 			printk(KERN_INFO "scsi_debug:    <%u %u %u %u> "
2628c65b1445SDouglas Gilbert 			       "non-zero result=0x%x\n", sdp->host->host_no,
2629c65b1445SDouglas Gilbert 			       sdp->channel, sdp->id, sdp->lun, scsi_result);
26301da177e4SLinus Torvalds 		}
26311da177e4SLinus Torvalds 	}
26321da177e4SLinus Torvalds 	if (cmnd && devip) {
26331da177e4SLinus Torvalds 		/* simulate autosense by this driver */
26341da177e4SLinus Torvalds 		if (SAM_STAT_CHECK_CONDITION == (scsi_result & 0xff))
26351da177e4SLinus Torvalds 			memcpy(cmnd->sense_buffer, devip->sense_buff,
26361da177e4SLinus Torvalds 			       (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ?
26371da177e4SLinus Torvalds 			       SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE);
26381da177e4SLinus Torvalds 	}
26391da177e4SLinus Torvalds 	if (delta_jiff <= 0) {
26401da177e4SLinus Torvalds 		if (cmnd)
26411da177e4SLinus Torvalds 			cmnd->result = scsi_result;
26421da177e4SLinus Torvalds 		if (done)
26431da177e4SLinus Torvalds 			done(cmnd);
26441da177e4SLinus Torvalds 		return 0;
26451da177e4SLinus Torvalds 	} else {
26461da177e4SLinus Torvalds 		unsigned long iflags;
26471da177e4SLinus Torvalds 		int k;
26481da177e4SLinus Torvalds 		struct sdebug_queued_cmd * sqcp = NULL;
26491da177e4SLinus Torvalds 
26501da177e4SLinus Torvalds 		spin_lock_irqsave(&queued_arr_lock, iflags);
265178d4e5a0SDouglas Gilbert 		for (k = 0; k < scsi_debug_max_queue; ++k) {
26521da177e4SLinus Torvalds 			sqcp = &queued_arr[k];
26531da177e4SLinus Torvalds 			if (! sqcp->in_use)
26541da177e4SLinus Torvalds 				break;
26551da177e4SLinus Torvalds 		}
265678d4e5a0SDouglas Gilbert 		if (k >= scsi_debug_max_queue) {
26571da177e4SLinus Torvalds 			spin_unlock_irqrestore(&queued_arr_lock, iflags);
26581da177e4SLinus Torvalds 			printk(KERN_WARNING "scsi_debug: can_queue exceeded\n");
26591da177e4SLinus Torvalds 			return 1;	/* report busy to mid level */
26601da177e4SLinus Torvalds 		}
26611da177e4SLinus Torvalds 		sqcp->in_use = 1;
26621da177e4SLinus Torvalds 		sqcp->a_cmnd = cmnd;
26631da177e4SLinus Torvalds 		sqcp->scsi_result = scsi_result;
26641da177e4SLinus Torvalds 		sqcp->done_funct = done;
26651da177e4SLinus Torvalds 		sqcp->cmnd_timer.function = timer_intr_handler;
26661da177e4SLinus Torvalds 		sqcp->cmnd_timer.data = k;
26671da177e4SLinus Torvalds 		sqcp->cmnd_timer.expires = jiffies + delta_jiff;
26681da177e4SLinus Torvalds 		add_timer(&sqcp->cmnd_timer);
26691da177e4SLinus Torvalds 		spin_unlock_irqrestore(&queued_arr_lock, iflags);
26701da177e4SLinus Torvalds 		if (cmnd)
26711da177e4SLinus Torvalds 			cmnd->result = 0;
26721da177e4SLinus Torvalds 		return 0;
26731da177e4SLinus Torvalds 	}
26741da177e4SLinus Torvalds }
267523183910SDouglas Gilbert /* Note: The following macros create attribute files in the
267623183910SDouglas Gilbert    /sys/module/scsi_debug/parameters directory. Unfortunately this
267723183910SDouglas Gilbert    driver is unaware of a change and cannot trigger auxiliary actions
267823183910SDouglas Gilbert    as it can when the corresponding attribute in the
267923183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory is changed.
268023183910SDouglas Gilbert  */
2681c65b1445SDouglas Gilbert module_param_named(add_host, scsi_debug_add_host, int, S_IRUGO | S_IWUSR);
2682c65b1445SDouglas Gilbert module_param_named(delay, scsi_debug_delay, int, S_IRUGO | S_IWUSR);
2683c65b1445SDouglas Gilbert module_param_named(dev_size_mb, scsi_debug_dev_size_mb, int, S_IRUGO);
2684c65b1445SDouglas Gilbert module_param_named(dsense, scsi_debug_dsense, int, S_IRUGO | S_IWUSR);
2685c65b1445SDouglas Gilbert module_param_named(every_nth, scsi_debug_every_nth, int, S_IRUGO | S_IWUSR);
268623183910SDouglas Gilbert module_param_named(fake_rw, scsi_debug_fake_rw, int, S_IRUGO | S_IWUSR);
2687c65b1445SDouglas Gilbert module_param_named(max_luns, scsi_debug_max_luns, int, S_IRUGO | S_IWUSR);
268878d4e5a0SDouglas Gilbert module_param_named(max_queue, scsi_debug_max_queue, int, S_IRUGO | S_IWUSR);
2689c65b1445SDouglas Gilbert module_param_named(no_lun_0, scsi_debug_no_lun_0, int, S_IRUGO | S_IWUSR);
269078d4e5a0SDouglas Gilbert module_param_named(no_uld, scsi_debug_no_uld, int, S_IRUGO);
2691c65b1445SDouglas Gilbert module_param_named(num_parts, scsi_debug_num_parts, int, S_IRUGO);
2692c65b1445SDouglas Gilbert module_param_named(num_tgts, scsi_debug_num_tgts, int, S_IRUGO | S_IWUSR);
2693c65b1445SDouglas Gilbert module_param_named(opts, scsi_debug_opts, int, S_IRUGO | S_IWUSR);
2694c65b1445SDouglas Gilbert module_param_named(ptype, scsi_debug_ptype, int, S_IRUGO | S_IWUSR);
2695c65b1445SDouglas Gilbert module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
2696c65b1445SDouglas Gilbert module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
269723183910SDouglas Gilbert module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
269823183910SDouglas Gilbert 		   S_IRUGO | S_IWUSR);
2699597136abSMartin K. Petersen module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
2700c6a44287SMartin K. Petersen module_param_named(dix, scsi_debug_dix, int, S_IRUGO);
2701c6a44287SMartin K. Petersen module_param_named(dif, scsi_debug_dif, int, S_IRUGO);
2702c6a44287SMartin K. Petersen module_param_named(guard, scsi_debug_guard, int, S_IRUGO);
2703c6a44287SMartin K. Petersen module_param_named(ato, scsi_debug_ato, int, S_IRUGO);
2704ea61fca5SMartin K. Petersen module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO);
2705e308b3d1SMartin K. Petersen module_param_named(opt_blks, scsi_debug_opt_blks, int, S_IRUGO);
2706ea61fca5SMartin K. Petersen module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO);
270744d92694SMartin K. Petersen module_param_named(unmap_max_blocks, scsi_debug_unmap_max_blocks, int, S_IRUGO);
270844d92694SMartin K. Petersen module_param_named(unmap_max_desc, scsi_debug_unmap_max_desc, int, S_IRUGO);
270944d92694SMartin K. Petersen module_param_named(unmap_granularity, scsi_debug_unmap_granularity, int, S_IRUGO);
271044d92694SMartin K. Petersen module_param_named(unmap_alignment, scsi_debug_unmap_alignment, int, S_IRUGO);
27111da177e4SLinus Torvalds 
27121da177e4SLinus Torvalds MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
27131da177e4SLinus Torvalds MODULE_DESCRIPTION("SCSI debug adapter driver");
27141da177e4SLinus Torvalds MODULE_LICENSE("GPL");
27151da177e4SLinus Torvalds MODULE_VERSION(SCSI_DEBUG_VERSION);
27161da177e4SLinus Torvalds 
27171da177e4SLinus Torvalds MODULE_PARM_DESC(add_host, "0..127 hosts allowed(def=1)");
27181da177e4SLinus Torvalds MODULE_PARM_DESC(delay, "# of jiffies to delay response(def=1)");
2719c65b1445SDouglas Gilbert MODULE_PARM_DESC(dev_size_mb, "size in MB of ram shared by devs(def=8)");
2720c65b1445SDouglas Gilbert MODULE_PARM_DESC(dsense, "use descriptor sense format(def=0 -> fixed)");
2721beb87c33SRandy Dunlap MODULE_PARM_DESC(every_nth, "timeout every nth command(def=0)");
272223183910SDouglas Gilbert MODULE_PARM_DESC(fake_rw, "fake reads/writes instead of copying (def=0)");
2723c65b1445SDouglas Gilbert MODULE_PARM_DESC(max_luns, "number of LUNs per target to simulate(def=1)");
272478d4e5a0SDouglas Gilbert MODULE_PARM_DESC(max_queue, "max number of queued commands (1 to 255(def))");
2725c65b1445SDouglas Gilbert MODULE_PARM_DESC(no_lun_0, "no LU number 0 (def=0 -> have lun 0)");
272678d4e5a0SDouglas Gilbert MODULE_PARM_DESC(no_uld, "stop ULD (e.g. sd driver) attaching (def=0))");
27271da177e4SLinus Torvalds MODULE_PARM_DESC(num_parts, "number of partitions(def=0)");
2728c65b1445SDouglas Gilbert MODULE_PARM_DESC(num_tgts, "number of targets per host to simulate(def=1)");
27296f3cbf55SDouglas Gilbert MODULE_PARM_DESC(opts, "1->noise, 2->medium_err, 4->timeout, 8->recovered_err... (def=0)");
27301da177e4SLinus Torvalds MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
27311da177e4SLinus Torvalds MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
2732c65b1445SDouglas Gilbert MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
273323183910SDouglas Gilbert MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
2734ea61fca5SMartin K. Petersen MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)");
2735ea61fca5SMartin K. Petersen MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)");
2736e308b3d1SMartin K. Petersen MODULE_PARM_DESC(opt_blks, "optimal transfer length in block (def=64)");
2737ea61fca5SMartin K. Petersen MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)");
2738c6a44287SMartin K. Petersen MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)");
2739c6a44287SMartin K. Petersen MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)");
2740c6a44287SMartin K. Petersen MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)");
2741c6a44287SMartin K. Petersen MODULE_PARM_DESC(ato, "application tag ownership: 0=disk 1=host (def=1)");
274244d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_max_blocks, "max # of blocks can be unmapped in one cmd (def=0)");
274344d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_max_desc, "max # of ranges that can be unmapped in one cmd (def=0)");
274444d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_granularity, "thin provisioning granularity in blocks (def=0)");
274544d92694SMartin K. Petersen MODULE_PARM_DESC(unmap_alignment, "lowest aligned thin provisioning lba (def=0)");
27461da177e4SLinus Torvalds 
27471da177e4SLinus Torvalds static char sdebug_info[256];
27481da177e4SLinus Torvalds 
27491da177e4SLinus Torvalds static const char * scsi_debug_info(struct Scsi_Host * shp)
27501da177e4SLinus Torvalds {
27511da177e4SLinus Torvalds 	sprintf(sdebug_info, "scsi_debug, version %s [%s], "
27521da177e4SLinus Torvalds 		"dev_size_mb=%d, opts=0x%x", SCSI_DEBUG_VERSION,
27531da177e4SLinus Torvalds 		scsi_debug_version_date, scsi_debug_dev_size_mb,
27541da177e4SLinus Torvalds 		scsi_debug_opts);
27551da177e4SLinus Torvalds 	return sdebug_info;
27561da177e4SLinus Torvalds }
27571da177e4SLinus Torvalds 
27581da177e4SLinus Torvalds /* scsi_debug_proc_info
27591da177e4SLinus Torvalds  * Used if the driver currently has no own support for /proc/scsi
27601da177e4SLinus Torvalds  */
27611da177e4SLinus Torvalds static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset,
27621da177e4SLinus Torvalds 				int length, int inout)
27631da177e4SLinus Torvalds {
27641da177e4SLinus Torvalds 	int len, pos, begin;
27651da177e4SLinus Torvalds 	int orig_length;
27661da177e4SLinus Torvalds 
27671da177e4SLinus Torvalds 	orig_length = length;
27681da177e4SLinus Torvalds 
27691da177e4SLinus Torvalds 	if (inout == 1) {
27701da177e4SLinus Torvalds 		char arr[16];
27711da177e4SLinus Torvalds 		int minLen = length > 15 ? 15 : length;
27721da177e4SLinus Torvalds 
27731da177e4SLinus Torvalds 		if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
27741da177e4SLinus Torvalds 			return -EACCES;
27751da177e4SLinus Torvalds 		memcpy(arr, buffer, minLen);
27761da177e4SLinus Torvalds 		arr[minLen] = '\0';
27771da177e4SLinus Torvalds 		if (1 != sscanf(arr, "%d", &pos))
27781da177e4SLinus Torvalds 			return -EINVAL;
27791da177e4SLinus Torvalds 		scsi_debug_opts = pos;
27801da177e4SLinus Torvalds 		if (scsi_debug_every_nth != 0)
27811da177e4SLinus Torvalds                         scsi_debug_cmnd_count = 0;
27821da177e4SLinus Torvalds 		return length;
27831da177e4SLinus Torvalds 	}
27841da177e4SLinus Torvalds 	begin = 0;
27851da177e4SLinus Torvalds 	pos = len = sprintf(buffer, "scsi_debug adapter driver, version "
27861da177e4SLinus Torvalds 	    "%s [%s]\n"
27871da177e4SLinus Torvalds 	    "num_tgts=%d, shared (ram) size=%d MB, opts=0x%x, "
27881da177e4SLinus Torvalds 	    "every_nth=%d(curr:%d)\n"
27891da177e4SLinus Torvalds 	    "delay=%d, max_luns=%d, scsi_level=%d\n"
27901da177e4SLinus Torvalds 	    "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n"
27911da177e4SLinus Torvalds 	    "number of aborts=%d, device_reset=%d, bus_resets=%d, "
2792c6a44287SMartin K. Petersen 	    "host_resets=%d\ndix_reads=%d dix_writes=%d dif_errors=%d\n",
27931da177e4SLinus Torvalds 	    SCSI_DEBUG_VERSION, scsi_debug_version_date, scsi_debug_num_tgts,
27941da177e4SLinus Torvalds 	    scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
27951da177e4SLinus Torvalds 	    scsi_debug_cmnd_count, scsi_debug_delay,
27961da177e4SLinus Torvalds 	    scsi_debug_max_luns, scsi_debug_scsi_level,
2797597136abSMartin K. Petersen 	    scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads,
2798597136abSMartin K. Petersen 	    sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets,
2799c6a44287SMartin K. Petersen 	    num_host_resets, dix_reads, dix_writes, dif_errors);
28001da177e4SLinus Torvalds 	if (pos < offset) {
28011da177e4SLinus Torvalds 		len = 0;
28021da177e4SLinus Torvalds 		begin = pos;
28031da177e4SLinus Torvalds 	}
28041da177e4SLinus Torvalds 	*start = buffer + (offset - begin);	/* Start of wanted data */
28051da177e4SLinus Torvalds 	len -= (offset - begin);
28061da177e4SLinus Torvalds 	if (len > length)
28071da177e4SLinus Torvalds 		len = length;
28081da177e4SLinus Torvalds 	return len;
28091da177e4SLinus Torvalds }
28101da177e4SLinus Torvalds 
28111da177e4SLinus Torvalds static ssize_t sdebug_delay_show(struct device_driver * ddp, char * buf)
28121da177e4SLinus Torvalds {
28131da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_delay);
28141da177e4SLinus Torvalds }
28151da177e4SLinus Torvalds 
28161da177e4SLinus Torvalds static ssize_t sdebug_delay_store(struct device_driver * ddp,
28171da177e4SLinus Torvalds 				  const char * buf, size_t count)
28181da177e4SLinus Torvalds {
28191da177e4SLinus Torvalds         int delay;
28201da177e4SLinus Torvalds 	char work[20];
28211da177e4SLinus Torvalds 
28221da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
28231da177e4SLinus Torvalds 		if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) {
28241da177e4SLinus Torvalds 			scsi_debug_delay = delay;
28251da177e4SLinus Torvalds 			return count;
28261da177e4SLinus Torvalds 		}
28271da177e4SLinus Torvalds 	}
28281da177e4SLinus Torvalds 	return -EINVAL;
28291da177e4SLinus Torvalds }
28301da177e4SLinus Torvalds DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_show,
28311da177e4SLinus Torvalds 	    sdebug_delay_store);
28321da177e4SLinus Torvalds 
28331da177e4SLinus Torvalds static ssize_t sdebug_opts_show(struct device_driver * ddp, char * buf)
28341da177e4SLinus Torvalds {
28351da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "0x%x\n", scsi_debug_opts);
28361da177e4SLinus Torvalds }
28371da177e4SLinus Torvalds 
28381da177e4SLinus Torvalds static ssize_t sdebug_opts_store(struct device_driver * ddp,
28391da177e4SLinus Torvalds 				 const char * buf, size_t count)
28401da177e4SLinus Torvalds {
28411da177e4SLinus Torvalds         int opts;
28421da177e4SLinus Torvalds 	char work[20];
28431da177e4SLinus Torvalds 
28441da177e4SLinus Torvalds         if (1 == sscanf(buf, "%10s", work)) {
28451da177e4SLinus Torvalds 		if (0 == strnicmp(work,"0x", 2)) {
28461da177e4SLinus Torvalds 			if (1 == sscanf(&work[2], "%x", &opts))
28471da177e4SLinus Torvalds 				goto opts_done;
28481da177e4SLinus Torvalds 		} else {
28491da177e4SLinus Torvalds 			if (1 == sscanf(work, "%d", &opts))
28501da177e4SLinus Torvalds 				goto opts_done;
28511da177e4SLinus Torvalds 		}
28521da177e4SLinus Torvalds 	}
28531da177e4SLinus Torvalds 	return -EINVAL;
28541da177e4SLinus Torvalds opts_done:
28551da177e4SLinus Torvalds 	scsi_debug_opts = opts;
28561da177e4SLinus Torvalds 	scsi_debug_cmnd_count = 0;
28571da177e4SLinus Torvalds 	return count;
28581da177e4SLinus Torvalds }
28591da177e4SLinus Torvalds DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_show,
28601da177e4SLinus Torvalds 	    sdebug_opts_store);
28611da177e4SLinus Torvalds 
28621da177e4SLinus Torvalds static ssize_t sdebug_ptype_show(struct device_driver * ddp, char * buf)
28631da177e4SLinus Torvalds {
28641da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ptype);
28651da177e4SLinus Torvalds }
28661da177e4SLinus Torvalds static ssize_t sdebug_ptype_store(struct device_driver * ddp,
28671da177e4SLinus Torvalds 				  const char * buf, size_t count)
28681da177e4SLinus Torvalds {
28691da177e4SLinus Torvalds         int n;
28701da177e4SLinus Torvalds 
28711da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
28721da177e4SLinus Torvalds 		scsi_debug_ptype = n;
28731da177e4SLinus Torvalds 		return count;
28741da177e4SLinus Torvalds 	}
28751da177e4SLinus Torvalds 	return -EINVAL;
28761da177e4SLinus Torvalds }
28771da177e4SLinus Torvalds DRIVER_ATTR(ptype, S_IRUGO | S_IWUSR, sdebug_ptype_show, sdebug_ptype_store);
28781da177e4SLinus Torvalds 
28791da177e4SLinus Torvalds static ssize_t sdebug_dsense_show(struct device_driver * ddp, char * buf)
28801da177e4SLinus Torvalds {
28811da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dsense);
28821da177e4SLinus Torvalds }
28831da177e4SLinus Torvalds static ssize_t sdebug_dsense_store(struct device_driver * ddp,
28841da177e4SLinus Torvalds 				  const char * buf, size_t count)
28851da177e4SLinus Torvalds {
28861da177e4SLinus Torvalds         int n;
28871da177e4SLinus Torvalds 
28881da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
28891da177e4SLinus Torvalds 		scsi_debug_dsense = n;
28901da177e4SLinus Torvalds 		return count;
28911da177e4SLinus Torvalds 	}
28921da177e4SLinus Torvalds 	return -EINVAL;
28931da177e4SLinus Torvalds }
28941da177e4SLinus Torvalds DRIVER_ATTR(dsense, S_IRUGO | S_IWUSR, sdebug_dsense_show,
28951da177e4SLinus Torvalds 	    sdebug_dsense_store);
28961da177e4SLinus Torvalds 
289723183910SDouglas Gilbert static ssize_t sdebug_fake_rw_show(struct device_driver * ddp, char * buf)
289823183910SDouglas Gilbert {
289923183910SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_fake_rw);
290023183910SDouglas Gilbert }
290123183910SDouglas Gilbert static ssize_t sdebug_fake_rw_store(struct device_driver * ddp,
290223183910SDouglas Gilbert 				    const char * buf, size_t count)
290323183910SDouglas Gilbert {
290423183910SDouglas Gilbert         int n;
290523183910SDouglas Gilbert 
290623183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
290723183910SDouglas Gilbert 		scsi_debug_fake_rw = n;
290823183910SDouglas Gilbert 		return count;
290923183910SDouglas Gilbert 	}
291023183910SDouglas Gilbert 	return -EINVAL;
291123183910SDouglas Gilbert }
291223183910SDouglas Gilbert DRIVER_ATTR(fake_rw, S_IRUGO | S_IWUSR, sdebug_fake_rw_show,
291323183910SDouglas Gilbert 	    sdebug_fake_rw_store);
291423183910SDouglas Gilbert 
2915c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_show(struct device_driver * ddp, char * buf)
2916c65b1445SDouglas Gilbert {
2917c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_lun_0);
2918c65b1445SDouglas Gilbert }
2919c65b1445SDouglas Gilbert static ssize_t sdebug_no_lun_0_store(struct device_driver * ddp,
2920c65b1445SDouglas Gilbert 				     const char * buf, size_t count)
2921c65b1445SDouglas Gilbert {
2922c65b1445SDouglas Gilbert         int n;
2923c65b1445SDouglas Gilbert 
2924c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
2925c65b1445SDouglas Gilbert 		scsi_debug_no_lun_0 = n;
2926c65b1445SDouglas Gilbert 		return count;
2927c65b1445SDouglas Gilbert 	}
2928c65b1445SDouglas Gilbert 	return -EINVAL;
2929c65b1445SDouglas Gilbert }
2930c65b1445SDouglas Gilbert DRIVER_ATTR(no_lun_0, S_IRUGO | S_IWUSR, sdebug_no_lun_0_show,
2931c65b1445SDouglas Gilbert 	    sdebug_no_lun_0_store);
2932c65b1445SDouglas Gilbert 
29331da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_show(struct device_driver * ddp, char * buf)
29341da177e4SLinus Torvalds {
29351da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_tgts);
29361da177e4SLinus Torvalds }
29371da177e4SLinus Torvalds static ssize_t sdebug_num_tgts_store(struct device_driver * ddp,
29381da177e4SLinus Torvalds 				     const char * buf, size_t count)
29391da177e4SLinus Torvalds {
29401da177e4SLinus Torvalds         int n;
29411da177e4SLinus Torvalds 
29421da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
29431da177e4SLinus Torvalds 		scsi_debug_num_tgts = n;
29441da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
29451da177e4SLinus Torvalds 		return count;
29461da177e4SLinus Torvalds 	}
29471da177e4SLinus Torvalds 	return -EINVAL;
29481da177e4SLinus Torvalds }
29491da177e4SLinus Torvalds DRIVER_ATTR(num_tgts, S_IRUGO | S_IWUSR, sdebug_num_tgts_show,
29501da177e4SLinus Torvalds 	    sdebug_num_tgts_store);
29511da177e4SLinus Torvalds 
29521da177e4SLinus Torvalds static ssize_t sdebug_dev_size_mb_show(struct device_driver * ddp, char * buf)
29531da177e4SLinus Torvalds {
29541da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dev_size_mb);
29551da177e4SLinus Torvalds }
29561da177e4SLinus Torvalds DRIVER_ATTR(dev_size_mb, S_IRUGO, sdebug_dev_size_mb_show, NULL);
29571da177e4SLinus Torvalds 
29581da177e4SLinus Torvalds static ssize_t sdebug_num_parts_show(struct device_driver * ddp, char * buf)
29591da177e4SLinus Torvalds {
29601da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_num_parts);
29611da177e4SLinus Torvalds }
29621da177e4SLinus Torvalds DRIVER_ATTR(num_parts, S_IRUGO, sdebug_num_parts_show, NULL);
29631da177e4SLinus Torvalds 
29641da177e4SLinus Torvalds static ssize_t sdebug_every_nth_show(struct device_driver * ddp, char * buf)
29651da177e4SLinus Torvalds {
29661da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_every_nth);
29671da177e4SLinus Torvalds }
29681da177e4SLinus Torvalds static ssize_t sdebug_every_nth_store(struct device_driver * ddp,
29691da177e4SLinus Torvalds 				      const char * buf, size_t count)
29701da177e4SLinus Torvalds {
29711da177e4SLinus Torvalds         int nth;
29721da177e4SLinus Torvalds 
29731da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &nth))) {
29741da177e4SLinus Torvalds 		scsi_debug_every_nth = nth;
29751da177e4SLinus Torvalds 		scsi_debug_cmnd_count = 0;
29761da177e4SLinus Torvalds 		return count;
29771da177e4SLinus Torvalds 	}
29781da177e4SLinus Torvalds 	return -EINVAL;
29791da177e4SLinus Torvalds }
29801da177e4SLinus Torvalds DRIVER_ATTR(every_nth, S_IRUGO | S_IWUSR, sdebug_every_nth_show,
29811da177e4SLinus Torvalds 	    sdebug_every_nth_store);
29821da177e4SLinus Torvalds 
29831da177e4SLinus Torvalds static ssize_t sdebug_max_luns_show(struct device_driver * ddp, char * buf)
29841da177e4SLinus Torvalds {
29851da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_luns);
29861da177e4SLinus Torvalds }
29871da177e4SLinus Torvalds static ssize_t sdebug_max_luns_store(struct device_driver * ddp,
29881da177e4SLinus Torvalds 				     const char * buf, size_t count)
29891da177e4SLinus Torvalds {
29901da177e4SLinus Torvalds         int n;
29911da177e4SLinus Torvalds 
29921da177e4SLinus Torvalds 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
29931da177e4SLinus Torvalds 		scsi_debug_max_luns = n;
29941da177e4SLinus Torvalds 		sdebug_max_tgts_luns();
29951da177e4SLinus Torvalds 		return count;
29961da177e4SLinus Torvalds 	}
29971da177e4SLinus Torvalds 	return -EINVAL;
29981da177e4SLinus Torvalds }
29991da177e4SLinus Torvalds DRIVER_ATTR(max_luns, S_IRUGO | S_IWUSR, sdebug_max_luns_show,
30001da177e4SLinus Torvalds 	    sdebug_max_luns_store);
30011da177e4SLinus Torvalds 
300278d4e5a0SDouglas Gilbert static ssize_t sdebug_max_queue_show(struct device_driver * ddp, char * buf)
300378d4e5a0SDouglas Gilbert {
300478d4e5a0SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_max_queue);
300578d4e5a0SDouglas Gilbert }
300678d4e5a0SDouglas Gilbert static ssize_t sdebug_max_queue_store(struct device_driver * ddp,
300778d4e5a0SDouglas Gilbert 				      const char * buf, size_t count)
300878d4e5a0SDouglas Gilbert {
300978d4e5a0SDouglas Gilbert         int n;
301078d4e5a0SDouglas Gilbert 
301178d4e5a0SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n > 0) &&
301278d4e5a0SDouglas Gilbert 	    (n <= SCSI_DEBUG_CANQUEUE)) {
301378d4e5a0SDouglas Gilbert 		scsi_debug_max_queue = n;
301478d4e5a0SDouglas Gilbert 		return count;
301578d4e5a0SDouglas Gilbert 	}
301678d4e5a0SDouglas Gilbert 	return -EINVAL;
301778d4e5a0SDouglas Gilbert }
301878d4e5a0SDouglas Gilbert DRIVER_ATTR(max_queue, S_IRUGO | S_IWUSR, sdebug_max_queue_show,
301978d4e5a0SDouglas Gilbert 	    sdebug_max_queue_store);
302078d4e5a0SDouglas Gilbert 
302178d4e5a0SDouglas Gilbert static ssize_t sdebug_no_uld_show(struct device_driver * ddp, char * buf)
302278d4e5a0SDouglas Gilbert {
302378d4e5a0SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_no_uld);
302478d4e5a0SDouglas Gilbert }
302578d4e5a0SDouglas Gilbert DRIVER_ATTR(no_uld, S_IRUGO, sdebug_no_uld_show, NULL);
302678d4e5a0SDouglas Gilbert 
30271da177e4SLinus Torvalds static ssize_t sdebug_scsi_level_show(struct device_driver * ddp, char * buf)
30281da177e4SLinus Torvalds {
30291da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_scsi_level);
30301da177e4SLinus Torvalds }
30311da177e4SLinus Torvalds DRIVER_ATTR(scsi_level, S_IRUGO, sdebug_scsi_level_show, NULL);
30321da177e4SLinus Torvalds 
3033c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_show(struct device_driver * ddp, char * buf)
3034c65b1445SDouglas Gilbert {
3035c65b1445SDouglas Gilbert         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_virtual_gb);
3036c65b1445SDouglas Gilbert }
3037c65b1445SDouglas Gilbert static ssize_t sdebug_virtual_gb_store(struct device_driver * ddp,
3038c65b1445SDouglas Gilbert 				       const char * buf, size_t count)
3039c65b1445SDouglas Gilbert {
3040c65b1445SDouglas Gilbert         int n;
3041c65b1445SDouglas Gilbert 
3042c65b1445SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
3043c65b1445SDouglas Gilbert 		scsi_debug_virtual_gb = n;
304428898873SFUJITA Tomonori 
304528898873SFUJITA Tomonori 		sdebug_capacity = get_sdebug_capacity();
304628898873SFUJITA Tomonori 
3047c65b1445SDouglas Gilbert 		return count;
3048c65b1445SDouglas Gilbert 	}
3049c65b1445SDouglas Gilbert 	return -EINVAL;
3050c65b1445SDouglas Gilbert }
3051c65b1445SDouglas Gilbert DRIVER_ATTR(virtual_gb, S_IRUGO | S_IWUSR, sdebug_virtual_gb_show,
3052c65b1445SDouglas Gilbert 	    sdebug_virtual_gb_store);
3053c65b1445SDouglas Gilbert 
30541da177e4SLinus Torvalds static ssize_t sdebug_add_host_show(struct device_driver * ddp, char * buf)
30551da177e4SLinus Torvalds {
30561da177e4SLinus Torvalds         return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_add_host);
30571da177e4SLinus Torvalds }
30581da177e4SLinus Torvalds 
30591da177e4SLinus Torvalds static ssize_t sdebug_add_host_store(struct device_driver * ddp,
30601da177e4SLinus Torvalds 				     const char * buf, size_t count)
30611da177e4SLinus Torvalds {
30621da177e4SLinus Torvalds 	int delta_hosts;
30631da177e4SLinus Torvalds 
3064f3df41cfSFUJITA Tomonori 	if (sscanf(buf, "%d", &delta_hosts) != 1)
30651da177e4SLinus Torvalds 		return -EINVAL;
30661da177e4SLinus Torvalds 	if (delta_hosts > 0) {
30671da177e4SLinus Torvalds 		do {
30681da177e4SLinus Torvalds 			sdebug_add_adapter();
30691da177e4SLinus Torvalds 		} while (--delta_hosts);
30701da177e4SLinus Torvalds 	} else if (delta_hosts < 0) {
30711da177e4SLinus Torvalds 		do {
30721da177e4SLinus Torvalds 			sdebug_remove_adapter();
30731da177e4SLinus Torvalds 		} while (++delta_hosts);
30741da177e4SLinus Torvalds 	}
30751da177e4SLinus Torvalds 	return count;
30761da177e4SLinus Torvalds }
30771da177e4SLinus Torvalds DRIVER_ATTR(add_host, S_IRUGO | S_IWUSR, sdebug_add_host_show,
30781da177e4SLinus Torvalds 	    sdebug_add_host_store);
30791da177e4SLinus Torvalds 
308023183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_show(struct device_driver * ddp,
308123183910SDouglas Gilbert 					  char * buf)
308223183910SDouglas Gilbert {
308323183910SDouglas Gilbert 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_vpd_use_hostno);
308423183910SDouglas Gilbert }
308523183910SDouglas Gilbert static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
308623183910SDouglas Gilbert 					   const char * buf, size_t count)
308723183910SDouglas Gilbert {
308823183910SDouglas Gilbert 	int n;
308923183910SDouglas Gilbert 
309023183910SDouglas Gilbert 	if ((count > 0) && (1 == sscanf(buf, "%d", &n)) && (n >= 0)) {
309123183910SDouglas Gilbert 		scsi_debug_vpd_use_hostno = n;
309223183910SDouglas Gilbert 		return count;
309323183910SDouglas Gilbert 	}
309423183910SDouglas Gilbert 	return -EINVAL;
309523183910SDouglas Gilbert }
309623183910SDouglas Gilbert DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
309723183910SDouglas Gilbert 	    sdebug_vpd_use_hostno_store);
309823183910SDouglas Gilbert 
3099597136abSMartin K. Petersen static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf)
3100597136abSMartin K. Petersen {
3101597136abSMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size);
3102597136abSMartin K. Petersen }
3103597136abSMartin K. Petersen DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL);
3104597136abSMartin K. Petersen 
3105c6a44287SMartin K. Petersen static ssize_t sdebug_dix_show(struct device_driver *ddp, char *buf)
3106c6a44287SMartin K. Petersen {
3107c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dix);
3108c6a44287SMartin K. Petersen }
3109c6a44287SMartin K. Petersen DRIVER_ATTR(dix, S_IRUGO, sdebug_dix_show, NULL);
3110c6a44287SMartin K. Petersen 
3111c6a44287SMartin K. Petersen static ssize_t sdebug_dif_show(struct device_driver *ddp, char *buf)
3112c6a44287SMartin K. Petersen {
3113c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_dif);
3114c6a44287SMartin K. Petersen }
3115c6a44287SMartin K. Petersen DRIVER_ATTR(dif, S_IRUGO, sdebug_dif_show, NULL);
3116c6a44287SMartin K. Petersen 
3117c6a44287SMartin K. Petersen static ssize_t sdebug_guard_show(struct device_driver *ddp, char *buf)
3118c6a44287SMartin K. Petersen {
3119c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_guard);
3120c6a44287SMartin K. Petersen }
3121c6a44287SMartin K. Petersen DRIVER_ATTR(guard, S_IRUGO, sdebug_guard_show, NULL);
3122c6a44287SMartin K. Petersen 
3123c6a44287SMartin K. Petersen static ssize_t sdebug_ato_show(struct device_driver *ddp, char *buf)
3124c6a44287SMartin K. Petersen {
3125c6a44287SMartin K. Petersen 	return scnprintf(buf, PAGE_SIZE, "%d\n", scsi_debug_ato);
3126c6a44287SMartin K. Petersen }
3127c6a44287SMartin K. Petersen DRIVER_ATTR(ato, S_IRUGO, sdebug_ato_show, NULL);
3128c6a44287SMartin K. Petersen 
312944d92694SMartin K. Petersen static ssize_t sdebug_map_show(struct device_driver *ddp, char *buf)
313044d92694SMartin K. Petersen {
313144d92694SMartin K. Petersen 	ssize_t count;
313244d92694SMartin K. Petersen 
313344d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity == 0)
313444d92694SMartin K. Petersen 		return scnprintf(buf, PAGE_SIZE, "0-%u\n",
313544d92694SMartin K. Petersen 				 sdebug_store_sectors);
313644d92694SMartin K. Petersen 
313744d92694SMartin K. Petersen 	count = bitmap_scnlistprintf(buf, PAGE_SIZE, map_storep, map_size);
313844d92694SMartin K. Petersen 
313944d92694SMartin K. Petersen 	buf[count++] = '\n';
314044d92694SMartin K. Petersen 	buf[count++] = 0;
314144d92694SMartin K. Petersen 
314244d92694SMartin K. Petersen 	return count;
314344d92694SMartin K. Petersen }
314444d92694SMartin K. Petersen DRIVER_ATTR(map, S_IRUGO, sdebug_map_show, NULL);
314544d92694SMartin K. Petersen 
3146c6a44287SMartin K. Petersen 
314723183910SDouglas Gilbert /* Note: The following function creates attribute files in the
314823183910SDouglas Gilbert    /sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
314923183910SDouglas Gilbert    files (over those found in the /sys/module/scsi_debug/parameters
315023183910SDouglas Gilbert    directory) is that auxiliary actions can be triggered when an attribute
315123183910SDouglas Gilbert    is changed. For example see: sdebug_add_host_store() above.
315223183910SDouglas Gilbert  */
31536ecaff7fSRandy Dunlap static int do_create_driverfs_files(void)
31541da177e4SLinus Torvalds {
31556ecaff7fSRandy Dunlap 	int ret;
31566ecaff7fSRandy Dunlap 
31576ecaff7fSRandy Dunlap 	ret = driver_create_file(&sdebug_driverfs_driver, &driver_attr_add_host);
31586ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay);
31596ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
31606ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dsense);
31616ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
316223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
31636ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
316478d4e5a0SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_max_queue);
316523183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
316678d4e5a0SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_no_uld);
31676ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
316823183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
31696ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ptype);
31706ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts);
31716ecaff7fSRandy Dunlap 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
317223183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
317323183910SDouglas Gilbert 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
3174597136abSMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
3175c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dix);
3176c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_dif);
3177c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_guard);
3178c6a44287SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_ato);
317944d92694SMartin K. Petersen 	ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_map);
31806ecaff7fSRandy Dunlap 	return ret;
31811da177e4SLinus Torvalds }
31821da177e4SLinus Torvalds 
31831da177e4SLinus Torvalds static void do_remove_driverfs_files(void)
31841da177e4SLinus Torvalds {
318544d92694SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_map);
3186c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ato);
3187c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_guard);
3188c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dif);
3189c6a44287SMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dix);
3190597136abSMartin K. Petersen 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
319123183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
319223183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
31931da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
31941da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts);
31951da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_ptype);
31961da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_tgts);
319723183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_parts);
319878d4e5a0SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_uld);
319923183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_no_lun_0);
320078d4e5a0SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_queue);
32011da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_max_luns);
320223183910SDouglas Gilbert 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_fake_rw);
32031da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_every_nth);
32041da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dsense);
32051da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_dev_size_mb);
32061da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay);
32071da177e4SLinus Torvalds 	driver_remove_file(&sdebug_driverfs_driver, &driver_attr_add_host);
32081da177e4SLinus Torvalds }
32091da177e4SLinus Torvalds 
32108dea0d02SFUJITA Tomonori static void pseudo_0_release(struct device *dev)
32118dea0d02SFUJITA Tomonori {
32128dea0d02SFUJITA Tomonori 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
32138dea0d02SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: pseudo_0_release() called\n");
32148dea0d02SFUJITA Tomonori }
32158dea0d02SFUJITA Tomonori 
32168dea0d02SFUJITA Tomonori static struct device pseudo_primary = {
321771610f55SKay Sievers 	.init_name	= "pseudo_0",
32188dea0d02SFUJITA Tomonori 	.release	= pseudo_0_release,
32198dea0d02SFUJITA Tomonori };
32208dea0d02SFUJITA Tomonori 
32211da177e4SLinus Torvalds static int __init scsi_debug_init(void)
32221da177e4SLinus Torvalds {
32235f2578e5SFUJITA Tomonori 	unsigned long sz;
32241da177e4SLinus Torvalds 	int host_to_add;
32251da177e4SLinus Torvalds 	int k;
32266ecaff7fSRandy Dunlap 	int ret;
32271da177e4SLinus Torvalds 
3228597136abSMartin K. Petersen 	switch (scsi_debug_sector_size) {
3229597136abSMartin K. Petersen 	case  512:
3230597136abSMartin K. Petersen 	case 1024:
3231597136abSMartin K. Petersen 	case 2048:
3232597136abSMartin K. Petersen 	case 4096:
3233597136abSMartin K. Petersen 		break;
3234597136abSMartin K. Petersen 	default:
3235c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: invalid sector_size %d\n",
3236597136abSMartin K. Petersen 		       scsi_debug_sector_size);
3237597136abSMartin K. Petersen 		return -EINVAL;
3238597136abSMartin K. Petersen 	}
3239597136abSMartin K. Petersen 
3240c6a44287SMartin K. Petersen 	switch (scsi_debug_dif) {
3241c6a44287SMartin K. Petersen 
3242c6a44287SMartin K. Petersen 	case SD_DIF_TYPE0_PROTECTION:
3243c6a44287SMartin K. Petersen 	case SD_DIF_TYPE1_PROTECTION:
3244395cef03SMartin K. Petersen 	case SD_DIF_TYPE2_PROTECTION:
3245c6a44287SMartin K. Petersen 	case SD_DIF_TYPE3_PROTECTION:
3246c6a44287SMartin K. Petersen 		break;
3247c6a44287SMartin K. Petersen 
3248c6a44287SMartin K. Petersen 	default:
3249395cef03SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: dif must be 0, 1, 2 or 3\n");
3250c6a44287SMartin K. Petersen 		return -EINVAL;
3251c6a44287SMartin K. Petersen 	}
3252c6a44287SMartin K. Petersen 
3253c6a44287SMartin K. Petersen 	if (scsi_debug_guard > 1) {
3254c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: guard must be 0 or 1\n");
3255c6a44287SMartin K. Petersen 		return -EINVAL;
3256c6a44287SMartin K. Petersen 	}
3257c6a44287SMartin K. Petersen 
3258c6a44287SMartin K. Petersen 	if (scsi_debug_ato > 1) {
3259c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: ato must be 0 or 1\n");
3260c6a44287SMartin K. Petersen 		return -EINVAL;
3261c6a44287SMartin K. Petersen 	}
3262c6a44287SMartin K. Petersen 
3263ea61fca5SMartin K. Petersen 	if (scsi_debug_physblk_exp > 15) {
3264ea61fca5SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: invalid physblk_exp %u\n",
3265ea61fca5SMartin K. Petersen 		       scsi_debug_physblk_exp);
3266ea61fca5SMartin K. Petersen 		return -EINVAL;
3267ea61fca5SMartin K. Petersen 	}
3268ea61fca5SMartin K. Petersen 
3269ea61fca5SMartin K. Petersen 	if (scsi_debug_lowest_aligned > 0x3fff) {
3270ea61fca5SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: lowest_aligned too big: %u\n",
3271ea61fca5SMartin K. Petersen 		       scsi_debug_lowest_aligned);
3272ea61fca5SMartin K. Petersen 		return -EINVAL;
3273ea61fca5SMartin K. Petersen 	}
3274ea61fca5SMartin K. Petersen 
32751da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb < 1)
32761da177e4SLinus Torvalds 		scsi_debug_dev_size_mb = 1;  /* force minimum 1 MB ramdisk */
32775f2578e5SFUJITA Tomonori 	sz = (unsigned long)scsi_debug_dev_size_mb * 1048576;
3278597136abSMartin K. Petersen 	sdebug_store_sectors = sz / scsi_debug_sector_size;
327928898873SFUJITA Tomonori 	sdebug_capacity = get_sdebug_capacity();
32801da177e4SLinus Torvalds 
32811da177e4SLinus Torvalds 	/* play around with geometry, don't waste too much on track 0 */
32821da177e4SLinus Torvalds 	sdebug_heads = 8;
32831da177e4SLinus Torvalds 	sdebug_sectors_per = 32;
32841da177e4SLinus Torvalds 	if (scsi_debug_dev_size_mb >= 16)
32851da177e4SLinus Torvalds 		sdebug_heads = 32;
32861da177e4SLinus Torvalds 	else if (scsi_debug_dev_size_mb >= 256)
32871da177e4SLinus Torvalds 		sdebug_heads = 64;
32881da177e4SLinus Torvalds 	sdebug_cylinders_per = (unsigned long)sdebug_capacity /
32891da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
32901da177e4SLinus Torvalds 	if (sdebug_cylinders_per >= 1024) {
32911da177e4SLinus Torvalds 		/* other LLDs do this; implies >= 1GB ram disk ... */
32921da177e4SLinus Torvalds 		sdebug_heads = 255;
32931da177e4SLinus Torvalds 		sdebug_sectors_per = 63;
32941da177e4SLinus Torvalds 		sdebug_cylinders_per = (unsigned long)sdebug_capacity /
32951da177e4SLinus Torvalds 			       (sdebug_sectors_per * sdebug_heads);
32961da177e4SLinus Torvalds 	}
32971da177e4SLinus Torvalds 
32981da177e4SLinus Torvalds 	fake_storep = vmalloc(sz);
32991da177e4SLinus Torvalds 	if (NULL == fake_storep) {
33001da177e4SLinus Torvalds 		printk(KERN_ERR "scsi_debug_init: out of memory, 1\n");
33011da177e4SLinus Torvalds 		return -ENOMEM;
33021da177e4SLinus Torvalds 	}
33031da177e4SLinus Torvalds 	memset(fake_storep, 0, sz);
33041da177e4SLinus Torvalds 	if (scsi_debug_num_parts > 0)
3305f58b0efbSFUJITA Tomonori 		sdebug_build_parts(fake_storep, sz);
33061da177e4SLinus Torvalds 
3307c6a44287SMartin K. Petersen 	if (scsi_debug_dif) {
3308c6a44287SMartin K. Petersen 		int dif_size;
3309c6a44287SMartin K. Petersen 
3310c6a44287SMartin K. Petersen 		dif_size = sdebug_store_sectors * sizeof(struct sd_dif_tuple);
3311c6a44287SMartin K. Petersen 		dif_storep = vmalloc(dif_size);
3312c6a44287SMartin K. Petersen 
3313c6a44287SMartin K. Petersen 		printk(KERN_ERR "scsi_debug_init: dif_storep %u bytes @ %p\n",
3314c6a44287SMartin K. Petersen 		       dif_size, dif_storep);
3315c6a44287SMartin K. Petersen 
3316c6a44287SMartin K. Petersen 		if (dif_storep == NULL) {
3317c6a44287SMartin K. Petersen 			printk(KERN_ERR "scsi_debug_init: out of mem. (DIX)\n");
3318c6a44287SMartin K. Petersen 			ret = -ENOMEM;
3319c6a44287SMartin K. Petersen 			goto free_vm;
3320c6a44287SMartin K. Petersen 		}
3321c6a44287SMartin K. Petersen 
3322c6a44287SMartin K. Petersen 		memset(dif_storep, 0xff, dif_size);
3323c6a44287SMartin K. Petersen 	}
3324c6a44287SMartin K. Petersen 
332544d92694SMartin K. Petersen 	if (scsi_debug_unmap_granularity) {
332644d92694SMartin K. Petersen 		unsigned int map_bytes;
332744d92694SMartin K. Petersen 
332844d92694SMartin K. Petersen 		if (scsi_debug_unmap_granularity < scsi_debug_unmap_alignment) {
332944d92694SMartin K. Petersen 			printk(KERN_ERR
333044d92694SMartin K. Petersen 			       "%s: ERR: unmap_granularity < unmap_alignment\n",
333144d92694SMartin K. Petersen 			       __func__);
333244d92694SMartin K. Petersen 			return -EINVAL;
333344d92694SMartin K. Petersen 		}
333444d92694SMartin K. Petersen 
333544d92694SMartin K. Petersen 		map_size = (sdebug_store_sectors / scsi_debug_unmap_granularity);
333644d92694SMartin K. Petersen 		map_bytes = map_size >> 3;
333744d92694SMartin K. Petersen 		map_storep = vmalloc(map_bytes);
333844d92694SMartin K. Petersen 
333944d92694SMartin K. Petersen 		printk(KERN_INFO "scsi_debug_init: %lu provisioning blocks\n",
334044d92694SMartin K. Petersen 		       map_size);
334144d92694SMartin K. Petersen 
334244d92694SMartin K. Petersen 		if (map_storep == NULL) {
334344d92694SMartin K. Petersen 			printk(KERN_ERR "scsi_debug_init: out of mem. (MAP)\n");
334444d92694SMartin K. Petersen 			ret = -ENOMEM;
334544d92694SMartin K. Petersen 			goto free_vm;
334644d92694SMartin K. Petersen 		}
334744d92694SMartin K. Petersen 
334844d92694SMartin K. Petersen 		memset(map_storep, 0x0, map_bytes);
334944d92694SMartin K. Petersen 
335044d92694SMartin K. Petersen 		/* Map first 1KB for partition table */
335144d92694SMartin K. Petersen 		if (scsi_debug_num_parts)
335244d92694SMartin K. Petersen 			map_region(0, 2);
335344d92694SMartin K. Petersen 	}
335444d92694SMartin K. Petersen 
33556ecaff7fSRandy Dunlap 	ret = device_register(&pseudo_primary);
33566ecaff7fSRandy Dunlap 	if (ret < 0) {
33576ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: device_register error: %d\n",
33586ecaff7fSRandy Dunlap 			ret);
33596ecaff7fSRandy Dunlap 		goto free_vm;
33606ecaff7fSRandy Dunlap 	}
33616ecaff7fSRandy Dunlap 	ret = bus_register(&pseudo_lld_bus);
33626ecaff7fSRandy Dunlap 	if (ret < 0) {
33636ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: bus_register error: %d\n",
33646ecaff7fSRandy Dunlap 			ret);
33656ecaff7fSRandy Dunlap 		goto dev_unreg;
33666ecaff7fSRandy Dunlap 	}
33676ecaff7fSRandy Dunlap 	ret = driver_register(&sdebug_driverfs_driver);
33686ecaff7fSRandy Dunlap 	if (ret < 0) {
33696ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_register error: %d\n",
33706ecaff7fSRandy Dunlap 			ret);
33716ecaff7fSRandy Dunlap 		goto bus_unreg;
33726ecaff7fSRandy Dunlap 	}
33736ecaff7fSRandy Dunlap 	ret = do_create_driverfs_files();
33746ecaff7fSRandy Dunlap 	if (ret < 0) {
33756ecaff7fSRandy Dunlap 		printk(KERN_WARNING "scsi_debug: driver_create_file error: %d\n",
33766ecaff7fSRandy Dunlap 			ret);
33776ecaff7fSRandy Dunlap 		goto del_files;
33786ecaff7fSRandy Dunlap 	}
33791da177e4SLinus Torvalds 
33806ecaff7fSRandy Dunlap 	init_all_queued();
33811da177e4SLinus Torvalds 
33821da177e4SLinus Torvalds 	host_to_add = scsi_debug_add_host;
33831da177e4SLinus Torvalds         scsi_debug_add_host = 0;
33841da177e4SLinus Torvalds 
33851da177e4SLinus Torvalds         for (k = 0; k < host_to_add; k++) {
33861da177e4SLinus Torvalds                 if (sdebug_add_adapter()) {
33871da177e4SLinus Torvalds                         printk(KERN_ERR "scsi_debug_init: "
33881da177e4SLinus Torvalds                                "sdebug_add_adapter failed k=%d\n", k);
33891da177e4SLinus Torvalds                         break;
33901da177e4SLinus Torvalds                 }
33911da177e4SLinus Torvalds         }
33921da177e4SLinus Torvalds 
33931da177e4SLinus Torvalds 	if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) {
33941da177e4SLinus Torvalds 		printk(KERN_INFO "scsi_debug_init: built %d host(s)\n",
33951da177e4SLinus Torvalds 		       scsi_debug_add_host);
33961da177e4SLinus Torvalds 	}
33971da177e4SLinus Torvalds 	return 0;
33986ecaff7fSRandy Dunlap 
33996ecaff7fSRandy Dunlap del_files:
34006ecaff7fSRandy Dunlap 	do_remove_driverfs_files();
34016ecaff7fSRandy Dunlap 	driver_unregister(&sdebug_driverfs_driver);
34026ecaff7fSRandy Dunlap bus_unreg:
34036ecaff7fSRandy Dunlap 	bus_unregister(&pseudo_lld_bus);
34046ecaff7fSRandy Dunlap dev_unreg:
34056ecaff7fSRandy Dunlap 	device_unregister(&pseudo_primary);
34066ecaff7fSRandy Dunlap free_vm:
340744d92694SMartin K. Petersen 	if (map_storep)
340844d92694SMartin K. Petersen 		vfree(map_storep);
3409c6a44287SMartin K. Petersen 	if (dif_storep)
3410c6a44287SMartin K. Petersen 		vfree(dif_storep);
34116ecaff7fSRandy Dunlap 	vfree(fake_storep);
34126ecaff7fSRandy Dunlap 
34136ecaff7fSRandy Dunlap 	return ret;
34141da177e4SLinus Torvalds }
34151da177e4SLinus Torvalds 
34161da177e4SLinus Torvalds static void __exit scsi_debug_exit(void)
34171da177e4SLinus Torvalds {
34181da177e4SLinus Torvalds 	int k = scsi_debug_add_host;
34191da177e4SLinus Torvalds 
34201da177e4SLinus Torvalds 	stop_all_queued();
34211da177e4SLinus Torvalds 	for (; k; k--)
34221da177e4SLinus Torvalds 		sdebug_remove_adapter();
34231da177e4SLinus Torvalds 	do_remove_driverfs_files();
34241da177e4SLinus Torvalds 	driver_unregister(&sdebug_driverfs_driver);
34251da177e4SLinus Torvalds 	bus_unregister(&pseudo_lld_bus);
34261da177e4SLinus Torvalds 	device_unregister(&pseudo_primary);
34271da177e4SLinus Torvalds 
3428c6a44287SMartin K. Petersen 	if (dif_storep)
3429c6a44287SMartin K. Petersen 		vfree(dif_storep);
3430c6a44287SMartin K. Petersen 
34311da177e4SLinus Torvalds 	vfree(fake_storep);
34321da177e4SLinus Torvalds }
34331da177e4SLinus Torvalds 
34341da177e4SLinus Torvalds device_initcall(scsi_debug_init);
34351da177e4SLinus Torvalds module_exit(scsi_debug_exit);
34361da177e4SLinus Torvalds 
34371da177e4SLinus Torvalds static void sdebug_release_adapter(struct device * dev)
34381da177e4SLinus Torvalds {
34391da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
34401da177e4SLinus Torvalds 
34411da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
34421da177e4SLinus Torvalds         kfree(sdbg_host);
34431da177e4SLinus Torvalds }
34441da177e4SLinus Torvalds 
34451da177e4SLinus Torvalds static int sdebug_add_adapter(void)
34461da177e4SLinus Torvalds {
34471da177e4SLinus Torvalds 	int k, devs_per_host;
34481da177e4SLinus Torvalds         int error = 0;
34491da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
34508b40228fSFUJITA Tomonori 	struct sdebug_dev_info *sdbg_devinfo, *tmp;
34511da177e4SLinus Torvalds 
345224669f75SJes Sorensen         sdbg_host = kzalloc(sizeof(*sdbg_host),GFP_KERNEL);
34531da177e4SLinus Torvalds         if (NULL == sdbg_host) {
34541da177e4SLinus Torvalds                 printk(KERN_ERR "%s: out of memory at line %d\n",
3455cadbd4a5SHarvey Harrison                        __func__, __LINE__);
34561da177e4SLinus Torvalds                 return -ENOMEM;
34571da177e4SLinus Torvalds         }
34581da177e4SLinus Torvalds 
34591da177e4SLinus Torvalds         INIT_LIST_HEAD(&sdbg_host->dev_info_list);
34601da177e4SLinus Torvalds 
34611da177e4SLinus Torvalds 	devs_per_host = scsi_debug_num_tgts * scsi_debug_max_luns;
34621da177e4SLinus Torvalds         for (k = 0; k < devs_per_host; k++) {
34635cb2fc06SFUJITA Tomonori 		sdbg_devinfo = sdebug_device_create(sdbg_host, GFP_KERNEL);
34645cb2fc06SFUJITA Tomonori 		if (!sdbg_devinfo) {
34651da177e4SLinus Torvalds                         printk(KERN_ERR "%s: out of memory at line %d\n",
3466cadbd4a5SHarvey Harrison                                __func__, __LINE__);
34671da177e4SLinus Torvalds                         error = -ENOMEM;
34681da177e4SLinus Torvalds 			goto clean;
34691da177e4SLinus Torvalds                 }
34701da177e4SLinus Torvalds         }
34711da177e4SLinus Torvalds 
34721da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
34731da177e4SLinus Torvalds         list_add_tail(&sdbg_host->host_list, &sdebug_host_list);
34741da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
34751da177e4SLinus Torvalds 
34761da177e4SLinus Torvalds         sdbg_host->dev.bus = &pseudo_lld_bus;
34771da177e4SLinus Torvalds         sdbg_host->dev.parent = &pseudo_primary;
34781da177e4SLinus Torvalds         sdbg_host->dev.release = &sdebug_release_adapter;
347971610f55SKay Sievers         dev_set_name(&sdbg_host->dev, "adapter%d", scsi_debug_add_host);
34801da177e4SLinus Torvalds 
34811da177e4SLinus Torvalds         error = device_register(&sdbg_host->dev);
34821da177e4SLinus Torvalds 
34831da177e4SLinus Torvalds         if (error)
34841da177e4SLinus Torvalds 		goto clean;
34851da177e4SLinus Torvalds 
34861da177e4SLinus Torvalds 	++scsi_debug_add_host;
34871da177e4SLinus Torvalds         return error;
34881da177e4SLinus Torvalds 
34891da177e4SLinus Torvalds clean:
34908b40228fSFUJITA Tomonori 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
34918b40228fSFUJITA Tomonori 				 dev_list) {
34921da177e4SLinus Torvalds 		list_del(&sdbg_devinfo->dev_list);
34931da177e4SLinus Torvalds 		kfree(sdbg_devinfo);
34941da177e4SLinus Torvalds 	}
34951da177e4SLinus Torvalds 
34961da177e4SLinus Torvalds 	kfree(sdbg_host);
34971da177e4SLinus Torvalds         return error;
34981da177e4SLinus Torvalds }
34991da177e4SLinus Torvalds 
35001da177e4SLinus Torvalds static void sdebug_remove_adapter(void)
35011da177e4SLinus Torvalds {
35021da177e4SLinus Torvalds         struct sdebug_host_info * sdbg_host = NULL;
35031da177e4SLinus Torvalds 
35041da177e4SLinus Torvalds         spin_lock(&sdebug_host_list_lock);
35051da177e4SLinus Torvalds         if (!list_empty(&sdebug_host_list)) {
35061da177e4SLinus Torvalds                 sdbg_host = list_entry(sdebug_host_list.prev,
35071da177e4SLinus Torvalds                                        struct sdebug_host_info, host_list);
35081da177e4SLinus Torvalds 		list_del(&sdbg_host->host_list);
35091da177e4SLinus Torvalds 	}
35101da177e4SLinus Torvalds         spin_unlock(&sdebug_host_list_lock);
35111da177e4SLinus Torvalds 
35121da177e4SLinus Torvalds 	if (!sdbg_host)
35131da177e4SLinus Torvalds 		return;
35141da177e4SLinus Torvalds 
35151da177e4SLinus Torvalds         device_unregister(&sdbg_host->dev);
35161da177e4SLinus Torvalds         --scsi_debug_add_host;
35171da177e4SLinus Torvalds }
35181da177e4SLinus Torvalds 
3519639db475SFUJITA Tomonori static
3520639db475SFUJITA Tomonori int scsi_debug_queuecommand(struct scsi_cmnd *SCpnt, done_funct_t done)
3521639db475SFUJITA Tomonori {
3522639db475SFUJITA Tomonori 	unsigned char *cmd = (unsigned char *) SCpnt->cmnd;
3523639db475SFUJITA Tomonori 	int len, k;
3524639db475SFUJITA Tomonori 	unsigned int num;
3525639db475SFUJITA Tomonori 	unsigned long long lba;
3526395cef03SMartin K. Petersen 	u32 ei_lba;
3527639db475SFUJITA Tomonori 	int errsts = 0;
3528639db475SFUJITA Tomonori 	int target = SCpnt->device->id;
3529639db475SFUJITA Tomonori 	struct sdebug_dev_info *devip = NULL;
3530639db475SFUJITA Tomonori 	int inj_recovered = 0;
3531639db475SFUJITA Tomonori 	int inj_transport = 0;
3532c6a44287SMartin K. Petersen 	int inj_dif = 0;
3533c6a44287SMartin K. Petersen 	int inj_dix = 0;
3534639db475SFUJITA Tomonori 	int delay_override = 0;
353544d92694SMartin K. Petersen 	int unmap = 0;
3536639db475SFUJITA Tomonori 
3537639db475SFUJITA Tomonori 	scsi_set_resid(SCpnt, 0);
3538639db475SFUJITA Tomonori 	if ((SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) && cmd) {
3539639db475SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: cmd ");
3540639db475SFUJITA Tomonori 		for (k = 0, len = SCpnt->cmd_len; k < len; ++k)
3541639db475SFUJITA Tomonori 			printk("%02x ", (int)cmd[k]);
3542639db475SFUJITA Tomonori 		printk("\n");
3543639db475SFUJITA Tomonori 	}
3544639db475SFUJITA Tomonori 
3545639db475SFUJITA Tomonori 	if (target == SCpnt->device->host->hostt->this_id) {
3546639db475SFUJITA Tomonori 		printk(KERN_INFO "scsi_debug: initiator's id used as "
3547639db475SFUJITA Tomonori 		       "target!\n");
3548639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3549639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3550639db475SFUJITA Tomonori 	}
3551639db475SFUJITA Tomonori 
3552639db475SFUJITA Tomonori 	if ((SCpnt->device->lun >= scsi_debug_max_luns) &&
3553639db475SFUJITA Tomonori 	    (SCpnt->device->lun != SAM2_WLUN_REPORT_LUNS))
3554639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3555639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3556639db475SFUJITA Tomonori 	devip = devInfoReg(SCpnt->device);
3557639db475SFUJITA Tomonori 	if (NULL == devip)
3558639db475SFUJITA Tomonori 		return schedule_resp(SCpnt, NULL, done,
3559639db475SFUJITA Tomonori 				     DID_NO_CONNECT << 16, 0);
3560639db475SFUJITA Tomonori 
3561639db475SFUJITA Tomonori 	if ((scsi_debug_every_nth != 0) &&
3562639db475SFUJITA Tomonori 	    (++scsi_debug_cmnd_count >= abs(scsi_debug_every_nth))) {
3563639db475SFUJITA Tomonori 		scsi_debug_cmnd_count = 0;
3564639db475SFUJITA Tomonori 		if (scsi_debug_every_nth < -1)
3565639db475SFUJITA Tomonori 			scsi_debug_every_nth = -1;
3566639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
3567639db475SFUJITA Tomonori 			return 0; /* ignore command causing timeout */
3568639db475SFUJITA Tomonori 		else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
3569639db475SFUJITA Tomonori 			inj_recovered = 1; /* to reads and writes below */
3570639db475SFUJITA Tomonori 		else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
3571639db475SFUJITA Tomonori 			inj_transport = 1; /* to reads and writes below */
3572c6a44287SMartin K. Petersen 		else if (SCSI_DEBUG_OPT_DIF_ERR & scsi_debug_opts)
3573c6a44287SMartin K. Petersen 			inj_dif = 1; /* to reads and writes below */
3574c6a44287SMartin K. Petersen 		else if (SCSI_DEBUG_OPT_DIX_ERR & scsi_debug_opts)
3575c6a44287SMartin K. Petersen 			inj_dix = 1; /* to reads and writes below */
3576639db475SFUJITA Tomonori 	}
3577639db475SFUJITA Tomonori 
3578639db475SFUJITA Tomonori 	if (devip->wlun) {
3579639db475SFUJITA Tomonori 		switch (*cmd) {
3580639db475SFUJITA Tomonori 		case INQUIRY:
3581639db475SFUJITA Tomonori 		case REQUEST_SENSE:
3582639db475SFUJITA Tomonori 		case TEST_UNIT_READY:
3583639db475SFUJITA Tomonori 		case REPORT_LUNS:
3584639db475SFUJITA Tomonori 			break;  /* only allowable wlun commands */
3585639db475SFUJITA Tomonori 		default:
3586639db475SFUJITA Tomonori 			if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3587639db475SFUJITA Tomonori 				printk(KERN_INFO "scsi_debug: Opcode: 0x%x "
3588639db475SFUJITA Tomonori 				       "not supported for wlun\n", *cmd);
3589639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3590639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3591639db475SFUJITA Tomonori 			errsts = check_condition_result;
3592639db475SFUJITA Tomonori 			return schedule_resp(SCpnt, devip, done, errsts,
3593639db475SFUJITA Tomonori 					     0);
3594639db475SFUJITA Tomonori 		}
3595639db475SFUJITA Tomonori 	}
3596639db475SFUJITA Tomonori 
3597639db475SFUJITA Tomonori 	switch (*cmd) {
3598639db475SFUJITA Tomonori 	case INQUIRY:     /* mandatory, ignore unit attention */
3599639db475SFUJITA Tomonori 		delay_override = 1;
3600639db475SFUJITA Tomonori 		errsts = resp_inquiry(SCpnt, target, devip);
3601639db475SFUJITA Tomonori 		break;
3602639db475SFUJITA Tomonori 	case REQUEST_SENSE:	/* mandatory, ignore unit attention */
3603639db475SFUJITA Tomonori 		delay_override = 1;
3604639db475SFUJITA Tomonori 		errsts = resp_requests(SCpnt, devip);
3605639db475SFUJITA Tomonori 		break;
3606639db475SFUJITA Tomonori 	case REZERO_UNIT:	/* actually this is REWIND for SSC */
3607639db475SFUJITA Tomonori 	case START_STOP:
3608639db475SFUJITA Tomonori 		errsts = resp_start_stop(SCpnt, devip);
3609639db475SFUJITA Tomonori 		break;
3610639db475SFUJITA Tomonori 	case ALLOW_MEDIUM_REMOVAL:
3611639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3612639db475SFUJITA Tomonori 		if (errsts)
3613639db475SFUJITA Tomonori 			break;
3614639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3615639db475SFUJITA Tomonori 			printk(KERN_INFO "scsi_debug: Medium removal %s\n",
3616639db475SFUJITA Tomonori 			       cmd[4] ? "inhibited" : "enabled");
3617639db475SFUJITA Tomonori 		break;
3618639db475SFUJITA Tomonori 	case SEND_DIAGNOSTIC:     /* mandatory */
3619639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3620639db475SFUJITA Tomonori 		break;
3621639db475SFUJITA Tomonori 	case TEST_UNIT_READY:     /* mandatory */
3622639db475SFUJITA Tomonori 		delay_override = 1;
3623639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3624639db475SFUJITA Tomonori 		break;
3625639db475SFUJITA Tomonori 	case RESERVE:
3626639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3627639db475SFUJITA Tomonori 		break;
3628639db475SFUJITA Tomonori 	case RESERVE_10:
3629639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3630639db475SFUJITA Tomonori 		break;
3631639db475SFUJITA Tomonori 	case RELEASE:
3632639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3633639db475SFUJITA Tomonori 		break;
3634639db475SFUJITA Tomonori 	case RELEASE_10:
3635639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3636639db475SFUJITA Tomonori 		break;
3637639db475SFUJITA Tomonori 	case READ_CAPACITY:
3638639db475SFUJITA Tomonori 		errsts = resp_readcap(SCpnt, devip);
3639639db475SFUJITA Tomonori 		break;
3640639db475SFUJITA Tomonori 	case SERVICE_ACTION_IN:
364144d92694SMartin K. Petersen 		if (cmd[1] == SAI_READ_CAPACITY_16)
364244d92694SMartin K. Petersen 			errsts = resp_readcap16(SCpnt, devip);
364344d92694SMartin K. Petersen 		else if (cmd[1] == SAI_GET_LBA_STATUS) {
364444d92694SMartin K. Petersen 
364544d92694SMartin K. Petersen 			if (scsi_debug_unmap_max_desc == 0) {
364644d92694SMartin K. Petersen 				mk_sense_buffer(devip, ILLEGAL_REQUEST,
364744d92694SMartin K. Petersen 						INVALID_COMMAND_OPCODE, 0);
364844d92694SMartin K. Petersen 				errsts = check_condition_result;
364944d92694SMartin K. Petersen 			} else
365044d92694SMartin K. Petersen 				errsts = resp_get_lba_status(SCpnt, devip);
365144d92694SMartin K. Petersen 		} else {
3652639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3653639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3654639db475SFUJITA Tomonori 			errsts = check_condition_result;
3655639db475SFUJITA Tomonori 		}
3656639db475SFUJITA Tomonori 		break;
3657639db475SFUJITA Tomonori 	case MAINTENANCE_IN:
3658639db475SFUJITA Tomonori 		if (MI_REPORT_TARGET_PGS != cmd[1]) {
3659639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3660639db475SFUJITA Tomonori 					INVALID_OPCODE, 0);
3661639db475SFUJITA Tomonori 			errsts = check_condition_result;
3662639db475SFUJITA Tomonori 			break;
3663639db475SFUJITA Tomonori 		}
3664639db475SFUJITA Tomonori 		errsts = resp_report_tgtpgs(SCpnt, devip);
3665639db475SFUJITA Tomonori 		break;
3666639db475SFUJITA Tomonori 	case READ_16:
3667639db475SFUJITA Tomonori 	case READ_12:
3668639db475SFUJITA Tomonori 	case READ_10:
3669395cef03SMartin K. Petersen 		/* READ{10,12,16} and DIF Type 2 are natural enemies */
3670395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
3671395cef03SMartin K. Petersen 		    cmd[1] & 0xe0) {
3672395cef03SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3673395cef03SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
3674395cef03SMartin K. Petersen 			errsts = check_condition_result;
3675395cef03SMartin K. Petersen 			break;
3676395cef03SMartin K. Petersen 		}
3677395cef03SMartin K. Petersen 
3678395cef03SMartin K. Petersen 		if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
3679395cef03SMartin K. Petersen 		     scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
3680395cef03SMartin K. Petersen 		    (cmd[1] & 0xe0) == 0)
3681395cef03SMartin K. Petersen 			printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
3682395cef03SMartin K. Petersen 
3683395cef03SMartin K. Petersen 		/* fall through */
3684639db475SFUJITA Tomonori 	case READ_6:
3685395cef03SMartin K. Petersen read:
3686639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3687639db475SFUJITA Tomonori 		if (errsts)
3688639db475SFUJITA Tomonori 			break;
3689639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3690639db475SFUJITA Tomonori 			break;
3691395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3692395cef03SMartin K. Petersen 		errsts = resp_read(SCpnt, lba, num, devip, ei_lba);
3693639db475SFUJITA Tomonori 		if (inj_recovered && (0 == errsts)) {
3694639db475SFUJITA Tomonori 			mk_sense_buffer(devip, RECOVERED_ERROR,
3695639db475SFUJITA Tomonori 					THRESHOLD_EXCEEDED, 0);
3696639db475SFUJITA Tomonori 			errsts = check_condition_result;
3697639db475SFUJITA Tomonori 		} else if (inj_transport && (0 == errsts)) {
3698639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ABORTED_COMMAND,
3699639db475SFUJITA Tomonori 					TRANSPORT_PROBLEM, ACK_NAK_TO);
3700639db475SFUJITA Tomonori 			errsts = check_condition_result;
3701c6a44287SMartin K. Petersen 		} else if (inj_dif && (0 == errsts)) {
3702c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
3703c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3704c6a44287SMartin K. Petersen 		} else if (inj_dix && (0 == errsts)) {
3705c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, 1);
3706c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3707639db475SFUJITA Tomonori 		}
3708639db475SFUJITA Tomonori 		break;
3709639db475SFUJITA Tomonori 	case REPORT_LUNS:	/* mandatory, ignore unit attention */
3710639db475SFUJITA Tomonori 		delay_override = 1;
3711639db475SFUJITA Tomonori 		errsts = resp_report_luns(SCpnt, devip);
3712639db475SFUJITA Tomonori 		break;
3713639db475SFUJITA Tomonori 	case VERIFY:		/* 10 byte SBC-2 command */
3714639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3715639db475SFUJITA Tomonori 		break;
3716639db475SFUJITA Tomonori 	case WRITE_16:
3717639db475SFUJITA Tomonori 	case WRITE_12:
3718639db475SFUJITA Tomonori 	case WRITE_10:
3719395cef03SMartin K. Petersen 		/* WRITE{10,12,16} and DIF Type 2 are natural enemies */
3720395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION &&
3721395cef03SMartin K. Petersen 		    cmd[1] & 0xe0) {
3722395cef03SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3723395cef03SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
3724395cef03SMartin K. Petersen 			errsts = check_condition_result;
3725395cef03SMartin K. Petersen 			break;
3726395cef03SMartin K. Petersen 		}
3727395cef03SMartin K. Petersen 
3728395cef03SMartin K. Petersen 		if ((scsi_debug_dif == SD_DIF_TYPE1_PROTECTION ||
3729395cef03SMartin K. Petersen 		     scsi_debug_dif == SD_DIF_TYPE3_PROTECTION) &&
3730395cef03SMartin K. Petersen 		    (cmd[1] & 0xe0) == 0)
3731395cef03SMartin K. Petersen 			printk(KERN_ERR "Unprotected RD/WR to DIF device\n");
3732395cef03SMartin K. Petersen 
3733395cef03SMartin K. Petersen 		/* fall through */
3734639db475SFUJITA Tomonori 	case WRITE_6:
3735395cef03SMartin K. Petersen write:
3736639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3737639db475SFUJITA Tomonori 		if (errsts)
3738639db475SFUJITA Tomonori 			break;
3739639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3740639db475SFUJITA Tomonori 			break;
3741395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3742395cef03SMartin K. Petersen 		errsts = resp_write(SCpnt, lba, num, devip, ei_lba);
3743639db475SFUJITA Tomonori 		if (inj_recovered && (0 == errsts)) {
3744639db475SFUJITA Tomonori 			mk_sense_buffer(devip, RECOVERED_ERROR,
3745639db475SFUJITA Tomonori 					THRESHOLD_EXCEEDED, 0);
3746639db475SFUJITA Tomonori 			errsts = check_condition_result;
3747c6a44287SMartin K. Petersen 		} else if (inj_dif && (0 == errsts)) {
3748c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ABORTED_COMMAND, 0x10, 1);
3749c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3750c6a44287SMartin K. Petersen 		} else if (inj_dix && (0 == errsts)) {
3751c6a44287SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x10, 1);
3752c6a44287SMartin K. Petersen 			errsts = illegal_condition_result;
3753639db475SFUJITA Tomonori 		}
3754639db475SFUJITA Tomonori 		break;
375544d92694SMartin K. Petersen 	case WRITE_SAME_16:
375644d92694SMartin K. Petersen 		if (cmd[1] & 0x8)
375744d92694SMartin K. Petersen 			unmap = 1;
375844d92694SMartin K. Petersen 		/* fall through */
375944d92694SMartin K. Petersen 	case WRITE_SAME:
376044d92694SMartin K. Petersen 		errsts = check_readiness(SCpnt, 0, devip);
376144d92694SMartin K. Petersen 		if (errsts)
376244d92694SMartin K. Petersen 			break;
376344d92694SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
376444d92694SMartin K. Petersen 		errsts = resp_write_same(SCpnt, lba, num, devip, ei_lba, unmap);
376544d92694SMartin K. Petersen 		break;
376644d92694SMartin K. Petersen 	case UNMAP:
376744d92694SMartin K. Petersen 		errsts = check_readiness(SCpnt, 0, devip);
376844d92694SMartin K. Petersen 		if (errsts)
376944d92694SMartin K. Petersen 			break;
377044d92694SMartin K. Petersen 
377144d92694SMartin K. Petersen 		if (scsi_debug_unmap_max_desc == 0) {
377244d92694SMartin K. Petersen 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
377344d92694SMartin K. Petersen 					INVALID_COMMAND_OPCODE, 0);
377444d92694SMartin K. Petersen 			errsts = check_condition_result;
377544d92694SMartin K. Petersen 		} else
377644d92694SMartin K. Petersen 			errsts = resp_unmap(SCpnt, devip);
377744d92694SMartin K. Petersen 		break;
3778639db475SFUJITA Tomonori 	case MODE_SENSE:
3779639db475SFUJITA Tomonori 	case MODE_SENSE_10:
3780639db475SFUJITA Tomonori 		errsts = resp_mode_sense(SCpnt, target, devip);
3781639db475SFUJITA Tomonori 		break;
3782639db475SFUJITA Tomonori 	case MODE_SELECT:
3783639db475SFUJITA Tomonori 		errsts = resp_mode_select(SCpnt, 1, devip);
3784639db475SFUJITA Tomonori 		break;
3785639db475SFUJITA Tomonori 	case MODE_SELECT_10:
3786639db475SFUJITA Tomonori 		errsts = resp_mode_select(SCpnt, 0, devip);
3787639db475SFUJITA Tomonori 		break;
3788639db475SFUJITA Tomonori 	case LOG_SENSE:
3789639db475SFUJITA Tomonori 		errsts = resp_log_sense(SCpnt, devip);
3790639db475SFUJITA Tomonori 		break;
3791639db475SFUJITA Tomonori 	case SYNCHRONIZE_CACHE:
3792639db475SFUJITA Tomonori 		delay_override = 1;
3793639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3794639db475SFUJITA Tomonori 		break;
3795639db475SFUJITA Tomonori 	case WRITE_BUFFER:
3796639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3797639db475SFUJITA Tomonori 		break;
3798639db475SFUJITA Tomonori 	case XDWRITEREAD_10:
3799639db475SFUJITA Tomonori 		if (!scsi_bidi_cmnd(SCpnt)) {
3800639db475SFUJITA Tomonori 			mk_sense_buffer(devip, ILLEGAL_REQUEST,
3801639db475SFUJITA Tomonori 					INVALID_FIELD_IN_CDB, 0);
3802639db475SFUJITA Tomonori 			errsts = check_condition_result;
3803639db475SFUJITA Tomonori 			break;
3804639db475SFUJITA Tomonori 		}
3805639db475SFUJITA Tomonori 
3806639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 0, devip);
3807639db475SFUJITA Tomonori 		if (errsts)
3808639db475SFUJITA Tomonori 			break;
3809639db475SFUJITA Tomonori 		if (scsi_debug_fake_rw)
3810639db475SFUJITA Tomonori 			break;
3811395cef03SMartin K. Petersen 		get_data_transfer_info(cmd, &lba, &num, &ei_lba);
3812395cef03SMartin K. Petersen 		errsts = resp_read(SCpnt, lba, num, devip, ei_lba);
3813639db475SFUJITA Tomonori 		if (errsts)
3814639db475SFUJITA Tomonori 			break;
3815395cef03SMartin K. Petersen 		errsts = resp_write(SCpnt, lba, num, devip, ei_lba);
3816639db475SFUJITA Tomonori 		if (errsts)
3817639db475SFUJITA Tomonori 			break;
3818639db475SFUJITA Tomonori 		errsts = resp_xdwriteread(SCpnt, lba, num, devip);
3819639db475SFUJITA Tomonori 		break;
3820395cef03SMartin K. Petersen 	case VARIABLE_LENGTH_CMD:
3821395cef03SMartin K. Petersen 		if (scsi_debug_dif == SD_DIF_TYPE2_PROTECTION) {
3822395cef03SMartin K. Petersen 
3823395cef03SMartin K. Petersen 			if ((cmd[10] & 0xe0) == 0)
3824395cef03SMartin K. Petersen 				printk(KERN_ERR
3825395cef03SMartin K. Petersen 				       "Unprotected RD/WR to DIF device\n");
3826395cef03SMartin K. Petersen 
3827395cef03SMartin K. Petersen 			if (cmd[9] == READ_32) {
3828395cef03SMartin K. Petersen 				BUG_ON(SCpnt->cmd_len < 32);
3829395cef03SMartin K. Petersen 				goto read;
3830395cef03SMartin K. Petersen 			}
3831395cef03SMartin K. Petersen 
3832395cef03SMartin K. Petersen 			if (cmd[9] == WRITE_32) {
3833395cef03SMartin K. Petersen 				BUG_ON(SCpnt->cmd_len < 32);
3834395cef03SMartin K. Petersen 				goto write;
3835395cef03SMartin K. Petersen 			}
3836395cef03SMartin K. Petersen 		}
3837395cef03SMartin K. Petersen 
3838395cef03SMartin K. Petersen 		mk_sense_buffer(devip, ILLEGAL_REQUEST,
3839395cef03SMartin K. Petersen 				INVALID_FIELD_IN_CDB, 0);
3840395cef03SMartin K. Petersen 		errsts = check_condition_result;
3841395cef03SMartin K. Petersen 		break;
3842395cef03SMartin K. Petersen 
3843639db475SFUJITA Tomonori 	default:
3844639db475SFUJITA Tomonori 		if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts)
3845639db475SFUJITA Tomonori 			printk(KERN_INFO "scsi_debug: Opcode: 0x%x not "
3846639db475SFUJITA Tomonori 			       "supported\n", *cmd);
3847639db475SFUJITA Tomonori 		errsts = check_readiness(SCpnt, 1, devip);
3848639db475SFUJITA Tomonori 		if (errsts)
3849639db475SFUJITA Tomonori 			break;	/* Unit attention takes precedence */
3850639db475SFUJITA Tomonori 		mk_sense_buffer(devip, ILLEGAL_REQUEST, INVALID_OPCODE, 0);
3851639db475SFUJITA Tomonori 		errsts = check_condition_result;
3852639db475SFUJITA Tomonori 		break;
3853639db475SFUJITA Tomonori 	}
3854639db475SFUJITA Tomonori 	return schedule_resp(SCpnt, devip, done, errsts,
3855639db475SFUJITA Tomonori 			     (delay_override ? 0 : scsi_debug_delay));
3856639db475SFUJITA Tomonori }
3857639db475SFUJITA Tomonori 
38589e603ca0SFUJITA Tomonori static struct scsi_host_template sdebug_driver_template = {
38599e603ca0SFUJITA Tomonori 	.proc_info =		scsi_debug_proc_info,
38609e603ca0SFUJITA Tomonori 	.proc_name =		sdebug_proc_name,
38619e603ca0SFUJITA Tomonori 	.name =			"SCSI DEBUG",
38629e603ca0SFUJITA Tomonori 	.info =			scsi_debug_info,
38639e603ca0SFUJITA Tomonori 	.slave_alloc =		scsi_debug_slave_alloc,
38649e603ca0SFUJITA Tomonori 	.slave_configure =	scsi_debug_slave_configure,
38659e603ca0SFUJITA Tomonori 	.slave_destroy =	scsi_debug_slave_destroy,
38669e603ca0SFUJITA Tomonori 	.ioctl =		scsi_debug_ioctl,
38679e603ca0SFUJITA Tomonori 	.queuecommand =		scsi_debug_queuecommand,
38689e603ca0SFUJITA Tomonori 	.eh_abort_handler =	scsi_debug_abort,
38699e603ca0SFUJITA Tomonori 	.eh_bus_reset_handler = scsi_debug_bus_reset,
38709e603ca0SFUJITA Tomonori 	.eh_device_reset_handler = scsi_debug_device_reset,
38719e603ca0SFUJITA Tomonori 	.eh_host_reset_handler = scsi_debug_host_reset,
38729e603ca0SFUJITA Tomonori 	.bios_param =		scsi_debug_biosparam,
38739e603ca0SFUJITA Tomonori 	.can_queue =		SCSI_DEBUG_CANQUEUE,
38749e603ca0SFUJITA Tomonori 	.this_id =		7,
38759e603ca0SFUJITA Tomonori 	.sg_tablesize =		256,
38769e603ca0SFUJITA Tomonori 	.cmd_per_lun =		16,
38779e603ca0SFUJITA Tomonori 	.max_sectors =		0xffff,
38789e603ca0SFUJITA Tomonori 	.use_clustering = 	DISABLE_CLUSTERING,
38799e603ca0SFUJITA Tomonori 	.module =		THIS_MODULE,
38809e603ca0SFUJITA Tomonori };
38819e603ca0SFUJITA Tomonori 
38821da177e4SLinus Torvalds static int sdebug_driver_probe(struct device * dev)
38831da177e4SLinus Torvalds {
38841da177e4SLinus Torvalds         int error = 0;
38851da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
38861da177e4SLinus Torvalds         struct Scsi_Host *hpnt;
3887c6a44287SMartin K. Petersen 	int host_prot;
38881da177e4SLinus Torvalds 
38891da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
38901da177e4SLinus Torvalds 
389178d4e5a0SDouglas Gilbert 	sdebug_driver_template.can_queue = scsi_debug_max_queue;
38921da177e4SLinus Torvalds 	hpnt = scsi_host_alloc(&sdebug_driver_template, sizeof(sdbg_host));
38931da177e4SLinus Torvalds 	if (NULL == hpnt) {
3894cadbd4a5SHarvey Harrison 		printk(KERN_ERR "%s: scsi_register failed\n", __func__);
38951da177e4SLinus Torvalds 		error = -ENODEV;
38961da177e4SLinus Torvalds 		return error;
38971da177e4SLinus Torvalds 	}
38981da177e4SLinus Torvalds 
38991da177e4SLinus Torvalds         sdbg_host->shost = hpnt;
39001da177e4SLinus Torvalds 	*((struct sdebug_host_info **)hpnt->hostdata) = sdbg_host;
39011da177e4SLinus Torvalds 	if ((hpnt->this_id >= 0) && (scsi_debug_num_tgts > hpnt->this_id))
39021da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts + 1;
39031da177e4SLinus Torvalds 	else
39041da177e4SLinus Torvalds 		hpnt->max_id = scsi_debug_num_tgts;
3905c65b1445SDouglas Gilbert 	hpnt->max_lun = SAM2_WLUN_REPORT_LUNS;	/* = scsi_debug_max_luns; */
39061da177e4SLinus Torvalds 
3907c6a44287SMartin K. Petersen 	host_prot = 0;
3908c6a44287SMartin K. Petersen 
3909c6a44287SMartin K. Petersen 	switch (scsi_debug_dif) {
3910c6a44287SMartin K. Petersen 
3911c6a44287SMartin K. Petersen 	case SD_DIF_TYPE1_PROTECTION:
3912c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE1_PROTECTION;
3913c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3914c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE1_PROTECTION;
3915c6a44287SMartin K. Petersen 		break;
3916c6a44287SMartin K. Petersen 
3917c6a44287SMartin K. Petersen 	case SD_DIF_TYPE2_PROTECTION:
3918c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE2_PROTECTION;
3919c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3920c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE2_PROTECTION;
3921c6a44287SMartin K. Petersen 		break;
3922c6a44287SMartin K. Petersen 
3923c6a44287SMartin K. Petersen 	case SD_DIF_TYPE3_PROTECTION:
3924c6a44287SMartin K. Petersen 		host_prot = SHOST_DIF_TYPE3_PROTECTION;
3925c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3926c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE3_PROTECTION;
3927c6a44287SMartin K. Petersen 		break;
3928c6a44287SMartin K. Petersen 
3929c6a44287SMartin K. Petersen 	default:
3930c6a44287SMartin K. Petersen 		if (scsi_debug_dix)
3931c6a44287SMartin K. Petersen 			host_prot |= SHOST_DIX_TYPE0_PROTECTION;
3932c6a44287SMartin K. Petersen 		break;
3933c6a44287SMartin K. Petersen 	}
3934c6a44287SMartin K. Petersen 
3935c6a44287SMartin K. Petersen 	scsi_host_set_prot(hpnt, host_prot);
3936c6a44287SMartin K. Petersen 
3937c6a44287SMartin K. Petersen 	printk(KERN_INFO "scsi_debug: host protection%s%s%s%s%s%s%s\n",
3938c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE1_PROTECTION) ? " DIF1" : "",
3939c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE2_PROTECTION) ? " DIF2" : "",
3940c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIF_TYPE3_PROTECTION) ? " DIF3" : "",
3941c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE0_PROTECTION) ? " DIX0" : "",
3942c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE1_PROTECTION) ? " DIX1" : "",
3943c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE2_PROTECTION) ? " DIX2" : "",
3944c6a44287SMartin K. Petersen 	       (host_prot & SHOST_DIX_TYPE3_PROTECTION) ? " DIX3" : "");
3945c6a44287SMartin K. Petersen 
3946c6a44287SMartin K. Petersen 	if (scsi_debug_guard == 1)
3947c6a44287SMartin K. Petersen 		scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_IP);
3948c6a44287SMartin K. Petersen 	else
3949c6a44287SMartin K. Petersen 		scsi_host_set_guard(hpnt, SHOST_DIX_GUARD_CRC);
3950c6a44287SMartin K. Petersen 
39511da177e4SLinus Torvalds         error = scsi_add_host(hpnt, &sdbg_host->dev);
39521da177e4SLinus Torvalds         if (error) {
3953cadbd4a5SHarvey Harrison                 printk(KERN_ERR "%s: scsi_add_host failed\n", __func__);
39541da177e4SLinus Torvalds                 error = -ENODEV;
39551da177e4SLinus Torvalds 		scsi_host_put(hpnt);
39561da177e4SLinus Torvalds         } else
39571da177e4SLinus Torvalds 		scsi_scan_host(hpnt);
39581da177e4SLinus Torvalds 
39591da177e4SLinus Torvalds 
39601da177e4SLinus Torvalds         return error;
39611da177e4SLinus Torvalds }
39621da177e4SLinus Torvalds 
39631da177e4SLinus Torvalds static int sdebug_driver_remove(struct device * dev)
39641da177e4SLinus Torvalds {
39651da177e4SLinus Torvalds         struct sdebug_host_info *sdbg_host;
39668b40228fSFUJITA Tomonori 	struct sdebug_dev_info *sdbg_devinfo, *tmp;
39671da177e4SLinus Torvalds 
39681da177e4SLinus Torvalds 	sdbg_host = to_sdebug_host(dev);
39691da177e4SLinus Torvalds 
39701da177e4SLinus Torvalds 	if (!sdbg_host) {
39711da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Unable to locate host info\n",
3972cadbd4a5SHarvey Harrison 		       __func__);
39731da177e4SLinus Torvalds 		return -ENODEV;
39741da177e4SLinus Torvalds 	}
39751da177e4SLinus Torvalds 
39761da177e4SLinus Torvalds         scsi_remove_host(sdbg_host->shost);
39771da177e4SLinus Torvalds 
39788b40228fSFUJITA Tomonori 	list_for_each_entry_safe(sdbg_devinfo, tmp, &sdbg_host->dev_info_list,
39798b40228fSFUJITA Tomonori 				 dev_list) {
39801da177e4SLinus Torvalds                 list_del(&sdbg_devinfo->dev_list);
39811da177e4SLinus Torvalds                 kfree(sdbg_devinfo);
39821da177e4SLinus Torvalds         }
39831da177e4SLinus Torvalds 
39841da177e4SLinus Torvalds         scsi_host_put(sdbg_host->shost);
39851da177e4SLinus Torvalds         return 0;
39861da177e4SLinus Torvalds }
39871da177e4SLinus Torvalds 
39888dea0d02SFUJITA Tomonori static int pseudo_lld_bus_match(struct device *dev,
39898dea0d02SFUJITA Tomonori 				struct device_driver *dev_driver)
39901da177e4SLinus Torvalds {
39918dea0d02SFUJITA Tomonori 	return 1;
39928dea0d02SFUJITA Tomonori }
39931da177e4SLinus Torvalds 
39948dea0d02SFUJITA Tomonori static struct bus_type pseudo_lld_bus = {
39958dea0d02SFUJITA Tomonori 	.name = "pseudo",
39968dea0d02SFUJITA Tomonori 	.match = pseudo_lld_bus_match,
39978dea0d02SFUJITA Tomonori 	.probe = sdebug_driver_probe,
39988dea0d02SFUJITA Tomonori 	.remove = sdebug_driver_remove,
39998dea0d02SFUJITA Tomonori };
4000