1/* Test code for D. Gilbert's extensions to the Linux OS SCSI generic ("sg")
2   device driver.
3*  Copyright (C) 1999 - 2002 D. Gilbert
4*  This program is free software; you can redistribute it and/or modify
5*  it under the terms of the GNU General Public License as published by
6*  the Free Software Foundation; either version 2, or (at your option)
7*  any later version.
8
9   This program scans the "sg" device space (ie actual + simulated SCSI
10   generic devices).
11   Options: -w   open writable (new driver opens readable unless -i)
12            -n   numeric scan: scan /dev/sg0,1,2, ....
13            -a   alpha scan: scan /dev/sga,b,c, ....
14            -i   do SCSI inquiry on device (implies -w)
15            -x   extra information output
16
17   By default this program will look for /dev/sg0 first (i.e. numeric scan)
18
19   Note: This program is written to work under both the original and
20   the new sg driver.
21
22   Version 1.00 20031022
23
24   F. Jansen - modification to extend beyond 26 sg devices.
25   M. Ridgeway - Roll code together for SCSI testing with one command line
26
276 byte INQUIRY command:
28[0x12][   |lu][pg cde][res   ][al len][cntrl ]
29*/
30
31#include <unistd.h>
32#include <fcntl.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <errno.h>
37#include <dirent.h>
38#include <limits.h>
39#include <time.h>
40#include <sys/ioctl.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <signal.h>
44#include <ctype.h>
45#include <pthread.h>
46#include <sys/sysmacros.h>
47#include <sys/time.h>
48#include <sys/mman.h>
49#include <linux/major.h>
50#include "sg_include.h"
51#include "sg_err.h"
52#include "llseek.h"
53
54#define ME "scsimain: "
55
56#ifndef O_DIRECT
57#define O_DIRECT 040000
58#endif
59
60#define NUMERIC_SCAN_DEF 1	/* change to 0 to make alpha scan default */
61//static char * version_str = "0.21 20030513";
62
63#define BPI (signed)(sizeof(int))
64#define READWRITE_BASE_NUM 0x12345678
65#define DEF_BLOCK_SIZE 512
66#define DEF_NUM_THREADS 16
67#define MAX_NUM_THREADS SG_MAX_QUEUE
68#define DEF_BLOCKS_PER_TRANSFER 128
69#define DEF_SCSI_CDBSZ 10
70#define MAX_SCSI_CDBSZ 16
71#define TUR_CMD_LEN 6
72#define DEVNAME_SZ 256
73#define MAX_HOLES 4
74
75#define OFF sizeof(struct sg_header)
76#define INQ_REPLY_LEN 96	/* logic assumes >= sizeof(inqCmdBlk) */
77#define INQUIRY_CMDLEN  6
78#define INQUIRY_CMD     0x12
79#define SENSE_BUFF_LEN 32	/* Arbitrary, could be larger */
80#define DEF_TIMEOUT 60000	/* 60,000 millisecs == 60 seconds */
81#define REASON_SZ 128
82
83#define SENSE_BUFF_SZ 64
84#define RCAP_REPLY_LEN 8
85#define LOG_SENSE_CMD     0x4d
86#define LOG_SENSE_CMDLEN  10
87#define MX_ALLOC_LEN (1024 * 17)
88#define D_ROOT_SZ 512
89#define STR_SZ 1024
90#define INOUTF_SZ 512
91#define EBUFF_SZ 512
92#define MDEV_NAME_SZ 256
93
94#define PG_CODE_ALL 0x00
95
96#define TRUE 1
97#define FALSE 0
98#define MAX_DEVICES 50
99
100#define NAME_LEN_MAX 256
101#define LEVELS 4
102
103#define SENSE_BUFF_LEN 32	/* Arbitrary, could be larger */
104#define INQ_ALLOC_LEN 255
105
106#ifndef SCSI_IOCTL_GET_PCI
107#define SCSI_IOCTL_GET_PCI 0x5387
108#endif
109
110#define READ_CAP_REPLY_LEN 8
111
112#ifndef RAW_MAJOR
113#define RAW_MAJOR 255		/*unlikey value */
114#endif
115
116#define FT_OTHER 1		/* filetype is probably normal */
117#define FT_SG 2			/* filetype is sg char device or supports
118				   SG_IO ioctl */
119#define FT_RAW 4		/* filetype is raw char device */
120#define FT_DEV_NULL 8		/* either "/dev/null" or "." as filename */
121#define FT_ST 16		/* filetype is st char device (tape) */
122#define FT_BLOCK 32		/* filetype is block device */
123
124#define DEV_NULL_MINOR_NUM 3
125
126#ifdef SG_GET_RESERVED_SIZE
127#define OPEN_FLAG O_RDONLY
128#else
129#define OPEN_FLAG O_RDWR
130#endif
131
132#ifndef SG_MAX_SENSE
133#define SG_MAX_SENSE 16
134#endif
135
136#define TEST_START 0
137#define TEST_BREAK 1
138#define TEST_STOP  2
139#define MAX_SG_DEVS 128
140#define MAX_SD_DEVS 128
141#define MAX_SR_DEVS 128
142#define MAX_ST_DEVS 128
143#define MAX_OSST_DEVS 128
144#define MAX_ERRORS 5
145
146#define LIN_DEV_TYPE_UNKNOWN 0
147#define LIN_DEV_TYPE_SD 1
148#define LIN_DEV_TYPE_SR 2
149#define LIN_DEV_TYPE_ST 3
150#define LIN_DEV_TYPE_SCD 4
151#define LIN_DEV_TYPE_OSST 5
152
153#define MODE_SENSE6_CMD      0x1a
154#define MODE_SENSE6_CMDLEN   6
155#define MODE_SENSE10_CMD     0x5a
156#define MODE_SENSE10_CMDLEN  10
157#define INQUIRY_CMD     0x12
158#define INQUIRY_CMDLEN  6
159#define MODE_ALLOC_LEN (1024 * 4)
160
161#define MODE_CODE_ALL 0x3f
162
163#define RB_MODE_DESC 3
164#define RB_MODE_DATA 2
165#define RB_DESC_LEN 4
166#define RB_MB_TO_READ 200
167#define RB_OPCODE 0x3C
168#define RB_CMD_LEN 10
169
170/* #define SG_DEBUG */
171
172#ifndef SG_FLAG_MMAP_IO
173#define SG_FLAG_MMAP_IO 4
174#endif
175#ifndef SG_SCSI_RESET
176#define SG_SCSI_RESET 0x2284
177#endif
178
179#ifndef SG_SCSI_RESET_NOTHING
180#define SG_SCSI_RESET_NOTHING 0
181#define SG_SCSI_RESET_DEVICE 1
182#define SG_SCSI_RESET_BUS 2
183#define SG_SCSI_RESET_HOST 3
184#endif
185#define LONG_TIMEOUT 2400000	/* 2,400,000 millisecs == 40 minutes */
186
187#define SEND_DIAGNOSTIC_CMD     0x1d
188#define SEND_DIAGNOSTIC_CMDLEN  6
189#define RECEIVE_DIAGNOSTIC_CMD     0x1c
190#define RECEIVE_DIAGNOSTIC_CMDLEN  6
191
192#define START_STOP		0x1b
193#define SYNCHRONIZE_CACHE	0x35
194
195#define DEF_START_TIMEOUT 120000	/* 120,000 millisecs == 2 minutes */
196
197#define DEVICE_RESET 0
198#define HOST_RESET   1
199#define BUS_RESET    2
200#define SG_HSZ sizeof(struct sg_header)
201#define OFFSET_HEADER (SG_HSZ - (2 * sizeof(int)))
202#define SIZEOF_BUFFER (256*1024)
203#define SIZEOF_BUFFER1 (16*1024)
204#define MAXPARM 32
205
206#define SETUP_MODE_PAGE(NPAGE, NPARAM)          \
207  status = get_mode_page(NPAGE, page_code);     \
208  if (status) { printf("\n"); return status; }   \
209  bdlen = buffer[11];                           \
210  pagestart = buffer + 12 + bdlen;
211
212typedef struct request_collection {	/* one instance visible to all threads */
213	int infd;
214	int skip;
215	int in_type;
216	int in_scsi_type;
217	int in_blk;		/* -\ next block address to read */
218	int in_count;		/*  | blocks remaining for next read */
219	int in_done_count;	/*  | count of completed in blocks */
220	int in_partial;		/*  | */
221	int in_stop;		/*  | */
222	pthread_mutex_t in_mutex;	/* -/ */
223	int outfd;
224	int seek;
225	int out_type;
226	int out_scsi_type;
227	int out_blk;		/* -\ next block address to write */
228	int out_count;		/*  | blocks remaining for next write */
229	int out_done_count;	/*  | count of completed out blocks */
230	int out_partial;	/*  | */
231	int out_stop;		/*  | */
232	pthread_mutex_t out_mutex;	/*  | */
233	pthread_cond_t out_sync_cv;	/* -/ hold writes until "in order" */
234	int bs;
235	int bpt;
236	int fua_mode;
237	int dio;
238	int dio_incomplete;	/* -\ */
239	int sum_of_resids;	/*  | */
240	pthread_mutex_t aux_mutex;	/* -/ (also serializes some printf()s */
241	int coe;
242	int cdbsz;
243	int debug;
244} Rq_coll;
245
246typedef struct request_element {	/* one instance per worker thread */
247	int infd;
248	int outfd;
249	int wr;
250	int blk;
251	int num_blks;
252	unsigned char *buffp;
253	unsigned char *alloc_bp;
254	sg_io_hdr_t io_hdr;
255	unsigned char cmd[MAX_SCSI_CDBSZ];
256	unsigned char sb[SENSE_BUFF_LEN];
257	int bs;
258	int fua_mode;
259	int dio;
260	int dio_incomplete;
261	int resid;
262	int in_scsi_type;
263	int out_scsi_type;
264	int cdbsz;
265	int debug;
266} Rq_elem;
267
268typedef struct my_map_info {
269	int active;
270	int lin_dev_type;
271	int oth_dev_num;
272	struct sg_scsi_id sg_dat;
273	char vendor[8];
274	char product[16];
275	char revision[4];
276} my_map_info_t;
277
278typedef struct sg_map {
279	int bus;
280	int channel;
281	int target_id;
282	int lun;
283	char *dev_name;
284} Sg_map;
285
286typedef struct my_scsi_idlun {
287/* why can't userland see this structure ??? */
288	int dev_id;
289	int host_unique_id;
290} My_scsi_idlun;
291
292struct page_code_desc {
293	int page_code;
294	const char *desc;
295};
296
297static const char *pg_control_str_arr[] = {
298	"current",
299	"changeable",
300	"default",
301	"saved"
302};
303
304char *devices[] =
305    { "/dev/sda", "/dev/sdb", "/dev/sdc", "/dev/sdd", "/dev/sde", "/dev/sdf",
306	"/dev/sdg", "/dev/sdh", "/dev/sdi", "/dev/sdj", "/dev/sdk", "/dev/sdl",
307	"/dev/sdm", "/dev/sdn", "/dev/sdo", "/dev/sdp", "/dev/sdq", "/dev/sdr",
308	"/dev/sds", "/dev/sdt", "/dev/sdu", "/dev/sdv", "/dev/sdw", "/dev/sdx",
309	"/dev/sdy", "/dev/sdz", "/dev/sdaa", "/dev/sdab", "/dev/sdac",
310	    "/dev/sdad",
311	"/dev/scd0", "/dev/scd1", "/dev/scd2", "/dev/scd3", "/dev/scd4",
312	    "/dev/scd5",
313	"/dev/scd6", "/dev/scd7", "/dev/scd8", "/dev/scd9", "/dev/scd10",
314	    "/dev/scd11",
315	"/dev/sr0", "/dev/sr1", "/dev/sr2", "/dev/sr3", "/dev/sr4", "/dev/sr5",
316	"/dev/sr6", "/dev/sr7", "/dev/sr8", "/dev/sr9", "/dev/sr10",
317	    "/dev/sr11",
318	"/dev/nst0", "/dev/nst1", "/dev/nst2", "/dev/nst3", "/dev/nst4",
319	    "/dev/nst5",
320	"/dev/nosst0", "/dev/nosst1", "/dev/nosst2", "/dev/nosst3",
321	    "/dev/nosst4"
322};
323
324static char *page_names[] = {
325	NULL,
326	"Read-Write Error Recovery",
327	"Disconnect-Reconnect",
328	"Format Device",
329	"Rigid Disk Geometry",
330	/* "Flexible Disk" */ NULL,
331	NULL,
332	"Verify Error Recovery",
333	"Caching",
334	"Peripheral Device",
335	"Control Mode",
336	/* "Medium Types Supported" */ NULL,
337	"Notch and Partition",
338	/* "CD-ROM" */ NULL,
339	/* "CD-ROM Audio Control" */ NULL,
340	NULL,
341	/* "Medium Partition (1)" */ NULL,
342	/* "Medium Partition (2)" */ NULL,
343	/* "Medium Partition (3)" */ NULL,
344	/* "Medium Partition (4)" */ NULL
345};
346
347#define MAX_PAGENO (sizeof(page_names)/sizeof(char *))
348
349/* Following 2 macros from D.R. Butenhof's POSIX threads book:
350   ISBN 0-201-63392-2 . [Highly recommended book.] */
351#define err_exit(code,text) do { \
352    fprintf(stderr, "%s at \"%s\":%d: %s\n", \
353        text, __FILE__, __LINE__, strerror(code)); \
354    exit(1); \
355    } while (0)
356
357static Sg_map sg_map_arr[(sizeof(devices) / sizeof(char *)) + 1];
358
359static const unsigned char scsi_command_size[8] = { 6, 10, 10, 12,
360	12, 12, 10, 10
361};
362const unsigned char rbCmdBlk[10] = { READ_BUFFER, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
363static const char *level_arr[LEVELS] = { "host", "bus", "target", "lun" };
364
365static const char *proc_allow_dio = "/proc/scsi/sg/allow_dio";
366static const char *devfs_id = "/dev/.devfsd";
367static my_map_info_t map_arr[MAX_SG_DEVS];
368static char ebuff[EBUFF_SZ];
369static int glob_fd;
370static char defectformat = 0x4;
371static sigset_t signal_set;
372static pthread_t sig_listen_thread_id;
373
374static int do_ide = 0;
375static int do_inq = 1;
376static int do_leaf = 1;
377static int do_extra = 1;
378static int do_quiet = 0;
379static int checked_sg = 1;
380static int sum_of_resids = 0;
381
382static int dd_count = -1;
383static int in_full = 0;
384static int in_partial = 0;
385static int out_full = 0;
386static int out_partial = 0;
387static int do_coe = 0;
388int base = READWRITE_BASE_NUM;
389unsigned char *cmpbuf = 0;
390static unsigned char buff_a[SIZEOF_BUFFER + SG_HSZ + 12];
391static unsigned char *buffer = buff_a + OFFSET_HEADER;
392
393typedef struct my_sg_scsi_id {
394	int host_no;		/* as in "scsi<n>" where 'n' is one of 0, 1, 2 etc */
395	int channel;
396	int scsi_id;		/* scsi id of target device */
397	int lun;
398	int scsi_type;		/* TYPE_... defined in scsi/scsi.h */
399	short h_cmd_per_lun;	/* host (adapter) maximum commands per lun */
400	short d_queue_depth;	/* device (or adapter) maximum queue length */
401	int unused1;		/* probably find a good use, set 0 for now */
402	int unused2;		/* ditto */
403} My_sg_scsi_id;
404
405// Prototypes
406int do_scsi_sgp_read_write(char *device);
407int do_scsi_sgm_read_write(char *device);
408void sg_in_operation(Rq_coll * clp, Rq_elem * rep);
409void sg_out_operation(Rq_coll * clp, Rq_elem * rep);
410int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
411void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks);
412int sg_start_io(Rq_elem * rep);
413int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp);
414int run_sg_scan_tests(void);
415int show_scsi_logs(char *device);
416int validate_device(char *device);
417int show_devfs_devices(void);
418void usage(void);
419int do_scsi_device_read_write(char *device);
420int do_scsi_inquiry(char *device, int hex_flag);
421int show_scsi_maps(void);
422int show_scsi_modes(char *device);
423int do_scsi_read_buffer(char *device);
424int show_scsi_read_capacity(char *device);
425int do_scsi_reset_devices(char *device, int reset_opts);
426int do_scsi_send_diagnostics(char *device);
427int do_scsi_start_stop(char *device, int startstop);
428int do_scsi_read_write_buffer(char *device);
429int do_scsi_test_unit_ready(char *device);
430int show_scsi_info(char *device);
431void print_msg(int msg_num, const char *msg);
432static void scan_dev_type(const char *leadin, int max_dev, int do_numeric,
433			  int lin_dev_type, int last_sg_ind);
434
435#ifdef SG_IO
436int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra);
437#endif
438
439static unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
440    { 0x12, 0, 0, 0, INQ_REPLY_LEN, 0 };
441
442void print_msg(int msg_num, const char *msg)
443{
444	switch (msg_num) {
445	case TEST_START:
446		printf
447		    ("\n****************** Starting Tests ***************************\n");
448		break;
449	case TEST_STOP:
450		printf
451		    ("\n****************** Tests Complete ***************************\n");
452		break;
453	case TEST_BREAK:
454		printf("\n------------------ %s Test ------------------\n\n",
455		       msg);
456		break;
457	}
458}
459
460int main(int argc, char *argv[])
461{
462	int rc = 0;
463
464	if (argc < 2) {
465		printf("\n\nERROR:No device passed to test\n\n");
466		usage();
467		return 1;
468	}
469
470	rc = validate_device(argv[1]);
471	if (rc == 0) {
472
473		print_msg(TEST_START, NULL);
474
475		rc = run_sg_scan_tests();
476		if (rc != 0) {
477			printf("ERROR: run_sg_scan_tests failed %d\n", rc);
478		}
479
480		rc = show_scsi_logs(argv[1]);
481		if (rc != 0) {
482			printf("ERROR: show_scsi_logs failed %d\n", rc);
483		}
484
485		rc = show_devfs_devices();
486		if (rc != 0) {
487			printf("ERROR: show_devfs_devices failed %d\n", rc);
488		}
489
490		rc = do_scsi_device_read_write(argv[1]);
491		if (rc != 0) {
492			printf("ERROR: do_scsi_devices_read_write failed %d\n",
493			       rc);
494		}
495
496		rc = do_scsi_inquiry(argv[1], TRUE);
497		if (rc != 0) {
498			printf("ERROR: do_scsi_inquiry HEX failed %d\n", rc);
499		} else {
500			rc = do_scsi_inquiry(argv[1], FALSE);
501			if (rc != 0) {
502				printf("ERROR: do_scsi_inquiry PCI failed %d\n",
503				       rc);
504			}
505		}
506
507		rc = show_scsi_maps();
508		if (rc != 0) {
509			printf("ERROR: show_scsi_maps failed %d\n", rc);
510		}
511
512		rc = show_scsi_modes(argv[1]);
513		if (rc != 0) {
514			printf("ERROR: show_scsi_modes failed %d\n", rc);
515		}
516
517		rc = do_scsi_read_buffer(argv[1]);
518		if (rc != 0 && rc != 1) {
519			printf("ERROR: do_scsi_read_buffer failed %d\n", rc);
520		}
521
522		rc = show_scsi_read_capacity(argv[1]);
523		if (rc != 0) {
524			printf("ERROR: show_scsi_read_capacity failed %d\n",
525			       rc);
526		}
527
528		rc |= do_scsi_reset_devices(argv[1], DEVICE_RESET);
529		rc |= do_scsi_reset_devices(argv[1], BUS_RESET);
530		rc |= do_scsi_reset_devices(argv[1], HOST_RESET);
531		if (rc != 0) {
532			printf("ERROR: do_scsi_reset_devices failed %d\n", rc);
533		}
534
535		rc = do_scsi_send_diagnostics(argv[1]);
536		if (rc != 0) {
537			printf("ERROR: do_scsi_send_diagnostics failed %d\n",
538			       rc);
539		}
540
541		rc |= do_scsi_start_stop(argv[1], FALSE);
542		rc |= do_scsi_start_stop(argv[1], TRUE);
543		if (rc != 0) {
544			printf("ERROR: do_scsi_start_top failed %d\n", rc);
545		}
546
547		rc = do_scsi_read_write_buffer(argv[1]);
548		if (rc != 0 && rc != 1) {
549			printf("ERROR: do_scsi_read_write_buffer failed %d\n",
550			       rc);
551		}
552
553		rc = do_scsi_test_unit_ready(argv[1]);
554		if (rc != 0) {
555			printf("ERROR: do_scsi_test_unit_ready failed %d\n",
556			       rc);
557		}
558
559		rc = show_scsi_info(argv[1]);
560		if (rc != 0) {
561			printf("ERROR: show_scsi_info failed %d\n", rc);
562		}
563
564		rc = do_scsi_sgp_read_write(argv[1]);
565		if (rc != 0) {
566			printf("ERROR: do_scsi_sgp_read_write failed %d\n", rc);
567		}
568
569		rc = do_scsi_sgm_read_write(argv[1]);
570		if (rc != 0) {
571			printf("ERROR: do_scsi_sgm_read_write failed %d\n", rc);
572		}
573
574		print_msg(TEST_STOP, NULL);
575	} else {
576		printf("\nERROR: Invalid device passed to test\n\n\n");
577		usage();
578
579	}
580
581	return 0;
582}
583
584int validate_device(char *device)
585{
586	int rc = 0;
587	int i, found = FALSE;
588	char device_string[25];
589
590	for (i = 0; i < MAX_DEVICES && !found; i++) {
591		sprintf(device_string, "/dev/sg%d", i);
592		//printf("checking %s \n", device_string);
593		if (strcmp(device, device_string) == 0) {
594			found = TRUE;
595		}
596	}
597
598	return rc;
599}
600
601void usage()
602{
603	printf("Usage: 'sg_scan [-a] [-n] [-w] [-i] [-x]'\n");
604	printf("    where: -a   do alpha scan (ie sga, sgb, sgc)\n");
605	printf("           -n   do numeric scan (ie sg0, sg1...) [default]\n");
606	printf("           -w   force open with read/write flag\n");
607	printf("           -i   do SCSI INQUIRY, output results\n");
608	printf("           -x   extra information output about queuing\n\n\n");
609}
610
611void make_dev_name(char *fname, const char *leadin, int k, int do_numeric)
612{
613	char buff[64];
614	int big, little;
615
616	strcpy(fname, leadin ? leadin : "/dev/sg");
617	if (do_numeric) {
618		sprintf(buff, "%d", k);
619		strcat(fname, buff);
620	} else {
621		if (k < 26) {
622			buff[0] = 'a' + (char)k;
623			buff[1] = '\0';
624			strcat(fname, buff);
625		} else if (k <= 255) {	/* assumes sequence goes x,y,z,aa,ab,ac etc */
626			big = k / 26;
627			little = k - (26 * big);
628			big = big - 1;
629
630			buff[0] = 'a' + (char)big;
631			buff[1] = 'a' + (char)little;
632			buff[2] = '\0';
633			strcat(fname, buff);
634		} else
635			strcat(fname, "xxxx");
636	}
637}
638
639int run_sg_scan_tests()
640{
641	int sg_fd, res, k, f;
642	unsigned char inqBuff[OFF + INQ_REPLY_LEN];
643	int inqInLen = OFF + sizeof(inqCmdBlk);
644	int inqOutLen = OFF + INQ_REPLY_LEN;
645	unsigned char *buffp = inqBuff + OFF;
646	struct sg_header *isghp = (struct sg_header *)inqBuff;
647	int do_numeric = NUMERIC_SCAN_DEF;
648	int do_inquiry = 0;
649	int do_extra = 1;
650	int writeable = 0;
651	int num_errors = 0;
652	int num_silent = 0;
653	int eacces_err = 0;
654	char fname[64];
655	My_scsi_idlun my_idlun;
656	int host_no;
657	int flags;
658	int emul;
659
660	print_msg(TEST_BREAK, __FUNCTION__);
661
662	flags = writeable ? O_RDWR : OPEN_FLAG;
663
664	do_numeric = 1;
665	writeable = O_RDONLY;
666	do_inquiry = 1;
667	do_extra = 1;
668
669	for (k = 0, res = 0; (k < 1000) && (num_errors < MAX_ERRORS);
670	     ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
671		if (res < 0) {
672			snprintf(ebuff, EBUFF_SZ, ME "Error closing %s ",
673				 fname);
674			perror(ME "close error");
675			return 1;
676		}
677		make_dev_name(fname, NULL, k, do_numeric);
678
679		sg_fd = open(fname, flags | O_NONBLOCK);
680		if (sg_fd < 0) {
681			if (EBUSY == errno) {
682				printf
683				    ("%s: device busy (O_EXCL lock), skipping\n",
684				     fname);
685				continue;
686			} else if ((ENODEV == errno) || (ENOENT == errno) ||
687				   (ENXIO == errno)) {
688				++num_errors;
689				++num_silent;
690				continue;
691			} else {
692				if (EACCES == errno)
693					eacces_err = 1;
694				snprintf(ebuff, EBUFF_SZ,
695					 ME "Error opening %s ", fname);
696				perror(ebuff);
697				++num_errors;
698				continue;
699			}
700		}
701		res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
702		if (res < 0) {
703			snprintf(ebuff, EBUFF_SZ,
704				 ME "device %s failed on scsi ioctl, skip",
705				 fname);
706			perror(ebuff);
707			++num_errors;
708			continue;
709		}
710		res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
711		if (res < 0) {
712			snprintf(ebuff, EBUFF_SZ, ME "device %s failed on scsi "
713				 "ioctl(2), skip", fname);
714			perror(ebuff);
715			++num_errors;
716			continue;
717		}
718#ifdef SG_EMULATED_HOST
719		res = ioctl(sg_fd, SG_EMULATED_HOST, &emul);
720		if (res < 0) {
721			snprintf(ebuff, EBUFF_SZ,
722				 ME "device %s failed on sg ioctl(3), skip",
723				 fname);
724			perror(ebuff);
725			++num_errors;
726			continue;
727		}
728#else
729		emul = 0;
730#endif
731		printf("%s: scsi%d channel=%d id=%d lun=%d", fname, host_no,
732		       (my_idlun.dev_id >> 16) & 0xff, my_idlun.dev_id & 0xff,
733		       (my_idlun.dev_id >> 8) & 0xff);
734		if (emul)
735			printf(" [em]");
736#if 0
737		printf(", huid=%d", my_idlun.host_unique_id);
738#endif
739#ifdef SG_GET_RESERVED_SIZE
740		{
741			My_sg_scsi_id m_id;	/* compatible with sg_scsi_id_t in sg.h */
742
743			res = ioctl(sg_fd, SG_GET_SCSI_ID, &m_id);
744			if (res < 0) {
745				snprintf(ebuff, EBUFF_SZ,
746					 ME "device %s ioctls(4), skip", fname);
747				perror(ebuff);
748				++num_errors;
749				continue;
750			}
751			printf("  type=%d", m_id.scsi_type);
752			if (do_extra)
753				printf(" cmd_per_lun=%hd queue_depth=%hd\n",
754				       m_id.h_cmd_per_lun, m_id.d_queue_depth);
755			else
756				printf("\n");
757		}
758#else
759		printf("\n");
760#endif
761		if (!do_inquiry)
762			continue;
763
764#ifdef SG_IO
765		if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &f) >= 0) && (f >= 30000)) {
766			res = sg3_inq(sg_fd, inqBuff, do_extra);
767			continue;
768		}
769#endif
770		memset(isghp, 0, sizeof(struct sg_header));
771		isghp->reply_len = inqOutLen;
772		memcpy(inqBuff + OFF, inqCmdBlk, INQUIRY_CMDLEN);
773
774		if (O_RDWR == (flags & O_ACCMODE)) {	/* turn on blocking */
775			f = fcntl(sg_fd, F_GETFL);
776			fcntl(sg_fd, F_SETFL, f & (~O_NONBLOCK));
777		} else {
778			close(sg_fd);
779			sg_fd = open(fname, O_RDWR);
780		}
781
782		res = write(sg_fd, inqBuff, inqInLen);
783		if (res < 0) {
784			snprintf(ebuff, EBUFF_SZ, ME "device %s writing, skip",
785				 fname);
786			perror(ebuff);
787			++num_errors;
788			continue;
789		}
790		res = read(sg_fd, inqBuff, inqOutLen);
791		if (res < 0) {
792			snprintf(ebuff, EBUFF_SZ, ME "device %s reading, skip",
793				 fname);
794			perror(ebuff);
795			++num_errors;
796			continue;
797		}
798#ifdef SG_GET_RESERVED_SIZE
799		if (!sg_chk_n_print("Error from Inquiry", isghp->target_status,
800				    isghp->host_status, isghp->driver_status,
801				    isghp->sense_buffer, SG_MAX_SENSE))
802			continue;
803#else
804		if ((isghp->result != 0) || (0 != isghp->sense_buffer[0])) {
805			printf("Error from Inquiry: result=%d\n",
806			       isghp->result);
807			if (0 != isghp->sense_buffer[0])
808				sg_print_sense("Error from Inquiry",
809					       isghp->sense_buffer,
810					       SG_MAX_SENSE);
811			continue;
812		}
813#endif
814		f = (int)*(buffp + 7);
815		printf("    %.8s  %.16s  %.4s ", buffp + 8, buffp + 16,
816		       buffp + 32);
817		printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x]\n",
818		       ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1),
819		       (*buffp & 0xe0) >> 5);
820	}
821	if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
822		printf("Stopping because there are too many error\n");
823		if (eacces_err)
824			printf("    root access may be required\n");
825	}
826	return 0;
827}
828
829#ifdef SG_IO
830int sg3_inq(int sg_fd, unsigned char *inqBuff, int do_extra)
831{
832	sg_io_hdr_t io_hdr;
833	unsigned char sense_buffer[32];
834	int ok;
835
836	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
837	io_hdr.interface_id = 'S';
838	io_hdr.cmd_len = sizeof(inqCmdBlk);
839	io_hdr.mx_sb_len = sizeof(sense_buffer);
840	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
841	io_hdr.dxfer_len = INQ_REPLY_LEN;
842	io_hdr.dxferp = inqBuff;
843	io_hdr.cmdp = inqCmdBlk;
844	io_hdr.sbp = sense_buffer;
845	io_hdr.timeout = 20000;	/* 20000 millisecs == 20 seconds */
846
847	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
848		perror(ME "Inquiry SG_IO ioctl error");
849		return 1;
850	}
851
852	/* now for the error processing */
853	ok = 0;
854	switch (sg_err_category3(&io_hdr)) {
855	case SG_ERR_CAT_CLEAN:
856	case SG_ERR_CAT_RECOVERED:
857		ok = 1;
858		break;
859	default:		/* won't bother decoding other categories */
860		sg_chk_n_print3("INQUIRY command error", &io_hdr);
861		break;
862	}
863
864	if (ok) {		/* output result if it is available */
865		char *p = (char *)inqBuff;
866		int f = (int)*(p + 7);
867		printf("    %.8s  %.16s  %.4s ", p + 8, p + 16, p + 32);
868		printf("[wide=%d sync=%d cmdq=%d sftre=%d pq=0x%x] ",
869		       ! !(f & 0x20), ! !(f & 0x10), ! !(f & 2), ! !(f & 1),
870		       (*p & 0xe0) >> 5);
871		if (do_extra)
872			printf("dur=%ums\n", io_hdr.duration);
873		else
874			printf("\n");
875	}
876	return 0;
877}
878#endif
879
880static int do_logs(int sg_fd, int ppc, int sp, int pc, int pg_code,
881		   int paramp, void *resp, int mx_resp_len, int noisy)
882{
883	int res;
884	unsigned char logsCmdBlk[LOG_SENSE_CMDLEN] =
885	    { LOG_SENSE_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
886	unsigned char sense_b[SENSE_BUFF_LEN];
887	sg_io_hdr_t io_hdr;
888
889	logsCmdBlk[1] = (unsigned char)((ppc ? 2 : 0) | (sp ? 1 : 0));
890	logsCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
891	logsCmdBlk[5] = (unsigned char)((paramp >> 8) & 0xff);
892	logsCmdBlk[6] = (unsigned char)(paramp & 0xff);
893	if (mx_resp_len > 0xffff) {
894		printf(ME "mx_resp_len too big\n");
895		return -1;
896	}
897	logsCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
898	logsCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
899
900	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
901	io_hdr.interface_id = 'S';
902	io_hdr.cmd_len = sizeof(logsCmdBlk);
903	io_hdr.mx_sb_len = sizeof(sense_b);
904	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
905	io_hdr.dxfer_len = mx_resp_len;
906	io_hdr.dxferp = resp;
907	io_hdr.cmdp = logsCmdBlk;
908	io_hdr.sbp = sense_b;
909	io_hdr.timeout = DEF_TIMEOUT;
910
911	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
912		perror("SG_IO (log sense) error");
913		return -1;
914	}
915#if 0
916	printf("SG_IO ioctl: status=%d, info=%d, sb_len_wr=%d\n",
917	       io_hdr.status, io_hdr.info, io_hdr.sb_len_wr);
918#endif
919	res = sg_err_category3(&io_hdr);
920	switch (res) {
921	case SG_ERR_CAT_CLEAN:
922	case SG_ERR_CAT_RECOVERED:
923		return 0;
924	default:
925		if (noisy) {
926			char ebuff[EBUFF_SZ];
927			snprintf(ebuff, EBUFF_SZ, ME "ppc=%d, sp=%d, "
928				 "pc=%d, page_code=%x, paramp=%x\n    ", ppc,
929				 sp, pc, pg_code, paramp);
930			sg_chk_n_print3(ebuff, &io_hdr);
931		}
932		return -1;
933	}
934}
935
936static void dStrHex(const char *str, int len, int no_ascii)
937{
938	const char *p = str;
939	unsigned char c;
940	char buff[82];
941	int a = 0;
942	const int bpstart = 5;
943	const int cpstart = 60;
944	int cpos = cpstart;
945	int bpos = bpstart;
946	int i, k;
947
948	if (len <= 0)
949		return;
950	memset(buff, ' ', 80);
951	buff[80] = '\0';
952	k = sprintf(buff + 1, "%.2x", a);
953	buff[k + 1] = ' ';
954	if (bpos >= ((bpstart + (9 * 3))))
955		bpos++;
956
957	for (i = 0; i < len; i++) {
958		c = *p++;
959		bpos += 3;
960		if (bpos == (bpstart + (9 * 3)))
961			bpos++;
962		sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
963		buff[bpos + 2] = ' ';
964		if (no_ascii)
965			buff[cpos++] = ' ';
966		else {
967			if ((c < ' ') || (c >= 0x7f))
968				c = '.';
969			buff[cpos++] = c;
970		}
971		if (cpos > (cpstart + 15)) {
972			printf("%s\n", buff);
973			bpos = bpstart;
974			cpos = cpstart;
975			a += 16;
976			memset(buff, ' ', 80);
977			k = sprintf(buff + 1, "%.2x", a);
978			buff[k + 1] = ' ';
979		}
980	}
981	if (cpos > cpstart) {
982		printf("%s\n", buff);
983	}
984}
985
986static void show_page_name(int page_no)
987{
988	switch (page_no) {
989	case 0x0:
990		printf("    0x00    Supported log pages\n");
991		break;
992	case 0x1:
993		printf("    0x01    Buffer over-run/under-run\n");
994		break;
995	case 0x2:
996		printf("    0x02    Error counters (write)\n");
997		break;
998	case 0x3:
999		printf("    0x03    Error counters (read)\n");
1000		break;
1001	case 0x4:
1002		printf("    0x04    Error counters (read reverse)\n");
1003		break;
1004	case 0x5:
1005		printf("    0x05    Error counters (verify)\n");
1006		break;
1007	case 0x6:
1008		printf("    0x06    Non-medium errors\n");
1009		break;
1010	case 0x7:
1011		printf("    0x07    Last n error events\n");
1012		break;
1013	case 0x8:
1014		printf("    0x08    Format status (sbc2)\n");
1015		break;
1016	case 0xb:
1017		printf("    0x0b    Last n deferred errors of "
1018		       "asynchronous events\n");
1019		break;
1020	case 0xc:
1021		printf("    0x0c    Sequential Access (ssc-2)\n");
1022		break;
1023	case 0xd:
1024		printf("    0x0d    Temperature\n");
1025		break;
1026	case 0xe:
1027		printf("    0x0e    Start-stop cycle counter\n");
1028		break;
1029	case 0xf:
1030		printf("    0x0f    Application client\n");
1031		break;
1032	case 0x10:
1033		printf("    0x10    Self-test results\n");
1034		break;
1035	case 0x18:
1036		printf("    0x18    Protocol specific port\n");
1037		break;
1038	case 0x2e:
1039		printf("    0x2e    Tape alerts (ssc-2)\n");
1040		break;
1041	case 0x2f:
1042		printf("    0x2f    Informational exceptions (SMART)\n");
1043		break;
1044	default:
1045		printf("    0x%.2x\n", page_no);
1046		break;
1047	}
1048}
1049
1050static void show_buffer_under_overrun_page(unsigned char *resp, int len)
1051{
1052	int k, j, num, pl, count_basis, cause;
1053	unsigned char *ucp;
1054	unsigned char *xp;
1055	unsigned long long ull;
1056
1057	printf("Buffer over-run/under-run page\n");
1058	num = len - 4;
1059	ucp = &resp[0] + 4;
1060	while (num > 3) {
1061		pl = ucp[3] + 4;
1062		count_basis = (ucp[1] >> 5) & 0x7;
1063		printf("  Count basis: ");
1064		switch (count_basis) {
1065		case 0:
1066			printf("undefined");
1067			break;
1068		case 1:
1069			printf("per command");
1070			break;
1071		case 2:
1072			printf("per failed reconnect");
1073			break;
1074		case 3:
1075			printf("per unit of time");
1076			break;
1077		default:
1078			printf("reserved [0x%x]", count_basis);
1079			break;
1080		}
1081		cause = (ucp[1] >> 1) & 0xf;
1082		printf(", Cause: ");
1083		switch (cause) {
1084		case 0:
1085			printf("bus busy");
1086			break;
1087		case 1:
1088			printf("transfer rate too slow");
1089			break;
1090		default:
1091			printf("reserved [0x%x]", cause);
1092			break;
1093		}
1094		printf(", Type: ");
1095		if (ucp[1] & 1)
1096			printf("over-run");
1097		else
1098			printf("under-run");
1099		printf(", count");
1100		k = pl - 4;
1101		xp = ucp + 4;
1102		if (k > sizeof(ull)) {
1103			xp += (k - sizeof(ull));
1104			k = sizeof(ull);
1105		}
1106		ull = 0;
1107		for (j = 0; j < k; ++j) {
1108			if (j > 0)
1109				ull <<= 8;
1110			ull |= xp[j];
1111		}
1112		printf(" = %llu\n", ull);
1113		num -= pl;
1114		ucp += pl;
1115	}
1116}
1117
1118static void show_error_counter_page(unsigned char *resp, int len)
1119{
1120	int k, j, num, pl, pc;
1121	unsigned char *ucp;
1122	unsigned char *xp;
1123	unsigned long long ull;
1124
1125	switch (resp[0]) {
1126	case 2:
1127		printf("Write error counter page\n");
1128		break;
1129	case 3:
1130		printf("Read error counter page\n");
1131		break;
1132	case 4:
1133		printf("Read Reverse error counter page\n");
1134		break;
1135	case 5:
1136		printf("Verify error counter page\n");
1137		break;
1138	default:
1139		printf("expecting error counter page, got page=0x%x\n",
1140		       resp[0]);
1141		return;
1142	}
1143	num = len - 4;
1144	ucp = &resp[0] + 4;
1145	while (num > 3) {
1146		pc = (ucp[0] << 8) | ucp[1];
1147		pl = ucp[3] + 4;
1148		switch (pc) {
1149		case 0:
1150			printf("  Errors corrected without substantion delay");
1151			break;
1152		case 1:
1153			printf("  Errors corrected with possible delays");
1154			break;
1155		case 2:
1156			printf("  Total operations");
1157			break;
1158		case 3:
1159			printf("  Total errors corrected");
1160			break;
1161		case 4:
1162			printf("  Total times correction algorithm processed");
1163			break;
1164		case 5:
1165			printf("  Total bytes processed");
1166			break;
1167		case 6:
1168			printf("  Total uncorrected errors");
1169			break;
1170		default:
1171			printf("  Reserved or vendor specific [0x%x]", pc);
1172			break;
1173		}
1174		k = pl - 4;
1175		xp = ucp + 4;
1176		if (k > sizeof(ull)) {
1177			xp += (k - sizeof(ull));
1178			k = sizeof(ull);
1179		}
1180		ull = 0;
1181		for (j = 0; j < k; ++j) {
1182			if (j > 0)
1183				ull <<= 8;
1184			ull |= xp[j];
1185		}
1186		printf(" = %llu\n", ull);
1187		num -= pl;
1188		ucp += pl;
1189	}
1190}
1191
1192static void show_non_medium_error_page(unsigned char *resp, int len)
1193{
1194	int k, j, num, pl, pc;
1195	unsigned char *ucp;
1196	unsigned char *xp;
1197	unsigned long long ull;
1198
1199	printf("Non-medium error page\n");
1200	num = len - 4;
1201	ucp = &resp[0] + 4;
1202	while (num > 3) {
1203		pc = (ucp[0] << 8) | ucp[1];
1204		pl = ucp[3] + 4;
1205		switch (pc) {
1206		case 0:
1207			printf("  Non-medium error count");
1208			break;
1209		default:
1210			if (pc <= 0x7fff)
1211				printf("  Reserved [0x%x]", pc);
1212			else
1213				printf("  Vendor specific [0x%x]", pc);
1214			break;
1215		}
1216		k = pl - 4;
1217		xp = ucp + 4;
1218		if (k > sizeof(ull)) {
1219			xp += (k - sizeof(ull));
1220			k = sizeof(ull);
1221		}
1222		ull = 0;
1223		for (j = 0; j < k; ++j) {
1224			if (j > 0)
1225				ull <<= 8;
1226			ull |= xp[j];
1227		}
1228		printf(" = %llu\n", ull);
1229		num -= pl;
1230		ucp += pl;
1231	}
1232}
1233
1234const char *self_test_code[] = {
1235	"default", "background short", "background extended", "reserved",
1236	"aborted background", "foreground short", "foreground extended",
1237	"reserved"
1238};
1239
1240const char *self_test_result[] = {
1241	"completed without error",
1242	"aborted by SEND DIAGNOSTIC",
1243	"aborted other than by SEND DIAGNOSTIC",
1244	"unknown error, unable to complete",
1245	"self test completed with failure in test segment (which one unkown)",
1246	"first segment in self test failed",
1247	"second segment in self test failed",
1248	"another segment in self test failed",
1249	"reserved", "reserved", "reserved", "reserved", "reserved", "reserved",
1250	"reserved",
1251	"self test in progress"
1252};
1253
1254static void show_self_test_page(unsigned char *resp, int len)
1255{
1256	int k, num, n, res;
1257	unsigned char *ucp;
1258	unsigned long long ull;
1259
1260	num = len - 4;
1261	if (num < 0x190) {
1262		printf("badly formed self-test results page\n");
1263		return;
1264	}
1265	printf("Self-test results page\n");
1266	for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20) {
1267		n = (ucp[6] << 8) | ucp[7];
1268		if ((0 == n) && (0 == ucp[4]))
1269			break;
1270		printf("  Parameter code=%d, accumulated power-on hours=%d\n",
1271		       (ucp[0] << 8) | ucp[1], n);
1272		printf("    self test code: %s [%d]\n",
1273		       self_test_code[(ucp[4] >> 5) & 0x7],
1274		       (ucp[4] >> 5) & 0x7);
1275		res = ucp[4] & 0xf;
1276		printf("    self test result: %s [%d]\n",
1277		       self_test_result[res], res);
1278		if (ucp[5])
1279			printf("    self-test number=%d\n", (int)ucp[5]);
1280		ull = ucp[8];
1281		ull <<= 8;
1282		ull |= ucp[9];
1283		ull <<= 8;
1284		ull |= ucp[10];
1285		ull <<= 8;
1286		ull |= ucp[11];
1287		ull <<= 8;
1288		ull |= ucp[12];
1289		ull <<= 8;
1290		ull |= ucp[13];
1291		ull <<= 8;
1292		ull |= ucp[14];
1293		ull <<= 8;
1294		ull |= ucp[14];
1295		ull <<= 8;
1296		ull |= ucp[15];
1297		if ((0xffffffffffffffffULL != ull) && (res > 0) && (res < 0xf))
1298			printf("    address of first error=0x%llx\n", ull);
1299		if (ucp[16] & 0xf)
1300			printf("    sense key=0x%x, asc=0x%x, asq=0x%x\n",
1301			       ucp[16] & 0xf, ucp[17], ucp[18]);
1302	}
1303}
1304
1305static void show_Temperature_page(unsigned char *resp, int len, int hdr)
1306{
1307	int k, num, extra, pc;
1308	unsigned char *ucp;
1309
1310	num = len - 4;
1311	ucp = &resp[0] + 4;
1312	if (num < 4) {
1313		printf("badly formed Temperature log page\n");
1314		return;
1315	}
1316	if (hdr)
1317		printf("Temperature log page\n");
1318	for (k = num; k > 0; k -= extra, ucp += extra) {
1319		if (k < 3) {
1320			printf("short Temperature log page\n");
1321			return;
1322		}
1323		extra = ucp[3] + 4;
1324		pc = ((ucp[0] << 8) & 0xff) + ucp[1];
1325		if (0 == pc) {
1326			if (extra > 5) {
1327				if (ucp[5] < 0xff)
1328					printf("  Current temperature= %d C\n",
1329					       ucp[5]);
1330				else
1331					printf
1332					    ("  Current temperature=<not available>\n");
1333			}
1334		} else if (1 == pc) {
1335			if (extra > 5) {
1336				if (ucp[5] < 0xff)
1337					printf
1338					    ("  Reference temperature= %d C\n",
1339					     ucp[5]);
1340				else
1341					printf
1342					    ("  Reference temperature=<not available>\n");
1343			}
1344
1345		} else {
1346			printf("  parameter code=0x%x, contents in hex:\n", pc);
1347			dStrHex((const char *)ucp, extra, 1);
1348		}
1349	}
1350}
1351
1352static void show_IE_page(unsigned char *resp, int len, int full)
1353{
1354	int k, num, extra, pc;
1355	unsigned char *ucp;
1356
1357	num = len - 4;
1358	ucp = &resp[0] + 4;
1359	if (num < 4) {
1360		printf("badly formed Informational Exceptions log page\n");
1361		return;
1362	}
1363	if (full)
1364		printf("Informational Exceptions log page\n");
1365	for (k = num; k > 0; k -= extra, ucp += extra) {
1366		if (k < 3) {
1367			printf("short Informational Exceptions log page\n");
1368			return;
1369		}
1370		extra = ucp[3] + 4;
1371		pc = ((ucp[0] << 8) & 0xff) + ucp[1];
1372		if (0 == pc) {
1373			if (extra > 5) {
1374				if (full)
1375					printf("  IE asc=0x%x, ascq=0x%x",
1376					       ucp[4], ucp[5]);
1377				if (extra > 6) {
1378					if (full)
1379						printf(",");
1380					if (ucp[6] < 0xff)
1381						printf
1382						    ("  Current temperature=%d C",
1383						     ucp[6]);
1384					else
1385						printf
1386						    ("  Current temperature=<not available>");
1387				}
1388				printf("\n");
1389			}
1390		} else if (full) {
1391			printf("  parameter code=0x%x, contents in hex:\n", pc);
1392			dStrHex((const char *)ucp, extra, 1);
1393		}
1394	}
1395}
1396
1397static void show_ascii_page(unsigned char *resp, int len)
1398{
1399	int k, n, num;
1400
1401	if (len < 0) {
1402		printf("response has bad length\n");
1403		return;
1404	}
1405	num = len - 4;
1406	switch (resp[0]) {
1407	case 0:
1408		printf("Supported pages:\n");
1409		for (k = 0; k < num; ++k)
1410			show_page_name((int)resp[4 + k]);
1411		break;
1412	case 0x1:
1413		show_buffer_under_overrun_page(resp, len);
1414		break;
1415	case 0x2:
1416	case 0x3:
1417	case 0x4:
1418	case 0x5:
1419		show_error_counter_page(resp, len);
1420		break;
1421	case 0x6:
1422		show_non_medium_error_page(resp, len);
1423		break;
1424	case 0xd:
1425		show_Temperature_page(resp, len, 1);
1426		break;
1427	case 0xe:
1428		if (len < 40) {
1429			printf("badly formed start-stop cycle counter page\n");
1430			break;
1431		}
1432		printf("Start-stop cycle counter page\n");
1433		printf("  Date of manufacture, year: %.4s, week: %.2s\n",
1434		       &resp[8], &resp[12]);
1435		printf("  Accounting date, year: %.4s, week: %.2s\n",
1436		       &resp[18], &resp[22]);
1437		n = (resp[28] << 24) | (resp[29] << 16) | (resp[30] << 8) |
1438		    resp[31];
1439		printf("  Specified cycle count over device lifetime=%d\n", n);
1440		n = (resp[36] << 24) | (resp[37] << 16) | (resp[38] << 8) |
1441		    resp[39];
1442		printf("  Accumulated start-stop cycles=%d\n", n);
1443		break;
1444	case 0x10:
1445		show_self_test_page(resp, len);
1446		break;
1447	case 0x2f:
1448		show_IE_page(resp, len, 1);
1449		break;
1450	default:
1451		printf("No ascii information for page=0x%x, here is hex:\n",
1452		       resp[0]);
1453		dStrHex((const char *)resp, len, 1);
1454		break;
1455	}
1456}
1457
1458static int fetchTemperature(int sg_fd, int do_hex, unsigned char *resp,
1459			    int max_len)
1460{
1461	int res = 0;
1462
1463	if (0 == do_logs(sg_fd, 0, 0, 1, 0xd, 0, resp, max_len, 0))
1464		show_Temperature_page(resp, (resp[2] << 8) + resp[3] + 4, 0);
1465	else if (0 == do_logs(sg_fd, 0, 0, 1, 0x2f, 0, resp, max_len, 0))
1466		show_IE_page(resp, (resp[2] << 8) + resp[3] + 4, 0);
1467	else {
1468		printf
1469		    ("Unable to find temperature in either log page (temperature "
1470		     "or IE)\n");
1471		res = 1;
1472	}
1473	close(sg_fd);
1474	return res;
1475}
1476
1477int show_scsi_logs(char *device)
1478{
1479	int sg_fd, k, pg_len;
1480	char *file_name = 0;
1481	unsigned char rsp_buff[MX_ALLOC_LEN];
1482	int pg_code = 0;
1483	int pc = 1;		/* N.B. some disks only give data for current cumulative */
1484	int paramp = 0;
1485	int do_list = 0;
1486	int do_ppc = 0;
1487	int do_sp = 0;
1488	int do_hex = 0;
1489	int do_all = 1;
1490	int do_temp = 0;
1491	int oflags = O_RDWR | O_NONBLOCK;
1492
1493	file_name = device;
1494	print_msg(TEST_BREAK, __FUNCTION__);
1495
1496	if ((sg_fd = open(file_name, oflags)) < 0) {
1497		snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
1498			 file_name);
1499		perror(ebuff);
1500		return 1;
1501	}
1502	/* Just to be safe, check we have a new sg device by trying an ioctl */
1503	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
1504		printf(ME "%s doesn't seem to be a version 3 sg device\n",
1505		       file_name);
1506		close(sg_fd);
1507		return 1;
1508	}
1509	if (do_list || do_all)
1510		pg_code = PG_CODE_ALL;
1511	pg_len = 0;
1512	if (1 == do_temp)
1513		return fetchTemperature(sg_fd, do_hex, rsp_buff, MX_ALLOC_LEN);
1514
1515	if (0 == do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp,
1516			 rsp_buff, MX_ALLOC_LEN, 1)) {
1517		pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
1518		if ((pg_len + 4) > MX_ALLOC_LEN) {
1519			printf
1520			    ("Only fetched %d bytes of response, truncate output\n",
1521			     MX_ALLOC_LEN);
1522			pg_len = MX_ALLOC_LEN - 4;
1523		}
1524		if (do_hex) {
1525			printf("Returned log page code=0x%x,  page len=0x%x\n",
1526			       rsp_buff[0], pg_len);
1527			dStrHex((const char *)rsp_buff, pg_len + 4, 1);
1528		} else
1529			show_ascii_page(rsp_buff, pg_len + 4);
1530	}
1531	if (do_all && (pg_len > 1)) {
1532		int my_len = pg_len - 1;
1533		unsigned char parr[256];
1534
1535		memcpy(parr, rsp_buff + 5, my_len);
1536		for (k = 0; k < my_len; ++k) {
1537			printf("\n");
1538			pg_code = parr[k];
1539			if (0 ==
1540			    do_logs(sg_fd, do_ppc, do_sp, pc, pg_code, paramp,
1541				    rsp_buff, MX_ALLOC_LEN, 1)) {
1542				pg_len = (rsp_buff[2] << 8) + rsp_buff[3];
1543				if ((pg_len + 4) > MX_ALLOC_LEN) {
1544					printf
1545					    ("Only fetched %d bytes of response, truncate "
1546					     "output\n", MX_ALLOC_LEN);
1547					pg_len = MX_ALLOC_LEN - 4;
1548				}
1549				if (do_hex) {
1550					printf
1551					    ("Returned log page code=0x%x,  page len=0x%x\n",
1552					     rsp_buff[0], pg_len);
1553					dStrHex((const char *)rsp_buff,
1554						pg_len + 4, 1);
1555				} else
1556					show_ascii_page(rsp_buff, pg_len + 4);
1557			}
1558		}
1559	}
1560	close(sg_fd);
1561	return 0;
1562}
1563
1564static int do_inquiry(int sg_fd, void *resp, int mx_resp_len)
1565{
1566	int res;
1567	unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
1568	    { INQUIRY_CMD, 0, 0, 0, 0, 0 };
1569	unsigned char sense_b[SENSE_BUFF_LEN];
1570	sg_io_hdr_t io_hdr;
1571
1572	inqCmdBlk[4] = (unsigned char)mx_resp_len;
1573	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
1574	io_hdr.interface_id = 'S';
1575	io_hdr.cmd_len = sizeof(inqCmdBlk);
1576	io_hdr.mx_sb_len = sizeof(sense_b);
1577	io_hdr.dxfer_direction = SG_DXFER_TO_FROM_DEV;
1578	io_hdr.dxfer_len = mx_resp_len;
1579	io_hdr.dxferp = resp;
1580	io_hdr.cmdp = inqCmdBlk;
1581	io_hdr.timeout = DEF_TIMEOUT;
1582
1583	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
1584		perror("SG_IO (inquiry) error");
1585		return -1;
1586	}
1587	res = sg_err_category3(&io_hdr);
1588	switch (res) {
1589	case SG_ERR_CAT_CLEAN:
1590	case SG_ERR_CAT_RECOVERED:
1591		return 0;
1592	default:
1593		sg_chk_n_print3("Failed INQUIRY", &io_hdr);
1594		return -1;
1595	}
1596}
1597
1598void leaf_dir(const char *lf, unsigned int *larr)
1599{
1600	char name[NAME_LEN_MAX * 2];
1601	int res;
1602
1603	if (do_quiet) {
1604		printf("%u\t%u\t%u\t%u\n", larr[0], larr[1], larr[2], larr[3]);
1605		return;
1606	}
1607	printf("%u\t%u\t%u\t%u\t%s\n", larr[0], larr[1], larr[2], larr[3], lf);
1608	if (do_leaf) {
1609		struct dirent *de_entry;
1610		struct dirent *de_result;
1611		DIR *sdir;
1612		int outpos;
1613
1614		if (NULL == (sdir = opendir(lf))) {
1615			fprintf(stderr, "leaf_dir: opendir of %s: failed\n",
1616				lf);
1617			return;
1618		}
1619		de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX);
1620		if (NULL == de_entry)
1621			return;
1622		res = 0;
1623		printf("\t");
1624		outpos = 8;
1625		while (1) {
1626			res = readdir_r(sdir, de_entry, &de_result);
1627			if (0 != res) {
1628				fprintf(stderr,
1629					"leaf_dir: readdir_r of %s: %s\n", lf,
1630					strerror(res));
1631				res = -2;
1632				break;
1633			}
1634			if (de_result == NULL)
1635				break;
1636			strncpy(name, de_entry->d_name, NAME_LEN_MAX * 2);
1637			if ((0 == strcmp("..", name))
1638			    || (0 == strcmp(".", name)))
1639				continue;
1640			if (do_extra) {
1641				struct stat st;
1642				char devname[NAME_LEN_MAX * 2];
1643
1644				strncpy(devname, lf, NAME_LEN_MAX * 2);
1645				strcat(devname, "/");
1646				strcat(devname, name);
1647				if (stat(devname, &st) < 0)
1648					return;
1649				if (S_ISCHR(st.st_mode)) {
1650					strcat(name, "(c ");
1651					sprintf(name + strlen(name), "%d %d)",
1652						major(st.st_rdev),
1653						minor(st.st_rdev));
1654				} else if (S_ISBLK(st.st_mode)) {
1655					strcat(name, "(b ");
1656					sprintf(name + strlen(name), "%d %d)",
1657						major(st.st_rdev),
1658						minor(st.st_rdev));
1659				}
1660			}
1661			res = strlen(name);
1662			if ((outpos + res + 2) > 80) {
1663				printf("\n\t");
1664				outpos = 8;
1665			}
1666			printf("%s  ", name);
1667			outpos += res + 2;
1668		}
1669		printf("\n");
1670	}
1671	if (do_inq) {
1672		int sg_fd;
1673		char buff[64];
1674
1675		memset(buff, 0, sizeof(buff));
1676		strncpy(name, lf, NAME_LEN_MAX * 2);
1677		strcat(name, "/generic");
1678		if ((sg_fd = open(name, O_RDONLY)) < 0) {
1679			if (!checked_sg) {
1680				checked_sg = 1;
1681				if ((sg_fd = open("/dev/sg0", O_RDONLY)) >= 0)
1682					close(sg_fd);	/* try and get sg module loaded */
1683				sg_fd = open(name, O_RDONLY);
1684			}
1685			if (sg_fd < 0) {
1686				printf("Unable to open sg device: %s, %s\n",
1687				       name, strerror(errno));
1688				return;
1689			}
1690		}
1691		if (0 != do_inquiry(sg_fd, buff, 64))
1692			return;
1693		close(sg_fd);
1694		dStrHex(buff, 64, 0);
1695	}
1696}
1697
1698/* Return 0 -> ok, -1 -> opendir() error, -2 -> readdir_r error,
1699         -3 -> malloc error */
1700int hbtl_scan(const char *path, int level, unsigned int *larr)
1701{
1702	struct dirent *de_entry;
1703	struct dirent *de_result;
1704	char new_path[NAME_LEN_MAX * 2];
1705	DIR *sdir;
1706	int res;
1707	size_t level_slen;
1708
1709	level_slen = strlen(level_arr[level]);
1710	if (NULL == (sdir = opendir(path))) {
1711		fprintf(stderr, "hbtl_scan: opendir of %s: failed\n", path);
1712		return -1;
1713	}
1714	de_entry = malloc(sizeof(struct dirent) + NAME_LEN_MAX);
1715	if (NULL == de_entry)
1716		return -3;
1717	res = 0;
1718	while (1) {
1719		res = readdir_r(sdir, de_entry, &de_result);
1720		if (0 != res) {
1721			fprintf(stderr, "hbtl_scan: readdir_r of %s: %s\n",
1722				path, strerror(res));
1723			res = -2;
1724			break;
1725		}
1726		if (de_result == NULL)
1727			break;
1728		if (0 ==
1729		    strncmp(level_arr[level], de_entry->d_name, level_slen)) {
1730			if (1 !=
1731			    sscanf(de_entry->d_name + level_slen, "%u",
1732				   larr + level))
1733				larr[level] = UINT_MAX;
1734			strncpy(new_path, path, NAME_LEN_MAX * 2);
1735			strcat(new_path, "/");
1736			strcat(new_path, de_entry->d_name);
1737			if ((level + 1) < LEVELS) {
1738				res = hbtl_scan(new_path, level + 1, larr);
1739				if (res < 0)
1740					break;
1741			} else
1742				leaf_dir(new_path, larr);
1743		}
1744	}
1745	free(de_entry);
1746	closedir(sdir);
1747	return res;
1748}
1749
1750int show_devfs_devices()
1751{
1752	int res;
1753	char ds_root[D_ROOT_SZ];
1754	char di_root[D_ROOT_SZ];
1755	unsigned int larr[LEVELS];
1756	struct stat st;
1757
1758	print_msg(TEST_BREAK, __FUNCTION__);
1759	strncpy(ds_root, "/dev", D_ROOT_SZ);
1760
1761	strncpy(di_root, ds_root, D_ROOT_SZ);
1762
1763	strcat(di_root, "/.devfsd");
1764
1765	if (stat(di_root, &st) < 0) {
1766		printf("Didn't find %s so perhaps devfs is not present,"
1767		       " attempting to continue ...\n", di_root);
1768	}
1769
1770	strncpy(di_root, ds_root, D_ROOT_SZ);
1771	strcat(ds_root, "/scsi");
1772	strcat(di_root, "/ide");
1773
1774	if (!do_ide)
1775		printf("SCSI scan:\n");
1776
1777	res = hbtl_scan(ds_root, 0, larr);
1778
1779	if (res < 0)
1780		printf("main: scsi hbtl_scan res=%d\n", res);
1781
1782	do_ide = TRUE;
1783	do_inq = 0;		/* won't try SCSI INQUIRY on IDE devices */
1784
1785	if (do_ide) {
1786		printf("\nIDE scan:\n");
1787		res = hbtl_scan(di_root, 0, larr);
1788
1789		if (res < 0)
1790			printf("main: ide hbtl_scan res=%d\n", res);
1791	}
1792	return 0;
1793}
1794
1795static void install_handler(int sig_num, void (*sig_handler) (int sig))
1796{
1797	struct sigaction sigact;
1798	sigaction(sig_num, NULL, &sigact);
1799	if (sigact.sa_handler != SIG_IGN) {
1800		sigact.sa_handler = sig_handler;
1801		sigemptyset(&sigact.sa_mask);
1802		sigact.sa_flags = 0;
1803		sigaction(sig_num, &sigact, NULL);
1804	}
1805}
1806
1807void print_stats()
1808{
1809	if (0 != dd_count)
1810		fprintf(stderr, "  remaining block count=%d\n", dd_count);
1811	fprintf(stderr, "%d+%d records in\n", in_full - in_partial, in_partial);
1812	fprintf(stderr, "%d+%d records out\n", out_full - out_partial,
1813		out_partial);
1814}
1815
1816static void interrupt_handler(int sig)
1817{
1818	struct sigaction sigact;
1819
1820	sigact.sa_handler = SIG_DFL;
1821	sigemptyset(&sigact.sa_mask);
1822	sigact.sa_flags = 0;
1823	sigaction(sig, &sigact, NULL);
1824	fprintf(stderr, "Interrupted by signal,");
1825	print_stats();
1826	kill(getpid(), sig);
1827}
1828
1829static void siginfo_handler(int sig)
1830{
1831	fprintf(stderr, "Progress report, continuing ...\n");
1832	print_stats();
1833}
1834
1835int dd_filetype(const char *filename)
1836{
1837	struct stat st;
1838	size_t len = strlen(filename);
1839
1840	if ((1 == len) && ('.' == filename[0]))
1841		return FT_DEV_NULL;
1842	if (stat(filename, &st) < 0)
1843		return FT_OTHER;
1844	if (S_ISCHR(st.st_mode)) {
1845		if ((MEM_MAJOR == major(st.st_rdev)) &&
1846		    (DEV_NULL_MINOR_NUM == minor(st.st_rdev)))
1847			return FT_DEV_NULL;
1848		if (RAW_MAJOR == major(st.st_rdev))
1849			return FT_RAW;
1850		if (SCSI_GENERIC_MAJOR == major(st.st_rdev))
1851			return FT_SG;
1852		if (SCSI_TAPE_MAJOR == major(st.st_rdev))
1853			return FT_ST;
1854	} else if (S_ISBLK(st.st_mode))
1855		return FT_BLOCK;
1856	return FT_OTHER;
1857}
1858
1859int read_capacity(int sg_fd, int *num_sect, int *sect_sz)
1860{
1861	int res;
1862	unsigned char rcCmdBlk[10] =
1863	    { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1864	unsigned char rcBuff[READ_CAP_REPLY_LEN];
1865	unsigned char sense_b[64];
1866	sg_io_hdr_t io_hdr;
1867
1868	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
1869	io_hdr.interface_id = 'S';
1870	io_hdr.cmd_len = sizeof(rcCmdBlk);
1871	io_hdr.mx_sb_len = sizeof(sense_b);
1872	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
1873	io_hdr.dxfer_len = sizeof(rcBuff);
1874	io_hdr.dxferp = rcBuff;
1875	io_hdr.cmdp = rcCmdBlk;
1876	io_hdr.sbp = sense_b;
1877	io_hdr.timeout = DEF_TIMEOUT;
1878
1879	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
1880		perror("read_capacity (SG_IO) error");
1881		return -1;
1882	}
1883	res = sg_err_category3(&io_hdr);
1884	if (SG_ERR_CAT_MEDIA_CHANGED == res)
1885		return 2;	/* probably have another go ... */
1886	else if (SG_ERR_CAT_CLEAN != res) {
1887		sg_chk_n_print3("read capacity", &io_hdr);
1888		return -1;
1889	}
1890	*num_sect = 1 + ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
1891			 (rcBuff[2] << 8) | rcBuff[3]);
1892	*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
1893	    (rcBuff[6] << 8) | rcBuff[7];
1894	return 0;
1895}
1896
1897/* Return of 0 -> success, -1 -> failure, 2 -> try again */
1898int sync_cache(int sg_fd)
1899{
1900	int res;
1901	unsigned char scCmdBlk[10] = { SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0,
1902		0, 0, 0
1903	};
1904	unsigned char sense_b[64];
1905	sg_io_hdr_t io_hdr;
1906
1907	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
1908	io_hdr.interface_id = 'S';
1909	io_hdr.cmd_len = sizeof(scCmdBlk);
1910	io_hdr.mx_sb_len = sizeof(sense_b);
1911	io_hdr.dxfer_direction = SG_DXFER_NONE;
1912	io_hdr.dxfer_len = 0;
1913	io_hdr.dxferp = NULL;
1914	io_hdr.cmdp = scCmdBlk;
1915	io_hdr.sbp = sense_b;
1916	io_hdr.timeout = DEF_TIMEOUT;
1917
1918	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
1919		perror("synchronize_cache (SG_IO) error");
1920		return -1;
1921	}
1922	res = sg_err_category3(&io_hdr);
1923	if (SG_ERR_CAT_MEDIA_CHANGED == res)
1924		return 2;	/* probably have another go ... */
1925	else if (SG_ERR_CAT_CLEAN != res) {
1926		sg_chk_n_print3("synchronize cache", &io_hdr);
1927		return -1;
1928	}
1929	return 0;
1930}
1931
1932int sg_build_scsi_cdb(unsigned char *cdbp, int cdb_sz, unsigned int blocks,
1933		      unsigned int start_block, int write_true, int fua,
1934		      int dpo)
1935{
1936	int rd_opcode[] = { 0x8, 0x28, 0xa8, 0x88 };
1937	int wr_opcode[] = { 0xa, 0x2a, 0xaa, 0x8a };
1938	int sz_ind;
1939
1940	memset(cdbp, 0, cdb_sz);
1941	if (dpo)
1942		cdbp[1] |= 0x10;
1943	if (fua)
1944		cdbp[1] |= 0x8;
1945	switch (cdb_sz) {
1946	case 6:
1947		sz_ind = 0;
1948		cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
1949					  rd_opcode[sz_ind]);
1950		cdbp[1] = (unsigned char)((start_block >> 16) & 0x1f);
1951		cdbp[2] = (unsigned char)((start_block >> 8) & 0xff);
1952		cdbp[3] = (unsigned char)(start_block & 0xff);
1953		cdbp[4] = (256 == blocks) ? 0 : (unsigned char)blocks;
1954		if (blocks > 256) {
1955			fprintf(stderr,
1956				ME "for 6 byte commands, maximum number of "
1957				"blocks is 256\n");
1958			return 1;
1959		}
1960		if ((start_block + blocks - 1) & (~0x1fffff)) {
1961			fprintf(stderr,
1962				ME "for 6 byte commands, can't address blocks"
1963				" beyond %d\n", 0x1fffff);
1964			return 1;
1965		}
1966		if (dpo || fua) {
1967			fprintf(stderr,
1968				ME "for 6 byte commands, neither dpo nor fua"
1969				" bits supported\n");
1970			return 1;
1971		}
1972		break;
1973	case 10:
1974		sz_ind = 1;
1975		cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
1976					  rd_opcode[sz_ind]);
1977		cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
1978		cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
1979		cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
1980		cdbp[5] = (unsigned char)(start_block & 0xff);
1981		cdbp[7] = (unsigned char)((blocks >> 8) & 0xff);
1982		cdbp[8] = (unsigned char)(blocks & 0xff);
1983		if (blocks & (~0xffff)) {
1984			fprintf(stderr,
1985				ME "for 10 byte commands, maximum number of "
1986				"blocks is %d\n", 0xffff);
1987			return 1;
1988		}
1989		break;
1990	case 12:
1991		sz_ind = 2;
1992		cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
1993					  rd_opcode[sz_ind]);
1994		cdbp[2] = (unsigned char)((start_block >> 24) & 0xff);
1995		cdbp[3] = (unsigned char)((start_block >> 16) & 0xff);
1996		cdbp[4] = (unsigned char)((start_block >> 8) & 0xff);
1997		cdbp[5] = (unsigned char)(start_block & 0xff);
1998		cdbp[6] = (unsigned char)((blocks >> 24) & 0xff);
1999		cdbp[7] = (unsigned char)((blocks >> 16) & 0xff);
2000		cdbp[8] = (unsigned char)((blocks >> 8) & 0xff);
2001		cdbp[9] = (unsigned char)(blocks & 0xff);
2002		break;
2003	case 16:
2004		sz_ind = 3;
2005		cdbp[0] = (unsigned char)(write_true ? wr_opcode[sz_ind] :
2006					  rd_opcode[sz_ind]);
2007		/* can't cope with block number > 32 bits (yet) */
2008		cdbp[6] = (unsigned char)((start_block >> 24) & 0xff);
2009		cdbp[7] = (unsigned char)((start_block >> 16) & 0xff);
2010		cdbp[8] = (unsigned char)((start_block >> 8) & 0xff);
2011		cdbp[9] = (unsigned char)(start_block & 0xff);
2012		cdbp[10] = (unsigned char)((blocks >> 24) & 0xff);
2013		cdbp[11] = (unsigned char)((blocks >> 16) & 0xff);
2014		cdbp[12] = (unsigned char)((blocks >> 8) & 0xff);
2015		cdbp[13] = (unsigned char)(blocks & 0xff);
2016		break;
2017	default:
2018		fprintf(stderr,
2019			ME "expected cdb size of 6, 10, 12, or 16 but got"
2020			"=%d\n", cdb_sz);
2021		return 1;
2022	}
2023	return 0;
2024}
2025
2026/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2027   2 -> try again */
2028int sg_read(int sg_fd, unsigned char *buff, int blocks, int from_block,
2029	    int bs, int cdbsz, int fua, int *diop)
2030{
2031	unsigned char rdCmd[MAX_SCSI_CDBSZ];
2032	unsigned char senseBuff[SENSE_BUFF_LEN];
2033	sg_io_hdr_t io_hdr;
2034
2035	if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) {
2036		fprintf(stderr,
2037			ME "bad rd cdb build, from_block=%d, blocks=%d\n",
2038			from_block, blocks);
2039		return -1;
2040	}
2041
2042	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
2043	io_hdr.interface_id = 'S';
2044	io_hdr.cmd_len = cdbsz;
2045	io_hdr.cmdp = rdCmd;
2046	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
2047	io_hdr.dxfer_len = bs * blocks;
2048	io_hdr.dxferp = buff;
2049	io_hdr.mx_sb_len = SENSE_BUFF_LEN;
2050	io_hdr.sbp = senseBuff;
2051	io_hdr.timeout = DEF_TIMEOUT;
2052	io_hdr.pack_id = from_block;
2053	if (diop && *diop)
2054		io_hdr.flags |= SG_FLAG_DIRECT_IO;
2055
2056	if (ioctl(sg_fd, SG_IO, &io_hdr)) {
2057		if (ENOMEM == errno)
2058			return 1;
2059		perror("reading (SG_IO) on sg device, error");
2060		return -1;
2061	}
2062	switch (sg_err_category3(&io_hdr)) {
2063	case SG_ERR_CAT_CLEAN:
2064		break;
2065	case SG_ERR_CAT_RECOVERED:
2066		fprintf(stderr,
2067			"Recovered error while reading block=%d, num=%d\n",
2068			from_block, blocks);
2069		break;
2070	case SG_ERR_CAT_MEDIA_CHANGED:
2071		return 2;
2072	default:
2073		sg_chk_n_print3("reading", &io_hdr);
2074		if (do_coe) {
2075			memset(buff, 0, bs * blocks);
2076			fprintf(stderr, ">> unable to read at blk=%d for "
2077				"%d bytes, use zeros\n", from_block,
2078				bs * blocks);
2079			return 0;	/* fudge success */
2080		} else
2081			return -1;
2082	}
2083	if (diop && *diop &&
2084	    ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
2085		*diop = 0;	/* flag that dio not done (completely) */
2086	sum_of_resids += io_hdr.resid;
2087#if SG_DEBUG
2088	fprintf(stderr, "duration=%u ms\n", io_hdr.duration);
2089#endif
2090	return 0;
2091}
2092
2093/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
2094   2 -> try again */
2095int sg_write(int sg_fd, unsigned char *buff, int blocks, int to_block,
2096	     int bs, int cdbsz, int fua, int *diop)
2097{
2098	unsigned char wrCmd[MAX_SCSI_CDBSZ];
2099	unsigned char senseBuff[SENSE_BUFF_LEN];
2100	sg_io_hdr_t io_hdr;
2101
2102	if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) {
2103		fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
2104			to_block, blocks);
2105		return -1;
2106	}
2107
2108	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
2109	io_hdr.interface_id = 'S';
2110	io_hdr.cmd_len = cdbsz;
2111	io_hdr.cmdp = wrCmd;
2112	io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
2113	io_hdr.dxfer_len = bs * blocks;
2114	io_hdr.dxferp = buff;
2115	io_hdr.mx_sb_len = SENSE_BUFF_LEN;
2116	io_hdr.sbp = senseBuff;
2117	io_hdr.timeout = DEF_TIMEOUT;
2118	io_hdr.pack_id = to_block;
2119	if (diop && *diop)
2120		io_hdr.flags |= SG_FLAG_DIRECT_IO;
2121
2122	if (ioctl(sg_fd, SG_IO, &io_hdr)) {
2123		if (ENOMEM == errno)
2124			return 1;
2125		perror("writing (SG_IO) on sg device, error");
2126		return -1;
2127	}
2128	switch (sg_err_category3(&io_hdr)) {
2129	case SG_ERR_CAT_CLEAN:
2130		break;
2131	case SG_ERR_CAT_RECOVERED:
2132		fprintf(stderr,
2133			"Recovered error while writing block=%d, num=%d\n",
2134			to_block, blocks);
2135		break;
2136	case SG_ERR_CAT_MEDIA_CHANGED:
2137		return 2;
2138	default:
2139		sg_chk_n_print3("writing", &io_hdr);
2140		if (do_coe) {
2141			fprintf(stderr, ">> ignored errors for out blk=%d for "
2142				"%d bytes\n", to_block, bs * blocks);
2143			return 0;	/* fudge success */
2144		} else
2145			return -1;
2146	}
2147	if (diop && *diop &&
2148	    ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
2149		*diop = 0;	/* flag that dio not done (completely) */
2150	return 0;
2151}
2152
2153int get_num(char *buf)
2154{
2155	int res, num;
2156	char c;
2157
2158	res = sscanf(buf, "%d%c", &num, &c);
2159	if (0 == res)
2160		return -1;
2161	else if (1 == res)
2162		return num;
2163	else {
2164		switch (c) {
2165		case 'c':
2166		case 'C':
2167			return num;
2168		case 'b':
2169		case 'B':
2170			return num * 512;
2171		case 'k':
2172			return num * 1024;
2173		case 'K':
2174			return num * 1000;
2175		case 'm':
2176			return num * 1024 * 1024;
2177		case 'M':
2178			return num * 1000000;
2179		case 'g':
2180			return num * 1024 * 1024 * 1024;
2181		case 'G':
2182			return num * 1000000000;
2183		default:
2184			fprintf(stderr, "unrecognized multiplier\n");
2185			return -1;
2186		}
2187	}
2188}
2189
2190int do_scsi_device_read_write(char *device)
2191{
2192	int skip = 0;
2193	int seek = 0;
2194	int bs = 0;
2195	int ibs = 0;
2196	int obs = 0;
2197	int bpt = DEF_BLOCKS_PER_TRANSFER;
2198	char inf[INOUTF_SZ];
2199	int in_type = FT_OTHER;
2200	char outf[INOUTF_SZ];
2201	int out_type = FT_OTHER;
2202	int dio = 0;
2203	int dio_incomplete = 0;
2204	int do_time = 1;
2205	int do_odir = 1;
2206	int scsi_cdbsz = DEF_SCSI_CDBSZ;
2207	int fua_mode = 0;
2208	int do_sync = 1;
2209	int do_blk_sgio = 1;
2210	int do_append = 1;
2211	int res, t, buf_sz, dio_tmp;
2212	int infd, outfd, blocks;
2213	unsigned char *wrkBuff;
2214	unsigned char *wrkPos;
2215	int in_num_sect = 0;
2216	int out_num_sect = 0;
2217	int in_sect_sz, out_sect_sz;
2218	char ebuff[EBUFF_SZ];
2219	int blocks_per;
2220	int req_count;
2221	struct timeval start_tm, end_tm;
2222
2223	print_msg(TEST_BREAK, __FUNCTION__);
2224	strcpy(inf, "/dev/zero");
2225	strcpy(outf, device);
2226
2227	if (bs <= 0) {
2228		bs = DEF_BLOCK_SIZE;
2229		fprintf(stderr,
2230			"Assume default 'bs' (block size) of %d bytes\n", bs);
2231	}
2232	if ((ibs && (ibs != bs)) || (obs && (obs != bs))) {
2233		fprintf(stderr,
2234			"If 'ibs' or 'obs' given must be same as 'bs'\n");
2235		usage();
2236		return 1;
2237	}
2238	if ((skip < 0) || (seek < 0)) {
2239		fprintf(stderr, "skip and seek cannot be negative\n");
2240		return 1;
2241	}
2242	if ((do_append > 0) && (seek > 0)) {
2243		fprintf(stderr, "Can't use both append and seek switches\n");
2244		return 1;
2245	}
2246#ifdef SG_DEBUG
2247	fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
2248		inf, skip, outf, seek, dd_count);
2249#endif
2250	install_handler(SIGINT, interrupt_handler);
2251	install_handler(SIGQUIT, interrupt_handler);
2252	install_handler(SIGPIPE, interrupt_handler);
2253	install_handler(SIGUSR1, siginfo_handler);
2254
2255	infd = STDIN_FILENO;
2256	outfd = STDOUT_FILENO;
2257	if (inf[0] && ('-' != inf[0])) {
2258		in_type = dd_filetype(inf);
2259
2260		if ((FT_BLOCK & in_type) && do_blk_sgio)
2261			in_type |= FT_SG;
2262
2263		if (FT_ST == in_type) {
2264			fprintf(stderr,
2265				ME "unable to use scsi tape device %s\n", inf);
2266			return 1;
2267		} else if (FT_SG & in_type) {
2268			if ((infd = open(inf, O_RDWR)) < 0) {
2269				snprintf(ebuff, EBUFF_SZ,
2270					 ME "could not open %s for sg reading",
2271					 inf);
2272				perror(ebuff);
2273				return 1;
2274			}
2275			t = bs * bpt;
2276			res = ioctl(infd, SG_SET_RESERVED_SIZE, &t);
2277			if (res < 0)
2278				perror(ME "SG_SET_RESERVED_SIZE error");
2279			res = ioctl(infd, SG_GET_VERSION_NUM, &t);
2280			if ((res < 0) || (t < 30000)) {
2281				if (FT_BLOCK & in_type)
2282					fprintf(stderr,
2283						ME
2284						"SG_IO unsupported on this block"
2285						" device\n");
2286				else
2287					fprintf(stderr,
2288						ME
2289						"sg driver prior to 3.x.y\n");
2290				return 1;
2291			}
2292		} else {
2293			if (do_odir && (FT_BLOCK == in_type))
2294				infd = open(inf, O_RDONLY | O_DIRECT);
2295			else
2296				infd = open(inf, O_RDONLY);
2297			if (infd < 0) {
2298				snprintf(ebuff, EBUFF_SZ,
2299					 ME "could not open %s for reading",
2300					 inf);
2301				perror(ebuff);
2302				return 1;
2303			} else if (skip > 0) {
2304				llse_loff_t offset = skip;
2305
2306				offset *= bs;	/* could exceed 32 bits here! */
2307				if (llse_llseek(infd, offset, SEEK_SET) < 0) {
2308					snprintf(ebuff, EBUFF_SZ,
2309						 ME
2310						 "couldn't skip to required position on %s",
2311						 inf);
2312					perror(ebuff);
2313					return 1;
2314				}
2315			}
2316		}
2317	}
2318
2319	if (outf[0] && ('-' != outf[0])) {
2320		out_type = dd_filetype(outf);
2321
2322		if ((FT_BLOCK & out_type) && do_blk_sgio)
2323			out_type |= FT_SG;
2324
2325		if (FT_ST == out_type) {
2326			fprintf(stderr,
2327				ME "unable to use scsi tape device %s\n", outf);
2328			return 1;
2329		} else if (FT_SG & out_type) {
2330			if ((outfd = open(outf, O_RDWR)) < 0) {
2331				snprintf(ebuff, EBUFF_SZ,
2332					 ME "could not open %s for sg writing",
2333					 outf);
2334				perror(ebuff);
2335				return 1;
2336			}
2337			t = bs * bpt;
2338			res = ioctl(outfd, SG_SET_RESERVED_SIZE, &t);
2339			if (res < 0)
2340				perror(ME "SG_SET_RESERVED_SIZE error");
2341			res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
2342			if ((res < 0) || (t < 30000)) {
2343				fprintf(stderr,
2344					ME "sg driver prior to 3.x.y\n");
2345				return 1;
2346			}
2347		} else if (FT_DEV_NULL & out_type)
2348			outfd = -1;	/* don't bother opening */
2349		else {
2350			if (FT_RAW != out_type) {
2351				int flags = O_WRONLY | O_CREAT;
2352
2353				if (do_odir && (FT_BLOCK == out_type))
2354					flags |= O_DIRECT;
2355				else if (do_append)
2356					flags |= O_APPEND;
2357				if ((outfd = open(outf, flags, 0666)) < 0) {
2358					snprintf(ebuff, EBUFF_SZ,
2359						 ME
2360						 "could not open %s for writing",
2361						 outf);
2362					perror(ebuff);
2363					return 1;
2364				}
2365			} else {
2366				if ((outfd = open(outf, O_WRONLY)) < 0) {
2367					snprintf(ebuff, EBUFF_SZ,
2368						 ME
2369						 "could not open %s for raw writing",
2370						 outf);
2371					perror(ebuff);
2372					return 1;
2373				}
2374			}
2375			if (seek > 0) {
2376				llse_loff_t offset = seek;
2377
2378				offset *= bs;	/* could exceed 32 bits here! */
2379				if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
2380					snprintf(ebuff, EBUFF_SZ,
2381						 ME
2382						 "couldn't seek to required position on %s",
2383						 outf);
2384					perror(ebuff);
2385					return 1;
2386				}
2387			}
2388		}
2389	}
2390	if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
2391		fprintf(stderr,
2392			"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
2393		return 1;
2394	}
2395
2396	if (dd_count < 0) {
2397		if (FT_SG & in_type) {
2398			res = read_capacity(infd, &in_num_sect, &in_sect_sz);
2399			if (2 == res) {
2400				fprintf(stderr,
2401					"Unit attention, media changed(in), continuing\n");
2402				res =
2403				    read_capacity(infd, &in_num_sect,
2404						  &in_sect_sz);
2405			}
2406			if (0 != res) {
2407				fprintf(stderr,
2408					"Unable to read capacity on %s\n", inf);
2409				in_num_sect = -1;
2410			} else {
2411				if (in_num_sect > skip)
2412					in_num_sect -= skip;
2413			}
2414		}
2415		if (FT_SG & out_type) {
2416			res = read_capacity(outfd, &out_num_sect, &out_sect_sz);
2417			if (2 == res) {
2418				fprintf(stderr,
2419					"Unit attention, media changed(out), continuing\n");
2420				res =
2421				    read_capacity(outfd, &out_num_sect,
2422						  &out_sect_sz);
2423			}
2424			if (0 != res) {
2425				fprintf(stderr,
2426					"Unable to read capacity on %s\n",
2427					outf);
2428				out_num_sect = -1;
2429			} else {
2430				if (out_num_sect > seek)
2431					out_num_sect -= seek;
2432			}
2433		}
2434#ifdef SG_DEBUG
2435		fprintf(stderr,
2436			"Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
2437			dd_count, in_num_sect, out_num_sect);
2438#endif
2439		if (in_num_sect > 0) {
2440			if (out_num_sect > 0)
2441				dd_count =
2442				    (in_num_sect >
2443				     out_num_sect) ? out_num_sect : in_num_sect;
2444			else
2445				dd_count = in_num_sect;
2446		} else
2447			dd_count = out_num_sect;
2448	}
2449	if (dd_count < 0) {
2450		fprintf(stderr, "Couldn't calculate count, please give one\n");
2451		return 1;
2452	}
2453
2454	if (dio || do_odir || (FT_RAW == in_type) || (FT_RAW == out_type)) {
2455		size_t psz = getpagesize();
2456		wrkBuff = malloc(bs * bpt + psz);
2457		if (0 == wrkBuff) {
2458			fprintf(stderr, "Not enough user memory for raw\n");
2459			return 1;
2460		}
2461		wrkPos = (unsigned char *)(((unsigned long)wrkBuff + psz - 1) &
2462					   (~(psz - 1)));
2463	} else {
2464		wrkBuff = malloc(bs * bpt);
2465		if (0 == wrkBuff) {
2466			fprintf(stderr, "Not enough user memory\n");
2467			return 1;
2468		}
2469		wrkPos = wrkBuff;
2470	}
2471
2472	blocks_per = bpt;
2473#ifdef SG_DEBUG
2474	fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
2475		dd_count, blocks_per);
2476#endif
2477	if (do_time) {
2478		start_tm.tv_sec = 0;
2479		start_tm.tv_usec = 0;
2480		gettimeofday(&start_tm, NULL);
2481	}
2482	req_count = dd_count;
2483
2484	while (dd_count > 0) {
2485		blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
2486		if (FT_SG & in_type) {
2487			int fua = fua_mode & 2;
2488
2489			dio_tmp = dio;
2490			res =
2491			    sg_read(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
2492				    fua, &dio_tmp);
2493			if (1 == res) {	/* ENOMEM, find what's available+try that */
2494				if (ioctl(infd, SG_GET_RESERVED_SIZE, &buf_sz) <
2495				    0) {
2496					perror("RESERVED_SIZE ioctls failed");
2497					break;
2498				}
2499				blocks_per = (buf_sz + bs - 1) / bs;
2500				blocks = blocks_per;
2501				fprintf(stderr,
2502					"Reducing read to %d blocks per loop\n",
2503					blocks_per);
2504				res =
2505				    sg_read(infd, wrkPos, blocks, skip, bs,
2506					    scsi_cdbsz, fua, &dio_tmp);
2507			} else if (2 == res) {
2508				fprintf(stderr,
2509					"Unit attention, media changed, continuing (r)\n");
2510				res =
2511				    sg_read(infd, wrkPos, blocks, skip, bs,
2512					    scsi_cdbsz, fua, &dio_tmp);
2513			}
2514			if (0 != res) {
2515				fprintf(stderr, "sg_read failed, skip=%d\n",
2516					skip);
2517				break;
2518			} else {
2519				in_full += blocks;
2520				if (dio && (0 == dio_tmp))
2521					dio_incomplete++;
2522			}
2523		} else {
2524			while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
2525			       (EINTR == errno)) ;
2526			if (res < 0) {
2527				snprintf(ebuff, EBUFF_SZ,
2528					 ME "reading, skip=%d ", skip);
2529				perror(ebuff);
2530				break;
2531			} else if (res < blocks * bs) {
2532				dd_count = 0;
2533				blocks = res / bs;
2534				if ((res % bs) > 0) {
2535					blocks++;
2536					in_partial++;
2537				}
2538			}
2539			in_full += blocks;
2540		}
2541
2542		if (FT_SG & out_type) {
2543			int fua = fua_mode & 1;
2544
2545			dio_tmp = dio;
2546			res =
2547			    sg_write(outfd, wrkPos, blocks, seek, bs,
2548				     scsi_cdbsz, fua, &dio_tmp);
2549			if (1 == res) {	/* ENOMEM, find what's available+try that */
2550				if (ioctl(outfd, SG_GET_RESERVED_SIZE, &buf_sz)
2551				    < 0) {
2552					perror("RESERVED_SIZE ioctls failed");
2553					break;
2554				}
2555				blocks_per = (buf_sz + bs - 1) / bs;
2556				blocks = blocks_per;
2557				fprintf(stderr,
2558					"Reducing write to %d blocks per loop\n",
2559					blocks);
2560				res =
2561				    sg_write(outfd, wrkPos, blocks, seek, bs,
2562					     scsi_cdbsz, fua, &dio_tmp);
2563			} else if (2 == res) {
2564				fprintf(stderr,
2565					"Unit attention, media changed, continuing (w)\n");
2566				res =
2567				    sg_write(outfd, wrkPos, blocks, seek, bs,
2568					     scsi_cdbsz, fua, &dio_tmp);
2569			} else if (0 != res) {
2570				fprintf(stderr, "sg_write failed, seek=%d\n",
2571					seek);
2572				break;
2573			} else {
2574				out_full += blocks;
2575				if (dio && (0 == dio_tmp))
2576					dio_incomplete++;
2577			}
2578		} else if (FT_DEV_NULL & out_type)
2579			out_full += blocks;	/* act as if written out without error */
2580		else {
2581			while (((res = write(outfd, wrkPos, blocks * bs)) < 0)
2582			       && (EINTR == errno)) ;
2583			if (res < 0) {
2584				snprintf(ebuff, EBUFF_SZ,
2585					 ME "writing, seek=%d ", seek);
2586				perror(ebuff);
2587				break;
2588			} else if (res < blocks * bs) {
2589				fprintf(stderr,
2590					"output file probably full, seek=%d ",
2591					seek);
2592				blocks = res / bs;
2593				out_full += blocks;
2594				if ((res % bs) > 0)
2595					out_partial++;
2596				break;
2597			} else
2598				out_full += blocks;
2599		}
2600		if (dd_count > 0)
2601			dd_count -= blocks;
2602		skip += blocks;
2603		seek += blocks;
2604	}
2605	if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
2606		struct timeval res_tm;
2607		double a, b;
2608
2609		gettimeofday(&end_tm, NULL);
2610		res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
2611		res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
2612		if (res_tm.tv_usec < 0) {
2613			--res_tm.tv_sec;
2614			res_tm.tv_usec += 1000000;
2615		}
2616		a = res_tm.tv_sec;
2617		a += (0.000001 * res_tm.tv_usec);
2618		b = (double)bs *(req_count - dd_count);
2619		printf("time to transfer data was %d.%06d secs",
2620		       (int)res_tm.tv_sec, (int)res_tm.tv_usec);
2621		if ((a > 0.00001) && (b > 511))
2622			printf(", %.2f MB/sec\n", b / (a * 1000000.0));
2623		else
2624			printf("\n");
2625	}
2626	if (do_sync) {
2627		if (FT_SG & out_type) {
2628			fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
2629			res = sync_cache(outfd);
2630			if (2 == res) {
2631				fprintf(stderr,
2632					"Unit attention, media changed(in), continuing\n");
2633				res = sync_cache(outfd);
2634			}
2635			if (0 != res)
2636				fprintf(stderr,
2637					"Unable to synchronize cache\n");
2638		}
2639	}
2640	free(wrkBuff);
2641	if (STDIN_FILENO != infd)
2642		close(infd);
2643	if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
2644		close(outfd);
2645	res = 0;
2646	if (0 != dd_count) {
2647		fprintf(stderr, "Some error occurred,");
2648		res = 2;
2649	}
2650	print_stats();
2651	if (dio_incomplete) {
2652		int fd;
2653		char c;
2654
2655		fprintf(stderr,
2656			">> Direct IO requested but incomplete %d times\n",
2657			dio_incomplete);
2658		if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
2659			if (1 == read(fd, &c, 1)) {
2660				if ('0' == c)
2661					fprintf(stderr,
2662						">>> %s set to '0' but should be set "
2663						"to '1' for direct IO\n",
2664						proc_allow_dio);
2665			}
2666			close(fd);
2667		}
2668	}
2669	if (sum_of_resids)
2670		fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
2671			sum_of_resids);
2672	return res;
2673}
2674
2675/* Returns 0 when successful, else -1 */
2676static int do_scsi_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
2677		       void *resp, int mx_resp_len, int noisy)
2678{
2679	int res;
2680	unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
2681	    { INQUIRY_CMD, 0, 0, 0, 0, 0 };
2682	unsigned char sense_b[SENSE_BUFF_LEN];
2683	sg_io_hdr_t io_hdr;
2684
2685	if (cmddt)
2686		inqCmdBlk[1] |= 2;
2687	if (evpd)
2688		inqCmdBlk[1] |= 1;
2689	inqCmdBlk[2] = (unsigned char)pg_op;
2690	inqCmdBlk[4] = (unsigned char)mx_resp_len;
2691	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
2692	io_hdr.interface_id = 'S';
2693	io_hdr.cmd_len = sizeof(inqCmdBlk);
2694	io_hdr.mx_sb_len = sizeof(sense_b);
2695	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
2696	io_hdr.dxfer_len = mx_resp_len;
2697	io_hdr.dxferp = resp;
2698	io_hdr.cmdp = inqCmdBlk;
2699	io_hdr.sbp = sense_b;
2700	io_hdr.timeout = DEF_TIMEOUT;
2701
2702	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
2703		perror("SG_IO (inquiry) error");
2704		return -1;
2705	}
2706	res = sg_err_category3(&io_hdr);
2707	switch (res) {
2708	case SG_ERR_CAT_CLEAN:
2709	case SG_ERR_CAT_RECOVERED:
2710		return 0;
2711	default:
2712		if (noisy) {
2713			char ebuff[EBUFF_SZ];
2714			snprintf(ebuff, EBUFF_SZ, "Inquiry error, CmdDt=%d, "
2715				 "EVPD=%d, page_opcode=%x ", cmddt, evpd,
2716				 pg_op);
2717			sg_chk_n_print3(ebuff, &io_hdr);
2718		}
2719		return -1;
2720	}
2721}
2722
2723int do_scsi_inquiry(char *device, int hex_flag)
2724{
2725	int sg_fd, k, j, num, len, act_len;
2726	int support_num;
2727	char *file_name = 0;
2728	char buff[MX_ALLOC_LEN + 1];
2729	unsigned char rsp_buff[MX_ALLOC_LEN + 1];
2730	unsigned int num_opcode = 0;
2731	int do_evpd = 0;
2732	int do_cmddt = 0;
2733	int do_cmdlst = 0;
2734	int do_hex = 0;
2735	int do_raw = 0;
2736	int do_pci = 0;
2737	int do_36 = 0;
2738	int oflags = O_RDONLY | O_NONBLOCK;
2739	int ansi_version = 0;
2740	int ret = 0;
2741
2742	file_name = device;
2743
2744	if (hex_flag) {
2745		do_hex = TRUE;
2746		print_msg(TEST_BREAK, __FUNCTION__);
2747	} else {
2748		do_pci = TRUE;
2749	}
2750
2751	if (do_pci)
2752		oflags = O_RDWR | O_NONBLOCK;
2753	if ((sg_fd = open(file_name, oflags)) < 0) {
2754		snprintf(ebuff, EBUFF_SZ, "sg_inq: error opening file: %s",
2755			 file_name);
2756		perror(ebuff);
2757		return 1;
2758	}
2759	/* Just to be safe, check we have a new sg device by trying an ioctl */
2760	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
2761		fprintf(stderr,
2762			"sg_inq: %s doesn't seem to be a version 3 sg device\n",
2763			file_name);
2764		close(sg_fd);
2765		return 1;
2766	}
2767	memset(rsp_buff, 0, MX_ALLOC_LEN + 1);
2768
2769	if (!(do_cmddt || do_evpd)) {
2770		if (!do_raw)
2771			printf("standard INQUIRY:\n");
2772		if (num_opcode > 0)
2773			printf
2774			    (" <<given opcode or page_code is being ignored>>\n");
2775
2776		if (0 == do_scsi_inq(sg_fd, 0, 0, 0, rsp_buff, 36, 1)) {
2777			len = rsp_buff[4] + 5;
2778			ansi_version = rsp_buff[2] & 0x7;
2779			if ((len > 36) && (len < 256) && (!do_36)) {
2780				if (do_scsi_inq
2781				    (sg_fd, 0, 0, 0, rsp_buff, len, 1)) {
2782					fprintf(stderr,
2783						"second INQUIRY (%d byte) failed\n",
2784						len);
2785					return 1;
2786				}
2787				if (len != (rsp_buff[4] + 5)) {
2788					fprintf(stderr,
2789						"strange, twin INQUIRYs yield different "
2790						"'additional length'\n");
2791					ret = 2;
2792				}
2793			}
2794			if (do_36) {
2795				act_len = len;
2796				len = 36;
2797			} else
2798				act_len = len;
2799			if (do_hex)
2800				dStrHex((const char *)rsp_buff, len, 0);
2801			else {
2802				printf
2803				    ("  PQual=%d, Device type=%d, RMB=%d, ANSI version=%d, ",
2804				     (rsp_buff[0] & 0xe0) >> 5,
2805				     rsp_buff[0] & 0x1f,
2806				     ! !(rsp_buff[1] & 0x80), ansi_version);
2807				printf("[full version=0x%02x]\n",
2808				       (unsigned int)rsp_buff[2]);
2809				printf
2810				    ("  AERC=%d, TrmTsk=%d, NormACA=%d, HiSUP=%d, "
2811				     "Resp data format=%d, SCCS=%d\n",
2812				     ! !(rsp_buff[3] & 0x80),
2813				     ! !(rsp_buff[3] & 0x40),
2814				     ! !(rsp_buff[3] & 0x20),
2815				     ! !(rsp_buff[3] & 0x10),
2816				     rsp_buff[3] & 0x0f,
2817				     ! !(rsp_buff[5] & 0x80));
2818				printf
2819				    ("  BQue=%d, EncServ=%d, MultiP=%d, MChngr=%d, "
2820				     "ACKREQQ=%d, ", ! !(rsp_buff[6] & 0x80),
2821				     ! !(rsp_buff[6] & 0x40),
2822				     ! !(rsp_buff[6] & 0x10),
2823				     ! !(rsp_buff[6] & 0x08),
2824				     ! !(rsp_buff[6] & 0x04));
2825				printf("Addr16=%d\n  RelAdr=%d, ",
2826				       ! !(rsp_buff[6] & 0x01),
2827				       ! !(rsp_buff[7] & 0x80));
2828				printf
2829				    ("WBus16=%d, Sync=%d, Linked=%d, TranDis=%d, ",
2830				     ! !(rsp_buff[7] & 0x20),
2831				     ! !(rsp_buff[7] & 0x10),
2832				     ! !(rsp_buff[7] & 0x08),
2833				     ! !(rsp_buff[7] & 0x04));
2834				printf("CmdQue=%d\n", ! !(rsp_buff[7] & 0x02));
2835				if (len > 56)
2836					printf
2837					    ("  Clocking=0x%x, QAS=%d, IUS=%d\n",
2838					     (rsp_buff[56] & 0x0c) >> 2,
2839					     ! !(rsp_buff[56] & 0x2),
2840					     ! !(rsp_buff[56] & 0x1));
2841				if (act_len == len)
2842					printf("    length=%d (0x%x)", len,
2843					       len);
2844				else
2845					printf
2846					    ("    length=%d (0x%x), but only read 36 bytes",
2847					     len, len);
2848				if ((ansi_version >= 2) && (len < 36))
2849					printf
2850					    ("  [for SCSI>=2, len>=36 is expected]\n");
2851				else
2852					printf("\n");
2853
2854				if (len <= 8)
2855					printf
2856					    (" Inquiry response length=%d\n, no vendor, "
2857					     "product or revision data\n", len);
2858				else {
2859					if (len < 36)
2860						rsp_buff[len] = '\0';
2861					memcpy(buff, &rsp_buff[8], 8);
2862					buff[8] = '\0';
2863					printf(" Vendor identification: %s\n",
2864					       buff);
2865					if (len <= 16)
2866						printf
2867						    (" Product identification: <none>\n");
2868					else {
2869						memcpy(buff, &rsp_buff[16], 16);
2870						buff[16] = '\0';
2871						printf
2872						    (" Product identification: %s\n",
2873						     buff);
2874					}
2875					if (len <= 32)
2876						printf
2877						    (" Product revision level: <none>\n");
2878					else {
2879						memcpy(buff, &rsp_buff[32], 4);
2880						buff[4] = '\0';
2881						printf
2882						    (" Product revision level: %s\n",
2883						     buff);
2884					}
2885				}
2886			}
2887			if (!do_raw &&
2888			    (0 ==
2889			     do_scsi_inq(sg_fd, 0, 1, 0x80, rsp_buff,
2890					 MX_ALLOC_LEN, 0))) {
2891				len = rsp_buff[3];
2892				if (len > 0) {
2893					memcpy(buff, rsp_buff + 4, len);
2894					buff[len] = '\0';
2895					printf(" Product serial number: %s\n",
2896					       buff);
2897				}
2898			}
2899		} else {
2900			printf("36 byte INQUIRY failed\n");
2901			return 1;
2902		}
2903	} else if (do_cmddt) {
2904		int reserved_cmddt;
2905		char op_name[128];
2906
2907		if (do_cmdlst) {
2908			printf("Supported command list:\n");
2909			for (k = 0; k < 256; ++k) {
2910				if (0 ==
2911				    do_scsi_inq(sg_fd, 1, 0, k, rsp_buff,
2912						MX_ALLOC_LEN, 1)) {
2913					support_num = rsp_buff[1] & 7;
2914					reserved_cmddt = rsp_buff[4];
2915					if ((3 == support_num)
2916					    || (5 == support_num)) {
2917						num = rsp_buff[5];
2918						for (j = 0; j < num; ++j)
2919							printf(" %.2x",
2920							       (int)rsp_buff[6 +
2921									     j]);
2922						if (5 == support_num)
2923							printf
2924							    ("  [vendor specific manner (5)]");
2925						sg_get_command_name((unsigned
2926								     char)k,
2927								    sizeof
2928								    (op_name) -
2929								    1, op_name);
2930						op_name[sizeof(op_name) - 1] =
2931						    '\0';
2932						printf("  %s\n", op_name);
2933					} else if ((4 == support_num)
2934						   || (6 == support_num))
2935						printf
2936						    ("  opcode=0x%.2x vendor specific (%d)\n",
2937						     k, support_num);
2938					else if ((0 == support_num)
2939						 && (reserved_cmddt > 0)) {
2940						printf
2941						    ("  opcode=0x%.2x ignored cmddt bit, "
2942						     "given standard INQUIRY response, stop\n",
2943						     k);
2944						break;
2945					}
2946				} else {
2947					fprintf(stderr,
2948						"CmdDt INQUIRY on opcode=0x%.2x: failed\n",
2949						k);
2950					break;
2951				}
2952			}
2953		} else {
2954			if (!do_raw) {
2955				printf("CmdDt INQUIRY, opcode=0x%.2x:  [",
2956				       num_opcode);
2957				sg_get_command_name((unsigned char)num_opcode,
2958						    sizeof(op_name) - 1,
2959						    op_name);
2960				op_name[sizeof(op_name) - 1] = '\0';
2961				printf("%s]\n", op_name);
2962			}
2963			if (0 == do_scsi_inq(sg_fd, 1, 0, num_opcode, rsp_buff,
2964					     MX_ALLOC_LEN, 1)) {
2965				len = rsp_buff[5] + 6;
2966				reserved_cmddt = rsp_buff[4];
2967				if (do_hex)
2968					dStrHex((const char *)rsp_buff, len, 0);
2969				else {
2970					const char *desc_p;
2971					int prnt_cmd = 0;
2972
2973					support_num = rsp_buff[1] & 7;
2974					num = rsp_buff[5];
2975					switch (support_num) {
2976					case 0:
2977						if (0 == reserved_cmddt)
2978							desc_p =
2979							    "no data available";
2980						else
2981							desc_p =
2982							    "ignored cmddt bit, standard INQUIRY "
2983							    "response";
2984						break;
2985					case 1:
2986						desc_p = "not supported";
2987						break;
2988					case 2:
2989						desc_p = "reserved (2)";
2990						break;
2991					case 3:
2992						desc_p =
2993						    "supported as per standard";
2994						prnt_cmd = 1;
2995						break;
2996					case 4:
2997						desc_p = "vendor specific (4)";
2998						break;
2999					case 5:
3000						desc_p =
3001						    "supported in vendor specific way";
3002						prnt_cmd = 1;
3003						break;
3004					case 6:
3005						desc_p = "vendor specific (6)";
3006						break;
3007					case 7:
3008						desc_p = "reserved (7)";
3009						break;
3010					default:
3011						desc_p = "impossible value > 7";
3012						break;
3013					}
3014					if (prnt_cmd) {
3015						printf("  Support field: %s [",
3016						       desc_p);
3017						for (j = 0; j < num; ++j)
3018							printf(" %.2x",
3019							       (int)rsp_buff[6 +
3020									     j]);
3021						printf(" ]\n");
3022					} else
3023						printf("  Support field: %s\n",
3024						       desc_p);
3025				}
3026			} else {
3027				fprintf(stderr,
3028					"CmdDt INQUIRY on opcode=0x%.2x: failed\n",
3029					num_opcode);
3030				return 1;
3031			}
3032
3033		}
3034	} else if (do_evpd) {
3035		if (!do_raw)
3036			printf("EVPD INQUIRY, page code=0x%.2x:\n", num_opcode);
3037		if (0 ==
3038		    do_scsi_inq(sg_fd, 0, 1, num_opcode, rsp_buff, MX_ALLOC_LEN,
3039				1)) {
3040			len = rsp_buff[3] + 4;
3041			if (num_opcode != rsp_buff[1])
3042				printf
3043				    ("non evpd respone; probably a STANDARD INQUIRY "
3044				     "response\n");
3045			else {
3046				if (!do_hex)
3047					printf(" Only hex output supported\n");
3048				dStrHex((const char *)rsp_buff, len, 0);
3049			}
3050		} else {
3051			fprintf(stderr,
3052				"EVPD INQUIRY, page code=0x%.2x: failed\n",
3053				num_opcode);
3054			return 1;
3055		}
3056	}
3057
3058	if (do_pci) {
3059		unsigned char slot_name[16];
3060
3061		printf("\n");
3062		memset(slot_name, '\0', sizeof(slot_name));
3063		if (ioctl(sg_fd, SCSI_IOCTL_GET_PCI, slot_name) < 0) {
3064			if (EINVAL == errno)
3065				printf
3066				    ("ioctl(SCSI_IOCTL_GET_PCI) not supported by this "
3067				     "kernel\n");
3068			else if (ENXIO == errno)
3069				printf
3070				    ("associated adapter not a PCI device?\n");
3071			else
3072				perror("ioctl(SCSI_IOCTL_GET_PCI) failed");
3073		} else
3074			printf("PCI:slot_name: %s\n", slot_name);
3075	}
3076
3077	close(sg_fd);
3078	return ret;
3079}
3080
3081int show_scsi_maps()
3082{
3083	int sg_fd, res, k;
3084	int do_numeric = NUMERIC_SCAN_DEF;
3085	int do_all_s = 1;
3086	int do_sd = 0;
3087	int do_st = 0;
3088	int do_osst = 0;
3089	int do_sr = 0;
3090	int do_scd = 0;
3091	int do_extra = 1;
3092	int do_inquiry = 0;
3093	char fname[64];
3094	int num_errors = 0;
3095	int num_silent = 0;
3096	int eacces_err = 0;
3097	int last_sg_ind = -1;
3098	struct stat stat_buf;
3099
3100	print_msg(TEST_BREAK, __FUNCTION__);
3101
3102	if (stat(devfs_id, &stat_buf) == 0)
3103		printf("# Note: the devfs pseudo file system is present\n");
3104
3105	for (k = 0, res = 0; (k < MAX_SG_DEVS) && (num_errors < MAX_ERRORS);
3106	     ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
3107		if (res < 0) {
3108			snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
3109			perror("sg_map: close error");
3110			return 1;
3111		}
3112		make_dev_name(fname, "/dev/sg", k, do_numeric);
3113
3114		sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
3115		if (sg_fd < 0) {
3116			if (EBUSY == errno) {
3117				map_arr[k].active = -2;
3118				continue;
3119			} else if ((ENODEV == errno) || (ENOENT == errno) ||
3120				   (ENXIO == errno)) {
3121				++num_errors;
3122				++num_silent;
3123				map_arr[k].active = -1;
3124				continue;
3125			} else {
3126				if (EACCES == errno)
3127					eacces_err = 1;
3128				snprintf(ebuff, EBUFF_SZ, "Error opening %s ",
3129					 fname);
3130				perror(ebuff);
3131				++num_errors;
3132				continue;
3133			}
3134		}
3135		res = ioctl(sg_fd, SG_GET_SCSI_ID, &map_arr[k].sg_dat);
3136		if (res < 0) {
3137			snprintf(ebuff, EBUFF_SZ,
3138				 "device %s failed on sg ioctl, skip", fname);
3139			perror(ebuff);
3140			++num_errors;
3141			continue;
3142		}
3143		if (do_inquiry) {
3144			char buff[36];
3145
3146			if (0 ==
3147			    do_scsi_inq(sg_fd, 0, 0, 0, buff, sizeof(buff),
3148					1)) {
3149				memcpy(map_arr[k].vendor, &buff[8], 8);
3150				memcpy(map_arr[k].product, &buff[16], 16);
3151				memcpy(map_arr[k].revision, &buff[32], 4);
3152			}
3153		}
3154		map_arr[k].active = 1;
3155		map_arr[k].oth_dev_num = -1;
3156		last_sg_ind = k;
3157	}
3158	if ((num_errors >= MAX_ERRORS) && (num_silent < num_errors)) {
3159		printf("Stopping because there are too many error\n");
3160		if (eacces_err)
3161			printf("    root access may be required\n");
3162		return 1;
3163	}
3164	if (last_sg_ind < 0) {
3165		printf("Stopping because no sg devices found\n");
3166	}
3167
3168	if (do_all_s || do_sd)
3169		scan_dev_type("/dev/sd", MAX_SD_DEVS, 0, LIN_DEV_TYPE_SD,
3170			      last_sg_ind);
3171	if (do_all_s || do_sr)
3172		scan_dev_type("/dev/sr", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SR,
3173			      last_sg_ind);
3174	if (do_all_s || do_scd)
3175		scan_dev_type("/dev/scd", MAX_SR_DEVS, 1, LIN_DEV_TYPE_SCD,
3176			      last_sg_ind);
3177	if (do_all_s || do_st)
3178		scan_dev_type("/dev/st", MAX_ST_DEVS, 1, LIN_DEV_TYPE_ST,
3179			      last_sg_ind);
3180	if (do_all_s || do_osst)
3181		scan_dev_type("/dev/osst", MAX_OSST_DEVS, 1, LIN_DEV_TYPE_OSST,
3182			      last_sg_ind);
3183
3184	for (k = 0; k <= last_sg_ind; ++k) {
3185		make_dev_name(fname, "/dev/sg", k, do_numeric);
3186		printf("%s", fname);
3187		switch (map_arr[k].active) {
3188		case -2:
3189			printf(do_extra ? "  -2 -2 -2 -2  -2" : "  busy");
3190			break;
3191		case -1:
3192			printf(do_extra ? "  -1 -1 -1 -1  -1" :
3193			       "  not present");
3194			break;
3195		case 0:
3196			printf(do_extra ? "  -3 -3 -3 -3  -3" :
3197			       "  some error\n");
3198			break;
3199		case 1:
3200			if (do_extra)
3201				printf("  %d %d %d %d  %d",
3202				       map_arr[k].sg_dat.host_no,
3203				       map_arr[k].sg_dat.channel,
3204				       map_arr[k].sg_dat.scsi_id,
3205				       map_arr[k].sg_dat.lun,
3206				       map_arr[k].sg_dat.scsi_type);
3207			switch (map_arr[k].lin_dev_type) {
3208			case LIN_DEV_TYPE_SD:
3209				make_dev_name(fname, "/dev/sd",
3210					      map_arr[k].oth_dev_num, 0);
3211				printf("  %s", fname);
3212				break;
3213			case LIN_DEV_TYPE_ST:
3214				make_dev_name(fname, "/dev/st",
3215					      map_arr[k].oth_dev_num, 1);
3216				printf("  %s", fname);
3217				break;
3218			case LIN_DEV_TYPE_OSST:
3219				make_dev_name(fname, "/dev/osst",
3220					      map_arr[k].oth_dev_num, 1);
3221				printf("  %s", fname);
3222				break;
3223			case LIN_DEV_TYPE_SR:
3224				make_dev_name(fname, "/dev/sr",
3225					      map_arr[k].oth_dev_num, 1);
3226				printf("  %s", fname);
3227				break;
3228			case LIN_DEV_TYPE_SCD:
3229				make_dev_name(fname, "/dev/scd",
3230					      map_arr[k].oth_dev_num, 1);
3231				printf("  %s", fname);
3232				break;
3233			default:
3234				break;
3235			}
3236			if (do_inquiry)
3237				printf("  %.8s  %.16s  %.4s", map_arr[k].vendor,
3238				       map_arr[k].product, map_arr[k].revision);
3239			break;
3240		default:
3241			printf("  bad logic\n");
3242			break;
3243		}
3244		printf("\n");
3245	}
3246	return 0;
3247}
3248
3249static int find_dev_in_sg_arr(My_scsi_idlun * my_idlun, int host_no,
3250			      int last_sg_ind)
3251{
3252	int k;
3253	struct sg_scsi_id *sidp;
3254
3255	for (k = 0; k <= last_sg_ind; ++k) {
3256		sidp = &(map_arr[k].sg_dat);
3257		if ((host_no == sidp->host_no) &&
3258		    ((my_idlun->dev_id & 0xff) == sidp->scsi_id) &&
3259		    (((my_idlun->dev_id >> 8) & 0xff) == sidp->lun) &&
3260		    (((my_idlun->dev_id >> 16) & 0xff) == sidp->channel))
3261			return k;
3262	}
3263	return -1;
3264}
3265
3266static void scan_dev_type(const char *leadin, int max_dev, int do_numeric,
3267			  int lin_dev_type, int last_sg_ind)
3268{
3269	int k, res, ind, sg_fd = 0;
3270	int num_errors = 0;
3271	int num_silent = 0;
3272	int host_no = -1;
3273	int nonMappedDevicesPresent = FALSE;
3274	My_scsi_idlun my_idlun;
3275	char fname[64];
3276
3277	for (k = 0, res = 0; (k < max_dev) && (num_errors < MAX_ERRORS);
3278	     ++k, res = (sg_fd >= 0) ? close(sg_fd) : 0) {
3279
3280/* ignore close() errors */
3281#if 0
3282		if (res < 0) {
3283			snprintf(ebuff, EBUFF_SZ, "Error closing %s ", fname);
3284			perror("sg_map: close error");
3285#ifndef IGN_CLOSE_ERR
3286			return;
3287#else
3288			++num_errors;
3289			sg_fd = 0;
3290#endif
3291		}
3292#endif
3293		make_dev_name(fname, leadin, k, do_numeric);
3294#ifdef DEBUG
3295		printf("Trying %s: ", fname);
3296#endif
3297
3298		sg_fd = open(fname, O_RDONLY | O_NONBLOCK);
3299		if (sg_fd < 0) {
3300#ifdef DEBUG
3301			printf("ERROR %i\n", errno);
3302#endif
3303			if (EBUSY == errno) {
3304				printf("Device %s is busy\n", fname);
3305				++num_errors;
3306				continue;
3307			} else if ((ENODEV == errno) || (ENOENT == errno) ||
3308				   (ENXIO == errno)) {
3309				++num_errors;
3310				++num_silent;
3311				continue;
3312			} else {
3313				snprintf(ebuff, EBUFF_SZ, "Error opening %s ",
3314					 fname);
3315				perror(ebuff);
3316				++num_errors;
3317				continue;
3318			}
3319		}
3320
3321		res = ioctl(sg_fd, SCSI_IOCTL_GET_IDLUN, &my_idlun);
3322		if (res < 0) {
3323			snprintf(ebuff, EBUFF_SZ,
3324				 "device %s failed on scsi ioctl(idlun), skip",
3325				 fname);
3326			perror(ebuff);
3327			++num_errors;
3328#ifdef DEBUG
3329			printf("Couldn't get IDLUN!\n");
3330#endif
3331			continue;
3332		}
3333		res = ioctl(sg_fd, SCSI_IOCTL_GET_BUS_NUMBER, &host_no);
3334		if (res < 0) {
3335			snprintf(ebuff, EBUFF_SZ,
3336				 "device %s failed on scsi ioctl(bus_number), skip",
3337				 fname);
3338			perror(ebuff);
3339			++num_errors;
3340#ifdef DEBUG
3341			printf("Couldn't get BUS!\n");
3342#endif
3343			continue;
3344		}
3345#ifdef DEBUG
3346		printf("%i(%x) %i %i %i %i\n", host_no, my_idlun.host_unique_id,
3347		       (my_idlun.dev_id >> 24) & 0xff,
3348		       (my_idlun.dev_id >> 16) & 0xff,
3349		       (my_idlun.dev_id >> 8) & 0xff, my_idlun.dev_id & 0xff);
3350#endif
3351		ind = find_dev_in_sg_arr(&my_idlun, host_no, last_sg_ind);
3352		if (ind >= 0) {
3353			map_arr[ind].oth_dev_num = k;
3354			map_arr[ind].lin_dev_type = lin_dev_type;
3355		} else if (ind != -1) {
3356			printf
3357			    ("Strange, could not find device %s mapped to sg device error %d??\n",
3358			     fname, ind);
3359		} else {
3360			nonMappedDevicesPresent = TRUE;
3361		}
3362	}
3363	if (nonMappedDevicesPresent) {
3364		printf("Unmapped Devices found...\n\n");
3365	}
3366}
3367
3368/* Returns 0 when successful, else -1 */
3369static int do_simple_inq(int sg_fd, void *resp, int mx_resp_len, int noisy)
3370{
3371	int res;
3372	unsigned char inqCmdBlk[INQUIRY_CMDLEN] =
3373	    { INQUIRY_CMD, 0, 0, 0, 0, 0 };
3374	unsigned char sense_b[SENSE_BUFF_LEN];
3375	sg_io_hdr_t io_hdr;
3376
3377	inqCmdBlk[4] = (unsigned char)mx_resp_len;
3378	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
3379	io_hdr.interface_id = 'S';
3380	io_hdr.cmd_len = sizeof(inqCmdBlk);
3381	io_hdr.mx_sb_len = sizeof(sense_b);
3382	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
3383	io_hdr.dxfer_len = mx_resp_len;
3384	io_hdr.dxferp = resp;
3385	io_hdr.cmdp = inqCmdBlk;
3386	io_hdr.sbp = sense_b;
3387	io_hdr.timeout = DEF_TIMEOUT;
3388
3389	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
3390		perror("SG_IO (inquiry) error");
3391		return -1;
3392	}
3393	res = sg_err_category3(&io_hdr);
3394	switch (res) {
3395	case SG_ERR_CAT_CLEAN:
3396	case SG_ERR_CAT_RECOVERED:
3397		return 0;
3398	default:
3399		if (noisy) {
3400			char ebuff[EBUFF_SZ];
3401			snprintf(ebuff, EBUFF_SZ, "Inquiry error ");
3402			sg_chk_n_print3(ebuff, &io_hdr);
3403		}
3404		return -1;
3405	}
3406}
3407
3408static int do_modes(int sg_fd, int dbd, int pc, int pg_code, int sub_pg_code,
3409		    void *resp, int mx_resp_len, int noisy, int mode6)
3410{
3411	int res;
3412	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
3413	    { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
3414	unsigned char sense_b[SENSE_BUFF_LEN];
3415	sg_io_hdr_t io_hdr;
3416
3417	modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0);
3418	modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
3419	modesCmdBlk[3] = (unsigned char)(sub_pg_code & 0xff);
3420	if (mx_resp_len > (mode6 ? 0xff : 0xffff)) {
3421		printf(ME "mx_resp_len too big\n");
3422		return -1;
3423	}
3424	if (mode6) {
3425		modesCmdBlk[0] = MODE_SENSE6_CMD;
3426		modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
3427	} else {
3428		modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
3429		modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
3430	}
3431
3432	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
3433	memset(sense_b, 0, sizeof(sense_b));
3434	io_hdr.interface_id = 'S';
3435	io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN;
3436	io_hdr.mx_sb_len = sizeof(sense_b);
3437	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
3438	io_hdr.dxfer_len = mx_resp_len;
3439	io_hdr.dxferp = resp;
3440	io_hdr.cmdp = modesCmdBlk;
3441	io_hdr.sbp = sense_b;
3442	io_hdr.timeout = DEF_TIMEOUT;
3443
3444	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
3445		perror("SG_IO (mode sense) error");
3446		return -1;
3447	}
3448	res = sg_err_category3(&io_hdr);
3449	switch (res) {
3450	case SG_ERR_CAT_CLEAN:
3451	case SG_ERR_CAT_RECOVERED:
3452		return 0;
3453	default:
3454		if (noisy) {
3455			char ebuff[EBUFF_SZ];
3456			snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d "
3457				 "pc=%d page_code=%x sub_page_code=%x\n     ",
3458				 dbd, pc, pg_code, sub_pg_code);
3459			sg_chk_n_print3(ebuff, &io_hdr);
3460		}
3461		if ((0x70 == (0x7f & sense_b[0])) && (0x20 == sense_b[12]) &&
3462		    (0x0 == sense_b[13])) {
3463			if (mode6)
3464				fprintf(stderr,
3465					">>>>>> drop '-6' switch and try again with "
3466					"a 10 byte MODE SENSE\n");
3467			else
3468				fprintf(stderr,
3469					">>>>>> add '-6' switch and try again with "
3470					"a 6 byte MODE SENSE\n");
3471		}
3472		return -1;
3473	}
3474}
3475
3476const char *scsi_ptype_strs[] = {
3477	"disk",
3478	"tape",
3479	"printer",
3480	"processor",
3481	"write once optical disk",
3482	"cd/dvd",
3483	"scanner",
3484	"optical memory device",
3485	"medium changer",
3486	"communications",
3487	"graphics",
3488	"graphics",
3489	"storage array controller",
3490	"enclosure services device",
3491	"simplified direct access device",
3492	"optical card reader/writer device",
3493};
3494
3495const char *get_ptype_str(int scsi_ptype)
3496{
3497	int num = sizeof(scsi_ptype_strs) / sizeof(scsi_ptype_strs[0]);
3498
3499	return (scsi_ptype < num) ? scsi_ptype_strs[scsi_ptype] : "";
3500}
3501
3502static struct page_code_desc pc_desc_all[] = {
3503	{0x0, "Unit Attention condition [vendor: page format not required]"},
3504	{0x2, "Disconnect-Reconnect"},
3505	{0xa, "Control"},
3506	{0x15, "Extended"},
3507	{0x16, "Extended device-type specific"},
3508	{0x18, "Protocol specific LUN"},
3509	{0x19, "Protocol specific port"},
3510	{0x1a, "Power condition"},
3511	{0x1c, "Informational exceptions control"},
3512	{0x3f, "[yields all supported pages]"},
3513};
3514
3515static struct page_code_desc pc_desc_disk[] = {
3516	{0x1, "Read-Write error recovery"},
3517	{0x3, "Format"},
3518	{0x4, "Rigid disk geometry"},
3519	{0x5, "Flexible geometry"},
3520	{0x7, "Verify error recovery"},
3521	{0x8, "Caching"},
3522	{0x9, "Peripheral device (spc-2 ?)"},
3523	{0xb, "Medium types supported"},
3524	{0xc, "Notch and partition"},
3525	{0xd, "Power condition (obsolete)"},
3526	{0x10, "XOR control"},
3527};
3528
3529static struct page_code_desc pc_desc_tape[] = {
3530	{0xf, "Data Compression"},
3531	{0x10, "Device config"},
3532	{0x11, "Medium Partition [1]"},
3533	{0x12, "Medium Partition [2]"},
3534	{0x13, "Medium Partition [3]"},
3535	{0x14, "Medium Partition [4]"},
3536	{0x1c, "Informational exceptions control (tape version)"},
3537};
3538
3539static struct page_code_desc pc_desc_cddvd[] = {
3540	{0x1, "Read-Write error recovery"},
3541	{0x3, "MRW"},
3542	{0x5, "Write parameters"},
3543	{0xd, "CD device parameters (obsolete)"},
3544	{0xe, "CD audio"},
3545	{0x1a, "Power condition"},
3546	{0x1c, "Fault/failure reporting control"},
3547	{0x1d, "Timeout and protect"},
3548	{0x2a, "MM capabilities and mechanical status (obsolete)"},
3549};
3550
3551static struct page_code_desc pc_desc_smc[] = {
3552	{0x1d, "Element address assignment"},
3553	{0x1e, "Transport geometry parameters"},
3554	{0x1f, "Device capabilities"},
3555};
3556
3557static struct page_code_desc pc_desc_scc[] = {
3558	{0x1b, "LUN mapping"},
3559};
3560
3561static struct page_code_desc pc_desc_ses[] = {
3562	{0x14, "Enclosure services management"},
3563};
3564
3565struct page_code_desc *find_mode_page_table(int scsi_ptype, int *size)
3566{
3567	switch (scsi_ptype) {
3568	case 0:		/* disk (direct access) type devices */
3569	case 4:
3570	case 7:
3571	case 0xe:
3572		*size = sizeof(pc_desc_disk) / sizeof(pc_desc_disk[0]);
3573		return &pc_desc_disk[0];
3574	case 1:		/* tape devices */
3575	case 2:
3576		*size = sizeof(pc_desc_tape) / sizeof(pc_desc_tape[0]);
3577		return &pc_desc_tape[0];
3578	case 5:		/* cd/dvd devices */
3579		*size = sizeof(pc_desc_cddvd) / sizeof(pc_desc_cddvd[0]);
3580		return &pc_desc_cddvd[0];
3581	case 8:		/* medium changer devices */
3582		*size = sizeof(pc_desc_smc) / sizeof(pc_desc_smc[0]);
3583		return &pc_desc_smc[0];
3584	case 0xc:		/* storage array devices */
3585		*size = sizeof(pc_desc_scc) / sizeof(pc_desc_scc[0]);
3586		return &pc_desc_scc[0];
3587	case 0xd:		/* enclosure services devices */
3588		*size = sizeof(pc_desc_ses) / sizeof(pc_desc_ses[0]);
3589		return &pc_desc_ses[0];
3590	}
3591	*size = 0;
3592	return NULL;
3593}
3594
3595const char *find_page_code_desc(int page_num, int scsi_ptype)
3596{
3597	int k;
3598	int num;
3599	const struct page_code_desc *pcdp;
3600
3601	pcdp = find_mode_page_table(scsi_ptype, &num);
3602	if (pcdp) {
3603		for (k = 0; k < num; ++k, ++pcdp) {
3604			if (page_num == pcdp->page_code)
3605				return pcdp->desc;
3606			else if (page_num < pcdp->page_code)
3607				break;
3608		}
3609	}
3610	pcdp = &pc_desc_all[0];
3611	num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]);
3612	for (k = 0; k < num; ++k, ++pcdp) {
3613		if (page_num == pcdp->page_code)
3614			return pcdp->desc;
3615		else if (page_num < pcdp->page_code)
3616			break;
3617	}
3618	return NULL;
3619}
3620
3621static void list_page_codes(int scsi_ptype)
3622{
3623	int k;
3624	int num = sizeof(pc_desc_all) / sizeof(pc_desc_all[0]);
3625	const struct page_code_desc *pcdp = &pc_desc_all[0];
3626	int num_ptype;
3627	const struct page_code_desc *pcd_ptypep;
3628
3629	pcd_ptypep = find_mode_page_table(scsi_ptype, &num_ptype);
3630	printf("Page_Code  Description\n");
3631	for (k = 0; k < 0x3f; ++k) {
3632		if (pcd_ptypep && (num_ptype > 0)) {
3633			if (k == pcd_ptypep->page_code) {
3634				printf(" 0x%02x      %s\n",
3635				       pcd_ptypep->page_code, pcd_ptypep->desc);
3636				++pcd_ptypep;
3637				--num_ptype;
3638				continue;
3639			} else if (k > pcd_ptypep->page_code) {
3640				pcd_ptypep++;
3641				--num_ptype;
3642			}
3643		}
3644		if (pcdp && (num > 0)) {
3645			if (k == pcdp->page_code) {
3646				printf(" 0x%02x      %s\n", pcdp->page_code,
3647				       pcdp->desc);
3648				++pcdp;
3649				--num;
3650				continue;
3651			} else if (k > pcdp->page_code) {
3652				pcdp++;
3653				--num;
3654			}
3655		}
3656	}
3657}
3658
3659int show_scsi_modes(char *device)
3660{
3661	int sg_fd, k, num, len, md_len, bd_len, longlba, page_num;
3662	char *file_name = 0;
3663	char ebuff[EBUFF_SZ];
3664	const char *descp;
3665	unsigned char rsp_buff[MODE_ALLOC_LEN];
3666	int rsp_buff_size = MODE_ALLOC_LEN;
3667	int pg_code = 0;
3668	int sub_pg_code = 0;
3669	int pc = 0;
3670	int do_all = 1;
3671	int do_dbd = 0;
3672	int do_hex = 0;
3673	int do_mode6 = 0;	/* Use MODE SENSE(6) instead of MODE SENSE(10) */
3674	int oflags = O_RDONLY | O_NONBLOCK;
3675	struct sg_scsi_id a_sid;
3676	int scsi_ptype, density_code_off;
3677	unsigned char *ucp;
3678	unsigned char uc;
3679
3680	print_msg(TEST_BREAK, __FUNCTION__);
3681
3682	file_name = device;
3683
3684	list_page_codes(0);
3685
3686	/* The 6 bytes command only allows up to 255 bytes of response data */
3687	if (do_mode6)
3688		rsp_buff_size = 255;
3689
3690	if ((sg_fd = open(file_name, oflags)) < 0) {
3691		snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
3692			 file_name);
3693		perror(ebuff);
3694		return 1;
3695	}
3696	/* Just to be safe, check we have a new sg device by trying an ioctl */
3697	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
3698		printf(ME "%s doesn't seem to be a version 3 sg device\n",
3699		       file_name);
3700		close(sg_fd);
3701		return 1;
3702	}
3703	if (ioctl(sg_fd, SG_GET_SCSI_ID, &a_sid) < 0) {
3704		unsigned char inqBuff[36];
3705
3706		if (do_simple_inq(sg_fd, inqBuff, sizeof(inqBuff), 1)) {
3707			printf(ME "%s doesn't respond to a SCSI INQUIRY\n",
3708			       file_name);
3709			close(sg_fd);
3710			return 1;
3711		}
3712		scsi_ptype = inqBuff[0] & 0x1f;	/* fetch peripheral device type */
3713	} else
3714		scsi_ptype = a_sid.scsi_type;
3715	printf("  SCSI peripheral type: %s [0x%x] (from INQUIRY)\n",
3716	       get_ptype_str(scsi_ptype), scsi_ptype);
3717
3718	if (do_all)
3719		pg_code = MODE_CODE_ALL;
3720
3721	if (0 == do_modes(sg_fd, do_dbd, pc, pg_code, sub_pg_code,
3722			  rsp_buff, rsp_buff_size, 1, do_mode6)) {
3723		int medium_type, specific, headerlen;
3724
3725		printf("Mode parameter header from %s byte MODE SENSE:\n",
3726		       (do_mode6 ? "6" : "10"));
3727		if (do_mode6) {
3728			headerlen = 4;
3729			if (do_hex)
3730				dStrHex((const char *)rsp_buff, headerlen, 1);
3731			md_len = rsp_buff[0] + 1;
3732			bd_len = rsp_buff[3];
3733			medium_type = rsp_buff[1];
3734			specific = rsp_buff[2];
3735			longlba = 0;	/* what is this field? */
3736		} else {
3737			headerlen = 8;
3738			md_len = (rsp_buff[0] << 8) + rsp_buff[1] + 2;
3739			bd_len = (rsp_buff[6] << 8) + rsp_buff[7];
3740			medium_type = rsp_buff[2];
3741			specific = rsp_buff[3];
3742			longlba = rsp_buff[4] & 1;
3743		}
3744		if (do_hex)
3745			dStrHex((const char *)rsp_buff, headerlen, 1);
3746		printf("  Mode data length=%d, medium type=0x%.2x, specific"
3747		       " param=0x%.2x, longlba=%d\n", md_len, medium_type,
3748		       specific, longlba);
3749		if (md_len > rsp_buff_size) {
3750			printf
3751			    ("Only fetched %d bytes of response, truncate output\n",
3752			     rsp_buff_size);
3753			md_len = rsp_buff_size;
3754			if (bd_len + headerlen > rsp_buff_size)
3755				bd_len = rsp_buff_size - headerlen;
3756		}
3757		printf("  Block descriptor length=%d\n", bd_len);
3758		if (bd_len > 0) {
3759			len = 8;
3760			density_code_off = 0;
3761			num = bd_len;
3762			if (longlba) {
3763				printf("> longlba block descriptors:\n");
3764				len = 16;
3765				density_code_off = 8;
3766			} else if (0 == scsi_ptype) {
3767				printf
3768				    ("> Direct access device block descriptors:\n");
3769				density_code_off = 4;
3770			} else
3771				printf
3772				    ("> General mode parameter block descriptors:\n");
3773
3774			ucp = rsp_buff + headerlen;
3775			while (num > 0) {
3776				printf("   Density code=0x%x\n",
3777				       *(ucp + density_code_off));
3778				dStrHex((const char *)ucp, len, 1);
3779				ucp += len;
3780				num -= len;
3781			}
3782			printf("\n");
3783		}
3784		ucp = rsp_buff + bd_len + headerlen;	/* start of mode page(s) */
3785		md_len -= bd_len + headerlen;	/* length of mode page(s) */
3786		while (md_len > 0) {	/* got mode page(s) */
3787			uc = *ucp;
3788			page_num = ucp[0] & 0x3f;
3789			if (do_hex)
3790				descp = NULL;
3791			else {
3792				descp =
3793				    find_page_code_desc(page_num, scsi_ptype);
3794				if (NULL == descp)
3795					snprintf(ebuff, EBUFF_SZ,
3796						 "vendor[0x%x]", page_num);
3797			}
3798			if (uc & 0x40) {
3799				len = (ucp[2] << 8) + ucp[3] + 4;
3800				if (do_hex)
3801					printf
3802					    (">> page_code=0x%x, subpage_code=0x%x, "
3803					     "page_control=%d\n", page_num,
3804					     ucp[1], pc);
3805				else
3806					printf
3807					    (">> page_code: %s, subpage_code=0x%x, "
3808					     "page_control: %s\n",
3809					     (descp ? descp : ebuff), ucp[1],
3810					     pg_control_str_arr[pc]);
3811			} else {
3812				len = ucp[1] + 2;
3813				if (do_hex)
3814					printf
3815					    (">> page_code=0x%x, page_control=%d\n",
3816					     page_num, pc);
3817				else
3818					printf
3819					    (">> page_code: %s, page_control: %s\n",
3820					     (descp ? descp : ebuff),
3821					     pg_control_str_arr[pc]);
3822			}
3823			dStrHex((const char *)ucp, len, 1);
3824			ucp += len;
3825			md_len -= len;
3826		}
3827	}
3828
3829	close(sg_fd);
3830	return 0;
3831}
3832
3833int do_scsi_read_buffer(char *device)
3834{
3835	int sg_fd, res;
3836	unsigned int k, num;
3837	unsigned char rbCmdBlk[RB_CMD_LEN];
3838	unsigned char *rbBuff = NULL;
3839	void *rawp = NULL;
3840	unsigned char sense_buffer[32];
3841	int buf_capacity = 0;
3842	int do_quick = 0;
3843	int do_dio = 0;
3844	int do_mmap = 1;
3845	int do_time = 0;
3846	int buf_size = 0;
3847	unsigned int total_size_mb = RB_MB_TO_READ;
3848	char *file_name = 0;
3849	size_t psz = getpagesize();
3850	int dio_incomplete = 0;
3851	sg_io_hdr_t io_hdr;
3852	struct timeval start_tm, end_tm;
3853#ifdef SG_DEBUG
3854	int clear = 1;
3855#endif
3856
3857	print_msg(TEST_BREAK, __FUNCTION__);
3858
3859	file_name = device;
3860
3861	sg_fd = open(file_name, O_RDONLY);
3862	if (sg_fd < 0) {
3863		perror(ME "open error");
3864		return 1;
3865	}
3866	/* Don't worry, being very careful not to write to a none-sg file ... */
3867	res = ioctl(sg_fd, SG_GET_VERSION_NUM, &k);
3868	if ((res < 0) || (k < 30000)) {
3869		printf(ME "not a sg device, or driver prior to 3.x\n");
3870		return 1;
3871	}
3872	if (do_mmap) {
3873		do_dio = 0;
3874		do_quick = 0;
3875	}
3876	if (NULL == (rawp = malloc(512))) {
3877		printf(ME "out of memory (query)\n");
3878		return 1;
3879	}
3880	rbBuff = rawp;
3881
3882	memset(rbCmdBlk, 0, RB_CMD_LEN);
3883	rbCmdBlk[0] = RB_OPCODE;
3884	rbCmdBlk[1] = RB_MODE_DESC;
3885	rbCmdBlk[8] = RB_DESC_LEN;
3886	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
3887	io_hdr.interface_id = 'S';
3888	io_hdr.cmd_len = sizeof(rbCmdBlk);
3889	io_hdr.mx_sb_len = sizeof(sense_buffer);
3890	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
3891	io_hdr.dxfer_len = RB_DESC_LEN;
3892	io_hdr.dxferp = rbBuff;
3893	io_hdr.cmdp = rbCmdBlk;
3894	io_hdr.sbp = sense_buffer;
3895	io_hdr.timeout = 60000;	/* 60000 millisecs == 60 seconds */
3896	/* do normal IO to find RB size (not dio or mmap-ed at this stage) */
3897
3898	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
3899		perror(ME "SG_IO READ BUFFER descriptor error");
3900		if (rawp)
3901			free(rawp);
3902		return 1;
3903	}
3904
3905	/* now for the error processing */
3906	switch (sg_err_category3(&io_hdr)) {
3907	case SG_ERR_CAT_CLEAN:
3908		break;
3909	case SG_ERR_CAT_RECOVERED:
3910		printf
3911		    ("Recovered error on READ BUFFER descriptor, continuing\n");
3912		break;
3913	default:		/* won't bother decoding other categories */
3914		sg_chk_n_print3("READ BUFFER descriptor error", &io_hdr);
3915		if (rawp)
3916			free(rawp);
3917		return 1;
3918	}
3919
3920	buf_capacity = ((rbBuff[1] << 16) | (rbBuff[2] << 8) | rbBuff[3]);
3921	printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
3922	       buf_capacity, (int)rbBuff[0]);
3923
3924	if (0 == buf_size)
3925		buf_size = buf_capacity;
3926	else if (buf_size > buf_capacity) {
3927		printf
3928		    ("Requested buffer size=%d exceeds reported capacity=%d\n",
3929		     buf_size, buf_capacity);
3930		if (rawp)
3931			free(rawp);
3932		return 1;
3933	}
3934	if (rawp) {
3935		free(rawp);
3936		rawp = NULL;
3937	}
3938
3939	if (!do_dio) {
3940		k = buf_size;
3941		if (do_mmap && (0 != (k % psz)))
3942			k = ((k / psz) + 1) * psz;	/* round up to page size */
3943		res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, &k);
3944		if (res < 0)
3945			perror(ME "SG_SET_RESERVED_SIZE error");
3946	}
3947
3948	if (do_mmap) {
3949		rbBuff = mmap(NULL, buf_size, PROT_READ, MAP_SHARED, sg_fd, 0);
3950		if (MAP_FAILED == rbBuff) {
3951			if (ENOMEM == errno)
3952				printf(ME "mmap() out of memory, try a smaller "
3953				       "buffer size than %d KB\n",
3954				       buf_size / 1024);
3955			else
3956				perror(ME "error using mmap()");
3957			return 1;
3958		}
3959	} else {		/* non mmap-ed IO */
3960		rawp = malloc(buf_size + (do_dio ? psz : 0));
3961		if (NULL == rawp) {
3962			printf(ME "out of memory (data)\n");
3963			return 1;
3964		}
3965		if (do_dio)	/* align to page boundary */
3966			rbBuff =
3967			    (unsigned char *)(((unsigned long)rawp + psz - 1) &
3968					      (~(psz - 1)));
3969		else
3970			rbBuff = rawp;
3971	}
3972
3973	num = (total_size_mb * 1024U * 1024U) / (unsigned int)buf_size;
3974	if (do_time) {
3975		start_tm.tv_sec = 0;
3976		start_tm.tv_usec = 0;
3977		gettimeofday(&start_tm, NULL);
3978	}
3979	/* main data reading loop */
3980	for (k = 0; k < num; ++k) {
3981		memset(rbCmdBlk, 0, RB_CMD_LEN);
3982		rbCmdBlk[0] = RB_OPCODE;
3983		rbCmdBlk[1] = RB_MODE_DATA;
3984		rbCmdBlk[6] = 0xff & (buf_size >> 16);
3985		rbCmdBlk[7] = 0xff & (buf_size >> 8);
3986		rbCmdBlk[8] = 0xff & buf_size;
3987#ifdef SG_DEBUG
3988		memset(rbBuff, 0, buf_size);
3989#endif
3990
3991		memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
3992		io_hdr.interface_id = 'S';
3993		io_hdr.cmd_len = sizeof(rbCmdBlk);
3994		io_hdr.mx_sb_len = sizeof(sense_buffer);
3995		io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
3996		io_hdr.dxfer_len = buf_size;
3997		if (!do_mmap)
3998			io_hdr.dxferp = rbBuff;
3999		io_hdr.cmdp = rbCmdBlk;
4000		io_hdr.sbp = sense_buffer;
4001		io_hdr.timeout = 20000;	/* 20000 millisecs == 20 seconds */
4002		io_hdr.pack_id = k;
4003		if (do_mmap)
4004			io_hdr.flags |= SG_FLAG_MMAP_IO;
4005		else if (do_dio)
4006			io_hdr.flags |= SG_FLAG_DIRECT_IO;
4007		else if (do_quick)
4008			io_hdr.flags |= SG_FLAG_NO_DXFER;
4009
4010		if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4011			if (ENOMEM == errno)
4012				printf(ME
4013				       "SG_IO data; out of memory, try a smaller "
4014				       "buffer size than %d KB\n",
4015				       buf_size / 1024);
4016			else
4017				perror(ME "SG_IO READ BUFFER data error");
4018			if (rawp)
4019				free(rawp);
4020			return 1;
4021		}
4022
4023		/* now for the error processing */
4024		switch (sg_err_category3(&io_hdr)) {
4025		case SG_ERR_CAT_CLEAN:
4026			break;
4027		case SG_ERR_CAT_RECOVERED:
4028			printf
4029			    ("Recovered error on READ BUFFER data, continuing\n");
4030			break;
4031		default:	/* won't bother decoding other categories */
4032			sg_chk_n_print3("READ BUFFER data error", &io_hdr);
4033			if (rawp)
4034				free(rawp);
4035			return 1;
4036		}
4037		if (do_dio &&
4038		    ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) !=
4039		     SG_INFO_DIRECT_IO))
4040			dio_incomplete = 1;	/* flag that dio not done (completely) */
4041
4042#ifdef SG_DEBUG
4043		if (clear) {
4044			for (j = 0; j < buf_size; ++j) {
4045				if (rbBuff[j] != 0) {
4046					clear = 0;
4047					break;
4048				}
4049			}
4050		}
4051#endif
4052	}
4053	if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
4054		struct timeval res_tm;
4055		double a, b;
4056
4057		gettimeofday(&end_tm, NULL);
4058		res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
4059		res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
4060		if (res_tm.tv_usec < 0) {
4061			--res_tm.tv_sec;
4062			res_tm.tv_usec += 1000000;
4063		}
4064		a = res_tm.tv_sec;
4065		a += (0.000001 * res_tm.tv_usec);
4066		b = (double)buf_size *num;
4067		printf("time to read data from buffer was %d.%06d secs",
4068		       (int)res_tm.tv_sec, (int)res_tm.tv_usec);
4069		if ((a > 0.00001) && (b > 511))
4070			printf(", %.2f MB/sec\n", b / (a * 1000000.0));
4071		else
4072			printf("\n");
4073	}
4074	if (dio_incomplete)
4075		printf(">> direct IO requested but not done\n");
4076	printf
4077	    ("Read %u MBytes (actual %u MB, %u bytes), buffer size=%d KBytes\n",
4078	     total_size_mb, (num * buf_size) / 1048576, num * buf_size,
4079	     buf_size / 1024);
4080
4081	if (rawp)
4082		free(rawp);
4083	res = close(sg_fd);
4084	if (res < 0) {
4085		perror(ME "close error");
4086		return 0;
4087	}
4088#ifdef SG_DEBUG
4089	if (clear)
4090		printf("read buffer always zero\n");
4091	else
4092		printf("read buffer non-zero\n");
4093#endif
4094	return 0;
4095}
4096
4097/* Performs a 10 byte READ CAPACITY command and fetches response. There is
4098 * evidently a 16 byte READ CAPACITY command coming.
4099 * Return of 0 -> success, -1 -> failure */
4100int do_readcap_10(int sg_fd, int pmi, unsigned int lba,
4101		  unsigned int *last_sect, unsigned int *sect_sz)
4102{
4103	int res;
4104	unsigned char rcCmdBlk[10] = { 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4105	unsigned char rcBuff[RCAP_REPLY_LEN];
4106	unsigned char sense_b[SENSE_BUFF_SZ];
4107	sg_io_hdr_t io_hdr;
4108
4109	if (pmi) {		/* lbs only valid when pmi set */
4110		rcCmdBlk[8] |= 1;
4111		rcCmdBlk[2] = (lba >> 24) & 0xff;
4112		rcCmdBlk[3] = (lba >> 16) & 0xff;
4113		rcCmdBlk[4] = (lba >> 8) & 0xff;
4114		rcCmdBlk[5] = lba & 0xff;
4115	}
4116	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4117	io_hdr.interface_id = 'S';
4118	io_hdr.cmd_len = sizeof(rcCmdBlk);
4119	io_hdr.mx_sb_len = sizeof(sense_b);
4120	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
4121	io_hdr.dxfer_len = sizeof(rcBuff);
4122	io_hdr.dxferp = rcBuff;
4123	io_hdr.cmdp = rcCmdBlk;
4124	io_hdr.sbp = sense_b;
4125	io_hdr.timeout = 60000;
4126
4127	while (1) {
4128		if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4129			perror("read_capacity (SG_IO) error");
4130			return -1;
4131		}
4132		res = sg_err_category3(&io_hdr);
4133		if (SG_ERR_CAT_MEDIA_CHANGED == res)
4134			continue;
4135		else if (SG_ERR_CAT_CLEAN != res) {
4136			sg_chk_n_print3("READ CAPACITY command error", &io_hdr);
4137			return -1;
4138		} else
4139			break;
4140	}
4141	*last_sect = ((rcBuff[0] << 24) | (rcBuff[1] << 16) |
4142		      (rcBuff[2] << 8) | rcBuff[3]);
4143	*sect_sz = (rcBuff[4] << 24) | (rcBuff[5] << 16) |
4144	    (rcBuff[6] << 8) | rcBuff[7];
4145	return 0;
4146}
4147
4148int show_scsi_read_capacity(char *device)
4149{
4150	int sg_fd, k, res;
4151	unsigned int lba = 0;
4152	int pmi = 1;
4153	unsigned int last_blk_addr, block_size;
4154	char ebuff[EBUFF_SZ];
4155	const char *file_name = 0;
4156
4157	print_msg(TEST_BREAK, __FUNCTION__);
4158
4159	file_name = device;
4160
4161	if ((0 == pmi) && (lba > 0)) {
4162		fprintf(stderr,
4163			ME "lba can only be non-zero when pmi is set\n");
4164		usage();
4165		return 1;
4166	}
4167	if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
4168		snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
4169			 file_name);
4170		perror(ebuff);
4171		return 1;
4172	}
4173	/* Just to be safe, check we have a new sg device by trying an ioctl */
4174	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
4175		printf(ME "%s doesn't seem to be a version 3 sg device\n",
4176		       file_name);
4177		close(sg_fd);
4178		return 1;
4179	}
4180	res = do_readcap_10(sg_fd, pmi, lba, &last_blk_addr, &block_size);
4181
4182	if (0 == res) {
4183		printf("Read Capacity results:\n");
4184		if (pmi)
4185			printf("   PMI mode: given lba=0x%x, last block before "
4186			       "delay=0x%x\n", lba, last_blk_addr);
4187		else
4188			printf
4189			    ("   Last block address=%u (0x%x), Number of blocks=%u\n",
4190			     last_blk_addr, last_blk_addr, last_blk_addr + 1);
4191		printf("   Block size = %u bytes\n", block_size);
4192	}
4193	close(sg_fd);
4194	return 0;
4195}
4196
4197int do_scsi_reset_devices(char *device, int reset_opt)
4198{
4199	int sg_fd, res, k;
4200	int do_device_reset = 0;
4201	int do_bus_reset = 0;
4202	int do_host_reset = 0;
4203	char *file_name = 0;
4204
4205	switch (reset_opt) {
4206	case DEVICE_RESET:
4207		print_msg(TEST_BREAK, __FUNCTION__);
4208		do_device_reset = 1;
4209		break;
4210	case HOST_RESET:
4211		do_host_reset = 1;
4212		break;
4213	case BUS_RESET:
4214		do_bus_reset = 1;
4215		break;
4216	}
4217
4218	file_name = device;
4219
4220	sg_fd = open(file_name, O_RDWR | O_NONBLOCK);
4221	if (sg_fd < 0) {
4222		perror("sg_reset: open error");
4223		return 1;
4224	}
4225
4226	k = SG_SCSI_RESET_NOTHING;
4227	if (do_device_reset) {
4228		printf("sg_reset: starting device reset\n");
4229		k = SG_SCSI_RESET_DEVICE;
4230	} else if (do_bus_reset) {
4231		printf("sg_reset: starting bus reset\n");
4232		k = SG_SCSI_RESET_BUS;
4233	} else if (do_host_reset) {
4234		printf("sg_reset: starting host reset\n");
4235		k = SG_SCSI_RESET_HOST;
4236	}
4237
4238	res = ioctl(sg_fd, SG_SCSI_RESET, &k);
4239	if (res < 0) {
4240		if (EBUSY == errno)
4241			printf("sg_reset: BUSY, may be resetting now\n");
4242		else if (EIO == errno)
4243			printf
4244			    ("sg_reset: requested type of reset may not be available\n");
4245		else if (EACCES == errno)
4246			printf("sg_reset: reset requires CAP_SYS_ADMIN (root) "
4247			       "permission\n");
4248		else if (EINVAL == errno)
4249			printf("sg_reset: SG_SCSI_RESET not supported\n");
4250		else if (EIO == errno)
4251			printf("sg_reset: scsi_reset_provider() call failed\n");
4252		else
4253			perror("sg_reset: SG_SCSI_RESET failed");
4254		return 1;
4255	}
4256	if (SG_SCSI_RESET_NOTHING == k)
4257		printf("sg_reset: did nothing, device is normal mode\n");
4258	else if (SG_SCSI_RESET_DEVICE == k)
4259		printf("sg_reset: completed device reset\n");
4260	else if (SG_SCSI_RESET_BUS == k)
4261		printf("sg_reset: completed bus reset\n");
4262	else if (SG_SCSI_RESET_HOST == k)
4263		printf("sg_reset: completed host reset\n");
4264
4265	if (close(sg_fd) < 0) {
4266		perror("sg_reset: close error");
4267		return 1;
4268	}
4269	return 0;
4270}
4271
4272static int do_senddiag(int sg_fd, int sf_code, int pf_bit, int sf_bit,
4273		       int devofl_bit, int unitofl_bit, void *outgoing_pg,
4274		       int outgoing_len, int noisy)
4275{
4276	int res;
4277	unsigned char senddiagCmdBlk[SEND_DIAGNOSTIC_CMDLEN] =
4278	    { SEND_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 };
4279	unsigned char sense_b[SENSE_BUFF_LEN];
4280	sg_io_hdr_t io_hdr;
4281
4282	senddiagCmdBlk[1] = (unsigned char)((sf_code << 5) | (pf_bit << 4) |
4283					    (sf_bit << 2) | (devofl_bit << 1) |
4284					    unitofl_bit);
4285	senddiagCmdBlk[3] = (unsigned char)((outgoing_len >> 8) & 0xff);
4286	senddiagCmdBlk[4] = (unsigned char)(outgoing_len & 0xff);
4287
4288	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4289	io_hdr.interface_id = 'S';
4290	io_hdr.cmd_len = SEND_DIAGNOSTIC_CMDLEN;
4291	io_hdr.mx_sb_len = sizeof(sense_b);
4292	io_hdr.dxfer_direction = outgoing_len ? SG_DXFER_TO_DEV : SG_DXFER_NONE;
4293	io_hdr.dxfer_len = outgoing_len;
4294	io_hdr.dxferp = outgoing_pg;
4295	io_hdr.cmdp = senddiagCmdBlk;
4296	io_hdr.sbp = sense_b;
4297	io_hdr.timeout = LONG_TIMEOUT;
4298
4299	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4300		perror("SG_IO (send diagnostic) error");
4301		return -1;
4302	}
4303	res = sg_err_category3(&io_hdr);
4304	switch (res) {
4305	case SG_ERR_CAT_CLEAN:
4306	case SG_ERR_CAT_RECOVERED:
4307		return 0;
4308	default:
4309		if (noisy) {
4310			char ebuff[EBUFF_SZ];
4311			snprintf(ebuff, EBUFF_SZ,
4312				 "Send diagnostic error, sf_code=0x%x, "
4313				 "pf_bit=%d, sf_bit=%d ", sf_code, pf_bit,
4314				 sf_bit);
4315			sg_chk_n_print3(ebuff, &io_hdr);
4316		}
4317		return -1;
4318	}
4319}
4320
4321static int do_rcvdiag(int sg_fd, int pcv, int pg_code, void *resp,
4322		      int mx_resp_len, int noisy)
4323{
4324	int res;
4325	unsigned char rcvdiagCmdBlk[RECEIVE_DIAGNOSTIC_CMDLEN] =
4326	    { RECEIVE_DIAGNOSTIC_CMD, 0, 0, 0, 0, 0 };
4327	unsigned char sense_b[SENSE_BUFF_LEN];
4328	sg_io_hdr_t io_hdr;
4329
4330	rcvdiagCmdBlk[1] = (unsigned char)(pcv ? 0x1 : 0);
4331	rcvdiagCmdBlk[2] = (unsigned char)(pg_code);
4332	rcvdiagCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff);
4333	rcvdiagCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
4334
4335	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4336	io_hdr.interface_id = 'S';
4337	io_hdr.cmd_len = RECEIVE_DIAGNOSTIC_CMDLEN;
4338	io_hdr.mx_sb_len = sizeof(sense_b);
4339	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
4340	io_hdr.dxfer_len = mx_resp_len;
4341	io_hdr.dxferp = resp;
4342	io_hdr.cmdp = rcvdiagCmdBlk;
4343	io_hdr.sbp = sense_b;
4344	io_hdr.timeout = DEF_TIMEOUT;
4345
4346	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4347		perror("SG_IO (receive diagnostic) error");
4348		return -1;
4349	}
4350	res = sg_err_category3(&io_hdr);
4351	switch (res) {
4352	case SG_ERR_CAT_CLEAN:
4353	case SG_ERR_CAT_RECOVERED:
4354		return 0;
4355	default:
4356		if (noisy) {
4357			char ebuff[EBUFF_SZ];
4358			snprintf(ebuff, EBUFF_SZ,
4359				 "Receive diagnostic error, pcv=%d, "
4360				 "page_code=%x ", pcv, pg_code);
4361			sg_chk_n_print3(ebuff, &io_hdr);
4362		}
4363		return -1;
4364	}
4365}
4366
4367/* Get last extended self-test time from mode page 0xa (for '-e' option) */
4368static int do_modes_0a(int sg_fd, void *resp, int mx_resp_len, int noisy,
4369		       int mode6)
4370{
4371	int res;
4372	unsigned char modesCmdBlk[MODE_SENSE10_CMDLEN] =
4373	    { MODE_SENSE10_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
4374	unsigned char sense_b[SENSE_BUFF_LEN];
4375	sg_io_hdr_t io_hdr;
4376	int dbd = 1;
4377	int pc = 0;
4378	int pg_code = 0xa;
4379
4380	modesCmdBlk[1] = (unsigned char)(dbd ? 0x8 : 0);
4381	modesCmdBlk[2] = (unsigned char)(((pc << 6) & 0xc0) | (pg_code & 0x3f));
4382	if (mx_resp_len > (mode6 ? 0xff : 0xffff)) {
4383		printf(ME "mx_resp_len too big\n");
4384		return -1;
4385	}
4386	if (mode6) {
4387		modesCmdBlk[0] = MODE_SENSE6_CMD;
4388		modesCmdBlk[4] = (unsigned char)(mx_resp_len & 0xff);
4389	} else {
4390		modesCmdBlk[7] = (unsigned char)((mx_resp_len >> 8) & 0xff);
4391		modesCmdBlk[8] = (unsigned char)(mx_resp_len & 0xff);
4392	}
4393
4394	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4395	io_hdr.interface_id = 'S';
4396	io_hdr.cmd_len = mode6 ? MODE_SENSE6_CMDLEN : MODE_SENSE10_CMDLEN;
4397	io_hdr.mx_sb_len = sizeof(sense_b);
4398	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
4399	io_hdr.dxfer_len = mx_resp_len;
4400	io_hdr.dxferp = resp;
4401	io_hdr.cmdp = modesCmdBlk;
4402	io_hdr.sbp = sense_b;
4403	io_hdr.timeout = DEF_TIMEOUT;
4404
4405	if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4406		perror("SG_IO (mode sense) error");
4407		return -1;
4408	}
4409	res = sg_err_category3(&io_hdr);
4410	switch (res) {
4411	case SG_ERR_CAT_CLEAN:
4412	case SG_ERR_CAT_RECOVERED:
4413		return 0;
4414	default:
4415		if (noisy) {
4416			char ebuff[EBUFF_SZ];
4417			snprintf(ebuff, EBUFF_SZ, "Mode sense error, dbd=%d, "
4418				 "pc=%d, page_code=%x ", dbd, pc, pg_code);
4419			sg_chk_n_print3(ebuff, &io_hdr);
4420		}
4421		return -1;
4422	}
4423}
4424
4425int do_scsi_send_diagnostics(char *device)
4426{
4427	int sg_fd, k, num, rsp_len;
4428	char *file_name = 0;
4429	unsigned char rsp_buff[MODE_ALLOC_LEN];
4430	int rsp_buff_size = MODE_ALLOC_LEN;
4431	int self_test_code = 6;
4432	int do_pf = 0;
4433	int do_doff = 0;
4434	int do_def_test = 0;
4435	int do_uoff = 0;
4436	int oflags = O_RDWR;
4437
4438	print_msg(TEST_BREAK, __FUNCTION__);
4439
4440	file_name = device;
4441
4442	if ((sg_fd = open(file_name, oflags)) < 0) {
4443		snprintf(ebuff, EBUFF_SZ, ME "error opening file: %s",
4444			 file_name);
4445		perror(ebuff);
4446		return 1;
4447	}
4448	/* Just to be safe, check we have a new sg device by trying an ioctl */
4449	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
4450		printf(ME "%s doesn't seem to be a version 3 sg device\n",
4451		       file_name);
4452		close(sg_fd);
4453		return 1;
4454	}
4455
4456	if (0 == do_modes_0a(sg_fd, rsp_buff, 32, 1, 0)) {
4457		/* Assume mode sense(10) response without block descriptors */
4458		num = (rsp_buff[0] << 8) + rsp_buff[1] - 6;
4459		if (num >= 0xc) {
4460			int secs;
4461
4462			secs = (rsp_buff[18] << 8) + rsp_buff[19];
4463			printf
4464			    ("Previous extended self-test duration=%d seconds "
4465			     "(%.2f minutes)\n", secs, secs / 60.0);
4466		} else
4467			printf("Extended self-test duration not available\n");
4468	} else
4469		printf("Extended self-test duration (mode page 0xa) failed\n");
4470
4471	memset(rsp_buff, 0, sizeof(rsp_buff));
4472	if (0 == do_senddiag(sg_fd, 0, do_pf, 0, 0, 0, rsp_buff, 4, 1)) {
4473		if (0 == do_rcvdiag(sg_fd, 0, 0, rsp_buff, rsp_buff_size, 1)) {
4474			printf("Supported diagnostic pages response:\n");
4475			rsp_len = (rsp_buff[2] << 8) + rsp_buff[3] + 4;
4476			for (k = 0; k < (rsp_len - 4); ++k)
4477				printf("  %s\n",
4478				       find_page_code_desc(rsp_buff[k + 4], 0));
4479		}
4480	}
4481
4482	if (0 == do_senddiag(sg_fd, self_test_code, do_pf, do_def_test,
4483			     do_doff, do_uoff, NULL, 0, 1)) {
4484		if ((5 == self_test_code) || (6 == self_test_code))
4485			printf("Foreground self test returned GOOD status\n");
4486		else if (do_def_test && (!do_doff) && (!do_uoff))
4487			printf("Default self test returned GOOD status\n");
4488	}
4489	close(sg_fd);
4490	return 0;
4491}
4492
4493static void do_start_stop(int fd, int start, int immed, int loej,
4494			  int power_conditions)
4495{
4496	unsigned char cmdblk[6] = {
4497		START_STOP,	/* Command */
4498		0,		/* Resvd/Immed */
4499		0,		/* Reserved */
4500		0,		/* Reserved */
4501		0,		/* PowCond/Resvd/LoEj/Start */
4502		0
4503	};			/* Reserved/Flag/Link */
4504	unsigned char sense_b[32];
4505	sg_io_hdr_t io_hdr;
4506	int k, res, debug = 1;
4507
4508	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4509	cmdblk[1] = immed & 1;
4510	cmdblk[4] = ((power_conditions & 0xf) << 4) |
4511	    ((loej & 1) << 1) | (start & 1);
4512	io_hdr.interface_id = 'S';
4513	io_hdr.cmd_len = sizeof(cmdblk);
4514	io_hdr.mx_sb_len = sizeof(sense_b);
4515	io_hdr.dxfer_direction = SG_DXFER_NONE;
4516	io_hdr.dxfer_len = 0;
4517	io_hdr.dxferp = NULL;
4518	io_hdr.cmdp = cmdblk;
4519	io_hdr.sbp = sense_b;
4520	io_hdr.timeout = DEF_START_TIMEOUT;
4521
4522	if (debug) {
4523		printf("  Start/Stop command:");
4524		for (k = 0; k < 6; ++k)
4525			printf(" %02x", cmdblk[k]);
4526		printf("\n");
4527	}
4528
4529	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
4530		perror("start_stop (SG_IO) error");
4531		return;
4532	}
4533	res = sg_err_category3(&io_hdr);
4534	if (SG_ERR_CAT_MEDIA_CHANGED == res) {
4535		fprintf(stderr, "media change report, try start_stop again\n");
4536		if (ioctl(fd, SG_IO, &io_hdr) < 0) {
4537			perror("start_stop (SG_IO) error");
4538			return;
4539		}
4540	}
4541	if (SG_ERR_CAT_CLEAN != res) {
4542		sg_chk_n_print3("start_stop", &io_hdr);
4543		return;
4544	}
4545	if (debug)
4546		fprintf(stderr, "start_stop [%s] successful\n",
4547			start ? "start" : "stop");
4548}
4549
4550static void do_sync_cache(int fd)
4551{
4552	unsigned char cmdblk[10] = {
4553		SYNCHRONIZE_CACHE,	/* Command */
4554		0,		/* Immed (2) */
4555		0, 0, 0, 0,	/* LBA */
4556		0,		/* Reserved */
4557		0, 0,		/* No of blocks */
4558		0
4559	};			/* Reserved/Flag/Link */
4560	unsigned char sense_b[32];
4561	sg_io_hdr_t io_hdr;
4562	int res, debug = 1;
4563
4564	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4565	io_hdr.interface_id = 'S';
4566	io_hdr.cmd_len = sizeof(cmdblk);
4567	io_hdr.mx_sb_len = sizeof(sense_b);
4568	io_hdr.dxfer_direction = SG_DXFER_NONE;
4569	io_hdr.dxfer_len = 0;
4570	io_hdr.dxferp = NULL;
4571	io_hdr.cmdp = cmdblk;
4572	io_hdr.sbp = sense_b;
4573	io_hdr.timeout = DEF_START_TIMEOUT;
4574
4575	if (ioctl(fd, SG_IO, &io_hdr) < 0) {
4576		perror("sync_cache (SG_IO) error");
4577		return;
4578	}
4579	res = sg_err_category3(&io_hdr);
4580	if (SG_ERR_CAT_MEDIA_CHANGED == res) {
4581		fprintf(stderr, "media change report, try sync_cache again\n");
4582		if (ioctl(fd, SG_IO, &io_hdr) < 0) {
4583			perror("sync_cache (SG_IO) error");
4584			return;
4585		}
4586	}
4587	if (SG_ERR_CAT_CLEAN != res) {
4588		sg_chk_n_print3("sync_cache", &io_hdr);
4589		return;
4590	}
4591	if (debug)
4592		fprintf(stderr, "synchronize cache successful\n");
4593}
4594
4595int do_scsi_start_stop(char *device, int startstop)
4596{
4597	int synccache = 1;
4598	char *file_name = 0;
4599	int fd;
4600	int immed = 1;
4601	int loej = 0;
4602	int power_conds = 0;
4603
4604	print_msg(TEST_BREAK, __FUNCTION__);
4605
4606	file_name = device;
4607
4608	fd = open(file_name, O_RDWR | O_NONBLOCK);
4609	if (fd < 0) {
4610		fprintf(stderr, "Error trying to open %s\n", file_name);
4611		perror("");
4612		usage();
4613		return 2;
4614	}
4615	if (ioctl(fd, SG_GET_TIMEOUT, 0) < 0) {
4616		fprintf(stderr, "Given file not block or SCSI "
4617			"generic device\n");
4618		close(fd);
4619		return 3;
4620	}
4621
4622	if (synccache)
4623		do_sync_cache(fd);
4624
4625	if (power_conds > 0)
4626		do_start_stop(fd, 0, immed, 0, power_conds);
4627	else if (startstop != -1)
4628		do_start_stop(fd, startstop, immed, loej, 0);
4629
4630	close(fd);
4631	return 0;
4632}
4633
4634int find_out_about_buffer(int sg_fd, int *buf_capacity, char *file_name)
4635{
4636	int res, buf_granul = 255;
4637	unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + 512);
4638	struct sg_header *rsghp = (struct sg_header *)rbBuff;
4639	int rbInLen = OFF + RB_DESC_LEN;
4640	int rbOutLen = OFF + sizeof(rbCmdBlk);
4641	unsigned char *buffp = rbBuff + OFF;
4642	rsghp->pack_len = 0;	/* don't care */
4643	rsghp->pack_id = 0;
4644	rsghp->reply_len = rbInLen;
4645	rsghp->twelve_byte = 0;
4646	rsghp->result = 0;
4647#ifndef SG_GET_RESERVED_SIZE
4648	rsghp->sense_buffer[0] = 0;
4649#endif
4650	memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
4651	rbBuff[OFF + 1] = RB_MODE_DESC;
4652	rbBuff[OFF + 8] = RB_DESC_LEN;
4653
4654	res = write(sg_fd, rbBuff, rbOutLen);
4655	if (res < 0) {
4656		perror("sg_test_rwbuf: write (desc) error");
4657		if (rbBuff)
4658			free(rbBuff);
4659		return 1;
4660	}
4661	if (res < rbOutLen) {
4662		printf("sg_test_rwbuf: wrote less (desc), ask=%d, got=%d\n",
4663		       rbOutLen, res);
4664		if (rbBuff)
4665			free(rbBuff);
4666		return 1;
4667	}
4668
4669	memset(rbBuff + OFF, 0, RB_DESC_LEN);
4670	res = read(sg_fd, rbBuff, rbInLen);
4671	if (res < 0) {
4672		perror("sg_test_rwbuf: read (desc) error");
4673		if (rbBuff)
4674			free(rbBuff);
4675		return 1;
4676	}
4677	if (res < rbInLen) {
4678		printf("sg_test_rwbuf: read less (desc), ask=%d, got=%d\n",
4679		       rbInLen, res);
4680		if (rbBuff)
4681			free(rbBuff);
4682		return 1;
4683	}
4684#ifdef SG_GET_RESERVED_SIZE
4685	if (!sg_chk_n_print("sg_test_rwbuf: desc", rsghp->target_status,
4686			    rsghp->host_status, rsghp->driver_status,
4687			    rsghp->sense_buffer, SG_MAX_SENSE)) {
4688		printf
4689		    ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
4690		     file_name);
4691		if (rbBuff)
4692			free(rbBuff);
4693		return 1;
4694	}
4695#else
4696	if ((rsghp->result != 0) || (0 != rsghp->sense_buffer[0])) {
4697		printf("sg_test_rwbuf: read(desc) result=%d\n", rsghp->result);
4698		if (0 != rsghp->sense_buffer[0])
4699			sg_print_sense("sg_test_rwbuf: desc",
4700				       rsghp->sense_buffer, SG_MAX_SENSE);
4701		printf
4702		    ("sg_test_rwbuf: perhaps %s doesn't support READ BUFFER\n",
4703		     file_name);
4704		if (rbBuff)
4705			free(rbBuff);
4706		return 1;
4707	}
4708#endif
4709	*(buf_capacity) = ((buffp[1] << 16) | (buffp[2] << 8) | buffp[3]);
4710	buf_granul = (unsigned char)buffp[0];
4711
4712	printf("READ BUFFER reports: %02x %02x %02x %02x %02x %02x %02x %02x\n",
4713	       buffp[0], buffp[1], buffp[2], buffp[3],
4714	       buffp[4], buffp[5], buffp[6], buffp[7]);
4715
4716	printf("READ BUFFER reports: buffer capacity=%d, offset boundary=%d\n",
4717	       *(buf_capacity), buf_granul);
4718#ifdef SG_DEF_RESERVED_SIZE
4719	res = ioctl(sg_fd, SG_SET_RESERVED_SIZE, buf_capacity);
4720	if (res < 0)
4721		perror("sg_test_rwbuf: SG_SET_RESERVED_SIZE error");
4722#endif
4723	return 0;
4724}
4725
4726int mymemcmp(unsigned char *bf1, unsigned char *bf2, int len)
4727{
4728	int df;
4729	for (df = 0; df < len; df++)
4730		if (bf1[df] != bf2[df])
4731			return df;
4732	return 0;
4733}
4734
4735int do_checksum(int *buf, int len, int quiet)
4736{
4737	int sum = base;
4738	int i;
4739	int rln = len;
4740	for (i = 0; i < len / BPI; i++)
4741		sum += buf[i];
4742	while (rln % BPI)
4743		sum += ((char *)buf)[--rln];
4744	if (sum != READWRITE_BASE_NUM) {
4745		if (!quiet)
4746			printf("sg_test_rwbuf: Checksum error (sz=%i): %08x\n",
4747			       len, sum);
4748		if (cmpbuf && !quiet) {
4749			int diff = mymemcmp(cmpbuf, (unsigned char *)buf, len);
4750			printf("Differ at pos %i/%i:\n", diff, len);
4751			for (i = 0; i < 24 && i + diff < len; i++)
4752				printf(" %02x", cmpbuf[i + diff]);
4753			printf("\n");
4754			for (i = 0; i < 24 && i + diff < len; i++)
4755				printf(" %02x",
4756				       ((unsigned char *)buf)[i + diff]);
4757			printf("\n");
4758		}
4759		return 2;
4760	} else
4761		return 0;
4762}
4763
4764void do_fill_buffer(int *buf, int len)
4765{
4766	int sum;
4767	int i;
4768	int rln = len;
4769	srand(time(0));
4770retry:
4771	if (len >= BPI)
4772		base = READWRITE_BASE_NUM + rand();
4773	else
4774		base = READWRITE_BASE_NUM + (char)rand();
4775	sum = base;
4776	for (i = 0; i < len / BPI - 1; i++) {
4777		/* we rely on rand() giving full range of int */
4778		buf[i] = rand();
4779		sum += buf[i];
4780	}
4781	while (rln % BPI) {
4782		((char *)buf)[--rln] = rand();
4783		sum += ((char *)buf)[rln];
4784	}
4785	if (len >= BPI)
4786		buf[len / BPI - 1] = READWRITE_BASE_NUM - sum;
4787	else
4788		((char *)buf)[0] = READWRITE_BASE_NUM + ((char *)buf)[0] - sum;
4789	if (do_checksum(buf, len, 1)) {
4790		if (len < BPI)
4791			goto retry;
4792		printf("sg_test_rwbuf: Memory corruption?\n");
4793		exit(1);
4794	}
4795	if (cmpbuf)
4796		memcpy(cmpbuf, (char *)buf, len);
4797}
4798
4799int read_buffer(int sg_fd, unsigned size)
4800{
4801	int res;
4802	unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size);
4803	struct sg_header *rsghp = (struct sg_header *)rbBuff;
4804
4805	int rbInLen = OFF + size;
4806	int rbOutLen = OFF + sizeof(rbCmdBlk);
4807	memset(rbBuff, 0, OFF + sizeof(rbCmdBlk) + size);
4808	rsghp->pack_len = 0;	/* don't care */
4809	rsghp->reply_len = rbInLen;
4810	rsghp->twelve_byte = 0;
4811	rsghp->result = 0;
4812	memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
4813	rbBuff[OFF + 1] = RB_MODE_DATA;
4814	rbBuff[OFF + 6] = 0xff & ((size) >> 16);
4815	rbBuff[OFF + 7] = 0xff & ((size) >> 8);
4816	rbBuff[OFF + 8] = 0xff & (size);
4817
4818	rsghp->pack_id = 2;
4819	res = write(sg_fd, rbBuff, rbOutLen);
4820	if (res < 0) {
4821		perror("sg_test_rwbuf: write (data) error");
4822		if (rbBuff)
4823			free(rbBuff);
4824		return 1;
4825	}
4826	if (res < rbOutLen) {
4827		printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
4828		       rbOutLen, res);
4829		if (rbBuff)
4830			free(rbBuff);
4831		return 1;
4832	}
4833
4834	res = read(sg_fd, rbBuff, rbInLen);
4835	if (res < 0) {
4836		perror("sg_test_rwbuf: read (data) error");
4837		if (rbBuff)
4838			free(rbBuff);
4839		return 1;
4840	}
4841	if (res < rbInLen) {
4842		printf("sg_test_rwbuf: read less (data), ask=%d, got=%d\n",
4843		       rbInLen, res);
4844		if (rbBuff)
4845			free(rbBuff);
4846		return 1;
4847	}
4848	res = do_checksum((int *)(rbBuff + OFF), size, 0);
4849	if (rbBuff)
4850		free(rbBuff);
4851	return res;
4852}
4853
4854int write_buffer(int sg_fd, unsigned size)
4855{
4856	int res;
4857	unsigned char *rbBuff = malloc(OFF + sizeof(rbCmdBlk) + size);
4858	struct sg_header *rsghp = (struct sg_header *)rbBuff;
4859	//unsigned char * buffp = rbBuff + OFF;
4860
4861	int rbInLen = OFF;
4862	int rbOutLen = OFF + sizeof(rbCmdBlk) + size;
4863
4864	do_fill_buffer((int *)(rbBuff + OFF + sizeof(rbCmdBlk)), size);
4865	rsghp->pack_len = 0;	/* don't care */
4866	rsghp->reply_len = rbInLen;
4867	rsghp->twelve_byte = 0;
4868	rsghp->result = 0;
4869	memcpy(rbBuff + OFF, rbCmdBlk, sizeof(rbCmdBlk));
4870	rbBuff[OFF + 0] = WRITE_BUFFER;
4871	rbBuff[OFF + 1] = RB_MODE_DATA;
4872	rbBuff[OFF + 6] = 0xff & ((size) >> 16);
4873	rbBuff[OFF + 7] = 0xff & ((size) >> 8);
4874	rbBuff[OFF + 8] = 0xff & (size);
4875
4876	rsghp->pack_id = 1;
4877	res = write(sg_fd, rbBuff, rbOutLen);
4878	if (res < 0) {
4879		perror("sg_test_rwbuf: write (data) error");
4880		if (rbBuff)
4881			free(rbBuff);
4882		return 1;
4883	}
4884	if (res < rbOutLen) {
4885		printf("sg_test_rwbuf: wrote less (data), ask=%d, got=%d\n",
4886		       rbOutLen, res);
4887		if (rbBuff)
4888			free(rbBuff);
4889		return 1;
4890	}
4891
4892	res = read(sg_fd, rbBuff, rbInLen);
4893	if (res < 0) {
4894		perror("sg_test_rwbuf: read (status) error");
4895		if (rbBuff)
4896			free(rbBuff);
4897		return 1;
4898	}
4899	if (rbBuff)
4900		free(rbBuff);
4901	return 0;
4902}
4903
4904int do_scsi_read_write_buffer(char *device)
4905{
4906	int sg_fd;
4907	int res, buf_capacity;
4908	char *file_name = device;
4909	struct stat a_st;
4910	int block_dev = 0;
4911
4912	print_msg(TEST_BREAK, __FUNCTION__);
4913
4914	sg_fd = open(file_name, O_RDWR);
4915	if (sg_fd < 0) {
4916		perror("sg_test_rwbuf: open error");
4917		return 1;
4918	}
4919	if (fstat(sg_fd, &a_st) < 0) {
4920		fprintf(stderr, "could do fstat() on fd ??\n");
4921		close(sg_fd);
4922		return 1;
4923	}
4924	if (S_ISBLK(a_st.st_mode))
4925		block_dev = 1;
4926	/* Don't worry, being very careful not to write to a none-sg file ... */
4927	if (block_dev || (ioctl(sg_fd, SG_GET_TIMEOUT, 0) < 0)) {
4928		/* perror("ioctl on generic device, error"); */
4929		printf("sg_test_rwbuf: not a sg device, or wrong driver\n");
4930		return 1;
4931	}
4932	if (find_out_about_buffer(sg_fd, &buf_capacity, file_name))
4933		return 1;
4934
4935	cmpbuf = malloc(buf_capacity);
4936	if (write_buffer(sg_fd, buf_capacity))
4937		return 3;
4938	res = read_buffer(sg_fd, buf_capacity);
4939	if (res)
4940		return (res + 4);
4941
4942	res = close(sg_fd);
4943	if (res < 0) {
4944		perror("sg_test_rwbuf: close error");
4945		return 6;
4946	}
4947	printf("Success\n");
4948	return 0;
4949}
4950
4951int do_scsi_test_unit_ready(char *device)
4952{
4953	int sg_fd, k;
4954	unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
4955	sg_io_hdr_t io_hdr;
4956	char *file_name = device;
4957	char ebuff[EBUFF_SZ];
4958	unsigned char sense_buffer[32];
4959	int num_turs = 10240;
4960	int num_errs = 0;
4961	int do_time = 1;
4962	struct timeval start_tm, end_tm;
4963
4964	print_msg(TEST_BREAK, __FUNCTION__);
4965
4966	if ((sg_fd = open(file_name, O_RDONLY)) < 0) {
4967		snprintf(ebuff, EBUFF_SZ,
4968			 "sg_turs: error opening file: %s", file_name);
4969		perror(ebuff);
4970		return 1;
4971	}
4972	/* Just to be safe, check we have a new sg driver by trying an ioctl */
4973	if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) {
4974		printf
4975		    ("sg_turs: %s isn't an sg device (or the sg driver is old)\n",
4976		     file_name);
4977		close(sg_fd);
4978		return 1;
4979	}
4980	/* Prepare TEST UNIT READY command */
4981	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
4982	io_hdr.interface_id = 'S';
4983	io_hdr.cmd_len = sizeof(turCmdBlk);
4984	io_hdr.mx_sb_len = sizeof(sense_buffer);
4985	io_hdr.dxfer_direction = SG_DXFER_NONE;
4986	io_hdr.cmdp = turCmdBlk;
4987	io_hdr.sbp = sense_buffer;
4988	io_hdr.timeout = 20000;	/* 20000 millisecs == 20 seconds */
4989	if (do_time) {
4990		start_tm.tv_sec = 0;
4991		start_tm.tv_usec = 0;
4992		gettimeofday(&start_tm, NULL);
4993	}
4994	for (k = 0; k < num_turs; ++k) {
4995		io_hdr.pack_id = k;
4996		if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
4997			perror("sg_turs: Test Unit Ready SG_IO ioctl error");
4998			close(sg_fd);
4999			return 1;
5000		}
5001		if (io_hdr.info & SG_INFO_OK_MASK) {
5002			++num_errs;
5003			if (1 == num_turs) {	/* then print out the error message */
5004				if (SG_ERR_CAT_CLEAN !=
5005				    sg_err_category3(&io_hdr))
5006					sg_chk_n_print3("tur", &io_hdr);
5007			}
5008		}
5009	}
5010	if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
5011		struct timeval res_tm;
5012		double a, b;
5013
5014		gettimeofday(&end_tm, NULL);
5015		res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
5016		res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
5017		if (res_tm.tv_usec < 0) {
5018			--res_tm.tv_sec;
5019			res_tm.tv_usec += 1000000;
5020		}
5021		a = res_tm.tv_sec;
5022		a += (0.000001 * res_tm.tv_usec);
5023		b = (double)num_turs;
5024		printf("time to perform commands was %d.%06d secs",
5025		       (int)res_tm.tv_sec, (int)res_tm.tv_usec);
5026		if (a > 0.00001)
5027			printf("; %.2f operations/sec\n", b / a);
5028		else
5029			printf("\n");
5030	}
5031
5032	printf("Completed %d Test Unit Ready commands with %d errors\n",
5033	       num_turs, num_errs);
5034	close(sg_fd);
5035	return 0;
5036}
5037
5038/* Returns 0 -> ok, 1 -> err, 2 -> recovered error */
5039static int do_sg_io(int sg_fd, unsigned char *buff)
5040{
5041/* N.B. Assuming buff contains pointer 'buffer' or 'buffer1' */
5042	struct sg_header *sghp = (struct sg_header *)(buff - OFF);
5043	int res;
5044
5045	sghp->pack_len = 0;
5046	sghp->reply_len = SG_HSZ + *(((int *)buff) + 1);
5047	sghp->pack_id = 0;
5048	sghp->twelve_byte = 0;
5049	sghp->other_flags = 0;
5050#ifndef SG_GET_RESERVED_SIZE
5051	sghp->sense_buffer[0] = 0;
5052#endif
5053#if 0
5054	sg_print_command(buff + 8);
5055	printf(" write_len=%d, read_len=%d\n",
5056	       SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff),
5057	       sghp->reply_len);
5058#endif
5059	res = write(sg_fd, (const void *)sghp,
5060		    SG_HSZ + sg_get_command_size(buff[8]) + *((int *)buff));
5061	if (res < 0) {
5062#ifdef SG_IO_DEBUG
5063		perror("write to sg failed");
5064#endif
5065		return 1;
5066	}
5067	res = read(sg_fd, (void *)sghp, sghp->reply_len);
5068	if (res < 0) {
5069#ifdef SG_IO_DEBUG
5070		perror("read from sg failed");
5071#endif
5072		return 1;
5073	}
5074#ifdef SG_GET_RESERVED_SIZE
5075	res = sg_err_category(sghp->target_status, sghp->host_status,
5076			      sghp->driver_status, sghp->sense_buffer,
5077			      SG_MAX_SENSE);
5078	switch (res) {
5079	case SG_ERR_CAT_CLEAN:
5080		return 0;
5081	case SG_ERR_CAT_RECOVERED:
5082		return 2;
5083	default:
5084#ifdef SG_IO_DEBUG
5085		sg_chk_n_print("read from sg", sghp->target_status,
5086			       sghp->host_status, sghp->driver_status,
5087			       sghp->sense_buffer, SG_MAX_SENSE);
5088#endif
5089		return 1;
5090	}
5091#else
5092	if (0 != sghp->sense_buffer[0]) {
5093#ifdef SG_IO_DEBUG
5094		int k;
5095		printf("read from sg, sense buffer (in hex):\n    ");
5096		for (k = 0; k < 16; ++k)
5097			printf("%02x ", (int)sghp->sense_buffer[k]);
5098		printf("\n");
5099#endif
5100		return 1;
5101	} else if (0 != sghp->result) {
5102#ifdef SG_IO_DEBUG
5103		printf("read from sg, bad result=%d\n", sghp->result);
5104#endif
5105		return 1;
5106	} else
5107		return 0;
5108#endif
5109}
5110
5111static char *get_page_name(int pageno)
5112{
5113	if ((pageno <= 0) || (pageno >= MAX_PAGENO) || (!page_names[pageno]))
5114		return "Mode";
5115	return page_names[pageno];
5116}
5117
5118static int getnbyte(unsigned char *pnt, int nbyte)
5119{
5120	unsigned int result;
5121	int i;
5122	result = 0;
5123	for (i = 0; i < nbyte; i++)
5124		result = (result << 8) | (pnt[i] & 0xff);
5125	return result;
5126}
5127
5128static void bitfield(unsigned char *pageaddr, char *text, int mask, int shift)
5129{
5130	printf("%-35s%d\n", text, (*pageaddr >> shift) & mask);
5131}
5132
5133static void notbitfield(unsigned char *pageaddr, char *text, int mask,
5134			int shift)
5135{
5136	printf("%-35s%d\n", text, !((*pageaddr >> shift) & mask));
5137}
5138
5139static void intfield(unsigned char *pageaddr, int nbytes, char *text)
5140{
5141	printf("%-35s%d\n", text, getnbyte(pageaddr, nbytes));
5142}
5143
5144static void hexfield(unsigned char *pageaddr, int nbytes, char *text)
5145{
5146	printf("%-35s0x%x\n", text, getnbyte(pageaddr, nbytes));
5147}
5148
5149static void hexdatafield(unsigned char *pageaddr, int nbytes, char *text)
5150{
5151	printf("%-35s0x", text);
5152	while (nbytes-- > 0)
5153		printf("%02x", *pageaddr++);
5154	putchar('\n');
5155}
5156
5157static int get_mode_page(int page, int page_code)
5158{
5159	int status, quiet;
5160	unsigned char *cmd;
5161
5162	memset(buffer, 0, SIZEOF_BUFFER);
5163
5164	quiet = page_code & ~3;
5165	page_code &= 3;
5166
5167	*((int *)buffer) = 0;	/* length of input data */
5168	*(((int *)buffer) + 1) = 0xff;	/* length of output data */
5169
5170	cmd = (unsigned char *)(((int *)buffer) + 2);
5171
5172	cmd[0] = MODE_SENSE;	/* MODE SENSE (6) */
5173	cmd[1] = 0x00;		/* lun = 0, inhibitting BD makes this fail
5174				   for me */
5175	cmd[2] = (page_code << 6) | page;
5176	cmd[3] = 0x00;		/* (reserved) */
5177	cmd[4] = (unsigned char)0xff;	/* allocation length */
5178	cmd[5] = 0x00;		/* control */
5179
5180	status = do_sg_io(glob_fd, buffer);
5181	if (status && (!quiet))
5182		fprintf(stdout, ">>> Unable to read %s Page %02xh\n",
5183			get_page_name(page), page);
5184	//dump (buffer+2, 46);
5185	return status;
5186}
5187
5188/* Same as above, but this time with MODE_SENSE_10 */
5189static int get_mode_page10(int page, int page_code)
5190{
5191	int status, quiet;
5192	unsigned char *cmd;
5193
5194	memset(buffer, 0, SIZEOF_BUFFER);
5195
5196	quiet = page_code & ~3;
5197	page_code &= 3;
5198
5199	*((int *)buffer) = 0;	/* length of input data */
5200	*(((int *)buffer) + 1) = 0xffff;	/* length of output buffer */
5201
5202	cmd = (unsigned char *)(((int *)buffer) + 2);
5203
5204	cmd[0] = MODE_SENSE_10;	/* MODE SENSE (10) */
5205	cmd[1] = 0x00;		/* lun = 0, inhibitting BD makes this fail
5206				   for me */
5207	cmd[2] = (page_code << 6) | page;
5208	cmd[3] = 0x00;		/* (reserved) */
5209	cmd[4] = 0x00;		/* (reserved) */
5210	cmd[5] = 0x00;		/* (reserved) */
5211	cmd[6] = 0x00;		/* (reserved) */
5212	cmd[7] = 0xff;		/* allocation length hi */
5213	cmd[8] = 0xff;		/* allocation length lo */
5214	cmd[9] = 0x00;		/* control */
5215
5216	status = do_sg_io(glob_fd, buffer);
5217	if (status && (!quiet))
5218		fprintf(stdout,
5219			">>> Unable to read %s Page %02xh with MODESENSE(10)\n",
5220			get_page_name(page), page);
5221	return status;
5222}
5223
5224/* Contents should point to the mode parameter header that we obtained
5225   in a prior read operation.  This way we do not have to work out the
5226   format of the beast */
5227
5228static int read_geometry(int page_code)
5229{
5230	int status;
5231	int bdlen;
5232	unsigned char *pagestart;
5233
5234	SETUP_MODE_PAGE(4, 9);
5235
5236	printf("Data from Rigid Disk Drive Geometry Page\n");
5237	printf("----------------------------------------\n");
5238	intfield(pagestart + 2, 3, "Number of cylinders");
5239	intfield(pagestart + 5, 1, "Number of heads");
5240	intfield(pagestart + 6, 3, "Starting write precomp");
5241	intfield(pagestart + 9, 3, "Starting reduced current");
5242	intfield(pagestart + 12, 2, "Drive step rate");
5243	intfield(pagestart + 14, 3, "Landing Zone Cylinder");
5244	bitfield(pagestart + 17, "RPL", 3, 0);
5245	intfield(pagestart + 18, 1, "Rotational Offset");
5246	intfield(pagestart + 20, 2, "Rotational Rate");
5247	printf("\n");
5248	return 0;
5249
5250}
5251
5252static int read_disconnect_reconnect_data(int page_code)
5253{
5254	int status;
5255	int bdlen;
5256	unsigned char *pagestart;
5257
5258	SETUP_MODE_PAGE(2, 7);
5259
5260	printf("Data from Disconnect-Reconnect Page\n");
5261	printf("-----------------------------------\n");
5262	intfield(pagestart + 2, 1, "Buffer full ratio");
5263	intfield(pagestart + 3, 1, "Buffer empty ratio");
5264	intfield(pagestart + 4, 2, "Bus Inactivity Limit");
5265	intfield(pagestart + 6, 2, "Disconnect Time Limit");
5266	intfield(pagestart + 8, 2, "Connect Time Limit");
5267	intfield(pagestart + 10, 2, "Maximum Burst Size");
5268	hexfield(pagestart + 12, 1, "DTDC");
5269	printf("\n");
5270	return 0;
5271
5272}
5273
5274static int read_control_page(int page_code)
5275{
5276	int status;
5277	int bdlen;
5278	unsigned char *pagestart;
5279
5280	SETUP_MODE_PAGE(10, 9);
5281
5282	printf("Data from Control Page\n");
5283	printf("----------------------\n");
5284	bitfield(pagestart + 2, "RLEC", 1, 0);
5285	bitfield(pagestart + 3, "QErr", 1, 1);
5286	bitfield(pagestart + 3, "DQue", 1, 0);
5287	bitfield(pagestart + 4, "EECA", 1, 7);
5288	bitfield(pagestart + 4, "RAENP", 1, 2);
5289	bitfield(pagestart + 4, "UUAENP", 1, 1);
5290	bitfield(pagestart + 4, "EAENP", 1, 0);
5291	bitfield(pagestart + 3, "Queue Algorithm Modifier", 0xf, 4);
5292	intfield(pagestart + 6, 2, "Ready AEN Holdoff Period");
5293	printf("\n");
5294	return 0;
5295
5296}
5297
5298static int error_recovery_page(int page_code)
5299{
5300	int status;
5301	int bdlen;
5302	unsigned char *pagestart;
5303
5304	SETUP_MODE_PAGE(1, 14);
5305	printf("Data from Error Recovery Page\n");
5306	printf("-----------------------------\n");
5307	bitfield(pagestart + 2, "AWRE", 1, 7);
5308	bitfield(pagestart + 2, "ARRE", 1, 6);
5309	bitfield(pagestart + 2, "TB", 1, 5);
5310	bitfield(pagestart + 2, "RC", 1, 4);
5311	bitfield(pagestart + 2, "EER", 1, 3);
5312	bitfield(pagestart + 2, "PER", 1, 2);
5313	bitfield(pagestart + 2, "DTE", 1, 1);
5314	bitfield(pagestart + 2, "DCR", 1, 0);
5315	intfield(pagestart + 3, 1, "Read Retry Count");
5316	intfield(pagestart + 4, 1, "Correction Span");
5317	intfield(pagestart + 5, 1, "Head Offset Count");
5318	intfield(pagestart + 6, 1, "Data Strobe Offset Count");
5319	intfield(pagestart + 8, 1, "Write Retry Count");
5320	intfield(pagestart + 10, 2, "Recovery Time Limit");
5321	printf("\n");
5322	return 0;
5323}
5324
5325static int notch_parameters_page(int page_code)
5326{
5327	int status;
5328	int bdlen;
5329	unsigned char *pagestart;
5330
5331	SETUP_MODE_PAGE(0xc, 7);
5332
5333	printf("Data from Notch Parameters Page\n");
5334	printf("-------------------------------\n");
5335	bitfield(pagestart + 2, "Notched Drive", 1, 7);
5336	bitfield(pagestart + 2, "Logical or Physical Notch", 1, 6);
5337	intfield(pagestart + 4, 2, "Max # of notches");
5338	intfield(pagestart + 6, 2, "Active Notch");
5339	if (pagestart[2] & 0x40) {
5340		intfield(pagestart + 8, 4, "Starting Boundary");
5341		intfield(pagestart + 12, 4, "Ending Boundary");
5342	} else {		/* Hex is more meaningful for physical notches */
5343		hexfield(pagestart + 8, 4, "Starting Boundary");
5344		hexfield(pagestart + 12, 4, "Ending Boundary");
5345	}
5346
5347	printf("0x%8.8x%8.8x", getnbyte(pagestart + 16, 4),
5348	       getnbyte(pagestart + 20, 4));
5349
5350	printf("\n");
5351	return 0;
5352}
5353
5354static char *formatname(int format)
5355{
5356	switch (format) {
5357	case 0x0:
5358		return "logical blocks";
5359	case 0x4:
5360		return "bytes from index [Cyl:Head:Off]\n"
5361		    "Offset -1 marks whole track as bad.\n";
5362	case 0x5:
5363		return "physical blocks [Cyl:Head:Sect]\n"
5364		    "Sector -1 marks whole track as bad.\n";
5365	}
5366	return "Weird, unknown format";
5367}
5368
5369static int read_defect_list(int page_code)
5370{
5371	int status = 0, i, len, reallen, table, k;
5372	unsigned char *cmd, *df = 0;
5373	int trunc;
5374
5375	printf("Data from Defect Lists\n" "----------------------\n");
5376	for (table = 0; table < 2; table++) {
5377		memset(buffer, 0, SIZEOF_BUFFER);
5378		trunc = 0;
5379
5380		*((int *)buffer) = 0;	/* length of input data */
5381		*(((int *)buffer) + 1) = 4;	/* length of output buffer */
5382
5383		cmd = (unsigned char *)(((int *)buffer) + 2);
5384
5385		cmd[0] = 0x37;	/* READ DEFECT DATA */
5386		cmd[1] = 0x00;	/* lun=0 */
5387		cmd[2] = (table ? 0x08 : 0x10) | defectformat;	/*  List, Format */
5388		cmd[3] = 0x00;	/* (reserved) */
5389		cmd[4] = 0x00;	/* (reserved) */
5390		cmd[5] = 0x00;	/* (reserved) */
5391		cmd[6] = 0x00;	/* (reserved) */
5392		cmd[7] = 0x00;	/* Alloc len */
5393		cmd[8] = 0x04;	/* Alloc len */
5394		cmd[9] = 0x00;	/* control */
5395
5396		i = do_sg_io(glob_fd, buffer);
5397		if (2 == i)
5398			i = 0;	/* Recovered error, probably returned a different
5399				   format */
5400		if (i) {
5401			fprintf(stdout, ">>> Unable to read %s defect data.\n",
5402				(table ? "grown" : "manufacturer"));
5403			status |= i;
5404			continue;
5405		}
5406		len = (buffer[10] << 8) | buffer[11];
5407		reallen = len;
5408		if (len > 0) {
5409			if (len >= 0xfff8) {
5410				len = SIZEOF_BUFFER - 8;
5411				k = len + 8;	/* length of defect list */
5412				*((int *)buffer) = 0;	/* length of input data */
5413				*(((int *)buffer) + 1) = k;	/* length of output buffer */
5414				((struct sg_header *)buffer)->twelve_byte = 1;
5415				cmd[0] = 0xB7;	/* READ DEFECT DATA */
5416				cmd[1] = (table ? 0x08 : 0x10) | defectformat;	/*  List, Format */
5417				cmd[2] = 0x00;	/* (reserved) */
5418				cmd[3] = 0x00;	/* (reserved) */
5419				cmd[4] = 0x00;	/* (reserved) */
5420				cmd[5] = 0x00;	/* (reserved) */
5421				cmd[6] = 0x00;	/* Alloc len */
5422				cmd[7] = (k >> 16);	/* Alloc len */
5423				cmd[8] = (k >> 8);	/* Alloc len */
5424				cmd[9] = (k & 0xff);	/* Alloc len */
5425				cmd[10] = 0x00;	/* reserved */
5426				cmd[11] = 0x00;	/* control */
5427				i = do_sg_io(glob_fd, buffer);
5428				if (i == 2)
5429					i = 0;
5430				if (i)
5431					goto trytenbyte;
5432				reallen =
5433				    (buffer[12] << 24 | buffer[13] << 16 |
5434				     buffer[14] << 8 | buffer[15]);
5435				len = reallen;
5436				if (len > SIZEOF_BUFFER - 8) {
5437					len = SIZEOF_BUFFER - 8;
5438					trunc = 1;
5439				}
5440				df = (unsigned char *)(buffer + 16);
5441			} else {
5442trytenbyte:
5443				if (len > 0xfff8) {
5444					len = 0xfff8;
5445					trunc = 1;
5446				}
5447				k = len + 4;	/* length of defect list */
5448				*((int *)buffer) = 0;	/* length of input data */
5449				*(((int *)buffer) + 1) = k;	/* length of output buffer */
5450				cmd[0] = 0x37;	/* READ DEFECT DATA */
5451				cmd[1] = 0x00;	/* lun=0 */
5452				cmd[2] = (table ? 0x08 : 0x10) | defectformat;	/*  List, Format */
5453				cmd[3] = 0x00;	/* (reserved) */
5454				cmd[4] = 0x00;	/* (reserved) */
5455				cmd[5] = 0x00;	/* (reserved) */
5456				cmd[6] = 0x00;	/* (reserved) */
5457				cmd[7] = (k >> 8);	/* Alloc len */
5458				cmd[8] = (k & 0xff);	/* Alloc len */
5459				cmd[9] = 0x00;	/* control */
5460				i = do_sg_io(glob_fd, buffer);
5461				df = (unsigned char *)(buffer + 12);
5462			}
5463		}
5464		if (2 == i)
5465			i = 0;	/* Recovered error, probably returned a different
5466				   format */
5467		if (i) {
5468			fprintf(stdout, ">>> Unable to read %s defect data.\n",
5469				(table ? "grown" : "manufacturer"));
5470			status |= i;
5471			continue;
5472		} else {
5473			if (table && !status)
5474				printf("\n");
5475			printf("%d entries (%d bytes) in %s table.\n"
5476			       "Format (%x) is: %s\n",
5477			       reallen / ((buffer[9] & 7) ? 8 : 4), reallen,
5478			       (table ? "grown" : "manufacturer"),
5479			       buffer[9] & 7, formatname(buffer[9] & 7));
5480			i = 0;
5481			if ((buffer[9] & 7) == 4) {
5482				while (len > 0) {
5483					snprintf((char *)buffer, 40,
5484						 "%6d:%3u:%8d", getnbyte(df, 3),
5485						 df[3], getnbyte(df + 4, 4));
5486					printf("%19s", (char *)buffer);
5487					len -= 8;
5488					df += 8;
5489					i++;
5490					if (i >= 4) {
5491						printf("\n");
5492						i = 0;
5493					} else
5494						printf("|");
5495				}
5496			} else if ((buffer[9] & 7) == 5) {
5497				while (len > 0) {
5498					snprintf((char *)buffer, 40,
5499						 "%6d:%2u:%5d", getnbyte(df, 3),
5500						 df[3], getnbyte(df + 4, 4));
5501					printf("%15s", (char *)buffer);
5502					len -= 8;
5503					df += 8;
5504					i++;
5505					if (i >= 5) {
5506						printf("\n");
5507						i = 0;
5508					} else
5509						printf("|");
5510				}
5511			} else {
5512				while (len > 0) {
5513					printf("%10d", getnbyte(df, 4));
5514					len -= 4;
5515					df += 4;
5516					i++;
5517					if (i >= 7) {
5518						printf("\n");
5519						i = 0;
5520					} else
5521						printf("|");
5522				}
5523			}
5524			if (i)
5525				printf("\n");
5526		}
5527		if (trunc)
5528			printf("[truncated]\n");
5529	}
5530	printf("\n");
5531	return status;
5532}
5533
5534static int read_cache(int page_code)
5535{
5536	int status;
5537	int bdlen;
5538	unsigned char *pagestart;
5539
5540	SETUP_MODE_PAGE(8, 9);
5541
5542	printf("Data from Caching Page\n");
5543	printf("----------------------\n");
5544	bitfield(pagestart + 2, "Write Cache", 1, 2);
5545	notbitfield(pagestart + 2, "Read Cache", 1, 0);
5546	bitfield(pagestart + 2, "Prefetch units", 1, 1);
5547	bitfield(pagestart + 3, "Demand Read Retention Priority", 0xf, 4);
5548	bitfield(pagestart + 3, "Demand Write Retention Priority", 0xf, 0);
5549	intfield(pagestart + 4, 2, "Disable Pre-fetch Transfer Length");
5550	intfield(pagestart + 6, 2, "Minimum Pre-fetch");
5551	intfield(pagestart + 8, 2, "Maximum Pre-fetch");
5552	intfield(pagestart + 10, 2, "Maximum Pre-fetch Ceiling");
5553	printf("\n");
5554	return 0;
5555}
5556
5557static int read_format_info(int page_code)
5558{
5559	int status;
5560	int bdlen;
5561	unsigned char *pagestart;
5562
5563	SETUP_MODE_PAGE(3, 13);
5564
5565	printf("Data from Format Device Page\n");
5566	printf("----------------------------\n");
5567	bitfield(pagestart + 20, "Removable Medium", 1, 5);
5568	bitfield(pagestart + 20, "Supports Hard Sectoring", 1, 6);
5569	bitfield(pagestart + 20, "Supports Soft Sectoring", 1, 7);
5570	bitfield(pagestart + 20, "Addresses assigned by surface", 1, 4);
5571	intfield(pagestart + 2, 2, "Tracks per Zone");
5572	intfield(pagestart + 4, 2, "Alternate sectors per zone");
5573	intfield(pagestart + 6, 2, "Alternate tracks per zone");
5574	intfield(pagestart + 8, 2, "Alternate tracks per lun");
5575	intfield(pagestart + 10, 2, "Sectors per track");
5576	intfield(pagestart + 12, 2, "Bytes per sector");
5577	intfield(pagestart + 14, 2, "Interleave");
5578	intfield(pagestart + 16, 2, "Track skew factor");
5579	intfield(pagestart + 18, 2, "Cylinder skew factor");
5580	printf("\n");
5581	return 0;
5582
5583}
5584
5585static int verify_error_recovery(int page_code)
5586{
5587	int status;
5588	int bdlen;
5589	unsigned char *pagestart;
5590
5591	SETUP_MODE_PAGE(7, 7);
5592
5593	printf("Data from Verify Error Recovery Page\n");
5594	printf("------------------------------------\n");
5595	bitfield(pagestart + 2, "EER", 1, 3);
5596	bitfield(pagestart + 2, "PER", 1, 2);
5597	bitfield(pagestart + 2, "DTE", 1, 1);
5598	bitfield(pagestart + 2, "DCR", 1, 0);
5599	intfield(pagestart + 3, 1, "Verify Retry Count");
5600	intfield(pagestart + 4, 1, "Verify Correction Span (bits)");
5601	intfield(pagestart + 10, 2, "Verify Recovery Time Limit (ms)");
5602
5603	printf("\n");
5604	return 0;
5605}
5606
5607static int peripheral_device_page(int page_code)
5608{
5609	static char *idents[] = {
5610		"X3.131: Small Computer System Interface",
5611		"X3.91M-1987: Storage Module Interface",
5612		"X3.170: Enhanced Small Device Interface",
5613		"X3.130-1986; X3T9.3/87-002: IPI-2",
5614		"X3.132-1987; X3.147-1988: IPI-3"
5615	};
5616	int status;
5617	int bdlen;
5618	unsigned ident;
5619	unsigned char *pagestart;
5620	char *name;
5621
5622	SETUP_MODE_PAGE(9, 2);
5623
5624	printf("Data from Peripheral Device Page\n");
5625	printf("--------------------------------\n");
5626
5627	ident = getnbyte(pagestart + 2, 2);
5628	if (ident < (sizeof(idents) / sizeof(char *)))
5629		name = idents[ident];
5630	else if (ident < 0x8000)
5631		name = "Reserved";
5632	else
5633		name = "Vendor Specific";
5634
5635	bdlen = pagestart[1] - 6;
5636	if (bdlen < 0)
5637		bdlen = 0;
5638	else
5639		SETUP_MODE_PAGE(9, 2);
5640
5641	hexfield(pagestart + 2, 2, "Interface Identifier");
5642	for (ident = 0; ident < 35; ident++)
5643		putchar(' ');
5644	puts(name);
5645
5646	hexdatafield(pagestart + 8, bdlen, "Vendor Specific Data");
5647
5648	printf("\n");
5649	return 0;
5650}
5651
5652/*  end  */
5653
5654static int do_user_page(int page_code, int page_no)
5655{
5656	int status;
5657	int bdlen;
5658	int i;
5659	//unsigned ident;
5660	unsigned char *pagestart;
5661	char *name;
5662
5663	SETUP_MODE_PAGE(page_no, 0);
5664	//printf ("Page 0x%02x len: %i\n", page_code, pagestart[1]);
5665
5666	name = "Vendor specific";
5667	for (i = 2; i < pagestart[1] + 2; i++) {
5668		char nm[8];
5669		snprintf(nm, 8, "%02x", i);
5670		hexdatafield(pagestart + i, 1, nm);
5671	}
5672
5673	printf("\n");
5674	puts(name);
5675	return 0;
5676}
5677
5678/*  end  */
5679
5680static int do_scsi_info_inquiry(int page_code)
5681{
5682	int status, i, x_interface = 0;
5683	unsigned char *cmd;
5684	unsigned char *pagestart;
5685	unsigned char tmp;
5686
5687	for (i = 0; i < 1024; i++) {
5688		buffer[i] = 0;
5689	}
5690
5691	*((int *)buffer) = 0;	/* length of input data */
5692	*(((int *)buffer) + 1) = 36;	/* length of output buffer */
5693
5694	cmd = (unsigned char *)(((int *)buffer) + 2);
5695
5696	cmd[0] = 0x12;		/* INQUIRY */
5697	cmd[1] = 0x00;		/* lun=0, evpd=0 */
5698	cmd[2] = 0x00;		/* page code = 0 */
5699	cmd[3] = 0x00;		/* (reserved) */
5700	cmd[4] = 0x24;		/* allocation length */
5701	cmd[5] = 0x00;		/* control */
5702
5703	status = do_sg_io(glob_fd, buffer);
5704	if (status) {
5705		printf("Error doing INQUIRY (1)");
5706		return status;
5707	}
5708
5709	pagestart = buffer + 8;
5710
5711	printf("Inquiry command\n");
5712	printf("---------------\n");
5713	bitfield(pagestart + 7, "Relative Address", 1, 7);
5714	bitfield(pagestart + 7, "Wide bus 32", 1, 6);
5715	bitfield(pagestart + 7, "Wide bus 16", 1, 5);
5716	bitfield(pagestart + 7, "Synchronous neg.", 1, 4);
5717	bitfield(pagestart + 7, "Linked Commands", 1, 3);
5718	bitfield(pagestart + 7, "Command Queueing", 1, 1);
5719	bitfield(pagestart + 7, "SftRe", 1, 0);
5720	bitfield(pagestart + 0, "Device Type", 0x1f, 0);
5721	bitfield(pagestart + 0, "Peripheral Qualifier", 0x7, 5);
5722	bitfield(pagestart + 1, "Removable?", 1, 7);
5723	bitfield(pagestart + 1, "Device Type Modifier", 0x7f, 0);
5724	bitfield(pagestart + 2, "ISO Version", 3, 6);
5725	bitfield(pagestart + 2, "ECMA Version", 7, 3);
5726	bitfield(pagestart + 2, "ANSI Version", 7, 0);
5727	bitfield(pagestart + 3, "AENC", 1, 7);
5728	bitfield(pagestart + 3, "TrmIOP", 1, 6);
5729	bitfield(pagestart + 3, "Response Data Format", 0xf, 0);
5730	tmp = pagestart[16];
5731	pagestart[16] = 0;
5732	printf("%s%s\n", (!x_interface ? "Vendor:                    " : ""),
5733	       pagestart + 8);
5734	pagestart[16] = tmp;
5735
5736	tmp = pagestart[32];
5737	pagestart[32] = 0;
5738	printf("%s%s\n", (!x_interface ? "Product:                   " : ""),
5739	       pagestart + 16);
5740	pagestart[32] = tmp;
5741
5742	printf("%s%s\n", (!x_interface ? "Revision level:            " : ""),
5743	       pagestart + 32);
5744
5745	printf("\n");
5746	return status;
5747
5748}
5749
5750static int do_serial_number(int page_code)
5751{
5752	int status, i, pagelen;
5753	unsigned char *cmd;
5754	unsigned char *pagestart;
5755
5756	for (i = 0; i < 1024; i++) {
5757		buffer[i] = 0;
5758	}
5759
5760	*((int *)buffer) = 0;	/* length of input data */
5761	*(((int *)buffer) + 1) = 4;	/* length of output buffer */
5762
5763	cmd = (unsigned char *)(((int *)buffer) + 2);
5764
5765	cmd[0] = 0x12;		/* INQUIRY */
5766	cmd[1] = 0x01;		/* lun=0, evpd=1 */
5767	cmd[2] = 0x80;		/* page code = 0x80, serial number */
5768	cmd[3] = 0x00;		/* (reserved) */
5769	cmd[4] = 0x04;		/* allocation length */
5770	cmd[5] = 0x00;		/* control */
5771
5772	status = do_sg_io(glob_fd, buffer);
5773	if (status) {
5774		printf("Error doing INQUIRY (evpd=1, serial number)\n");
5775		return status;
5776	}
5777
5778	pagestart = buffer + 8;
5779
5780	pagelen = 4 + pagestart[3];
5781	*((int *)buffer) = 0;	/* length of input data */
5782	*(((int *)buffer) + 1) = pagelen;	/* length of output buffer */
5783
5784	cmd[0] = 0x12;		/* INQUIRY */
5785	cmd[1] = 0x01;		/* lun=0, evpd=1 */
5786	cmd[2] = 0x80;		/* page code = 0x80, serial number */
5787	cmd[3] = 0x00;		/* (reserved) */
5788	cmd[4] = (unsigned char)pagelen;	/* allocation length */
5789	cmd[5] = 0x00;		/* control */
5790
5791	status = do_sg_io(glob_fd, buffer);
5792	if (status) {
5793		printf("Error doing INQUIRY (evpd=1, serial number, len)\n");
5794		return status;
5795	}
5796
5797	printf("Serial Number '");
5798	for (i = 0; i < pagestart[3]; i++)
5799		printf("%c", pagestart[4 + i]);
5800	printf("'\n");
5801	printf("\n");
5802
5803	return status;
5804}
5805
5806/* Print out a list of the known devices on the system */
5807static void show_devices()
5808{
5809	int k, j, fd, err, bus;
5810	My_scsi_idlun m_idlun;
5811	char name[MDEV_NAME_SZ];
5812	char ebuff[EBUFF_SZ];
5813	int do_numeric = 1;
5814	int max_holes = MAX_HOLES;
5815
5816	for (k = 0, j = 0; k < sizeof(devices) / sizeof(char *); k++) {
5817		fd = open(devices[k], O_RDONLY | O_NONBLOCK);
5818		if (fd < 0)
5819			continue;
5820		err =
5821		    ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &(sg_map_arr[j].bus));
5822		if (err < 0) {
5823			snprintf(ebuff, EBUFF_SZ,
5824				 "SCSI(1) ioctl on %s failed", devices[k]);
5825			perror(ebuff);
5826			close(fd);
5827			continue;
5828		}
5829		err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
5830		if (err < 0) {
5831			snprintf(ebuff, EBUFF_SZ,
5832				 "SCSI(2) ioctl on %s failed", devices[k]);
5833			perror(ebuff);
5834			close(fd);
5835			continue;
5836		}
5837		sg_map_arr[j].channel = (m_idlun.dev_id >> 16) & 0xff;
5838		sg_map_arr[j].lun = (m_idlun.dev_id >> 8) & 0xff;
5839		sg_map_arr[j].target_id = m_idlun.dev_id & 0xff;
5840		sg_map_arr[j].dev_name = devices[k];
5841
5842		printf("[scsi%d ch=%d id=%d lun=%d %s] ", sg_map_arr[j].bus,
5843		       sg_map_arr[j].channel, sg_map_arr[j].target_id,
5844		       sg_map_arr[j].lun, sg_map_arr[j].dev_name);
5845
5846		++j;
5847		printf("%s ", devices[k]);
5848		close(fd);
5849	};
5850	printf("\n");
5851	for (k = 0; k < MAX_SG_DEVS; k++) {
5852		make_dev_name(name, NULL, k, do_numeric);
5853		fd = open(name, O_RDWR | O_NONBLOCK);
5854		if (fd < 0) {
5855			if ((ENOENT == errno) && (0 == k)) {
5856				do_numeric = 0;
5857				make_dev_name(name, NULL, k, do_numeric);
5858				fd = open(name, O_RDWR | O_NONBLOCK);
5859			}
5860			if (fd < 0) {
5861				if (EBUSY == errno)
5862					continue;	/* step over if O_EXCL already on it */
5863				else {
5864#if 0
5865					snprintf(ebuff, EBUFF_SZ,
5866						 "open on %s failed (%d)", name,
5867						 errno);
5868					perror(ebuff);
5869#endif
5870					if (max_holes-- > 0)
5871						continue;
5872					else
5873						break;
5874				}
5875			}
5876		}
5877		max_holes = MAX_HOLES;
5878		err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
5879		if (err < 0) {
5880			snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed",
5881				 name);
5882			perror(ebuff);
5883			close(fd);
5884			continue;
5885		}
5886		err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
5887		if (err < 0) {
5888			snprintf(ebuff, EBUFF_SZ, "SCSI(3) ioctl on %s failed",
5889				 name);
5890			perror(ebuff);
5891			close(fd);
5892			continue;
5893		}
5894
5895		printf("[scsi%d ch=%d id=%d lun=%d %s]", bus,
5896		       (m_idlun.dev_id >> 16) & 0xff, m_idlun.dev_id & 0xff,
5897		       (m_idlun.dev_id >> 8) & 0xff, name);
5898
5899		for (j = 0; sg_map_arr[j].dev_name; ++j) {
5900			if ((bus == sg_map_arr[j].bus) &&
5901			    ((m_idlun.dev_id & 0xff) == sg_map_arr[j].target_id)
5902			    && (((m_idlun.dev_id >> 16) & 0xff) ==
5903				sg_map_arr[j].channel)
5904			    && (((m_idlun.dev_id >> 8) & 0xff) ==
5905				sg_map_arr[j].lun)) {
5906				printf("%s [=%s  scsi%d ch=%d id=%d lun=%d]\n",
5907				       name, sg_map_arr[j].dev_name, bus,
5908				       ((m_idlun.dev_id >> 16) & 0xff),
5909				       m_idlun.dev_id & 0xff,
5910				       ((m_idlun.dev_id >> 8) & 0xff));
5911				break;
5912			}
5913		}
5914		if (NULL == sg_map_arr[j].dev_name)
5915			printf("%s [scsi%d ch=%d id=%d lun=%d]\n", name, bus,
5916			       ((m_idlun.dev_id >> 16) & 0xff),
5917			       m_idlun.dev_id & 0xff,
5918			       ((m_idlun.dev_id >> 8) & 0xff));
5919		close(fd);
5920	}
5921	printf("\n");
5922}
5923
5924static int show_pages(int page_code)
5925{
5926	int offset;
5927	int length;
5928	int i;
5929	unsigned long long pages_sup = 0;
5930	unsigned long long pages_mask = 0;
5931
5932	if (!get_mode_page10(0x3f, page_code | 0x10)) {
5933		length = 9 + getnbyte(buffer + 8, 2);
5934		offset = 16 + getnbyte(buffer + 14, 2);
5935	} else if (!get_mode_page(0x3f, page_code | 0x10)) {
5936		length = 9 + buffer[8];
5937		offset = 12 + buffer[11];
5938	} else {		/* Assume SCSI-1 and fake settings to report NO pages */
5939		offset = 10;
5940		length = 0;
5941	}
5942
5943	/* Get mask of pages supported by prog: */
5944	for (i = 0; i < MAX_PAGENO; i++)
5945		if (page_names[i])
5946			pages_mask |= (1LL << i);
5947
5948	/* Get pages listed in mode_pages */
5949	while (offset < length) {
5950		pages_sup |= (1LL << (buffer[offset] & 0x3f));
5951		offset += 2 + buffer[offset + 1];
5952	}
5953
5954	/* Mask out pages unsupported by this binary */
5955	pages_sup &= pages_mask;
5956
5957	/* Notch page supported? */
5958	if (pages_sup & (1LL << 12)) {
5959		if (get_mode_page(12, 0))
5960			return 2;
5961		offset = 12 + buffer[11];
5962	} else {		/* Fake empty notch page */
5963		memset(buffer, 0, SIZEOF_BUFFER);
5964		offset = 0;
5965	}
5966
5967	pages_mask = getnbyte(buffer + offset + 16, 4);
5968	pages_mask <<= 32;
5969	pages_mask += getnbyte(buffer + offset + 20, 4);
5970
5971	puts("Mode Pages supported by this binary and target:");
5972	puts("-----------------------------------------------");
5973	for (i = 0; i < MAX_PAGENO; i++)
5974		if (pages_sup & (1LL << i))
5975			printf("%02xh: %s Page%s\n", i, get_page_name(i),
5976			       (pages_mask & (1LL << i)) ? " (notched)" : "");
5977	if (pages_sup & (1LL << 12)) {
5978		printf("\nCurrent notch is %d.\n",
5979		       getnbyte(buffer + offset + 6, 2));
5980	}
5981	if (!pages_sup)
5982		puts("No mode pages supported (SCSI-1?).");
5983
5984	return 0;
5985}
5986
5987static int open_sg_dev(char *devname)
5988{
5989	int fd, err, bus, bbus, k;
5990	My_scsi_idlun m_idlun, mm_idlun;
5991	int do_numeric = 1;
5992	char name[DEVNAME_SZ];
5993	struct stat a_st;
5994	int block_dev = 0;
5995
5996	strncpy(name, devname, DEVNAME_SZ);
5997	name[DEVNAME_SZ - 1] = '\0';
5998	fd = open(name, O_RDONLY);
5999	if (fd < 0)
6000		return fd;
6001	if (fstat(fd, &a_st) < 0) {
6002		fprintf(stderr, "could do fstat() on fd ??\n");
6003		close(fd);
6004		return -9999;
6005	}
6006	if (S_ISBLK(a_st.st_mode))
6007		block_dev = 1;
6008	if (block_dev || (ioctl(fd, SG_GET_TIMEOUT, 0) < 0)) {
6009		err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bus);
6010		if (err < 0) {
6011			perror("A SCSI device name is required\n");
6012			close(fd);
6013			return -9999;
6014		}
6015		err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &m_idlun);
6016		if (err < 0) {
6017			perror("A SCSI device name is required\n");
6018			close(fd);
6019			return -9999;
6020		}
6021		close(fd);
6022
6023		for (k = 0; k < MAX_SG_DEVS; k++) {
6024			make_dev_name(name, NULL, k, do_numeric);
6025			fd = open(name, O_RDWR | O_NONBLOCK);
6026			if (fd < 0) {
6027				if ((ENOENT == errno) && (0 == k)) {
6028					do_numeric = 0;
6029					make_dev_name(name, NULL, k,
6030						      do_numeric);
6031					fd = open(name, O_RDWR | O_NONBLOCK);
6032				}
6033				if (fd < 0) {
6034					if (EBUSY == errno)
6035						continue;	/* step over if O_EXCL already on it */
6036					else
6037						break;
6038				}
6039			}
6040			err = ioctl(fd, SCSI_IOCTL_GET_BUS_NUMBER, &bbus);
6041			if (err < 0) {
6042				perror("sg ioctl failed");
6043				close(fd);
6044				fd = -9999;
6045			}
6046			err = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &mm_idlun);
6047			if (err < 0) {
6048				perror("sg ioctl failed");
6049				close(fd);
6050				fd = -9999;
6051			}
6052			if ((bus == bbus) &&
6053			    ((m_idlun.dev_id & 0xff) ==
6054			     (mm_idlun.dev_id & 0xff))
6055			    && (((m_idlun.dev_id >> 8) & 0xff) ==
6056				((mm_idlun.dev_id >> 8) & 0xff))
6057			    && (((m_idlun.dev_id >> 16) & 0xff) ==
6058				((mm_idlun.dev_id >> 16) & 0xff)))
6059				break;
6060			else {
6061				close(fd);
6062				fd = -9999;
6063			}
6064		}
6065	}
6066	if (fd >= 0) {
6067#ifdef SG_GET_RESERVED_SIZE
6068		int size;
6069
6070		if (ioctl(fd, SG_GET_RESERVED_SIZE, &size) < 0) {
6071			fprintf(stderr,
6072				"Compiled with new driver, running on old!!\n");
6073			close(fd);
6074			return -9999;
6075		}
6076#endif
6077		close(fd);
6078		return open(name, O_RDWR);
6079	} else
6080		return fd;
6081}
6082
6083int show_scsi_info(char *device)
6084{
6085	int page_code = 0;
6086	int status = 0;
6087
6088	print_msg(TEST_BREAK, __FUNCTION__);
6089
6090	show_devices();
6091
6092	glob_fd = open_sg_dev(device);
6093	if (glob_fd < 0) {
6094		if (-9999 == glob_fd)
6095			fprintf(stderr,
6096				"Couldn't find sg device corresponding to %s\n",
6097				device);
6098		else {
6099			perror("sginfo(open)");
6100			fprintf(stderr,
6101				"file=%s, or no corresponding sg device found\n",
6102				device);
6103			fprintf(stderr, "Is sg driver loaded?\n");
6104		}
6105		return 1;
6106	}
6107
6108	status |= do_scsi_info_inquiry(page_code);
6109
6110	status |= do_serial_number(page_code);
6111
6112	status |= read_geometry(page_code);
6113
6114	status |= read_cache(page_code);
6115
6116	status |= read_format_info(page_code);
6117
6118	status |= error_recovery_page(page_code);
6119
6120	status |= read_control_page(page_code);
6121
6122	status |= read_disconnect_reconnect_data(page_code);
6123
6124	status |= read_defect_list(page_code);
6125
6126	status |= notch_parameters_page(page_code);
6127
6128	status |= verify_error_recovery(page_code);
6129
6130	status |= peripheral_device_page(page_code);
6131
6132	status |= do_user_page(page_code, 0);
6133
6134	status |= show_pages(page_code);
6135
6136	return status;
6137}
6138
6139/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
6140   2 -> try again */
6141int sg_read2(int sg_fd, unsigned char *buff, int blocks, int from_block,
6142	     int bs, int cdbsz, int fua, int do_mmap)
6143{
6144	unsigned char rdCmd[MAX_SCSI_CDBSZ];
6145	unsigned char senseBuff[SENSE_BUFF_LEN];
6146	sg_io_hdr_t io_hdr;
6147	int res;
6148
6149	if (sg_build_scsi_cdb(rdCmd, cdbsz, blocks, from_block, 0, fua, 0)) {
6150		fprintf(stderr,
6151			ME "bad rd cdb build, from_block=%d, blocks=%d\n",
6152			from_block, blocks);
6153		return -1;
6154	}
6155	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
6156	io_hdr.interface_id = 'S';
6157	io_hdr.cmd_len = cdbsz;
6158	io_hdr.cmdp = rdCmd;
6159	io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
6160	io_hdr.dxfer_len = bs * blocks;
6161	if (!do_mmap)
6162		io_hdr.dxferp = buff;
6163	io_hdr.mx_sb_len = SENSE_BUFF_LEN;
6164	io_hdr.sbp = senseBuff;
6165	io_hdr.timeout = DEF_TIMEOUT;
6166	io_hdr.pack_id = from_block;
6167	if (do_mmap)
6168		io_hdr.flags |= SG_FLAG_MMAP_IO;
6169
6170	while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
6171	       (EINTR == errno)) ;
6172	if (res < 0) {
6173		if (ENOMEM == errno)
6174			return 1;
6175		perror("reading (wr) on sg device, error");
6176		return -1;
6177	}
6178
6179	while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
6180	       (EINTR == errno)) ;
6181	if (res < 0) {
6182		perror("reading (rd) on sg device, error");
6183		return -1;
6184	}
6185	switch (sg_err_category3(&io_hdr)) {
6186	case SG_ERR_CAT_CLEAN:
6187		break;
6188	case SG_ERR_CAT_RECOVERED:
6189		fprintf(stderr,
6190			"Recovered error while reading block=%d, num=%d\n",
6191			from_block, blocks);
6192		break;
6193	case SG_ERR_CAT_MEDIA_CHANGED:
6194		return 2;
6195	default:
6196		sg_chk_n_print3("reading", &io_hdr);
6197		return -1;
6198	}
6199	sum_of_resids += io_hdr.resid;
6200#if SG_DEBUG
6201	fprintf(stderr, "duration=%u ms\n", io_hdr.duration);
6202#endif
6203	return 0;
6204}
6205
6206/* -1 -> unrecoverable error, 0 -> successful, 1 -> recoverable (ENOMEM),
6207   2 -> try again */
6208int sg_write2(int sg_fd, unsigned char *buff, int blocks, int to_block,
6209	      int bs, int cdbsz, int fua, int do_mmap, int *diop)
6210{
6211	unsigned char wrCmd[MAX_SCSI_CDBSZ];
6212	unsigned char senseBuff[SENSE_BUFF_LEN];
6213	sg_io_hdr_t io_hdr;
6214	int res;
6215
6216	if (sg_build_scsi_cdb(wrCmd, cdbsz, blocks, to_block, 1, fua, 0)) {
6217		fprintf(stderr, ME "bad wr cdb build, to_block=%d, blocks=%d\n",
6218			to_block, blocks);
6219		return -1;
6220	}
6221
6222	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
6223	io_hdr.interface_id = 'S';
6224	io_hdr.cmd_len = cdbsz;
6225	io_hdr.cmdp = wrCmd;
6226	io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
6227	io_hdr.dxfer_len = bs * blocks;
6228	if (!do_mmap)
6229		io_hdr.dxferp = buff;
6230	io_hdr.mx_sb_len = SENSE_BUFF_LEN;
6231	io_hdr.sbp = senseBuff;
6232	io_hdr.timeout = DEF_TIMEOUT;
6233	io_hdr.pack_id = to_block;
6234	if (do_mmap)
6235		io_hdr.flags |= SG_FLAG_MMAP_IO;
6236	if (diop && *diop)
6237		io_hdr.flags |= SG_FLAG_DIRECT_IO;
6238
6239	while (((res = write(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
6240	       (EINTR == errno)) ;
6241	if (res < 0) {
6242		if (ENOMEM == errno)
6243			return 1;
6244		perror("writing (wr) on sg device, error");
6245		return -1;
6246	}
6247
6248	while (((res = read(sg_fd, &io_hdr, sizeof(io_hdr))) < 0) &&
6249	       (EINTR == errno)) ;
6250	if (res < 0) {
6251		perror("writing (rd) on sg device, error");
6252		return -1;
6253	}
6254	switch (sg_err_category3(&io_hdr)) {
6255	case SG_ERR_CAT_CLEAN:
6256		break;
6257	case SG_ERR_CAT_RECOVERED:
6258		fprintf(stderr,
6259			"Recovered error while writing block=%d, num=%d\n",
6260			to_block, blocks);
6261		break;
6262	case SG_ERR_CAT_MEDIA_CHANGED:
6263		return 2;
6264	default:
6265		sg_chk_n_print3("writing", &io_hdr);
6266		return -1;
6267	}
6268	if (diop && *diop &&
6269	    ((io_hdr.info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
6270		*diop = 0;	/* flag that dio not done (completely) */
6271	return 0;
6272}
6273
6274int do_scsi_sgm_read_write(char *device)
6275{
6276	int skip = 0;
6277	int seek = 0;
6278	int bs = 0;
6279	int bpt = DEF_BLOCKS_PER_TRANSFER;
6280	char inf[INOUTF_SZ];
6281	int in_type = FT_OTHER;
6282	char outf[INOUTF_SZ];
6283	int out_type = FT_OTHER;
6284	int res, t;
6285	int infd, outfd, blocks;
6286	unsigned char *wrkPos;
6287	unsigned char *wrkBuff = NULL;
6288	unsigned char *wrkMmap = NULL;
6289	int in_num_sect = 0;
6290	int in_res_sz = 0;
6291	int out_num_sect = 0;
6292	int out_res_sz = 0;
6293	int do_time = 1;
6294	int scsi_cdbsz = DEF_SCSI_CDBSZ;
6295	int do_sync = 1;
6296	int do_dio = 0;
6297	int num_dio_not_done = 0;
6298	int fua_mode = 0;
6299	int in_sect_sz, out_sect_sz;
6300	char ebuff[EBUFF_SZ];
6301	int blocks_per;
6302	int req_count;
6303	size_t psz = getpagesize();
6304	struct timeval start_tm, end_tm;
6305
6306	print_msg(TEST_BREAK, __FUNCTION__);
6307
6308	strcpy(inf, "/dev/zero");
6309	strcpy(outf, device);
6310
6311	install_handler(SIGINT, interrupt_handler);
6312	install_handler(SIGQUIT, interrupt_handler);
6313	install_handler(SIGPIPE, interrupt_handler);
6314	install_handler(SIGUSR1, siginfo_handler);
6315
6316	infd = STDIN_FILENO;
6317	outfd = STDOUT_FILENO;
6318
6319	in_type = dd_filetype(inf);
6320
6321	if (FT_ST == in_type) {
6322		fprintf(stderr, ME "unable to use scsi tape device %s\n", inf);
6323		return 1;
6324	} else if (FT_SG == in_type) {
6325		if ((infd = open(inf, O_RDWR)) < 0) {
6326			snprintf(ebuff, EBUFF_SZ,
6327				 ME "could not open %s for sg reading", inf);
6328			perror(ebuff);
6329			return 1;
6330		}
6331		res = ioctl(infd, SG_GET_VERSION_NUM, &t);
6332		if ((res < 0) || (t < 30122)) {
6333			fprintf(stderr, ME "sg driver prior to 3.1.22\n");
6334			return 1;
6335		}
6336		in_res_sz = bs * bpt;
6337		if (0 != (in_res_sz % psz))	/* round up to next page */
6338			in_res_sz = ((in_res_sz / psz) + 1) * psz;
6339		if (ioctl(infd, SG_GET_RESERVED_SIZE, &t) < 0) {
6340			perror(ME "SG_GET_RESERVED_SIZE error");
6341			return 1;
6342		}
6343		if (in_res_sz > t) {
6344			if (ioctl(infd, SG_SET_RESERVED_SIZE, &in_res_sz) < 0) {
6345				perror(ME "SG_SET_RESERVED_SIZE error");
6346				return 1;
6347			}
6348		}
6349		wrkMmap = mmap(NULL, in_res_sz, PROT_READ | PROT_WRITE,
6350			       MAP_SHARED, infd, 0);
6351		if (MAP_FAILED == wrkMmap) {
6352			snprintf(ebuff, EBUFF_SZ,
6353				 ME "error using mmap() on file: %s", inf);
6354			perror(ebuff);
6355			return 1;
6356		}
6357	} else {
6358		if ((infd = open(inf, O_RDONLY)) < 0) {
6359			snprintf(ebuff, EBUFF_SZ,
6360				 ME "could not open %s for reading", inf);
6361			perror(ebuff);
6362			return 1;
6363		} else if (skip > 0) {
6364			llse_loff_t offset = skip;
6365
6366			offset *= bs;	/* could exceed 32 bits here! */
6367			if (llse_llseek(infd, offset, SEEK_SET) < 0) {
6368				snprintf(ebuff, EBUFF_SZ, ME "couldn't skip to "
6369					 "required position on %s", inf);
6370				perror(ebuff);
6371				return 1;
6372			}
6373		}
6374	}
6375
6376	if (outf[0] && ('-' != outf[0])) {
6377		out_type = dd_filetype(outf);
6378
6379		if (FT_ST == out_type) {
6380			fprintf(stderr,
6381				ME "unable to use scsi tape device %s\n", outf);
6382			return 1;
6383		} else if (FT_SG == out_type) {
6384			if ((outfd = open(outf, O_RDWR)) < 0) {
6385				snprintf(ebuff, EBUFF_SZ,
6386					 ME "could not open %s for "
6387					 "sg writing", outf);
6388				perror(ebuff);
6389				return 1;
6390			}
6391			res = ioctl(outfd, SG_GET_VERSION_NUM, &t);
6392			if ((res < 0) || (t < 30122)) {
6393				fprintf(stderr,
6394					ME "sg driver prior to 3.1.22\n");
6395				return 1;
6396			}
6397			if (ioctl(outfd, SG_GET_RESERVED_SIZE, &t) < 0) {
6398				perror(ME "SG_GET_RESERVED_SIZE error");
6399				return 1;
6400			}
6401			out_res_sz = bs * bpt;
6402			if (out_res_sz > t) {
6403				if (ioctl
6404				    (outfd, SG_SET_RESERVED_SIZE,
6405				     &out_res_sz) < 0) {
6406					perror(ME "SG_SET_RESERVED_SIZE error");
6407					return 1;
6408				}
6409			}
6410			if (NULL == wrkMmap) {
6411				wrkMmap =
6412				    mmap(NULL, out_res_sz,
6413					 PROT_READ | PROT_WRITE, MAP_SHARED,
6414					 outfd, 0);
6415				if (MAP_FAILED == wrkMmap) {
6416					snprintf(ebuff, EBUFF_SZ,
6417						 ME
6418						 "error using mmap() on file: %s",
6419						 outf);
6420					perror(ebuff);
6421					return 1;
6422				}
6423			}
6424		} else if (FT_DEV_NULL == out_type)
6425			outfd = -1;	/* don't bother opening */
6426		else {
6427			if (FT_RAW != out_type) {
6428				if ((outfd =
6429				     open(outf, O_WRONLY | O_CREAT,
6430					  0666)) < 0) {
6431					snprintf(ebuff, EBUFF_SZ,
6432						 ME
6433						 "could not open %s for writing",
6434						 outf);
6435					perror(ebuff);
6436					return 1;
6437				}
6438			} else {
6439				if ((outfd = open(outf, O_WRONLY)) < 0) {
6440					snprintf(ebuff, EBUFF_SZ,
6441						 ME "could not open %s "
6442						 "for raw writing", outf);
6443					perror(ebuff);
6444					return 1;
6445				}
6446			}
6447			if (seek > 0) {
6448				llse_loff_t offset = seek;
6449
6450				offset *= bs;	/* could exceed 32 bits here! */
6451				if (llse_llseek(outfd, offset, SEEK_SET) < 0) {
6452					snprintf(ebuff, EBUFF_SZ,
6453						 ME "couldn't seek to "
6454						 "required position on %s",
6455						 outf);
6456					perror(ebuff);
6457					return 1;
6458				}
6459			}
6460		}
6461	}
6462	if ((STDIN_FILENO == infd) && (STDOUT_FILENO == outfd)) {
6463		fprintf(stderr,
6464			"Can't have both 'if' as stdin _and_ 'of' as stdout\n");
6465		return 1;
6466	}
6467#if 0
6468	if ((FT_OTHER == in_type) && (FT_OTHER == out_type)) {
6469		fprintf(stderr, "Both 'if' and 'of' can't be ordinary files\n");
6470		return 1;
6471	}
6472#endif
6473	if (dd_count < 0) {
6474		if (FT_SG == in_type) {
6475			res = read_capacity(infd, &in_num_sect, &in_sect_sz);
6476			if (2 == res) {
6477				fprintf(stderr,
6478					"Unit attention, media changed(in), continuing\n");
6479				res =
6480				    read_capacity(infd, &in_num_sect,
6481						  &in_sect_sz);
6482			}
6483			if (0 != res) {
6484				fprintf(stderr,
6485					"Unable to read capacity on %s\n", inf);
6486				in_num_sect = -1;
6487			} else {
6488#if 0
6489				if (0 == in_sect_sz)
6490					in_sect_sz = bs;
6491				else if (in_sect_sz > bs)
6492					in_num_sect *= (in_sect_sz / bs);
6493				else if (in_sect_sz < bs)
6494					in_num_sect /= (bs / in_sect_sz);
6495#endif
6496				if (in_num_sect > skip)
6497					in_num_sect -= skip;
6498			}
6499		}
6500		if (FT_SG == out_type) {
6501			res = read_capacity(outfd, &out_num_sect, &out_sect_sz);
6502			if (2 == res) {
6503				fprintf(stderr,
6504					"Unit attention, media changed(out), continuing\n");
6505				res =
6506				    read_capacity(outfd, &out_num_sect,
6507						  &out_sect_sz);
6508			}
6509			if (0 != res) {
6510				fprintf(stderr,
6511					"Unable to read capacity on %s\n",
6512					outf);
6513				out_num_sect = -1;
6514			} else {
6515				if (out_num_sect > seek)
6516					out_num_sect -= seek;
6517			}
6518		}
6519#ifdef SG_DEBUG
6520		fprintf(stderr,
6521			"Start of loop, count=%d, in_num_sect=%d, out_num_sect=%d\n",
6522			dd_count, in_num_sect, out_num_sect);
6523#endif
6524		if (in_num_sect > 0) {
6525			if (out_num_sect > 0)
6526				dd_count =
6527				    (in_num_sect >
6528				     out_num_sect) ? out_num_sect : in_num_sect;
6529			else
6530				dd_count = in_num_sect;
6531		} else
6532			dd_count = out_num_sect;
6533	}
6534	if (dd_count < 0) {
6535		fprintf(stderr, "Couldn't calculate count, please give one\n");
6536		return 1;
6537	}
6538	if (do_dio && (FT_SG != in_type)) {
6539		do_dio = 0;
6540		fprintf(stderr,
6541			">>> dio only performed on 'of' side when 'if' is"
6542			" an sg device\n");
6543	}
6544	if (do_dio) {
6545		int fd;
6546		char c;
6547
6548		if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
6549			if (1 == read(fd, &c, 1)) {
6550				if ('0' == c)
6551					fprintf(stderr,
6552						">>> %s set to '0' but should be set "
6553						"to '1' for direct IO\n",
6554						proc_allow_dio);
6555			}
6556			close(fd);
6557		}
6558	}
6559
6560	if (wrkMmap)
6561		wrkPos = wrkMmap;
6562	else {
6563		if ((FT_RAW == in_type) || (FT_RAW == out_type)) {
6564			wrkBuff = malloc(bs * bpt + psz);
6565			if (0 == wrkBuff) {
6566				fprintf(stderr,
6567					"Not enough user memory for raw\n");
6568				return 1;
6569			}
6570			wrkPos =
6571			    (unsigned char *)(((unsigned long)wrkBuff + psz - 1)
6572					      & (~(psz - 1)));
6573		} else {
6574			wrkBuff = malloc(bs * bpt);
6575			if (0 == wrkBuff) {
6576				fprintf(stderr, "Not enough user memory\n");
6577				return 1;
6578			}
6579			wrkPos = wrkBuff;
6580		}
6581	}
6582
6583	blocks_per = bpt;
6584#ifdef SG_DEBUG
6585	fprintf(stderr, "Start of loop, count=%d, blocks_per=%d\n",
6586		dd_count, blocks_per);
6587#endif
6588	if (do_time) {
6589		start_tm.tv_sec = 0;
6590		start_tm.tv_usec = 0;
6591		gettimeofday(&start_tm, NULL);
6592	}
6593	req_count = dd_count;
6594
6595	while (dd_count > 0) {
6596		blocks = (dd_count > blocks_per) ? blocks_per : dd_count;
6597		if (FT_SG == in_type) {
6598			int fua = fua_mode & 2;
6599
6600			res =
6601			    sg_read2(infd, wrkPos, blocks, skip, bs, scsi_cdbsz,
6602				     fua, 1);
6603			if (2 == res) {
6604				fprintf(stderr,
6605					"Unit attention, media changed, continuing (r)\n");
6606				res =
6607				    sg_read2(infd, wrkPos, blocks, skip, bs,
6608					     scsi_cdbsz, fua, 1);
6609			}
6610			if (0 != res) {
6611				fprintf(stderr, "sg_read2 failed, skip=%d\n",
6612					skip);
6613				break;
6614			} else
6615				in_full += blocks;
6616		} else {
6617			while (((res = read(infd, wrkPos, blocks * bs)) < 0) &&
6618			       (EINTR == errno)) ;
6619			if (res < 0) {
6620				snprintf(ebuff, EBUFF_SZ,
6621					 ME "reading, skip=%d ", skip);
6622				perror(ebuff);
6623				break;
6624			} else if (res < blocks * bs) {
6625				dd_count = 0;
6626				blocks = res / bs;
6627				if ((res % bs) > 0) {
6628					blocks++;
6629					in_partial++;
6630				}
6631			}
6632			in_full += blocks;
6633		}
6634
6635		if (FT_SG == out_type) {
6636			int do_mmap = (FT_SG == in_type) ? 0 : 1;
6637			int fua = fua_mode & 1;
6638			int dio_res = do_dio;
6639
6640			res =
6641			    sg_write2(outfd, wrkPos, blocks, seek, bs,
6642				      scsi_cdbsz, fua, do_mmap, &dio_res);
6643			if (2 == res) {
6644				fprintf(stderr,
6645					"Unit attention, media changed, continuing (w)\n");
6646				res =
6647				    sg_write2(outfd, wrkPos, blocks, seek, bs,
6648					      scsi_cdbsz, fua, do_mmap,
6649					      &dio_res);
6650			} else if (0 != res) {
6651				fprintf(stderr, "sg_write2 failed, seek=%d\n",
6652					seek);
6653				break;
6654			} else {
6655				out_full += blocks;
6656				if (do_dio && (0 == dio_res))
6657					num_dio_not_done++;
6658			}
6659		} else if (FT_DEV_NULL == out_type)
6660			out_full += blocks;	/* act as if written out without error */
6661		else {
6662			while (((res = write(outfd, wrkPos, blocks * bs)) < 0)
6663			       && (EINTR == errno)) ;
6664			if (res < 0) {
6665				snprintf(ebuff, EBUFF_SZ,
6666					 ME "writing, seek=%d ", seek);
6667				perror(ebuff);
6668				break;
6669			} else if (res < blocks * bs) {
6670				fprintf(stderr,
6671					"output file probably full, seek=%d ",
6672					seek);
6673				blocks = res / bs;
6674				out_full += blocks;
6675				if ((res % bs) > 0)
6676					out_partial++;
6677				break;
6678			} else
6679				out_full += blocks;
6680		}
6681		if (dd_count > 0)
6682			dd_count -= blocks;
6683		skip += blocks;
6684		seek += blocks;
6685	}
6686	if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
6687		struct timeval res_tm;
6688		double a, b;
6689
6690		gettimeofday(&end_tm, NULL);
6691		res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
6692		res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
6693		if (res_tm.tv_usec < 0) {
6694			--res_tm.tv_sec;
6695			res_tm.tv_usec += 1000000;
6696		}
6697		a = res_tm.tv_sec;
6698		a += (0.000001 * res_tm.tv_usec);
6699		b = (double)bs *(req_count - dd_count);
6700		printf("time to transfer data was %d.%06d secs",
6701		       (int)res_tm.tv_sec, (int)res_tm.tv_usec);
6702		if ((a > 0.00001) && (b > 511))
6703			printf(", %.2f MB/sec\n", b / (a * 1000000.0));
6704		else
6705			printf("\n");
6706	}
6707	if (do_sync) {
6708		if (FT_SG == out_type) {
6709			fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
6710			res = sync_cache(outfd);
6711			if (2 == res) {
6712				fprintf(stderr,
6713					"Unit attention, media changed(in), continuing\n");
6714				res = sync_cache(outfd);
6715			}
6716			if (0 != res)
6717				fprintf(stderr,
6718					"Unable to synchronize cache\n");
6719		}
6720	}
6721
6722	if (wrkBuff)
6723		free(wrkBuff);
6724	if (STDIN_FILENO != infd)
6725		close(infd);
6726	if ((STDOUT_FILENO != outfd) && (FT_DEV_NULL != out_type))
6727		close(outfd);
6728	res = 0;
6729	if (0 != dd_count) {
6730		fprintf(stderr, "Some error occurred,");
6731		res = 2;
6732	}
6733	print_stats();
6734	if (sum_of_resids)
6735		fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
6736			sum_of_resids);
6737	if (num_dio_not_done)
6738		fprintf(stderr, ">> dio requested but _not done %d times\n",
6739			num_dio_not_done);
6740	return res;
6741}
6742
6743static void guarded_stop_in(Rq_coll * clp)
6744{
6745	pthread_mutex_lock(&clp->in_mutex);
6746	clp->in_stop = 1;
6747	pthread_mutex_unlock(&clp->in_mutex);
6748}
6749
6750static void guarded_stop_out(Rq_coll * clp)
6751{
6752	pthread_mutex_lock(&clp->out_mutex);
6753	clp->out_stop = 1;
6754	pthread_mutex_unlock(&clp->out_mutex);
6755}
6756
6757static void guarded_stop_both(Rq_coll * clp)
6758{
6759	guarded_stop_in(clp);
6760	guarded_stop_out(clp);
6761}
6762
6763void *sig_listen_thread(void *v_clp)
6764{
6765	Rq_coll *clp = (Rq_coll *) v_clp;
6766	int sig_number;
6767
6768	while (1) {
6769		sigwait(&signal_set, &sig_number);
6770		if (SIGINT == sig_number) {
6771			fprintf(stderr, ME "interrupted by SIGINT\n");
6772			guarded_stop_both(clp);
6773			pthread_cond_broadcast(&clp->out_sync_cv);
6774		}
6775	}
6776	return NULL;
6777}
6778
6779void cleanup_in(void *v_clp)
6780{
6781	Rq_coll *clp = (Rq_coll *) v_clp;
6782
6783	fprintf(stderr, "thread cancelled while in mutex held\n");
6784	clp->in_stop = 1;
6785	pthread_mutex_unlock(&clp->in_mutex);
6786	guarded_stop_out(clp);
6787	pthread_cond_broadcast(&clp->out_sync_cv);
6788}
6789
6790void cleanup_out(void *v_clp)
6791{
6792	Rq_coll *clp = (Rq_coll *) v_clp;
6793
6794	fprintf(stderr, "thread cancelled while out mutex held\n");
6795	clp->out_stop = 1;
6796	pthread_mutex_unlock(&clp->out_mutex);
6797	guarded_stop_in(clp);
6798	pthread_cond_broadcast(&clp->out_sync_cv);
6799}
6800
6801void *read_write_thread(void *v_clp)
6802{
6803	Rq_coll *clp = (Rq_coll *) v_clp;
6804	Rq_elem rel;
6805	Rq_elem *rep = &rel;
6806	size_t psz = 0;
6807	int sz = clp->bpt * clp->bs;
6808	int stop_after_write = 0;
6809	int seek_skip = clp->seek - clp->skip;
6810	int blocks, status;
6811
6812	memset(rep, 0, sizeof(Rq_elem));
6813	psz = getpagesize();
6814	if (NULL == (rep->alloc_bp = malloc(sz + psz)))
6815		err_exit(ENOMEM, "out of memory creating user buffers\n");
6816	rep->buffp =
6817	    (unsigned char *)(((unsigned long)rep->alloc_bp + psz - 1) &
6818			      (~(psz - 1)));
6819	/* Follow clp members are constant during lifetime of thread */
6820	rep->bs = clp->bs;
6821	rep->fua_mode = clp->fua_mode;
6822	rep->dio = clp->dio;
6823	rep->infd = clp->infd;
6824	rep->outfd = clp->outfd;
6825	rep->debug = clp->debug;
6826	rep->in_scsi_type = clp->in_scsi_type;
6827	rep->out_scsi_type = clp->out_scsi_type;
6828	rep->cdbsz = clp->cdbsz;
6829
6830	while (1) {
6831		status = pthread_mutex_lock(&clp->in_mutex);
6832		if (0 != status)
6833			err_exit(status, "lock in_mutex");
6834		if (clp->in_stop || (clp->in_count <= 0)) {
6835			/* no more to do, exit loop then thread */
6836			status = pthread_mutex_unlock(&clp->in_mutex);
6837			if (0 != status)
6838				err_exit(status, "unlock in_mutex");
6839			break;
6840		}
6841		blocks = (clp->in_count > clp->bpt) ? clp->bpt : clp->in_count;
6842		rep->wr = 0;
6843		rep->blk = clp->in_blk;
6844		rep->num_blks = blocks;
6845		clp->in_blk += blocks;
6846		clp->in_count -= blocks;
6847
6848		pthread_cleanup_push(cleanup_in, (void *)clp);
6849		if (FT_SG == clp->in_type)
6850			sg_in_operation(clp, rep);	/* lets go of in_mutex mid operation */
6851		else {
6852			stop_after_write =
6853			    normal_in_operation(clp, rep, blocks);
6854			status = pthread_mutex_unlock(&clp->in_mutex);
6855			if (0 != status)
6856				err_exit(status, "unlock in_mutex");
6857		}
6858		pthread_cleanup_pop(0);
6859
6860		status = pthread_mutex_lock(&clp->out_mutex);
6861		if (0 != status)
6862			err_exit(status, "lock out_mutex");
6863		if (FT_DEV_NULL != clp->out_type) {
6864			while ((!clp->out_stop) &&
6865			       ((rep->blk + seek_skip) != clp->out_blk)) {
6866				/* if write would be out of sequence then wait */
6867				pthread_cleanup_push(cleanup_out, (void *)clp);
6868				status =
6869				    pthread_cond_wait(&clp->out_sync_cv,
6870						      &clp->out_mutex);
6871				if (0 != status)
6872					err_exit(status, "cond out_sync_cv");
6873				pthread_cleanup_pop(0);
6874			}
6875		}
6876
6877		if (clp->out_stop || (clp->out_count <= 0)) {
6878			if (!clp->out_stop)
6879				clp->out_stop = 1;
6880			status = pthread_mutex_unlock(&clp->out_mutex);
6881			if (0 != status)
6882				err_exit(status, "unlock out_mutex");
6883			break;
6884		}
6885		if (stop_after_write)
6886			clp->out_stop = 1;
6887		rep->wr = 1;
6888		rep->blk = clp->out_blk;
6889		/* rep->num_blks = blocks; */
6890		clp->out_blk += blocks;
6891		clp->out_count -= blocks;
6892
6893		pthread_cleanup_push(cleanup_out, (void *)clp);
6894		if (FT_SG == clp->out_type)
6895			sg_out_operation(clp, rep);	/* releases out_mutex mid operation */
6896		else if (FT_DEV_NULL == clp->out_type) {
6897			/* skip actual write operation */
6898			clp->out_done_count -= blocks;
6899			status = pthread_mutex_unlock(&clp->out_mutex);
6900			if (0 != status)
6901				err_exit(status, "unlock out_mutex");
6902		} else {
6903			normal_out_operation(clp, rep, blocks);
6904			status = pthread_mutex_unlock(&clp->out_mutex);
6905			if (0 != status)
6906				err_exit(status, "unlock out_mutex");
6907		}
6908		pthread_cleanup_pop(0);
6909
6910		if (stop_after_write)
6911			break;
6912		pthread_cond_broadcast(&clp->out_sync_cv);
6913	}			/* end of while loop */
6914	if (rep->alloc_bp)
6915		free(rep->alloc_bp);
6916	status = pthread_mutex_lock(&clp->in_mutex);
6917	if (0 != status)
6918		err_exit(status, "lock in_mutex");
6919	if (!clp->in_stop)
6920		clp->in_stop = 1;	/* flag other workers to stop */
6921	status = pthread_mutex_unlock(&clp->in_mutex);
6922	if (0 != status)
6923		err_exit(status, "unlock in_mutex");
6924	pthread_cond_broadcast(&clp->out_sync_cv);
6925	return stop_after_write ? NULL : v_clp;
6926}
6927
6928int normal_in_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
6929{
6930	int res;
6931	int stop_after_write = 0;
6932
6933	/* enters holding in_mutex */
6934	while (((res = read(clp->infd, rep->buffp,
6935			    blocks * clp->bs)) < 0) && (EINTR == errno)) ;
6936	if (res < 0) {
6937		if (clp->coe) {
6938			memset(rep->buffp, 0, rep->num_blks * rep->bs);
6939			fprintf(stderr,
6940				">> substituted zeros for in blk=%d for "
6941				"%d bytes, %s\n", rep->blk,
6942				rep->num_blks * rep->bs, strerror(errno));
6943			res = rep->num_blks * clp->bs;
6944		} else {
6945			fprintf(stderr, "error in normal read, %s\n",
6946				strerror(errno));
6947			clp->in_stop = 1;
6948			guarded_stop_out(clp);
6949			return 1;
6950		}
6951	}
6952	if (res < blocks * clp->bs) {
6953		int o_blocks = blocks;
6954		stop_after_write = 1;
6955		blocks = res / clp->bs;
6956		if ((res % clp->bs) > 0) {
6957			blocks++;
6958			clp->in_partial++;
6959		}
6960		/* Reverse out + re-apply blocks on clp */
6961		clp->in_blk -= o_blocks;
6962		clp->in_count += o_blocks;
6963		rep->num_blks = blocks;
6964		clp->in_blk += blocks;
6965		clp->in_count -= blocks;
6966	}
6967	clp->in_done_count -= blocks;
6968	return stop_after_write;
6969}
6970
6971void normal_out_operation(Rq_coll * clp, Rq_elem * rep, int blocks)
6972{
6973	int res;
6974
6975	/* enters holding out_mutex */
6976	while (((res = write(clp->outfd, rep->buffp,
6977			     rep->num_blks * clp->bs)) < 0)
6978	       && (EINTR == errno)) ;
6979	if (res < 0) {
6980		if (clp->coe) {
6981			fprintf(stderr, ">> ignored error for out blk=%d for "
6982				"%d bytes, %s\n", rep->blk,
6983				rep->num_blks * rep->bs, strerror(errno));
6984			res = rep->num_blks * clp->bs;
6985		} else {
6986			fprintf(stderr, "error normal write, %s\n",
6987				strerror(errno));
6988			guarded_stop_in(clp);
6989			clp->out_stop = 1;
6990			return;
6991		}
6992	}
6993	if (res < blocks * clp->bs) {
6994		blocks = res / clp->bs;
6995		if ((res % clp->bs) > 0) {
6996			blocks++;
6997			clp->out_partial++;
6998		}
6999		rep->num_blks = blocks;
7000	}
7001	clp->out_done_count -= blocks;
7002}
7003
7004void sg_in_operation(Rq_coll * clp, Rq_elem * rep)
7005{
7006	int res;
7007	int status;
7008
7009	/* enters holding in_mutex */
7010	while (1) {
7011		res = sg_start_io(rep);
7012		if (1 == res)
7013			err_exit(ENOMEM, "sg starting in command");
7014		else if (res < 0) {
7015			fprintf(stderr, ME "inputting to sg failed, blk=%d\n",
7016				rep->blk);
7017			status = pthread_mutex_unlock(&clp->in_mutex);
7018			if (0 != status)
7019				err_exit(status, "unlock in_mutex");
7020			guarded_stop_both(clp);
7021			return;
7022		}
7023		/* Now release in mutex to let other reads run in parallel */
7024		status = pthread_mutex_unlock(&clp->in_mutex);
7025		if (0 != status)
7026			err_exit(status, "unlock in_mutex");
7027
7028		res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
7029		if (res < 0) {
7030			if (clp->coe) {
7031				memset(rep->buffp, 0, rep->num_blks * rep->bs);
7032				fprintf(stderr,
7033					">> substituted zeros for in blk=%d for "
7034					"%d bytes\n", rep->blk,
7035					rep->num_blks * rep->bs);
7036			} else {
7037				fprintf(stderr,
7038					"error finishing sg in command\n");
7039				guarded_stop_both(clp);
7040				return;
7041			}
7042		}
7043		if (res <= 0) {	/* looks good, going to return */
7044			if (rep->dio_incomplete || rep->resid) {
7045				status = pthread_mutex_lock(&clp->aux_mutex);
7046				if (0 != status)
7047					err_exit(status, "lock aux_mutex");
7048				clp->dio_incomplete += rep->dio_incomplete;
7049				clp->sum_of_resids += rep->resid;
7050				status = pthread_mutex_unlock(&clp->aux_mutex);
7051				if (0 != status)
7052					err_exit(status, "unlock aux_mutex");
7053			}
7054			status = pthread_mutex_lock(&clp->in_mutex);
7055			if (0 != status)
7056				err_exit(status, "lock in_mutex");
7057			clp->in_done_count -= rep->num_blks;
7058			status = pthread_mutex_unlock(&clp->in_mutex);
7059			if (0 != status)
7060				err_exit(status, "unlock in_mutex");
7061			return;
7062		}
7063		/* else assume 1 == res so try again with same addr, count info */
7064		/* now re-acquire read mutex for balance */
7065		/* N.B. This re-read could now be out of read sequence */
7066		status = pthread_mutex_lock(&clp->in_mutex);
7067		if (0 != status)
7068			err_exit(status, "lock in_mutex");
7069	}
7070}
7071
7072void sg_out_operation(Rq_coll * clp, Rq_elem * rep)
7073{
7074	int res;
7075	int status;
7076
7077	/* enters holding out_mutex */
7078	while (1) {
7079		res = sg_start_io(rep);
7080		if (1 == res)
7081			err_exit(ENOMEM, "sg starting out command");
7082		else if (res < 0) {
7083			fprintf(stderr,
7084				ME "outputting from sg failed, blk=%d\n",
7085				rep->blk);
7086			status = pthread_mutex_unlock(&clp->out_mutex);
7087			if (0 != status)
7088				err_exit(status, "unlock out_mutex");
7089			guarded_stop_both(clp);
7090			return;
7091		}
7092		/* Now release in mutex to let other reads run in parallel */
7093		status = pthread_mutex_unlock(&clp->out_mutex);
7094		if (0 != status)
7095			err_exit(status, "unlock out_mutex");
7096
7097		res = sg_finish_io(rep->wr, rep, &clp->aux_mutex);
7098		if (res < 0) {
7099			if (clp->coe)
7100				fprintf(stderr,
7101					">> ignored error for out blk=%d for "
7102					"%d bytes\n", rep->blk,
7103					rep->num_blks * rep->bs);
7104			else {
7105				fprintf(stderr,
7106					"error finishing sg out command\n");
7107				guarded_stop_both(clp);
7108				return;
7109			}
7110		}
7111		if (res <= 0) {
7112			if (rep->dio_incomplete || rep->resid) {
7113				status = pthread_mutex_lock(&clp->aux_mutex);
7114				if (0 != status)
7115					err_exit(status, "lock aux_mutex");
7116				clp->dio_incomplete += rep->dio_incomplete;
7117				clp->sum_of_resids += rep->resid;
7118				status = pthread_mutex_unlock(&clp->aux_mutex);
7119				if (0 != status)
7120					err_exit(status, "unlock aux_mutex");
7121			}
7122			status = pthread_mutex_lock(&clp->out_mutex);
7123			if (0 != status)
7124				err_exit(status, "lock out_mutex");
7125			clp->out_done_count -= rep->num_blks;
7126			status = pthread_mutex_unlock(&clp->out_mutex);
7127			if (0 != status)
7128				err_exit(status, "unlock out_mutex");
7129			return;
7130		}
7131		/* else assume 1 == res so try again with same addr, count info */
7132		/* now re-acquire out mutex for balance */
7133		/* N.B. This re-write could now be out of write sequence */
7134		status = pthread_mutex_lock(&clp->out_mutex);
7135		if (0 != status)
7136			err_exit(status, "lock out_mutex");
7137	}
7138}
7139
7140int sg_start_io(Rq_elem * rep)
7141{
7142	sg_io_hdr_t *hp = &rep->io_hdr;
7143	int fua = rep->wr ? (rep->fua_mode & 1) : (rep->fua_mode & 2);
7144	int res;
7145
7146	if (sg_build_scsi_cdb(rep->cmd, rep->cdbsz, rep->num_blks, rep->blk,
7147			      rep->wr, fua, 0)) {
7148		fprintf(stderr, ME "bad cdb build, start_blk=%d, blocks=%d\n",
7149			rep->blk, rep->num_blks);
7150		return -1;
7151	}
7152	memset(hp, 0, sizeof(sg_io_hdr_t));
7153	hp->interface_id = 'S';
7154	hp->cmd_len = rep->cdbsz;
7155	hp->cmdp = rep->cmd;
7156	hp->dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
7157	hp->dxfer_len = rep->bs * rep->num_blks;
7158	hp->dxferp = rep->buffp;
7159	hp->mx_sb_len = sizeof(rep->sb);
7160	hp->sbp = rep->sb;
7161	hp->timeout = DEF_TIMEOUT;
7162	hp->usr_ptr = rep;
7163	hp->pack_id = rep->blk;
7164	if (rep->dio)
7165		hp->flags |= SG_FLAG_DIRECT_IO;
7166	if (rep->debug > 8) {
7167		fprintf(stderr, "sg_start_io: SCSI %s, blk=%d num_blks=%d\n",
7168			rep->wr ? "WRITE" : "READ", rep->blk, rep->num_blks);
7169		sg_print_command(hp->cmdp);
7170		fprintf(stderr, "dir=%d, len=%d, dxfrp=%p, cmd_len=%d\n",
7171			hp->dxfer_direction, hp->dxfer_len, hp->dxferp,
7172			hp->cmd_len);
7173	}
7174
7175	while (((res = write(rep->wr ? rep->outfd : rep->infd, hp,
7176			     sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ;
7177	if (res < 0) {
7178		if (ENOMEM == errno)
7179			return 1;
7180		perror("starting io on sg device, error");
7181		return -1;
7182	}
7183	return 0;
7184}
7185
7186/* -1 -> unrecoverable error, 0 -> successful, 1 -> try again */
7187int sg_finish_io(int wr, Rq_elem * rep, pthread_mutex_t * a_mutp)
7188{
7189	int res, status;
7190	sg_io_hdr_t io_hdr;
7191	sg_io_hdr_t *hp;
7192#if 0
7193	static int testing = 0;	/* thread dubious! */
7194#endif
7195
7196	memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
7197	/* FORCE_PACK_ID active set only read packet with matching pack_id */
7198	io_hdr.interface_id = 'S';
7199	io_hdr.dxfer_direction = rep->wr ? SG_DXFER_TO_DEV : SG_DXFER_FROM_DEV;
7200	io_hdr.pack_id = rep->blk;
7201
7202	while (((res = read(wr ? rep->outfd : rep->infd, &io_hdr,
7203			    sizeof(sg_io_hdr_t))) < 0) && (EINTR == errno)) ;
7204	if (res < 0) {
7205		perror("finishing io on sg device, error");
7206		return -1;
7207	}
7208	if (rep != (Rq_elem *) io_hdr.usr_ptr)
7209		err_exit(0,
7210			 "sg_finish_io: bad usr_ptr, request-response mismatch\n");
7211	memcpy(&rep->io_hdr, &io_hdr, sizeof(sg_io_hdr_t));
7212	hp = &rep->io_hdr;
7213
7214	switch (sg_err_category3(hp)) {
7215	case SG_ERR_CAT_CLEAN:
7216		break;
7217	case SG_ERR_CAT_RECOVERED:
7218		fprintf(stderr, "Recovered error on block=%d, num=%d\n",
7219			rep->blk, rep->num_blks);
7220		break;
7221	case SG_ERR_CAT_MEDIA_CHANGED:
7222		return 1;
7223	default:
7224		{
7225			char ebuff[EBUFF_SZ];
7226
7227			snprintf(ebuff, EBUFF_SZ,
7228				 "%s blk=%d", rep->wr ? "writing" : "reading",
7229				 rep->blk);
7230			status = pthread_mutex_lock(a_mutp);
7231			if (0 != status)
7232				err_exit(status, "lock aux_mutex");
7233			sg_chk_n_print3(ebuff, hp);
7234			status = pthread_mutex_unlock(a_mutp);
7235			if (0 != status)
7236				err_exit(status, "unlock aux_mutex");
7237			return -1;
7238		}
7239	}
7240#if 0
7241	if (0 == (++testing % 100))
7242		return -1;
7243#endif
7244	if (rep->dio &&
7245	    ((hp->info & SG_INFO_DIRECT_IO_MASK) != SG_INFO_DIRECT_IO))
7246		rep->dio_incomplete = 1;	/* count dios done as indirect IO */
7247	else
7248		rep->dio_incomplete = 0;
7249	rep->resid = hp->resid;
7250	if (rep->debug > 8)
7251		fprintf(stderr, "sg_finish_io: completed %s\n",
7252			wr ? "WRITE" : "READ");
7253	return 0;
7254}
7255
7256int sg_prepare(int fd, int bs, int bpt, int *scsi_typep)
7257{
7258	int res, t;
7259
7260	res = ioctl(fd, SG_GET_VERSION_NUM, &t);
7261	if ((res < 0) || (t < 30000)) {
7262		fprintf(stderr, ME "sg driver prior to 3.x.y\n");
7263		return 1;
7264	}
7265	res = 0;
7266	t = bs * bpt;
7267	res = ioctl(fd, SG_SET_RESERVED_SIZE, &t);
7268	if (res < 0)
7269		perror(ME "SG_SET_RESERVED_SIZE error");
7270	t = 1;
7271	res = ioctl(fd, SG_SET_FORCE_PACK_ID, &t);
7272	if (res < 0)
7273		perror(ME "SG_SET_FORCE_PACK_ID error");
7274	if (scsi_typep) {
7275		struct sg_scsi_id info;
7276
7277		res = ioctl(fd, SG_GET_SCSI_ID, &info);
7278		if (res < 0)
7279			perror(ME "SG_SET_SCSI_ID error");
7280		*scsi_typep = info.scsi_type;
7281	}
7282	return 0;
7283}
7284
7285int do_scsi_sgp_read_write(char *device)
7286{
7287	int skip = 0;
7288	int seek = 0;
7289	int count = -1;
7290	char inf[INOUTF_SZ];
7291	char outf[INOUTF_SZ];
7292	int res, k;
7293	int in_num_sect = 0;
7294	int out_num_sect = 0;
7295	int num_threads = DEF_NUM_THREADS;
7296	pthread_t threads[MAX_NUM_THREADS];
7297	int do_time = 1;
7298	int do_sync = 1;
7299	int in_sect_sz, out_sect_sz, status, infull, outfull;
7300	void *vp;
7301	char ebuff[EBUFF_SZ];
7302	struct timeval start_tm, end_tm;
7303	Rq_coll rcoll;
7304
7305	print_msg(TEST_BREAK, __FUNCTION__);
7306
7307	memset(&rcoll, 0, sizeof(Rq_coll));
7308	rcoll.bpt = DEF_BLOCKS_PER_TRANSFER;
7309	rcoll.in_type = FT_OTHER;
7310	rcoll.out_type = FT_OTHER;
7311	rcoll.cdbsz = DEF_SCSI_CDBSZ;
7312
7313	strcpy(inf, "/dev/zero");
7314	strcpy(outf, device);
7315
7316	if (rcoll.bs <= 0) {
7317		rcoll.bs = DEF_BLOCK_SIZE;
7318		fprintf(stderr,
7319			"Assume default 'bs' (block size) of %d bytes\n",
7320			rcoll.bs);
7321	}
7322
7323	if (rcoll.debug)
7324		fprintf(stderr, ME "if=%s skip=%d of=%s seek=%d count=%d\n",
7325			inf, skip, outf, seek, count);
7326
7327	rcoll.infd = STDIN_FILENO;
7328	rcoll.outfd = STDOUT_FILENO;
7329	if (inf[0] && ('-' != inf[0])) {
7330		rcoll.in_type = dd_filetype(inf);
7331
7332		if (FT_ST == rcoll.in_type) {
7333			fprintf(stderr,
7334				ME "unable to use scsi tape device %s\n", inf);
7335			return 1;
7336		} else if (FT_SG == rcoll.in_type) {
7337			if ((rcoll.infd = open(inf, O_RDWR)) < 0) {
7338				snprintf(ebuff, EBUFF_SZ,
7339					 ME "could not open %s for sg reading",
7340					 inf);
7341				perror(ebuff);
7342				return 1;
7343			}
7344			if (sg_prepare(rcoll.infd, rcoll.bs, rcoll.bpt,
7345				       &rcoll.in_scsi_type))
7346				return 1;
7347		} else {
7348			if ((rcoll.infd = open(inf, O_RDONLY)) < 0) {
7349				snprintf(ebuff, EBUFF_SZ,
7350					 ME "could not open %s for reading",
7351					 inf);
7352				perror(ebuff);
7353				return 1;
7354			} else if (skip > 0) {
7355				llse_loff_t offset = skip;
7356
7357				offset *= rcoll.bs;	/* could exceed 32 here! */
7358				if (llse_llseek(rcoll.infd, offset, SEEK_SET) <
7359				    0) {
7360					snprintf(ebuff, EBUFF_SZ,
7361						 ME
7362						 "couldn't skip to required position on %s",
7363						 inf);
7364					perror(ebuff);
7365					return 1;
7366				}
7367			}
7368		}
7369	}
7370	if (outf[0] && ('-' != outf[0])) {
7371		rcoll.out_type = dd_filetype(outf);
7372
7373		if (FT_ST == rcoll.out_type) {
7374			fprintf(stderr,
7375				ME "unable to use scsi tape device %s\n", outf);
7376			return 1;
7377		} else if (FT_SG == rcoll.out_type) {
7378			if ((rcoll.outfd = open(outf, O_RDWR)) < 0) {
7379				snprintf(ebuff, EBUFF_SZ,
7380					 ME "could not open %s for sg writing",
7381					 outf);
7382				perror(ebuff);
7383				return 1;
7384			}
7385
7386			if (sg_prepare(rcoll.outfd, rcoll.bs, rcoll.bpt,
7387				       &rcoll.out_scsi_type))
7388				return 1;
7389		} else if (FT_DEV_NULL == rcoll.out_type)
7390			rcoll.outfd = -1;	/* don't bother opening */
7391		else {
7392			if (FT_RAW != rcoll.out_type) {
7393				if ((rcoll.outfd =
7394				     open(outf, O_WRONLY | O_CREAT,
7395					  0666)) < 0) {
7396					snprintf(ebuff, EBUFF_SZ,
7397						 ME
7398						 "could not open %s for writing",
7399						 outf);
7400					perror(ebuff);
7401					return 1;
7402				}
7403			} else {
7404				if ((rcoll.outfd = open(outf, O_WRONLY)) < 0) {
7405					snprintf(ebuff, EBUFF_SZ,
7406						 ME
7407						 "could not open %s for raw writing",
7408						 outf);
7409					perror(ebuff);
7410					return 1;
7411				}
7412			}
7413			if (seek > 0) {
7414				llse_loff_t offset = seek;
7415
7416				offset *= rcoll.bs;	/* could exceed 32 bits here! */
7417				if (llse_llseek(rcoll.outfd, offset, SEEK_SET) <
7418				    0) {
7419					snprintf(ebuff, EBUFF_SZ,
7420						 ME
7421						 "couldn't seek to required position on %s",
7422						 outf);
7423					perror(ebuff);
7424					return 1;
7425				}
7426			}
7427		}
7428	}
7429	if ((STDIN_FILENO == rcoll.infd) && (STDOUT_FILENO == rcoll.outfd)) {
7430		fprintf(stderr,
7431			"Disallow both if and of to be stdin and stdout");
7432		return 1;
7433	}
7434	if (count < 0) {
7435		if (FT_SG == rcoll.in_type) {
7436			res =
7437			    read_capacity(rcoll.infd, &in_num_sect,
7438					  &in_sect_sz);
7439			if (2 == res) {
7440				fprintf(stderr,
7441					"Unit attention, media changed(in), continuing\n");
7442				res =
7443				    read_capacity(rcoll.infd, &in_num_sect,
7444						  &in_sect_sz);
7445			}
7446			if (0 != res) {
7447				fprintf(stderr,
7448					"Unable to read capacity on %s\n", inf);
7449				in_num_sect = -1;
7450			} else {
7451				if (in_num_sect > skip)
7452					in_num_sect -= skip;
7453			}
7454		}
7455		if (FT_SG == rcoll.out_type) {
7456			res =
7457			    read_capacity(rcoll.outfd, &out_num_sect,
7458					  &out_sect_sz);
7459			if (2 == res) {
7460				fprintf(stderr,
7461					"Unit attention, media changed(out), continuing\n");
7462				res =
7463				    read_capacity(rcoll.outfd, &out_num_sect,
7464						  &out_sect_sz);
7465			}
7466			if (0 != res) {
7467				fprintf(stderr,
7468					"Unable to read capacity on %s\n",
7469					outf);
7470				out_num_sect = -1;
7471			} else {
7472				if (out_num_sect > seek)
7473					out_num_sect -= seek;
7474			}
7475		}
7476		if (in_num_sect > 0) {
7477			if (out_num_sect > 0)
7478				count =
7479				    (in_num_sect >
7480				     out_num_sect) ? out_num_sect : in_num_sect;
7481			else
7482				count = in_num_sect;
7483		} else
7484			count = out_num_sect;
7485	}
7486	if (rcoll.debug > 1)
7487		fprintf(stderr, "Start of loop, count=%d, in_num_sect=%d, "
7488			"out_num_sect=%d\n", count, in_num_sect, out_num_sect);
7489	if (count < 0) {
7490		fprintf(stderr, "Couldn't calculate count, please give one\n");
7491		return 1;
7492	}
7493
7494	rcoll.in_count = count;
7495	rcoll.in_done_count = count;
7496	rcoll.skip = skip;
7497	rcoll.in_blk = skip;
7498	rcoll.out_count = count;
7499	rcoll.out_done_count = count;
7500	rcoll.seek = seek;
7501	rcoll.out_blk = seek;
7502	status = pthread_mutex_init(&rcoll.in_mutex, NULL);
7503	if (0 != status)
7504		err_exit(status, "init in_mutex");
7505	status = pthread_mutex_init(&rcoll.out_mutex, NULL);
7506	if (0 != status)
7507		err_exit(status, "init out_mutex");
7508	status = pthread_mutex_init(&rcoll.aux_mutex, NULL);
7509	if (0 != status)
7510		err_exit(status, "init aux_mutex");
7511	status = pthread_cond_init(&rcoll.out_sync_cv, NULL);
7512	if (0 != status)
7513		err_exit(status, "init out_sync_cv");
7514
7515	sigemptyset(&signal_set);
7516	sigaddset(&signal_set, SIGINT);
7517	status = pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
7518	if (0 != status)
7519		err_exit(status, "pthread_sigmask");
7520	status = pthread_create(&sig_listen_thread_id, NULL,
7521				sig_listen_thread, (void *)&rcoll);
7522	if (0 != status)
7523		err_exit(status, "pthread_create, sig...");
7524
7525	if (do_time) {
7526		start_tm.tv_sec = 0;
7527		start_tm.tv_usec = 0;
7528		gettimeofday(&start_tm, NULL);
7529	}
7530
7531/* vvvvvvvvvvv  Start worker threads  vvvvvvvvvvvvvvvvvvvvvvvv */
7532	if ((rcoll.out_done_count > 0) && (num_threads > 0)) {
7533		/* Run 1 work thread to shake down infant retryable stuff */
7534		status = pthread_mutex_lock(&rcoll.out_mutex);
7535		if (0 != status)
7536			err_exit(status, "lock out_mutex");
7537		status = pthread_create(&threads[0], NULL, read_write_thread,
7538					(void *)&rcoll);
7539		if (0 != status)
7540			err_exit(status, "pthread_create");
7541		if (rcoll.debug)
7542			fprintf(stderr, "Starting worker thread k=0\n");
7543
7544		/* wait for any broadcast */
7545		pthread_cleanup_push(cleanup_out, (void *)&rcoll);
7546		status =
7547		    pthread_cond_wait(&rcoll.out_sync_cv, &rcoll.out_mutex);
7548		if (0 != status)
7549			err_exit(status, "cond out_sync_cv");
7550		pthread_cleanup_pop(0);
7551		status = pthread_mutex_unlock(&rcoll.out_mutex);
7552		if (0 != status)
7553			err_exit(status, "unlock out_mutex");
7554
7555		/* now start the rest of the threads */
7556		for (k = 1; k < num_threads; ++k) {
7557			status =
7558			    pthread_create(&threads[k], NULL, read_write_thread,
7559					   (void *)&rcoll);
7560			if (0 != status)
7561				err_exit(status, "pthread_create");
7562			if (rcoll.debug)
7563				fprintf(stderr, "Starting worker thread k=%d\n",
7564					k);
7565		}
7566
7567		/* now wait for worker threads to finish */
7568		for (k = 0; k < num_threads; ++k) {
7569			status = pthread_join(threads[k], &vp);
7570			if (0 != status)
7571				err_exit(status, "pthread_join");
7572			if (rcoll.debug)
7573				fprintf(stderr,
7574					"Worker thread k=%d terminated\n", k);
7575		}
7576	}
7577
7578	if ((do_time) && (start_tm.tv_sec || start_tm.tv_usec)) {
7579		struct timeval res_tm;
7580		double a, b;
7581
7582		gettimeofday(&end_tm, NULL);
7583		res_tm.tv_sec = end_tm.tv_sec - start_tm.tv_sec;
7584		res_tm.tv_usec = end_tm.tv_usec - start_tm.tv_usec;
7585		if (res_tm.tv_usec < 0) {
7586			--res_tm.tv_sec;
7587			res_tm.tv_usec += 1000000;
7588		}
7589		a = res_tm.tv_sec;
7590		a += (0.000001 * res_tm.tv_usec);
7591		b = (double)rcoll.bs * (count - rcoll.out_done_count);
7592		printf("time to transfer data was %d.%06d secs",
7593		       (int)res_tm.tv_sec, (int)res_tm.tv_usec);
7594		if ((a > 0.00001) && (b > 511))
7595			printf(", %.2f MB/sec\n", b / (a * 1000000.0));
7596		else
7597			printf("\n");
7598	}
7599	if (do_sync) {
7600		if (FT_SG == rcoll.out_type) {
7601			fprintf(stderr, ">> Synchronizing cache on %s\n", outf);
7602			res = sync_cache(rcoll.outfd);
7603			if (2 == res) {
7604				fprintf(stderr,
7605					"Unit attention, media changed(in), continuing\n");
7606				res = sync_cache(rcoll.outfd);
7607			}
7608			if (0 != res)
7609				fprintf(stderr,
7610					"Unable to synchronize cache\n");
7611		}
7612	}
7613
7614	status = pthread_cancel(sig_listen_thread_id);
7615	if (0 != status)
7616		err_exit(status, "pthread_cancel");
7617	if (STDIN_FILENO != rcoll.infd)
7618		close(rcoll.infd);
7619	if ((STDOUT_FILENO != rcoll.outfd) && (FT_DEV_NULL != rcoll.out_type))
7620		close(rcoll.outfd);
7621	res = 0;
7622	if (0 != rcoll.out_count) {
7623		fprintf(stderr,
7624			">>>> Some error occurred, remaining blocks=%d\n",
7625			rcoll.out_count);
7626		res = 2;
7627	}
7628	infull = count - rcoll.in_done_count - rcoll.in_partial;
7629	fprintf(stderr, "%d+%d records in\n", infull, rcoll.in_partial);
7630	outfull = count - rcoll.out_done_count - rcoll.out_partial;
7631	fprintf(stderr, "%d+%d records out\n", outfull, rcoll.out_partial);
7632	if (rcoll.dio_incomplete) {
7633		int fd;
7634		char c;
7635
7636		fprintf(stderr,
7637			">> Direct IO requested but incomplete %d times\n",
7638			rcoll.dio_incomplete);
7639		if ((fd = open(proc_allow_dio, O_RDONLY)) >= 0) {
7640			if (1 == read(fd, &c, 1)) {
7641				if ('0' == c)
7642					fprintf(stderr,
7643						">>> %s set to '0' but should be set "
7644						"to '1' for direct IO\n",
7645						proc_allow_dio);
7646			}
7647			close(fd);
7648		}
7649	}
7650	if (rcoll.sum_of_resids)
7651		fprintf(stderr, ">> Non-zero sum of residual counts=%d\n",
7652			rcoll.sum_of_resids);
7653	return res;
7654}
7655