camcontrol.c revision 41962
1/*
2 * Copyright (c) 1997, 1998 Kenneth D. Merry
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 *    derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 *	$Id: camcontrol.c,v 1.6 1998/11/12 17:47:24 ken Exp $
29 */
30
31#include <sys/ioctl.h>
32#include <sys/types.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <ctype.h>
39#include <err.h>
40
41#include <cam/cam.h>
42#include <cam/cam_debug.h>
43#include <cam/cam_ccb.h>
44#include <cam/scsi/scsi_all.h>
45#include <cam/scsi/scsi_da.h>
46#include <cam/scsi/scsi_pass.h>
47#include <cam/scsi/scsi_message.h>
48#include <camlib.h>
49#include "camcontrol.h"
50
51#define DEFAULT_DEVICE "da"
52#define DEFAULT_UNIT 	0
53
54typedef enum {
55	CAM_ARG_NONE		= 0x00000000,
56	CAM_ARG_DEVLIST		= 0x00000001,
57	CAM_ARG_TUR		= 0x00000002,
58	CAM_ARG_INQUIRY		= 0x00000003,
59	CAM_ARG_STARTSTOP	= 0x00000004,
60	CAM_ARG_RESCAN		= 0x00000005,
61	CAM_ARG_READ_DEFECTS	= 0x00000006,
62	CAM_ARG_MODE_PAGE	= 0x00000007,
63	CAM_ARG_SCSI_CMD	= 0x00000008,
64	CAM_ARG_DEVTREE		= 0x00000009,
65	CAM_ARG_USAGE		= 0x0000000a,
66	CAM_ARG_DEBUG		= 0x0000000b,
67	CAM_ARG_RESET		= 0x0000000c,
68	CAM_ARG_OPT_MASK	= 0x0000000f,
69	CAM_ARG_VERBOSE		= 0x00000010,
70	CAM_ARG_DEVICE		= 0x00000020,
71	CAM_ARG_BUS		= 0x00000040,
72	CAM_ARG_TARGET		= 0x00000080,
73	CAM_ARG_LUN		= 0x00000100,
74	CAM_ARG_EJECT		= 0x00000200,
75	CAM_ARG_UNIT		= 0x00000400,
76	CAM_ARG_FORMAT_BLOCK	= 0x00000800,
77	CAM_ARG_FORMAT_BFI	= 0x00001000,
78	CAM_ARG_FORMAT_PHYS	= 0x00002000,
79	CAM_ARG_PLIST		= 0x00004000,
80	CAM_ARG_GLIST		= 0x00008000,
81	CAM_ARG_GET_SERIAL	= 0x00010000,
82	CAM_ARG_GET_STDINQ	= 0x00020000,
83	CAM_ARG_GET_XFERRATE	= 0x00040000,
84	CAM_ARG_INQ_MASK	= 0x00070000,
85	CAM_ARG_MODE_EDIT	= 0x00080000,
86	CAM_ARG_PAGE_CNTL	= 0x00100000,
87	CAM_ARG_TIMEOUT		= 0x00200000,
88	CAM_ARG_CMD_IN		= 0x00400000,
89	CAM_ARG_CMD_OUT		= 0x00800000,
90	CAM_ARG_DBD		= 0x01000000,
91	CAM_ARG_ERR_RECOVER	= 0x02000000,
92	CAM_ARG_RETRIES		= 0x04000000,
93	CAM_ARG_START_UNIT	= 0x08000000,
94	CAM_ARG_DEBUG_INFO	= 0x10000000,
95	CAM_ARG_DEBUG_TRACE	= 0x20000000,
96	CAM_ARG_DEBUG_SUBTRACE	= 0x40000000,
97	CAM_ARG_DEBUG_CDB	= 0x80000000,
98	CAM_ARG_FLAG_MASK	= 0xfffffff0
99} cam_argmask;
100
101struct camcontrol_opts {
102	char 		*optname;
103	cam_argmask	argnum;
104	const char	*subopt;
105};
106
107extern int optreset;
108
109static const char scsicmd_opts[] = "c:i:o:";
110static const char readdefect_opts[] = "f:GP";
111
112struct camcontrol_opts option_table[] = {
113	{"tur", CAM_ARG_TUR, NULL},
114	{"inquiry", CAM_ARG_INQUIRY, "DSR"},
115	{"start", CAM_ARG_STARTSTOP | CAM_ARG_START_UNIT, NULL},
116	{"stop", CAM_ARG_STARTSTOP, NULL},
117	{"eject", CAM_ARG_STARTSTOP | CAM_ARG_EJECT, NULL},
118	{"rescan", CAM_ARG_RESCAN, NULL},
119	{"reset", CAM_ARG_RESET, NULL},
120	{"cmd", CAM_ARG_SCSI_CMD, scsicmd_opts},
121	{"command", CAM_ARG_SCSI_CMD, scsicmd_opts},
122	{"defects", CAM_ARG_READ_DEFECTS, readdefect_opts},
123	{"defectlist", CAM_ARG_READ_DEFECTS, readdefect_opts},
124	{"devlist", CAM_ARG_DEVTREE, NULL},
125	{"periphlist", CAM_ARG_DEVLIST, NULL},
126	{"modepage", CAM_ARG_MODE_PAGE, "dem:P:"},
127	{"debug", CAM_ARG_DEBUG, "ITSc"},
128	{"help", CAM_ARG_USAGE, NULL},
129	{"-?", CAM_ARG_USAGE, NULL},
130	{"-h", CAM_ARG_USAGE, NULL},
131	{NULL, 0, NULL}
132};
133
134typedef enum {
135	CC_OR_NOT_FOUND,
136	CC_OR_AMBIGUOUS,
137	CC_OR_FOUND
138} camcontrol_optret;
139
140cam_argmask arglist;
141
142
143camcontrol_optret getoption(char *arg, cam_argmask *argnum, char **subopt);
144static int getdevlist(struct cam_device *device);
145static int getdevtree(void);
146static int testunitready(struct cam_device *device, int retry_count,
147			 int timeout);
148static int scsistart(struct cam_device *device, int startstop, int loadeject,
149		     int retry_count, int timeout);
150static int scsidoinquiry(struct cam_device *device, int argc, char **argv,
151			 char *combinedopt, int retry_count, int timeout);
152static int scsiinquiry(struct cam_device *device, int retry_count, int timeout);
153static int scsiserial(struct cam_device *device, int retry_count, int timeout);
154static int scsixferrate(struct cam_device *device);
155static int dorescan_or_reset(int argc, char **argv, int rescan);
156static int rescan_or_reset_bus(int bus, int rescan);
157static int scanlun_or_reset_dev(int bus, int target, int lun, int scan);
158static int readdefects(struct cam_device *device, int argc, char **argv,
159		       char *combinedopt, int retry_count, int timeout);
160static void modepage(struct cam_device *device, int argc, char **argv,
161		     char *combinedopt, int retry_count, int timeout);
162static int scsicmd(struct cam_device *device, int argc, char **argv,
163		   char *combinedopt, int retry_count, int timeout);
164
165camcontrol_optret
166getoption(char *arg, cam_argmask *argnum, char **subopt)
167{
168	struct camcontrol_opts *opts;
169	int num_matches = 0;
170
171	for (opts = option_table; (opts != NULL) && (opts->optname != NULL);
172	     opts++) {
173		if (strncmp(opts->optname, arg, strlen(arg)) == 0) {
174			*argnum = opts->argnum;
175			*subopt = (char *)opts->subopt;
176			if (++num_matches > 1)
177				return(CC_OR_AMBIGUOUS);
178		}
179	}
180
181	if (num_matches > 0)
182		return(CC_OR_FOUND);
183	else
184		return(CC_OR_NOT_FOUND);
185}
186
187static int
188getdevlist(struct cam_device *device)
189{
190	union ccb *ccb;
191	char status[32];
192	int error = 0;
193
194	ccb = cam_getccb(device);
195
196	ccb->ccb_h.func_code = XPT_GDEVLIST;
197	ccb->ccb_h.flags = CAM_DIR_NONE;
198	ccb->ccb_h.retry_count = 1;
199	ccb->cgdl.index = 0;
200	ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS;
201	while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) {
202		if (cam_send_ccb(device, ccb) < 0) {
203			perror("error getting device list");
204			cam_freeccb(ccb);
205			return(1);
206		}
207
208		status[0] = '\0';
209
210		switch (ccb->cgdl.status) {
211			case CAM_GDEVLIST_MORE_DEVS:
212				strcpy(status, "MORE");
213				break;
214			case CAM_GDEVLIST_LAST_DEVICE:
215				strcpy(status, "LAST");
216				break;
217			case CAM_GDEVLIST_LIST_CHANGED:
218				strcpy(status, "CHANGED");
219				break;
220			case CAM_GDEVLIST_ERROR:
221				strcpy(status, "ERROR");
222				error = 1;
223				break;
224		}
225
226		fprintf(stdout, "%s%d:  generation: %d index: %d status: %s\n",
227			ccb->cgdl.periph_name,
228			ccb->cgdl.unit_number,
229			ccb->cgdl.generation,
230			ccb->cgdl.index,
231			status);
232
233		/*
234		 * If the list has changed, we need to start over from the
235		 * beginning.
236		 */
237		if (ccb->cgdl.status == CAM_GDEVLIST_LIST_CHANGED)
238			ccb->cgdl.index = 0;
239	}
240
241	cam_freeccb(ccb);
242
243	return(error);
244}
245
246static int
247getdevtree(void)
248{
249	union ccb ccb;
250	int bufsize, i, fd;
251	int need_close = 0;
252	int error = 0;
253
254	if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
255		warn("couldn't open %s", XPT_DEVICE);
256		return(1);
257	}
258
259	bzero(&(&ccb.ccb_h)[1], sizeof(struct ccb_dev_match));
260
261	ccb.ccb_h.func_code = XPT_DEV_MATCH;
262	bufsize = sizeof(struct dev_match_result) * 100;
263	ccb.cdm.match_buf_len = bufsize;
264	ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
265	ccb.cdm.num_matches = 0;
266
267	/*
268	 * We fetch all nodes, since we display most of them in the default
269	 * case, and all in the verbose case.
270	 */
271	ccb.cdm.num_patterns = 0;
272	ccb.cdm.pattern_buf_len = 0;
273
274	/*
275	 * We do the ioctl multiple times if necessary, in case there are
276	 * more than 100 nodes in the EDT.
277	 */
278	do {
279		if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
280			warn("error sending CAMIOCOMMAND ioctl");
281			error = 1;
282			break;
283		}
284
285		if ((ccb.ccb_h.status != CAM_REQ_CMP)
286		 || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
287		    && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
288			fprintf(stderr, "got CAM error %#x, CDM error %d\n",
289				ccb.ccb_h.status, ccb.cdm.status);
290			error = 1;
291			break;
292		}
293
294		for (i = 0; i < ccb.cdm.num_matches; i++) {
295			switch(ccb.cdm.matches[i].type) {
296			case DEV_MATCH_BUS: {
297				struct bus_match_result *bus_result;
298
299				/*
300				 * Only print the bus information if the
301				 * user turns on the verbose flag.
302				 */
303				if ((arglist & CAM_ARG_VERBOSE) == 0)
304					break;
305
306				bus_result =
307					&ccb.cdm.matches[i].result.bus_result;
308
309				if (need_close) {
310					fprintf(stdout, ")\n");
311					need_close = 0;
312				}
313
314				fprintf(stdout, "scbus%d on %s%d bus %d:\n",
315					bus_result->path_id,
316					bus_result->dev_name,
317					bus_result->unit_number,
318					bus_result->bus_id);
319				break;
320			}
321			case DEV_MATCH_DEVICE: {
322				struct device_match_result *dev_result;
323				char vendor[16], product[48], revision[16];
324				char tmpstr[256];
325
326				dev_result =
327				     &ccb.cdm.matches[i].result.device_result;
328
329				cam_strvis(vendor, dev_result->inq_data.vendor,
330					   sizeof(dev_result->inq_data.vendor),
331					   sizeof(vendor));
332				cam_strvis(product,
333					   dev_result->inq_data.product,
334					   sizeof(dev_result->inq_data.product),
335					   sizeof(product));
336				cam_strvis(revision,
337					   dev_result->inq_data.revision,
338					  sizeof(dev_result->inq_data.revision),
339					   sizeof(revision));
340				sprintf(tmpstr, "<%s %s %s>", vendor, product,
341					revision);
342				if (need_close) {
343					fprintf(stdout, ")\n");
344					need_close = 0;
345				}
346
347				fprintf(stdout, "%-33s  at scbus%d "
348					"target %d lun %d (",
349					tmpstr,
350					dev_result->path_id,
351					dev_result->target_id,
352					dev_result->target_lun);
353				break;
354			}
355			case DEV_MATCH_PERIPH: {
356				struct periph_match_result *periph_result;
357
358				periph_result =
359				      &ccb.cdm.matches[i].result.periph_result;
360
361				if (need_close)
362					fprintf(stdout, ",");
363
364				fprintf(stdout, "%s%d",
365					periph_result->periph_name,
366					periph_result->unit_number);
367
368				need_close = 1;
369				break;
370			}
371			default:
372				fprintf(stdout, "unknown match type\n");
373				break;
374			}
375		}
376
377	} while ((ccb.ccb_h.status == CAM_REQ_CMP)
378		&& (ccb.cdm.status == CAM_DEV_MATCH_MORE));
379
380	if (need_close)
381		fprintf(stdout, ")\n");
382
383	close(fd);
384
385	return(error);
386}
387
388static int
389testunitready(struct cam_device *device, int retry_count, int timeout)
390{
391	int error = 0;
392	union ccb *ccb;
393
394	ccb = cam_getccb(device);
395
396	scsi_test_unit_ready(&ccb->csio,
397			     /* retries */ retry_count,
398			     /* cbfcnp */ NULL,
399			     /* tag_action */ MSG_SIMPLE_Q_TAG,
400			     /* sense_len */ SSD_FULL_SIZE,
401			     /* timeout */ timeout ? timeout : 5000);
402
403	/* Disable freezing the device queue */
404	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
405
406	if (arglist & CAM_ARG_ERR_RECOVER)
407		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
408
409	if (cam_send_ccb(device, ccb) < 0) {
410		perror("error sending test unit ready");
411
412		if (arglist & CAM_ARG_VERBOSE) {
413		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
414			    CAM_SCSI_STATUS_ERROR)
415				scsi_sense_print(device, &ccb->csio, stderr);
416			else
417				fprintf(stderr, "CAM status is %#x\n",
418					ccb->ccb_h.status);
419		}
420
421		cam_freeccb(ccb);
422		return(1);
423	}
424
425	if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
426		fprintf(stdout, "Unit is ready\n");
427	else {
428		fprintf(stdout, "Unit is not ready\n");
429		error = 1;
430
431		if (arglist & CAM_ARG_VERBOSE) {
432		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
433			    CAM_SCSI_STATUS_ERROR)
434				scsi_sense_print(device, &ccb->csio, stderr);
435			else
436				fprintf(stderr, "CAM status is %#x\n",
437					ccb->ccb_h.status);
438		}
439	}
440
441	cam_freeccb(ccb);
442
443	return(error);
444}
445
446static int
447scsistart(struct cam_device *device, int startstop, int loadeject,
448	  int retry_count, int timeout)
449{
450	union ccb *ccb;
451	int error = 0;
452
453	ccb = cam_getccb(device);
454
455	/*
456	 * If we're stopping, send an ordered tag so the drive in question
457	 * will finish any previously queued writes before stopping.  If
458	 * the device isn't capable of tagged queueing, or if tagged
459	 * queueing is turned off, the tag action is a no-op.
460	 */
461	scsi_start_stop(&ccb->csio,
462			/* retries */ retry_count,
463			/* cbfcnp */ NULL,
464			/* tag_action */ startstop ? MSG_SIMPLE_Q_TAG :
465						     MSG_ORDERED_Q_TAG,
466			/* start/stop */ startstop,
467			/* load_eject */ loadeject,
468			/* immediate */ 0,
469			/* sense_len */ SSD_FULL_SIZE,
470			/* timeout */ timeout ? timeout : 120000);
471
472	/* Disable freezing the device queue */
473	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
474
475	if (arglist & CAM_ARG_ERR_RECOVER)
476		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
477
478	if (cam_send_ccb(device, ccb) < 0) {
479		perror("error sending start unit");
480
481		if (arglist & CAM_ARG_VERBOSE) {
482		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
483			    CAM_SCSI_STATUS_ERROR)
484				scsi_sense_print(device, &ccb->csio, stderr);
485			else
486				fprintf(stderr, "CAM status is %#x\n",
487					ccb->ccb_h.status);
488		}
489
490		cam_freeccb(ccb);
491		return(1);
492	}
493
494	if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
495		if (startstop) {
496			fprintf(stdout, "Unit started successfully");
497			if (loadeject)
498				fprintf(stdout,", Media loaded\n");
499			else
500				fprintf(stdout,"\n");
501		} else {
502			fprintf(stdout, "Unit stopped successfully");
503			if (loadeject)
504				fprintf(stdout, ", Media ejected\n");
505			else
506				fprintf(stdout, "\n");
507		}
508	else {
509		error = 1;
510		if (startstop)
511			fprintf(stdout,
512				"Error received from start unit command\n");
513		else
514			fprintf(stdout,
515				"Error received from stop unit command\n");
516
517		if (arglist & CAM_ARG_VERBOSE) {
518		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
519			    CAM_SCSI_STATUS_ERROR)
520				scsi_sense_print(device, &ccb->csio, stderr);
521			else
522				fprintf(stderr, "CAM status is %#x\n",
523					ccb->ccb_h.status);
524		}
525	}
526
527	cam_freeccb(ccb);
528
529	return(error);
530}
531
532static int
533scsidoinquiry(struct cam_device *device, int argc, char **argv,
534	      char *combinedopt, int retry_count, int timeout)
535{
536	int c;
537	int error = 0;
538
539	while ((c = getopt(argc, argv, combinedopt)) != -1) {
540		switch(c) {
541		case 'D':
542			arglist |= CAM_ARG_GET_STDINQ;
543			break;
544		case 'R':
545			arglist |= CAM_ARG_GET_XFERRATE;
546			break;
547		case 'S':
548			arglist |= CAM_ARG_GET_SERIAL;
549			break;
550		default:
551			break;
552		}
553	}
554
555	/*
556	 * If the user didn't specify any inquiry options, he wants all of
557	 * them.
558	 */
559	if ((arglist & CAM_ARG_INQ_MASK) == 0)
560		arglist |= CAM_ARG_INQ_MASK;
561
562	if (arglist & CAM_ARG_GET_STDINQ)
563		error = scsiinquiry(device, retry_count, timeout);
564
565	if (error != 0)
566		return(error);
567
568	if (arglist & CAM_ARG_GET_SERIAL)
569		scsiserial(device, retry_count, timeout);
570
571	if (error != 0)
572		return(error);
573
574	if (arglist & CAM_ARG_GET_XFERRATE)
575		error = scsixferrate(device);
576
577	return(error);
578}
579
580static int
581scsiinquiry(struct cam_device *device, int retry_count, int timeout)
582{
583	union ccb *ccb;
584	struct scsi_inquiry_data *inq_buf;
585	int error = 0;
586
587	ccb = cam_getccb(device);
588
589	if (ccb == NULL) {
590		warnx("couldn't allocate CCB");
591		return(1);
592	}
593
594	/* cam_getccb cleans up the header, caller has to zero the payload */
595	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
596
597	inq_buf = (struct scsi_inquiry_data *)malloc(
598		sizeof(struct scsi_inquiry_data));
599
600	if (inq_buf == NULL) {
601		cam_freeccb(ccb);
602		warnx("can't malloc memory for inquiry\n");
603		return(1);
604	}
605	bzero(inq_buf, sizeof(*inq_buf));
606
607	scsi_inquiry(&ccb->csio,
608		     /* retries */ retry_count,
609		     /* cbfcnp */ NULL,
610		     /* tag_action */ MSG_SIMPLE_Q_TAG,
611		     /* inq_buf */ (u_int8_t *)inq_buf,
612		     /* inq_len */ sizeof(struct scsi_inquiry_data),
613		     /* evpd */ 0,
614		     /* page_code */ 0,
615		     /* sense_len */ SSD_FULL_SIZE,
616		     /* timeout */ timeout ? timeout : 5000);
617
618	/* Disable freezing the device queue */
619	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
620
621	if (arglist & CAM_ARG_ERR_RECOVER)
622		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
623
624	if (cam_send_ccb(device, ccb) < 0) {
625		perror("error sending SCSI inquiry");
626
627		if (arglist & CAM_ARG_VERBOSE) {
628		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
629			    CAM_SCSI_STATUS_ERROR)
630				scsi_sense_print(device, &ccb->csio, stderr);
631			else
632				fprintf(stderr, "CAM status is %#x\n",
633					ccb->ccb_h.status);
634		}
635
636		cam_freeccb(ccb);
637		return(1);
638	}
639
640	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
641		error = 1;
642
643		if (arglist & CAM_ARG_VERBOSE) {
644		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
645			    CAM_SCSI_STATUS_ERROR)
646				scsi_sense_print(device, &ccb->csio, stderr);
647			else
648				fprintf(stderr, "CAM status is %#x\n",
649					ccb->ccb_h.status);
650		}
651	}
652
653	cam_freeccb(ccb);
654
655	if (error != 0) {
656		free(inq_buf);
657		return(error);
658	}
659
660	scsi_print_inquiry(inq_buf);
661
662	free(inq_buf);
663
664	if (arglist & CAM_ARG_GET_SERIAL)
665		fprintf(stdout, "Serial Number ");
666
667	return(0);
668}
669
670static int
671scsiserial(struct cam_device *device, int retry_count, int timeout)
672{
673	union ccb *ccb;
674	struct scsi_vpd_unit_serial_number *serial_buf;
675	char serial_num[SVPD_SERIAL_NUM_SIZE + 1];
676	int error = 0;
677
678	ccb = cam_getccb(device);
679
680	if (ccb == NULL) {
681		warnx("couldn't allocate CCB");
682		return(1);
683	}
684
685	/* cam_getccb cleans up the header, caller has to zero the payload */
686	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
687
688	serial_buf = (struct scsi_vpd_unit_serial_number *)
689		malloc(sizeof(*serial_buf));
690
691	if (serial_buf == NULL) {
692		cam_freeccb(ccb);
693		warnx("can't malloc memory for serial number");
694		return(1);
695	}
696
697	scsi_inquiry(&ccb->csio,
698		     /*retries*/ retry_count,
699		     /*cbfcnp*/ NULL,
700		     /* tag_action */ MSG_SIMPLE_Q_TAG,
701		     /* inq_buf */ (u_int8_t *)serial_buf,
702		     /* inq_len */ sizeof(*serial_buf),
703		     /* evpd */ 1,
704		     /* page_code */ SVPD_UNIT_SERIAL_NUMBER,
705		     /* sense_len */ SSD_FULL_SIZE,
706		     /* timeout */ timeout ? timeout : 5000);
707
708	/* Disable freezing the device queue */
709	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
710
711	if (arglist & CAM_ARG_ERR_RECOVER)
712		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
713
714	if (cam_send_ccb(device, ccb) < 0) {
715		warn("error getting serial number");
716
717		if (arglist & CAM_ARG_VERBOSE) {
718		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
719			    CAM_SCSI_STATUS_ERROR)
720				scsi_sense_print(device, &ccb->csio, stderr);
721			else
722				fprintf(stderr, "CAM status is %#x\n",
723					ccb->ccb_h.status);
724		}
725
726		cam_freeccb(ccb);
727		free(serial_buf);
728		return(1);
729	}
730
731	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
732		error = 1;
733
734		if (arglist & CAM_ARG_VERBOSE) {
735		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
736			    CAM_SCSI_STATUS_ERROR)
737				scsi_sense_print(device, &ccb->csio, stderr);
738			else
739				fprintf(stderr, "CAM status is %#x\n",
740					ccb->ccb_h.status);
741		}
742	}
743
744	cam_freeccb(ccb);
745
746	if (error != 0) {
747		free(serial_buf);
748		return(error);
749	}
750
751	bcopy(serial_buf->serial_num, serial_num, serial_buf->length);
752	serial_num[serial_buf->length] = '\0';
753
754	if (((arglist & CAM_ARG_GET_STDINQ) == 0)
755	 && (arglist & CAM_ARG_GET_XFERRATE))
756		fprintf(stdout, "Serial Number ");
757
758	fprintf(stdout, "%.60s\n", serial_num);
759
760	free(serial_buf);
761
762	return(0);
763}
764
765static int
766scsixferrate(struct cam_device *device)
767{
768	u_int32_t freq;
769	u_int32_t speed;
770
771	if (device->sync_period != 0)
772		freq = scsi_calc_syncsrate(device->sync_period);
773	else
774		freq = 0;
775
776	speed = freq;
777	speed *= (0x01 << device->bus_width);
778	fprintf(stdout, "%d.%dMB/s transfers ", speed / 1000, speed % 1000);
779
780	if (device->sync_period != 0)
781                fprintf(stdout, "(%d.%dMHz, offset %d", freq / 1000,
782			freq % 1000, device->sync_offset);
783
784        if (device->bus_width != 0) {
785                if (device->sync_period == 0)
786                        fprintf(stdout, "(");
787                else
788                        fprintf(stdout, ", ");
789                fprintf(stdout, "%dbit)", 8 * (0x01 << device->bus_width));
790        } else if (device->sync_period != 0)
791                fprintf(stdout, ")");
792
793        if (device->inq_data.flags & SID_CmdQue)
794                fprintf(stdout, ", Tagged Queueing Enabled");
795
796        fprintf(stdout, "\n");
797
798	return(0);
799}
800
801static int
802dorescan_or_reset(int argc, char **argv, int rescan)
803{
804	static const char *must =
805		"you must specify a bus, or a bus:target:lun to %s";
806	int error = 0;
807	int bus = -1, target = -1, lun = -1;
808	char *tstr, *tmpstr = NULL;
809
810	if (argc < 3) {
811		warnx(must, rescan? "rescan" : "reset");
812		return(1);
813	}
814	/*
815	 * Parse out a bus, or a bus, target and lun in the following
816	 * format:
817	 * bus
818	 * bus:target:lun
819	 * It is an error to specify a bus and target, but not a lun.
820	 */
821	tstr = argv[optind];
822
823	while (isspace(*tstr) && (*tstr != '\0'))
824		tstr++;
825
826	tmpstr = (char *)strtok(tstr, ":");
827	if ((tmpstr != NULL) && (*tmpstr != '\0')){
828		bus = strtol(tmpstr, NULL, 0);
829		arglist |= CAM_ARG_BUS;
830		tmpstr = (char *)strtok(NULL, ":");
831		if ((tmpstr != NULL) && (*tmpstr != '\0')){
832			target = strtol(tmpstr, NULL, 0);
833			arglist |= CAM_ARG_TARGET;
834			tmpstr = (char *)strtok(NULL, ":");
835			if ((tmpstr != NULL) && (*tmpstr != '\0')){
836				lun = strtol(tmpstr, NULL, 0);
837				arglist |= CAM_ARG_LUN;
838			} else {
839				error = 1;
840				warnx(must, rescan? "rescan" : "reset");
841			}
842		}
843	} else {
844		error = 1;
845		warnx(must, rescan? "rescan" : "reset");
846	}
847
848
849	if (error == 0) {
850		if ((arglist & CAM_ARG_BUS)
851		 && (arglist & CAM_ARG_TARGET)
852		 && (arglist & CAM_ARG_LUN))
853			error = scanlun_or_reset_dev(bus, target, lun, rescan);
854		else if (arglist & CAM_ARG_BUS)
855			error = rescan_or_reset_bus(bus, rescan);
856		else {
857			error = 1;
858			warnx(must, rescan? "rescan" : "reset");
859		}
860	}
861	return(error);
862}
863
864static int
865rescan_or_reset_bus(int bus, int rescan)
866{
867	union ccb ccb;
868	int fd;
869
870	if (bus < 0) {
871		warnx("invalid bus number %d", bus);
872		return(1);
873	}
874
875	if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
876		warnx("error opening tranport layer device %s", XPT_DEVICE);
877		warn("%s", XPT_DEVICE);
878		return(1);
879	}
880
881	ccb.ccb_h.func_code = rescan? XPT_SCAN_BUS : XPT_RESET_BUS;
882	ccb.ccb_h.path_id = bus;
883	ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
884	ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
885	ccb.crcn.flags = CAM_FLAG_NONE;
886
887	/* run this at a low priority */
888	ccb.ccb_h.pinfo.priority = 5;
889
890	if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
891		warn("CAMIOCOMMAND ioctl failed");
892		close(fd);
893		return(1);
894	}
895
896	close(fd);
897
898	if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
899		fprintf(stdout, "%s of bus %d was successful\n", bus,
900		    rescan? "Re-scan" : "Reset");
901		return(0);
902	} else {
903		fprintf(stdout, "%s of bus %d returned error %#x\n",
904		    rescan? "Re-scan" : "Reset", bus,
905		    ccb.ccb_h.status & CAM_STATUS_MASK);
906		return(1);
907	}
908}
909
910static int
911scanlun_or_reset_dev(int bus, int target, int lun, int scan)
912{
913	union ccb ccb;
914	int fd;
915
916	if (bus < 0) {
917		warnx("invalid bus number %d", bus);
918		return(1);
919	}
920
921	if (target < 0) {
922		warnx("invalid target number %d", target);
923		return(1);
924	}
925
926	if (lun < 0) {
927		warnx("invalid lun number %d", lun);
928		return(1);
929	}
930
931	if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
932		warnx("error opening tranport layer device %s\n",
933			XPT_DEVICE);
934		warn("%s", XPT_DEVICE);
935		return(1);
936	}
937
938	ccb.ccb_h.func_code = (scan)? XPT_SCAN_LUN : XPT_RESET_DEV;
939	ccb.ccb_h.path_id = bus;
940	ccb.ccb_h.target_id = target;
941	ccb.ccb_h.target_lun = lun;
942	ccb.crcn.flags = CAM_FLAG_NONE;
943
944	/* run this at a low priority */
945	ccb.ccb_h.pinfo.priority = 5;
946
947	if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) {
948		warn("CAMIOCOMMAND ioctl failed");
949		close(fd);
950		return(1);
951	}
952
953	close(fd);
954
955	if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
956		fprintf(stdout, "%s of %d:%d:%d was successful\n",
957		    scan? "Re-scan" : "Reset", bus, target, lun);
958		return(0);
959	} else {
960		fprintf(stdout, "%s of %d:%d:%d returned error %#x\n",
961		    scan? "Re-scan" : "Reset", bus, target, lun,
962		    ccb.ccb_h.status & CAM_STATUS_MASK);
963		return(1);
964	}
965}
966
967static int
968readdefects(struct cam_device *device, int argc, char **argv,
969	    char *combinedopt, int retry_count, int timeout)
970{
971	union ccb *ccb = NULL;
972	struct scsi_read_defect_data_10 *rdd_cdb;
973	u_int8_t *defect_list = NULL;
974	u_int32_t dlist_length = 65000;
975	u_int32_t returned_length = 0;
976	u_int32_t num_returned = 0;
977	u_int8_t returned_format;
978	register int i;
979	int c, error = 0;
980	int lists_specified = 0;
981
982	while ((c = getopt(argc, argv, combinedopt)) != -1) {
983		switch(c){
984		case 'f':
985		{
986			char *tstr;
987			tstr = optarg;
988			while (isspace(*tstr) && (*tstr != '\0'))
989				tstr++;
990			if (strcmp(tstr, "block") == 0)
991				arglist |= CAM_ARG_FORMAT_BLOCK;
992			else if (strcmp(tstr, "bfi") == 0)
993				arglist |= CAM_ARG_FORMAT_BFI;
994			else if (strcmp(tstr, "phys") == 0)
995				arglist |= CAM_ARG_FORMAT_PHYS;
996			else {
997				error = 1;
998				warnx("invalid defect format %s", tstr);
999				goto defect_bailout;
1000			}
1001			break;
1002		}
1003		case 'G':
1004			arglist |= CAM_ARG_GLIST;
1005			break;
1006		case 'P':
1007			arglist |= CAM_ARG_PLIST;
1008			break;
1009		default:
1010			break;
1011		}
1012	}
1013
1014	ccb = cam_getccb(device);
1015
1016	/*
1017	 * Hopefully 65000 bytes is enough to hold the defect list.  If it
1018	 * isn't, the disk is probably dead already.  We'd have to go with
1019	 * 12 byte command (i.e. alloc_length is 32 bits instead of 16)
1020	 * to hold them all.
1021	 */
1022	defect_list = malloc(dlist_length);
1023
1024	rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes;
1025
1026	/*
1027	 * cam_getccb() zeros the CCB header only.  So we need to zero the
1028	 * payload portion of the ccb.
1029	 */
1030	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
1031
1032	cam_fill_csio(&ccb->csio,
1033		      /*retries*/ retry_count,
1034		      /*cbfcnp*/ NULL,
1035		      /*flags*/ CAM_DIR_IN | (arglist & CAM_ARG_ERR_RECOVER) ?
1036					      CAM_PASS_ERR_RECOVER : 0,
1037		      /*tag_action*/ MSG_SIMPLE_Q_TAG,
1038		      /*data_ptr*/ defect_list,
1039		      /*dxfer_len*/ dlist_length,
1040		      /*sense_len*/ SSD_FULL_SIZE,
1041		      /*cdb_len*/ sizeof(struct scsi_read_defect_data_10),
1042		      /*timeout*/ timeout ? timeout : 5000);
1043
1044	rdd_cdb->opcode = READ_DEFECT_DATA_10;
1045	if (arglist & CAM_ARG_FORMAT_BLOCK)
1046		rdd_cdb->format = SRDD10_BLOCK_FORMAT;
1047	else if (arglist & CAM_ARG_FORMAT_BFI)
1048		rdd_cdb->format = SRDD10_BYTES_FROM_INDEX_FORMAT;
1049	else if (arglist & CAM_ARG_FORMAT_PHYS)
1050		rdd_cdb->format = SRDD10_PHYSICAL_SECTOR_FORMAT;
1051	else {
1052		error = 1;
1053		warnx("no defect list format specified");
1054		goto defect_bailout;
1055	}
1056	if (arglist & CAM_ARG_PLIST) {
1057		rdd_cdb->format |= SRDD10_PLIST;
1058		lists_specified++;
1059	}
1060
1061	if (arglist & CAM_ARG_GLIST) {
1062		rdd_cdb->format |= SRDD10_GLIST;
1063		lists_specified++;
1064	}
1065
1066	scsi_ulto2b(dlist_length, rdd_cdb->alloc_length);
1067
1068	/* Disable freezing the device queue */
1069	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
1070
1071	if (cam_send_ccb(device, ccb) < 0) {
1072		perror("error reading defect list");
1073
1074		if (arglist & CAM_ARG_VERBOSE) {
1075		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
1076			    CAM_SCSI_STATUS_ERROR)
1077				scsi_sense_print(device, &ccb->csio, stderr);
1078			else
1079				fprintf(stderr, "CAM status is %#x\n",
1080					ccb->ccb_h.status);
1081		}
1082
1083		error = 1;
1084		goto defect_bailout;
1085	}
1086
1087	if (arglist & CAM_ARG_VERBOSE)
1088		scsi_sense_print(device, &ccb->csio, stderr);
1089
1090	returned_length = scsi_2btoul(((struct
1091		scsi_read_defect_data_hdr_10 *)defect_list)->length);
1092
1093	returned_format = ((struct scsi_read_defect_data_hdr_10 *)
1094			defect_list)->format;
1095
1096	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
1097		struct scsi_sense_data *sense;
1098		int error_code, sense_key, asc, ascq;
1099
1100		sense = &ccb->csio.sense_data;
1101		scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
1102
1103		/*
1104		 * According to the SCSI spec, if the disk doesn't support
1105		 * the requested format, it will generally return a sense
1106		 * key of RECOVERED ERROR, and an additional sense code
1107		 * of "DEFECT LIST NOT FOUND".  So, we check for that, and
1108		 * also check to make sure that the returned length is
1109		 * greater than 0, and then print out whatever format the
1110		 * disk gave us.
1111		 */
1112		if ((sense_key == SSD_KEY_RECOVERED_ERROR)
1113		 && (asc == 0x1c) && (ascq == 0x00)
1114		 && (returned_length > 0)) {
1115			warnx("requested defect format not available");
1116			switch(returned_format & SRDDH10_DLIST_FORMAT_MASK) {
1117			case SRDD10_BLOCK_FORMAT:
1118				warnx("Device returned block format");
1119				break;
1120			case SRDD10_BYTES_FROM_INDEX_FORMAT:
1121				warnx("Device returned bytes from index"
1122				      " format");
1123				break;
1124			case SRDD10_PHYSICAL_SECTOR_FORMAT:
1125				warnx("Device returned physical sector format");
1126				break;
1127			default:
1128				error = 1;
1129				warnx("Device returned unknown defect"
1130				     " data format %#x", returned_format);
1131				goto defect_bailout;
1132				break; /* NOTREACHED */
1133			}
1134		} else {
1135			error = 1;
1136			warnx("Error returned from read defect data command");
1137			goto defect_bailout;
1138		}
1139	}
1140
1141	/*
1142	 * XXX KDM  I should probably clean up the printout format for the
1143	 * disk defects.
1144	 */
1145	switch (returned_format & SRDDH10_DLIST_FORMAT_MASK){
1146		case SRDDH10_PHYSICAL_SECTOR_FORMAT:
1147		{
1148			struct scsi_defect_desc_phys_sector *dlist;
1149
1150			dlist = (struct scsi_defect_desc_phys_sector *)
1151				(defect_list +
1152				sizeof(struct scsi_read_defect_data_hdr_10));
1153
1154			num_returned = returned_length /
1155				sizeof(struct scsi_defect_desc_phys_sector);
1156
1157			fprintf(stderr, "Got %d defect", num_returned);
1158
1159			if ((lists_specified == 0) || (num_returned == 0)) {
1160				fprintf(stderr, "s.\n");
1161				break;
1162			} else if (num_returned == 1)
1163				fprintf(stderr, ":\n");
1164			else
1165				fprintf(stderr, "s:\n");
1166
1167			for (i = 0; i < num_returned; i++) {
1168				fprintf(stdout, "%d:%d:%d\n",
1169					scsi_3btoul(dlist[i].cylinder),
1170					dlist[i].head,
1171					scsi_4btoul(dlist[i].sector));
1172			}
1173			break;
1174		}
1175		case SRDDH10_BYTES_FROM_INDEX_FORMAT:
1176		{
1177			struct scsi_defect_desc_bytes_from_index *dlist;
1178
1179			dlist = (struct scsi_defect_desc_bytes_from_index *)
1180				(defect_list +
1181				sizeof(struct scsi_read_defect_data_hdr_10));
1182
1183			num_returned = returned_length /
1184			      sizeof(struct scsi_defect_desc_bytes_from_index);
1185
1186			fprintf(stderr, "Got %d defect", num_returned);
1187
1188			if ((lists_specified == 0) || (num_returned == 0)) {
1189				fprintf(stderr, "s.\n");
1190				break;
1191			} else if (num_returned == 1)
1192				fprintf(stderr, ":\n");
1193			else
1194				fprintf(stderr, "s:\n");
1195
1196			for (i = 0; i < num_returned; i++) {
1197				fprintf(stdout, "%d:%d:%d\n",
1198					scsi_3btoul(dlist[i].cylinder),
1199					dlist[i].head,
1200					scsi_4btoul(dlist[i].bytes_from_index));
1201			}
1202			break;
1203		}
1204		case SRDDH10_BLOCK_FORMAT:
1205		{
1206			struct scsi_defect_desc_block *dlist;
1207
1208			dlist = (struct scsi_defect_desc_block *)(defect_list +
1209				sizeof(struct scsi_read_defect_data_hdr_10));
1210
1211			num_returned = returned_length /
1212			      sizeof(struct scsi_defect_desc_block);
1213
1214			fprintf(stderr, "Got %d defect", num_returned);
1215
1216			if ((lists_specified == 0) || (num_returned == 0)) {
1217				fprintf(stderr, "s.\n");
1218				break;
1219			} else if (num_returned == 1)
1220				fprintf(stderr, ":\n");
1221			else
1222				fprintf(stderr, "s:\n");
1223
1224			for (i = 0; i < num_returned; i++)
1225				fprintf(stdout, "%u\n",
1226					scsi_4btoul(dlist[i].address));
1227			break;
1228		}
1229		default:
1230			fprintf(stderr, "Unknown defect format %d\n",
1231				returned_format & SRDDH10_DLIST_FORMAT_MASK);
1232			error = 1;
1233			break;
1234	}
1235defect_bailout:
1236
1237	if (defect_list != NULL)
1238		free(defect_list);
1239
1240	if (ccb != NULL)
1241		cam_freeccb(ccb);
1242
1243	return(error);
1244}
1245
1246#if 0
1247void
1248reassignblocks(struct cam_device *device, u_int32_t *blocks, int num_blocks)
1249{
1250	union ccb *ccb;
1251
1252	ccb = cam_getccb(device);
1253
1254	cam_freeccb(ccb);
1255}
1256#endif
1257
1258void
1259mode_sense(struct cam_device *device, int mode_page, int page_control,
1260	   int dbd, int retry_count, int timeout, u_int8_t *data, int datalen)
1261{
1262	union ccb *ccb;
1263	int retval;
1264
1265	ccb = cam_getccb(device);
1266
1267	if (ccb == NULL)
1268		errx(1, "mode_sense: couldn't allocate CCB");
1269
1270	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
1271
1272	scsi_mode_sense(&ccb->csio,
1273			/* retries */ retry_count,
1274			/* cbfcnp */ NULL,
1275			/* tag_action */ MSG_SIMPLE_Q_TAG,
1276			/* dbd */ dbd,
1277			/* page_code */ page_control << 6,
1278			/* page */ mode_page,
1279			/* param_buf */ data,
1280			/* param_len */ datalen,
1281			/* sense_len */ SSD_FULL_SIZE,
1282			/* timeout */ timeout ? timeout : 5000);
1283
1284	if (arglist & CAM_ARG_ERR_RECOVER)
1285		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
1286
1287	/* Disable freezing the device queue */
1288	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
1289
1290	if (((retval = cam_send_ccb(device, ccb)) < 0)
1291	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
1292		if (arglist & CAM_ARG_VERBOSE) {
1293		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
1294			    CAM_SCSI_STATUS_ERROR)
1295				scsi_sense_print(device, &ccb->csio, stderr);
1296			else
1297				fprintf(stderr, "CAM status is %#x\n",
1298					ccb->ccb_h.status);
1299		}
1300		cam_freeccb(ccb);
1301		cam_close_device(device);
1302		if (retval < 0)
1303			err(1, "error sending mode sense command");
1304		else
1305			errx(1, "error sending mode sense command");
1306	}
1307
1308	cam_freeccb(ccb);
1309}
1310
1311void
1312mode_select(struct cam_device *device, int save_pages, int retry_count,
1313	   int timeout, u_int8_t *data, int datalen)
1314{
1315	union ccb *ccb;
1316	int retval;
1317
1318	ccb = cam_getccb(device);
1319
1320	if (ccb == NULL)
1321		errx(1, "mode_select: couldn't allocate CCB");
1322
1323	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
1324
1325	scsi_mode_select(&ccb->csio,
1326			 /* retries */ retry_count,
1327			 /* cbfcnp */ NULL,
1328			 /* tag_action */ MSG_SIMPLE_Q_TAG,
1329			 /* scsi_page_fmt */ 1,
1330			 /* save_pages */ save_pages,
1331			 /* param_buf */ data,
1332			 /* param_len */ datalen,
1333			 /* sense_len */ SSD_FULL_SIZE,
1334			 /* timeout */ timeout ? timeout : 5000);
1335
1336	if (arglist & CAM_ARG_ERR_RECOVER)
1337		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
1338
1339	/* Disable freezing the device queue */
1340	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
1341
1342	if (((retval = cam_send_ccb(device, ccb)) < 0)
1343	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
1344		if (arglist & CAM_ARG_VERBOSE) {
1345		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
1346			    CAM_SCSI_STATUS_ERROR)
1347				scsi_sense_print(device, &ccb->csio, stderr);
1348			else
1349				fprintf(stderr, "CAM status is %#x\n",
1350					ccb->ccb_h.status);
1351		}
1352		cam_freeccb(ccb);
1353		cam_close_device(device);
1354
1355		if (retval < 0)
1356			err(1, "error sending mode select command");
1357		else
1358			errx(1, "error sending mode select command");
1359
1360	}
1361
1362	cam_freeccb(ccb);
1363}
1364
1365void
1366modepage(struct cam_device *device, int argc, char **argv, char *combinedopt,
1367	 int retry_count, int timeout)
1368{
1369	int c, mode_page = -1, page_control = 0;
1370
1371	while ((c = getopt(argc, argv, combinedopt)) != -1) {
1372		switch(c) {
1373		case 'd':
1374			arglist |= CAM_ARG_DBD;
1375			break;
1376		case 'e':
1377			arglist |= CAM_ARG_MODE_EDIT;
1378			break;
1379		case 'm':
1380			mode_page = strtol(optarg, NULL, 0);
1381			if (mode_page < 0)
1382				errx(1, "invalid mode page %d", mode_page);
1383			break;
1384		case 'P':
1385			page_control = strtol(optarg, NULL, 0);
1386			if ((page_control < 0) || (page_control > 3))
1387				errx(1, "invalid page control field %d",
1388				     page_control);
1389			arglist |= CAM_ARG_PAGE_CNTL;
1390			break;
1391		default:
1392			break;
1393		}
1394	}
1395
1396	if (mode_page == -1)
1397		errx(1, "you must specify a mode page!");
1398
1399	mode_edit(device, mode_page, page_control, arglist & CAM_ARG_DBD,
1400		  arglist & CAM_ARG_MODE_EDIT, retry_count, timeout);
1401}
1402
1403static int
1404scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt,
1405	int retry_count, int timeout)
1406{
1407	union ccb *ccb;
1408	u_int32_t flags = CAM_DIR_NONE;
1409	u_int8_t *data_ptr = NULL;
1410	u_int8_t cdb[20];
1411	struct get_hook hook;
1412	int c, data_bytes = 0;
1413	int cdb_len = 0;
1414	char *datastr = NULL, *tstr;
1415	int error = 0;
1416	int fd_data = 0;
1417	int retval;
1418
1419	ccb = cam_getccb(device);
1420
1421	if (ccb == NULL) {
1422		warnx("scsicmd: error allocating ccb");
1423		return(1);
1424	}
1425
1426	bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio));
1427
1428	while ((c = getopt(argc, argv, combinedopt)) != -1) {
1429		switch(c) {
1430		case 'c':
1431			tstr = optarg;
1432			while (isspace(*tstr) && (*tstr != '\0'))
1433				tstr++;
1434			hook.argc = argc - optind;
1435			hook.argv = argv + optind;
1436			hook.got = 0;
1437			buff_encode_visit(cdb, sizeof(cdb), tstr,
1438					  iget, &hook);
1439			/*
1440			 * Increment optind by the number of arguments the
1441			 * encoding routine processed.  After each call to
1442			 * getopt(3), optind points to the argument that
1443			 * getopt should process _next_.  In this case,
1444			 * that means it points to the first command string
1445			 * argument, if there is one.  Once we increment
1446			 * this, it should point to either the next command
1447			 * line argument, or it should be past the end of
1448			 * the list.
1449			 */
1450			optind += hook.got;
1451			break;
1452		case 'i':
1453			if (arglist & CAM_ARG_CMD_OUT) {
1454				warnx("command must either be "
1455				      "read or write, not both");
1456				error = 1;
1457				goto scsicmd_bailout;
1458			}
1459			arglist |= CAM_ARG_CMD_IN;
1460			flags = CAM_DIR_IN;
1461			data_bytes = strtol(optarg, NULL, 0);
1462			if (data_bytes <= 0) {
1463				warnx("invalid number of input bytes %d",
1464				      data_bytes);
1465				error = 1;
1466				goto scsicmd_bailout;
1467			}
1468			hook.argc = argc - optind;
1469			hook.argv = argv + optind;
1470			hook.got = 0;
1471			optind++;
1472			datastr = cget(&hook, NULL);
1473			/*
1474			 * If the user supplied "-" instead of a format, he
1475			 * wants the data to be written to stdout.
1476			 */
1477			if ((datastr != NULL)
1478			 && (datastr[0] == '-'))
1479				fd_data = 1;
1480
1481			data_ptr = (u_int8_t *)malloc(data_bytes);
1482			break;
1483		case 'o':
1484			if (arglist & CAM_ARG_CMD_IN) {
1485				warnx("command must either be "
1486				      "read or write, not both");
1487				error = 1;
1488				goto scsicmd_bailout;
1489			}
1490			arglist |= CAM_ARG_CMD_OUT;
1491			flags = CAM_DIR_OUT;
1492			data_bytes = strtol(optarg, NULL, 0);
1493			if (data_bytes <= 0) {
1494				warnx("invalid number of output bytes %d",
1495				      data_bytes);
1496				error = 1;
1497				goto scsicmd_bailout;
1498			}
1499			hook.argc = argc - optind;
1500			hook.argv = argv + optind;
1501			hook.got = 0;
1502			datastr = cget(&hook, NULL);
1503			data_ptr = (u_int8_t *)malloc(data_bytes);
1504			/*
1505			 * If the user supplied "-" instead of a format, he
1506			 * wants the data to be read from stdin.
1507			 */
1508			if ((datastr != NULL)
1509			 && (datastr[0] == '-'))
1510				fd_data = 1;
1511			else
1512				buff_encode_visit(data_ptr, data_bytes, datastr,
1513						  iget, &hook);
1514			optind += hook.got;
1515			break;
1516		default:
1517			break;
1518		}
1519	}
1520
1521	/*
1522	 * If fd_data is set, and we're writing to the device, we need to
1523	 * read the data the user wants written from stdin.
1524	 */
1525	if ((fd_data == 1) && (arglist & CAM_ARG_CMD_OUT)) {
1526		size_t amt_read;
1527		int amt_to_read = data_bytes;
1528		u_int8_t *buf_ptr = data_ptr;
1529
1530		for (amt_read = 0; amt_to_read > 0;
1531		     amt_read = read(0, buf_ptr, amt_to_read)) {
1532			if (amt_read == -1) {
1533				warn("error reading data from stdin");
1534				error = 1;
1535				goto scsicmd_bailout;
1536			}
1537			amt_to_read -= amt_read;
1538			buf_ptr += amt_read;
1539		}
1540	}
1541
1542	if (arglist & CAM_ARG_ERR_RECOVER)
1543		flags |= CAM_PASS_ERR_RECOVER;
1544
1545	/* Disable freezing the device queue */
1546	flags |= CAM_DEV_QFRZDIS;
1547
1548	/*
1549	 * This is taken from the SCSI-3 draft spec.
1550	 * (T10/1157D revision 0.3)
1551	 * The top 3 bits of an opcode are the group code.  The next 5 bits
1552	 * are the command code.
1553	 * Group 0:  six byte commands
1554	 * Group 1:  ten byte commands
1555	 * Group 2:  ten byte commands
1556	 * Group 3:  reserved
1557	 * Group 4:  sixteen byte commands
1558	 * Group 5:  twelve byte commands
1559	 * Group 6:  vendor specific
1560	 * Group 7:  vendor specific
1561	 */
1562	switch((cdb[0] >> 5) & 0x7) {
1563		case 0:
1564			cdb_len = 6;
1565			break;
1566		case 1:
1567		case 2:
1568			cdb_len = 10;
1569			break;
1570		case 3:
1571		case 6:
1572		case 7:
1573			cdb_len = 1;
1574			break;
1575		case 4:
1576			cdb_len = 16;
1577			break;
1578		case 5:
1579			cdb_len = 12;
1580			break;
1581	}
1582
1583	/*
1584	 * We should probably use csio_build_visit or something like that
1585	 * here, but it's easier to encode arguments as you go.  The
1586	 * alternative would be skipping the CDB argument and then encoding
1587	 * it here, since we've got the data buffer argument by now.
1588	 */
1589	bcopy(cdb, &ccb->csio.cdb_io.cdb_bytes, cdb_len);
1590
1591	cam_fill_csio(&ccb->csio,
1592		      /*retries*/ retry_count,
1593		      /*cbfcnp*/ NULL,
1594		      /*flags*/ flags,
1595		      /*tag_action*/ MSG_SIMPLE_Q_TAG,
1596		      /*data_ptr*/ data_ptr,
1597		      /*dxfer_len*/ data_bytes,
1598		      /*sense_len*/ SSD_FULL_SIZE,
1599		      /*cdb_len*/ cdb_len,
1600		      /*timeout*/ timeout ? timeout : 5000);
1601
1602	if (((retval = cam_send_ccb(device, ccb)) < 0)
1603	 || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
1604		if (retval < 0)
1605			warn("error sending command");
1606		else
1607			warnx("error sending command");
1608
1609		if (arglist & CAM_ARG_VERBOSE) {
1610		 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) ==
1611			    CAM_SCSI_STATUS_ERROR)
1612				scsi_sense_print(device, &ccb->csio, stderr);
1613			else
1614				fprintf(stderr, "CAM status is %#x\n",
1615					ccb->ccb_h.status);
1616		}
1617
1618		error = 1;
1619		goto scsicmd_bailout;
1620	}
1621
1622
1623	if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP)
1624	 && (arglist & CAM_ARG_CMD_IN)
1625	 && (data_bytes > 0)) {
1626		if (fd_data == 0) {
1627			buff_decode_visit(data_ptr, data_bytes, datastr,
1628					  arg_put, NULL);
1629			fprintf(stdout, "\n");
1630		} else {
1631			size_t amt_written;
1632			int amt_to_write = data_bytes;
1633			u_int8_t *buf_ptr = data_ptr;
1634
1635			for (amt_written = 0; (amt_to_write > 0) &&
1636			     (amt_written =write(1, buf_ptr,amt_to_write))> 0;){
1637				amt_to_write -= amt_written;
1638				buf_ptr += amt_written;
1639			}
1640			if (amt_written == -1) {
1641				warn("error writing data to stdout");
1642				error = 1;
1643				goto scsicmd_bailout;
1644			} else if ((amt_written == 0)
1645				&& (amt_to_write > 0)) {
1646				warnx("only wrote %u bytes out of %u",
1647				      data_bytes - amt_to_write, data_bytes);
1648			}
1649		}
1650	}
1651
1652scsicmd_bailout:
1653
1654	if ((data_bytes > 0) && (data_ptr != NULL))
1655		free(data_ptr);
1656
1657	cam_freeccb(ccb);
1658
1659	return(error);
1660}
1661
1662static int
1663camdebug(int argc, char **argv, char *combinedopt)
1664{
1665	int c, fd;
1666	int bus = -1, target = -1, lun = -1;
1667	char *tstr, *tmpstr = NULL;
1668	union ccb ccb;
1669	int error = 0;
1670
1671	bzero(&ccb, sizeof(union ccb));
1672
1673	while ((c = getopt(argc, argv, combinedopt)) != -1) {
1674		switch(c) {
1675		case 'I':
1676			arglist |= CAM_ARG_DEBUG_INFO;
1677			ccb.cdbg.flags |= CAM_DEBUG_INFO;
1678			break;
1679		case 'S':
1680			arglist |= CAM_ARG_DEBUG_TRACE;
1681			ccb.cdbg.flags |= CAM_DEBUG_TRACE;
1682			break;
1683		case 'T':
1684			arglist |= CAM_ARG_DEBUG_SUBTRACE;
1685			ccb.cdbg.flags |= CAM_DEBUG_SUBTRACE;
1686			break;
1687		case 'c':
1688			arglist |= CAM_ARG_DEBUG_CDB;
1689			ccb.cdbg.flags |= CAM_DEBUG_CDB;
1690			break;
1691		default:
1692			break;
1693		}
1694	}
1695
1696	if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
1697		warnx("error opening transport layer device %s", XPT_DEVICE);
1698		warn("%s", XPT_DEVICE);
1699		return(1);
1700	}
1701	argc -= optind;
1702	argv += optind;
1703
1704	if (argc <= 0) {
1705		warnx("you must specify \"off\", \"all\" or a bus,");
1706		warnx("bus:target, or bus:target:lun");
1707		close(fd);
1708		return(1);
1709	}
1710
1711	tstr = *argv;
1712
1713	while (isspace(*tstr) && (*tstr != '\0'))
1714		tstr++;
1715
1716	if (strncmp(tstr, "off", 3) == 0) {
1717		ccb.cdbg.flags = CAM_DEBUG_NONE;
1718		arglist &= ~(CAM_ARG_DEBUG_INFO|CAM_ARG_DEBUG_TRACE|
1719			     CAM_ARG_DEBUG_SUBTRACE);
1720	} else if (strncmp(tstr, "all", 3) != 0) {
1721		tmpstr = (char *)strtok(tstr, ":");
1722		if ((tmpstr != NULL) && (*tmpstr != '\0')){
1723			bus = strtol(tmpstr, NULL, 0);
1724			arglist |= CAM_ARG_BUS;
1725			tmpstr = (char *)strtok(NULL, ":");
1726			if ((tmpstr != NULL) && (*tmpstr != '\0')){
1727				target = strtol(tmpstr, NULL, 0);
1728				arglist |= CAM_ARG_TARGET;
1729				tmpstr = (char *)strtok(NULL, ":");
1730				if ((tmpstr != NULL) && (*tmpstr != '\0')){
1731					lun = strtol(tmpstr, NULL, 0);
1732					arglist |= CAM_ARG_LUN;
1733				}
1734			}
1735		} else {
1736			error = 1;
1737			warnx("you must specify \"all\", \"off\", or a bus,");
1738			warnx("bus:target, or bus:target:lun to debug");
1739		}
1740	}
1741
1742	if (error == 0) {
1743
1744		ccb.ccb_h.func_code = XPT_DEBUG;
1745		ccb.ccb_h.path_id = bus;
1746		ccb.ccb_h.target_id = target;
1747		ccb.ccb_h.target_lun = lun;
1748
1749		if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
1750			warn("CAMIOCOMMAND ioctl failed");
1751			error = 1;
1752		}
1753
1754		if (error == 0) {
1755			if ((ccb.ccb_h.status & CAM_STATUS_MASK) ==
1756			     CAM_FUNC_NOTAVAIL) {
1757				warnx("CAM debugging not available");
1758				warnx("you need to put options CAMDEBUG in"
1759				      " your kernel config file!");
1760				error = 1;
1761			} else if ((ccb.ccb_h.status & CAM_STATUS_MASK) !=
1762				    CAM_REQ_CMP) {
1763				warnx("XPT_DEBUG CCB failed with status %#x",
1764				      ccb.ccb_h.status);
1765				error = 1;
1766			} else {
1767				if (ccb.cdbg.flags == CAM_DEBUG_NONE) {
1768					fprintf(stderr,
1769						"Debugging turned off\n");
1770				} else {
1771					fprintf(stderr,
1772						"Debugging enabled for "
1773						"%d:%d:%d\n",
1774						bus, target, lun);
1775				}
1776			}
1777		}
1778		close(fd);
1779	}
1780
1781	return(error);
1782}
1783
1784void
1785usage(void)
1786{
1787	fprintf(stderr,
1788"usage:  camcontrol <command> [ generic args ] [ command args ]\n"
1789"        camcontrol devlist    [-v]\n"
1790"        camcontrol periphlist [-n dev_name] [-u unit]\n"
1791"        camcontrol tur        [generic args]\n"
1792"        camcontrol inquiry    [generic args] [-D] [-S] [-R]\n"
1793"        camcontrol start      [generic args]\n"
1794"        camcontrol stop       [generic args]\n"
1795"        camcontrol eject      [generic args]\n"
1796"        camcontrol rescan     <bus[:target:lun]>\n"
1797"        camcontrol reset      <bus[:target:lun]>\n"
1798"        camcontrol defects    [generic args] <-f format> [-P][-G]\n"
1799"        camcontrol modepage   [generic args] <-m page> [-P pagectl][-e][-d]\n"
1800"        camcontrol cmd        [generic args] <-c cmd [args]> \n"
1801"                              [-i len fmt|-o len fmt [args]]\n"
1802"        camcontrol debug      [-I][-T][-S][-c] <all|bus[:target[:lun]]|off>\n"
1803"Specify one of the following options:\n"
1804"devlist     list all CAM devices\n"
1805"periphlist  list all CAM peripheral drivers attached to a device\n"
1806"tur         send a test unit ready to the named device\n"
1807"inquiry     send a SCSI inquiry command to the named device\n"
1808"start       send a Start Unit command to the device\n"
1809"stop        send a Stop Unit command to the device\n"
1810"eject       send a Stop Unit command to the device with the eject bit set\n"
1811"rescan      rescan the given bus, or bus:target:lun\n"
1812"reset       reset the given bus, or bus:target:lun\n"
1813"defects     read the defect list of the specified device\n"
1814"modepage    display or edit (-e) the given mode page\n"
1815"cmd         send the given scsi command, may need -i or -o as well\n"
1816"debug       turn debugging on/off for a bus, target, or lun, or all devices\n"
1817"Generic arguments:\n"
1818"-v                be verbose, print out sense information\n"
1819"-t timeout        command timeout in seconds, overrides default timeout\n"
1820"-n dev_name       specify device name (default is %s)\n"
1821"-u unit           specify unit number (default is %d)\n"
1822"-E                have the kernel attempt to perform SCSI error recovery\n"
1823"-C count          specify the SCSI command retry count (needs -E to work)\n"
1824"modepage arguments:\n"
1825"-e                edit the specified mode page\n"
1826"-B                disable block descriptors for mode sense\n"
1827"-P pgctl          page control field 0-3\n"
1828"defects arguments:\n"
1829"-f format         specify defect list format (block, bfi or phys)\n"
1830"-G                get the grown defect list\n"
1831"-P                get the permanant defect list\n"
1832"inquiry arguments:\n"
1833"-D                get the standard inquiry data\n"
1834"-S                get the serial number\n"
1835"-R                get the transfer rate, etc.\n"
1836"cmd arguments:\n"
1837"-c cdb [args]     specify the SCSI CDB\n"
1838"-i len fmt        specify input data and input data format\n"
1839"-o len fmt [args] specify output data and output data fmt\n"
1840"debug arguments:\n"
1841"-I                CAM_DEBUG_INFO -- scsi commands, errors, data\n"
1842"-T                CAM_DEBUG_TRACE -- routine flow tracking\n"
1843"-S                CAM_DEBUG_SUBTRACE -- internal routine command flow\n"
1844"-c                CAM_DEBUG_CDB -- print out SCSI CDBs only\n",
1845DEFAULT_DEVICE, DEFAULT_UNIT);
1846}
1847
1848int
1849main(int argc, char **argv)
1850{
1851	int c;
1852	char *device = NULL;
1853	int unit = 0;
1854	struct cam_device *cam_dev = NULL;
1855	int timeout = 0, retry_count = 1;
1856	camcontrol_optret optreturn;
1857	char *tstr;
1858	char *mainopt = "C:En:t:u:v";
1859	char *subopt = NULL;
1860	char combinedopt[256];
1861	int error = 0;
1862
1863	arglist = CAM_ARG_NONE;
1864
1865	if (argc < 2) {
1866		usage();
1867		exit(1);
1868	}
1869
1870	/*
1871	 * Get the base option.
1872	 */
1873	optreturn = getoption(argv[1], &arglist, &subopt);
1874
1875	if (optreturn == CC_OR_AMBIGUOUS) {
1876		warnx("ambiguous option %s", argv[1]);
1877		usage();
1878		exit(1);
1879	} else if (optreturn == CC_OR_NOT_FOUND) {
1880		warnx("option %s not found", argv[1]);
1881		usage();
1882		exit(1);
1883	}
1884
1885	/*
1886	 * Ahh, getopt(3) is a pain.
1887	 *
1888	 * This is a gross hack.  There really aren't many other good
1889	 * options (excuse the pun) for parsing options in a situation like
1890	 * this.  getopt is kinda braindead, so you end up having to run
1891	 * through the options twice, and give each invocation of getopt
1892	 * the option string for the other invocation.
1893	 *
1894	 * You would think that you could just have two groups of options.
1895	 * The first group would get parsed by the first invocation of
1896	 * getopt, and the second group would get parsed by the second
1897	 * invocation of getopt.  It doesn't quite work out that way.  When
1898	 * the first invocation of getopt finishes, it leaves optind pointing
1899	 * to the argument _after_ the first argument in the second group.
1900	 * So when the second invocation of getopt comes around, it doesn't
1901	 * recognize the first argument it gets and then bails out.
1902	 *
1903	 * A nice alternative would be to have a flag for getopt that says
1904	 * "just keep parsing arguments even when you encounter an unknown
1905	 * argument", but there isn't one.  So there's no real clean way to
1906	 * easily parse two sets of arguments without having one invocation
1907	 * of getopt know about the other.
1908	 *
1909	 * Without this hack, the first invocation of getopt would work as
1910	 * long as the generic arguments are first, but the second invocation
1911	 * (in the subfunction) would fail in one of two ways.  In the case
1912	 * where you don't set optreset, it would fail because optind may be
1913	 * pointing to the argument after the one it should be pointing at.
1914	 * In the case where you do set optreset, and reset optind, it would
1915	 * fail because getopt would run into the first set of options, which
1916	 * it doesn't understand.
1917	 *
1918	 * All of this would "sort of" work if you could somehow figure out
1919	 * whether optind had been incremented one option too far.  The
1920	 * mechanics of that, however, are more daunting than just giving
1921	 * both invocations all of the expect options for either invocation.
1922	 *
1923	 * Needless to say, I wouldn't mind if someone invented a better
1924	 * (non-GPL!) command line parsing interface than getopt.  I
1925	 * wouldn't mind if someone added more knobs to getopt to make it
1926	 * work better.  Who knows, I may talk myself into doing it someday,
1927	 * if the standards weenies let me.  As it is, it just leads to
1928	 * hackery like this and causes people to avoid it in some cases.
1929	 *
1930	 * KDM, September 8th, 1998
1931	 */
1932	if (subopt != NULL)
1933		sprintf(combinedopt, "%s%s", mainopt, subopt);
1934	else
1935		sprintf(combinedopt, "%s", mainopt);
1936
1937	/*
1938	 * Start getopt processing at argv[2], since we've already accepted
1939	 * argv[1] as the command name.
1940	 */
1941	optind = 2;
1942
1943	/*
1944	 * Now we run through the argument list looking for generic
1945	 * options, and ignoring options that possibly belong to
1946	 * subfunctions.
1947	 */
1948	while ((c = getopt(argc, argv, combinedopt))!= -1){
1949		switch(c) {
1950			case 'C':
1951				retry_count = strtol(optarg, NULL, 0);
1952				if (retry_count < 0)
1953					errx(1, "retry count %d is < 0",
1954					     retry_count);
1955				arglist |= CAM_ARG_RETRIES;
1956				break;
1957			case 'E':
1958				arglist |= CAM_ARG_ERR_RECOVER;
1959				break;
1960			case 'n':
1961				arglist |= CAM_ARG_DEVICE;
1962				tstr = optarg;
1963				while (isspace(*tstr) && (*tstr != '\0'))
1964					tstr++;
1965				device = (char *)strdup(tstr);
1966				break;
1967			case 't':
1968				timeout = strtol(optarg, NULL, 0);
1969				if (timeout < 0)
1970					errx(1, "invalid timeout %d", timeout);
1971				/* Convert the timeout from seconds to ms */
1972				timeout *= 1000;
1973				arglist |= CAM_ARG_TIMEOUT;
1974				break;
1975			case 'u':
1976				arglist |= CAM_ARG_UNIT;
1977				unit = strtol(optarg, NULL, 0);
1978				break;
1979			case 'v':
1980				arglist |= CAM_ARG_VERBOSE;
1981				break;
1982			default:
1983				break;
1984		}
1985	}
1986
1987	if ((arglist & CAM_ARG_DEVICE) == 0)
1988		device = (char *)strdup(DEFAULT_DEVICE);
1989
1990	if ((arglist & CAM_ARG_UNIT) == 0)
1991		unit = DEFAULT_UNIT;
1992
1993	/*
1994	 * For most commands we'll want to open the passthrough device
1995	 * associated with the specified device.  In the case of the rescan
1996	 * commands, we don't use a passthrough device at all, just the
1997	 * transport layer device.
1998	 */
1999	if (((arglist & CAM_ARG_OPT_MASK) != CAM_ARG_RESCAN)
2000	 && ((arglist & CAM_ARG_OPT_MASK) != CAM_ARG_RESET)
2001	 && ((arglist & CAM_ARG_OPT_MASK) != CAM_ARG_DEVTREE)
2002	 && ((arglist & CAM_ARG_OPT_MASK) != CAM_ARG_USAGE)
2003	 && ((arglist & CAM_ARG_OPT_MASK) != CAM_ARG_DEBUG)) {
2004
2005		if ((cam_dev = cam_open_spec_device(device,unit,O_RDWR,
2006		     NULL))== NULL)
2007			errx(1,"%s", cam_errbuf);
2008	}
2009
2010	/*
2011	 * Reset optind to 2, and reset getopt, so these routines can parse
2012	 * the arguments again.
2013	 */
2014	optind = 2;
2015	optreset = 1;
2016
2017	switch(arglist & CAM_ARG_OPT_MASK) {
2018		case CAM_ARG_DEVLIST:
2019			error = getdevlist(cam_dev);
2020			break;
2021		case CAM_ARG_DEVTREE:
2022			error = getdevtree();
2023			break;
2024		case CAM_ARG_TUR:
2025			error = testunitready(cam_dev, retry_count, timeout);
2026			break;
2027		case CAM_ARG_INQUIRY:
2028			error = scsidoinquiry(cam_dev, argc, argv, combinedopt,
2029					      retry_count, timeout);
2030			break;
2031		case CAM_ARG_STARTSTOP:
2032			error = scsistart(cam_dev, arglist & CAM_ARG_START_UNIT,
2033					  arglist & CAM_ARG_EJECT, retry_count,
2034					  timeout);
2035			break;
2036		case CAM_ARG_RESCAN:
2037			error = dorescan_or_reset(argc, argv, 1);
2038			break;
2039		case CAM_ARG_RESET:
2040			error = dorescan_or_reset(argc, argv, 0);
2041			break;
2042		case CAM_ARG_READ_DEFECTS:
2043			error = readdefects(cam_dev, argc, argv, combinedopt,
2044					    retry_count, timeout);
2045			break;
2046		case CAM_ARG_MODE_PAGE:
2047			modepage(cam_dev, argc, argv, combinedopt,
2048				 retry_count, timeout);
2049			break;
2050		case CAM_ARG_SCSI_CMD:
2051			error = scsicmd(cam_dev, argc, argv, combinedopt,
2052					retry_count, timeout);
2053			break;
2054		case CAM_ARG_DEBUG:
2055			error = camdebug(argc, argv, combinedopt);
2056			break;
2057		default:
2058			usage();
2059			error = 1;
2060			break;
2061	}
2062
2063	if (cam_dev != NULL)
2064		cam_close_device(cam_dev);
2065
2066	exit(error);
2067}
2068