1*7eee88edSriastradh /* $NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh Exp $ */ 2c9a47c22Sthorpej 3c9a47c22Sthorpej /*- 40683880eSthorpej * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. 5c9a47c22Sthorpej * All rights reserved. 6c9a47c22Sthorpej * 7c9a47c22Sthorpej * This code is derived from software contributed to The NetBSD Foundation 8c9a47c22Sthorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9c9a47c22Sthorpej * NASA Ames Research Center. 10c9a47c22Sthorpej * 11c9a47c22Sthorpej * Redistribution and use in source and binary forms, with or without 12c9a47c22Sthorpej * modification, are permitted provided that the following conditions 13c9a47c22Sthorpej * are met: 14c9a47c22Sthorpej * 1. Redistributions of source code must retain the above copyright 15c9a47c22Sthorpej * notice, this list of conditions and the following disclaimer. 16c9a47c22Sthorpej * 2. Redistributions in binary form must reproduce the above copyright 17c9a47c22Sthorpej * notice, this list of conditions and the following disclaimer in the 18c9a47c22Sthorpej * documentation and/or other materials provided with the distribution. 19c9a47c22Sthorpej * 20c9a47c22Sthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21c9a47c22Sthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22c9a47c22Sthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23c9a47c22Sthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24c9a47c22Sthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25c9a47c22Sthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26c9a47c22Sthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27c9a47c22Sthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28c9a47c22Sthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29c9a47c22Sthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30c9a47c22Sthorpej * POSSIBILITY OF SUCH DAMAGE. 31c9a47c22Sthorpej */ 32c9a47c22Sthorpej 33c9a47c22Sthorpej /* 34c9a47c22Sthorpej * scsictl(8) - a program to manipulate SCSI devices and busses. 35c9a47c22Sthorpej */ 36c2a3b5ecSagc #include <sys/cdefs.h> 37c2a3b5ecSagc 38c2a3b5ecSagc #ifndef lint 39*7eee88edSriastradh __RCSID("$NetBSD: scsictl.c,v 1.42 2024/11/10 01:55:06 riastradh Exp $"); 40c2a3b5ecSagc #endif 41c2a3b5ecSagc 42c9a47c22Sthorpej #include <sys/param.h> 43c9a47c22Sthorpej #include <sys/ioctl.h> 44c9a47c22Sthorpej #include <sys/scsiio.h> 45c9a47c22Sthorpej #include <err.h> 46c9a47c22Sthorpej #include <errno.h> 47c9a47c22Sthorpej #include <fcntl.h> 48661eb28dSginsbach #include <limits.h> 49c9a47c22Sthorpej #include <stdio.h> 50c9a47c22Sthorpej #include <stdlib.h> 51f17194a0Smlelstv #include <stdbool.h> 52c9a47c22Sthorpej #include <string.h> 53c9a47c22Sthorpej #include <unistd.h> 54c9a47c22Sthorpej #include <util.h> 55c9a47c22Sthorpej 56df9803ceSthorpej #include <dev/scsipi/scsi_spc.h> 57c9a47c22Sthorpej #include <dev/scsipi/scsipi_all.h> 58c9a47c22Sthorpej #include <dev/scsipi/scsi_disk.h> 59c9a47c22Sthorpej #include <dev/scsipi/scsipiconf.h> 60c9a47c22Sthorpej 61c9a47c22Sthorpej #include "extern.h" 62c9a47c22Sthorpej 63c9a47c22Sthorpej struct command { 64c9a47c22Sthorpej const char *cmd_name; 65958ed46fShubertf const char *arg_names; 669bab889dSxtraeme void (*cmd_func)(int, char *[]); 67c9a47c22Sthorpej }; 68c9a47c22Sthorpej 69baa8e84bSjoerg __dead static void usage(void); 70c9a47c22Sthorpej 719211d764Sjakllsch static int fd; /* file descriptor for device */ 72c9a47c22Sthorpej const char *dvname; /* device name */ 739211d764Sjakllsch static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 749211d764Sjakllsch static const char *cmdname; /* command user issued */ 759211d764Sjakllsch static struct scsi_addr dvaddr; /* SCSI device's address */ 76c9a47c22Sthorpej 779211d764Sjakllsch static void device_defects(int, char *[]); 789211d764Sjakllsch static void device_format(int, char *[]); 799211d764Sjakllsch static void device_identify(int, char *[]); 809211d764Sjakllsch static void device_reassign(int, char *[]); 819211d764Sjakllsch static void device_release(int, char *[]); 829211d764Sjakllsch static void device_reserve(int, char *[]); 839211d764Sjakllsch static void device_reset(int, char *[]); 849211d764Sjakllsch static void device_debug(int, char *[]); 859211d764Sjakllsch static void device_prevent(int, char *[]); 869211d764Sjakllsch static void device_allow(int, char *[]); 879211d764Sjakllsch static void device_start(int, char *[]); 889211d764Sjakllsch static void device_stop(int, char *[]); 899211d764Sjakllsch static void device_tur(int, char *[]); 909211d764Sjakllsch static void device_getcache(int, char *[]); 919211d764Sjakllsch static void device_setcache(int, char *[]); 929211d764Sjakllsch static void device_flushcache(int, char *[]); 939211d764Sjakllsch static void device_setspeed(int, char *[]); 94b0be1b67Sflxd static void device_getrealloc(int, char *[]); 95b0be1b67Sflxd static void device_setrealloc(int, char *[]); 96100b2229Smlelstv static void device_reportluns(int, char *[]); 97c9a47c22Sthorpej 989211d764Sjakllsch static struct command device_commands[] = { 99661eb28dSginsbach { "defects", "[primary] [grown] [block|byte|physical]", 100661eb28dSginsbach device_defects }, 10141194c0eSmjacob { "format", "[blocksize [immediate]]", device_format }, 102f17194a0Smlelstv { "identify", "[vpd]", device_identify }, 10389fa8aefSmjl { "reassign", "blkno [blkno [...]]", device_reassign }, 10441194c0eSmjacob { "release", "", device_release }, 10541194c0eSmjacob { "reserve", "", device_reserve }, 10689fa8aefSmjl { "reset", "", device_reset }, 1071b08fb13Spetrov { "debug", "level", device_debug }, 10846743e21Smycroft { "prevent", "", device_prevent }, 10946743e21Smycroft { "allow", "", device_allow }, 11041194c0eSmjacob { "start", "", device_start }, 11141194c0eSmjacob { "stop", "", device_stop }, 11241194c0eSmjacob { "tur", "", device_tur }, 1130683880eSthorpej { "getcache", "", device_getcache }, 1140683880eSthorpej { "setcache", "none|r|w|rw [save]", device_setcache }, 1154950c2c9Smycroft { "flushcache", "", device_flushcache }, 116e47fe82aSbouyer { "setspeed", "[speed]", device_setspeed }, 117b0be1b67Sflxd { "getrealloc", "", device_getrealloc }, 118b0be1b67Sflxd { "setrealloc", "none|r|w|rw [save]", device_setrealloc }, 119100b2229Smlelstv { "reportluns", "normal|wellknown|all|#", device_reportluns }, 120958ed46fShubertf { NULL, NULL, NULL }, 121c9a47c22Sthorpej }; 122c9a47c22Sthorpej 1239211d764Sjakllsch static void bus_reset(int, char *[]); 1249211d764Sjakllsch static void bus_scan(int, char *[]); 1259211d764Sjakllsch static void bus_detach(int, char *[]); 126c9a47c22Sthorpej 1279211d764Sjakllsch static struct command bus_commands[] = { 12889fa8aefSmjl { "reset", "", bus_reset }, 12989fa8aefSmjl { "scan", "target lun", bus_scan }, 13003fd5e67Sbouyer { "detach", "target lun", bus_detach }, 131958ed46fShubertf { NULL, NULL, NULL }, 132c9a47c22Sthorpej }; 133c9a47c22Sthorpej 134c9a47c22Sthorpej int 1359bab889dSxtraeme main(int argc, char *argv[]) 136c9a47c22Sthorpej { 137c9a47c22Sthorpej struct command *commands; 138c9a47c22Sthorpej int i; 139c9a47c22Sthorpej 140c9a47c22Sthorpej /* Must have at least: device command */ 141c9a47c22Sthorpej if (argc < 3) 142c9a47c22Sthorpej usage(); 143c9a47c22Sthorpej 144c9a47c22Sthorpej /* Skip program name, get and skip device name and command. */ 145c9a47c22Sthorpej dvname = argv[1]; 146c9a47c22Sthorpej cmdname = argv[2]; 147c9a47c22Sthorpej argv += 3; 148c9a47c22Sthorpej argc -= 3; 149c9a47c22Sthorpej 150c9a47c22Sthorpej /* 151c9a47c22Sthorpej * Open the device and determine if it's a scsibus or an actual 152c9a47c22Sthorpej * device. Devices respond to the SCIOCIDENTIFY ioctl. 153c9a47c22Sthorpej */ 154c9a47c22Sthorpej fd = opendisk(dvname, O_RDWR, dvname_store, sizeof(dvname_store), 0); 155c9a47c22Sthorpej if (fd == -1) { 156c9a47c22Sthorpej if (errno == ENOENT) { 157c9a47c22Sthorpej /* 158c9a47c22Sthorpej * Device doesn't exist. Probably trying to open 159c9a47c22Sthorpej * a device which doesn't use disk semantics for 160c0b197f9Sthorpej * device name. Try again, specifying "cooked", 161c0b197f9Sthorpej * which leaves off the "r" in front of the device's 162c0b197f9Sthorpej * name. 163c9a47c22Sthorpej */ 164c0b197f9Sthorpej fd = opendisk(dvname, O_RDWR, dvname_store, 165c0b197f9Sthorpej sizeof(dvname_store), 1); 166c9a47c22Sthorpej if (fd == -1) 167c9a47c22Sthorpej err(1, "%s", dvname); 168d3c740d7Sjwise } else 169c9a47c22Sthorpej err(1, "%s", dvname); 170c0b197f9Sthorpej } 171c0b197f9Sthorpej 172c9a47c22Sthorpej /* 173c0b197f9Sthorpej * Point the dvname at the actual device name that opendisk() opened. 174c9a47c22Sthorpej */ 175c9a47c22Sthorpej dvname = dvname_store; 176c9a47c22Sthorpej 177c9a47c22Sthorpej if (ioctl(fd, SCIOCIDENTIFY, &dvaddr) < 0) 178c9a47c22Sthorpej commands = bus_commands; 179c9a47c22Sthorpej else 180c9a47c22Sthorpej commands = device_commands; 181c9a47c22Sthorpej 182c9a47c22Sthorpej /* Look up and call the command. */ 183c9a47c22Sthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 184c9a47c22Sthorpej if (strcmp(cmdname, commands[i].cmd_name) == 0) 185c9a47c22Sthorpej break; 186c9a47c22Sthorpej if (commands[i].cmd_name == NULL) 18793e37412Sad errx(1, "unknown %s command: %s", 188c9a47c22Sthorpej commands == bus_commands ? "bus" : "device", cmdname); 189c9a47c22Sthorpej 190c9a47c22Sthorpej (*commands[i].cmd_func)(argc, argv); 191c9a47c22Sthorpej exit(0); 192c9a47c22Sthorpej } 193c9a47c22Sthorpej 194baa8e84bSjoerg static void 1959bab889dSxtraeme usage(void) 196c9a47c22Sthorpej { 197958ed46fShubertf int i; 198c9a47c22Sthorpej 199b635f565Sjmmv fprintf(stderr, "usage: %s device command [arg [...]]\n", 2008a986b2eScgd getprogname()); 201958ed46fShubertf 20289fa8aefSmjl fprintf(stderr, " Commands pertaining to scsi devices:\n"); 203958ed46fShubertf for (i = 0; device_commands[i].cmd_name != NULL; i++) 204958ed46fShubertf fprintf(stderr, "\t%s %s\n", device_commands[i].cmd_name, 205958ed46fShubertf device_commands[i].arg_names); 20689fa8aefSmjl fprintf(stderr, " Commands pertaining to scsi busses:\n"); 207958ed46fShubertf for (i = 0; bus_commands[i].cmd_name != NULL; i++) 208958ed46fShubertf fprintf(stderr, "\t%s %s\n", bus_commands[i].cmd_name, 209958ed46fShubertf bus_commands[i].arg_names); 2102829eb17Sad fprintf(stderr, " Use `any' or `all' to wildcard target or lun\n"); 211958ed46fShubertf 212c9a47c22Sthorpej exit(1); 213c9a47c22Sthorpej } 214c9a47c22Sthorpej 215c9a47c22Sthorpej /* 216c9a47c22Sthorpej * DEVICE COMMANDS 217c9a47c22Sthorpej */ 218c9a47c22Sthorpej 219c9a47c22Sthorpej /* 220661eb28dSginsbach * device_read_defect: 221661eb28dSginsbach * 222661eb28dSginsbach * Read primary and/or growth defect list in physical or block 223661eb28dSginsbach * format from a direct access device. 224661eb28dSginsbach * 225661eb28dSginsbach * XXX Does not handle very large defect lists. Needs SCSI3 12 226661eb28dSginsbach * byte READ DEFECT DATA command. 227661eb28dSginsbach */ 228661eb28dSginsbach 2299211d764Sjakllsch static void print_bf_dd(union scsi_defect_descriptor *); 2309211d764Sjakllsch static void print_bfif_dd(union scsi_defect_descriptor *); 2319211d764Sjakllsch static void print_psf_dd(union scsi_defect_descriptor *); 232661eb28dSginsbach 2339211d764Sjakllsch static void 2349bab889dSxtraeme device_defects(int argc, char *argv[]) 235661eb28dSginsbach { 236661eb28dSginsbach struct scsi_read_defect_data cmd; 237661eb28dSginsbach struct scsi_read_defect_data_data *data; 238661eb28dSginsbach size_t dlen; 239661eb28dSginsbach int i, dlfmt = -1; 240661eb28dSginsbach int defects; 241661eb28dSginsbach char msg[256]; 2429bab889dSxtraeme void (*pfunc)(union scsi_defect_descriptor *); 243661eb28dSginsbach #define RDD_P_G_MASK 0x18 244661eb28dSginsbach #define RDD_DLF_MASK 0x7 245661eb28dSginsbach 246661eb28dSginsbach dlen = USHRT_MAX; /* XXX - this may not be enough room 247661eb28dSginsbach * for all of the defects. 248661eb28dSginsbach */ 249661eb28dSginsbach data = malloc(dlen); 250661eb28dSginsbach if (data == NULL) 251661eb28dSginsbach errx(1, "unable to allocate defect list"); 252661eb28dSginsbach memset(data, 0, dlen); 253661eb28dSginsbach memset(&cmd, 0, sizeof(cmd)); 254925bd672Slukem defects = 0; 255925bd672Slukem pfunc = NULL; 256661eb28dSginsbach 257661eb28dSginsbach /* determine which defect list(s) to read. */ 258661eb28dSginsbach for (i = 0; i < argc; i++) { 259661eb28dSginsbach if (strncmp("primary", argv[i], 7) == 0) { 260661eb28dSginsbach cmd.flags |= RDD_PRIMARY; 261661eb28dSginsbach continue; 262661eb28dSginsbach } 263661eb28dSginsbach if (strncmp("grown", argv[i], 5) == 0) { 264661eb28dSginsbach cmd.flags |= RDD_GROWN; 265661eb28dSginsbach continue; 266661eb28dSginsbach } 267661eb28dSginsbach break; 268661eb28dSginsbach } 269661eb28dSginsbach 270661eb28dSginsbach /* no defect list sepecified, assume both. */ 271661eb28dSginsbach if ((cmd.flags & (RDD_PRIMARY|RDD_GROWN)) == 0) 272661eb28dSginsbach cmd.flags |= (RDD_PRIMARY|RDD_GROWN); 273661eb28dSginsbach 274661eb28dSginsbach /* list format option. */ 275661eb28dSginsbach if (i < argc) { 276661eb28dSginsbach if (strncmp("block", argv[i], 5) == 0) { 277661eb28dSginsbach cmd.flags |= RDD_BF; 278661eb28dSginsbach dlfmt = RDD_BF; 279661eb28dSginsbach } 280661eb28dSginsbach else if (strncmp("byte", argv[i], 4) == 0) { 281661eb28dSginsbach cmd.flags |= RDD_BFIF; 282661eb28dSginsbach dlfmt = RDD_BFIF; 283661eb28dSginsbach } 284661eb28dSginsbach else if (strncmp("physical", argv[i], 4) == 0) { 285661eb28dSginsbach cmd.flags |= RDD_PSF; 286661eb28dSginsbach dlfmt = RDD_PSF; 287661eb28dSginsbach } 288661eb28dSginsbach else { 289661eb28dSginsbach usage(); 290661eb28dSginsbach } 291661eb28dSginsbach } 292661eb28dSginsbach 293661eb28dSginsbach /* 294661eb28dSginsbach * no list format specified; since block format not 295661eb28dSginsbach * recommended use physical sector format as default. 296661eb28dSginsbach */ 297661eb28dSginsbach if (dlfmt < 0) { 298661eb28dSginsbach cmd.flags |= RDD_PSF; 299661eb28dSginsbach dlfmt = RDD_PSF; 300661eb28dSginsbach } 301661eb28dSginsbach 302661eb28dSginsbach cmd.opcode = SCSI_READ_DEFECT_DATA; 303661eb28dSginsbach _lto2b(dlen, &cmd.length[0]); 304661eb28dSginsbach 305661eb28dSginsbach scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 306661eb28dSginsbach 307661eb28dSginsbach msg[0] = '\0'; 308661eb28dSginsbach 309661eb28dSginsbach /* is the defect list in the format asked for? */ 310661eb28dSginsbach if ((data->flags & RDD_DLF_MASK) != dlfmt) { 311661eb28dSginsbach strcpy(msg, "\n\tnotice:" 312661eb28dSginsbach "requested defect list format not supported by device\n\n"); 313661eb28dSginsbach dlfmt = (data->flags & RDD_DLF_MASK); 314661eb28dSginsbach } 315661eb28dSginsbach 316661eb28dSginsbach if (data->flags & RDD_PRIMARY) 317661eb28dSginsbach strcat(msg, "primary"); 318661eb28dSginsbach 319661eb28dSginsbach if (data->flags & RDD_GROWN) { 320661eb28dSginsbach if (data->flags & RDD_PRIMARY) 321661eb28dSginsbach strcat(msg, " and "); 322661eb28dSginsbach strcat(msg, "grown"); 323661eb28dSginsbach } 324661eb28dSginsbach 325661eb28dSginsbach strcat(msg, " defects"); 326661eb28dSginsbach 327661eb28dSginsbach if ((data->flags & RDD_P_G_MASK) == 0) 328661eb28dSginsbach strcat(msg, ": none reported\n"); 329661eb28dSginsbach 330661eb28dSginsbach printf("%s: scsibus%d target %d lun %d %s", 331661eb28dSginsbach dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 332661eb28dSginsbach dvaddr.addr.scsi.lun, msg); 333661eb28dSginsbach 334661eb28dSginsbach /* device did not return either defect list. */ 335661eb28dSginsbach if ((data->flags & RDD_P_G_MASK) == 0) 336661eb28dSginsbach return; 337661eb28dSginsbach 338661eb28dSginsbach switch (dlfmt) { 339661eb28dSginsbach case RDD_BF: 340661eb28dSginsbach defects = _2btol(data->length) / 341661eb28dSginsbach sizeof(struct scsi_defect_descriptor_bf); 342661eb28dSginsbach pfunc = print_bf_dd; 343661eb28dSginsbach strcpy(msg, "block address\n" 344661eb28dSginsbach "-------------\n"); 345661eb28dSginsbach break; 346661eb28dSginsbach case RDD_BFIF: 347661eb28dSginsbach defects = _2btol(data->length) / 348661eb28dSginsbach sizeof(struct scsi_defect_descriptor_bfif); 349661eb28dSginsbach pfunc = print_bfif_dd; 350661eb28dSginsbach strcpy(msg, " bytes from\n" 351661eb28dSginsbach "cylinder head index\n" 352661eb28dSginsbach "-------- ---- ----------\n"); 353661eb28dSginsbach break; 354661eb28dSginsbach case RDD_PSF: 355661eb28dSginsbach defects = _2btol(data->length) / 356661eb28dSginsbach sizeof(struct scsi_defect_descriptor_psf); 357661eb28dSginsbach pfunc = print_psf_dd; 358661eb28dSginsbach strcpy(msg, "cylinder head sector\n" 359661eb28dSginsbach "-------- ---- ----------\n"); 360661eb28dSginsbach break; 361661eb28dSginsbach } 362661eb28dSginsbach 363661eb28dSginsbach /* device did not return any defects. */ 364661eb28dSginsbach if (defects == 0) { 365661eb28dSginsbach printf(": none\n"); 366661eb28dSginsbach return; 367661eb28dSginsbach } 368661eb28dSginsbach 369661eb28dSginsbach printf(": %d\n", defects); 370661eb28dSginsbach 371661eb28dSginsbach /* print heading. */ 372661eb28dSginsbach printf("%s", msg); 373661eb28dSginsbach 374661eb28dSginsbach /* print defect list. */ 375661eb28dSginsbach for (i = 0 ; i < defects; i++) { 376661eb28dSginsbach pfunc(&data->defect_descriptor[i]); 377661eb28dSginsbach } 378661eb28dSginsbach 379661eb28dSginsbach free(data); 380661eb28dSginsbach return; 381661eb28dSginsbach } 382661eb28dSginsbach 383661eb28dSginsbach /* 384661eb28dSginsbach * print_bf_dd: 385661eb28dSginsbach * 386661eb28dSginsbach * Print a block format defect descriptor. 387661eb28dSginsbach */ 3889211d764Sjakllsch static void 3899bab889dSxtraeme print_bf_dd(union scsi_defect_descriptor *dd) 390661eb28dSginsbach { 391661eb28dSginsbach u_int32_t block; 392661eb28dSginsbach 393661eb28dSginsbach block = _4btol(dd->bf.block_address); 394661eb28dSginsbach 395661eb28dSginsbach printf("%13u\n", block); 396661eb28dSginsbach } 397661eb28dSginsbach 398661eb28dSginsbach #define DEFECTIVE_TRACK 0xffffffff 399661eb28dSginsbach 400661eb28dSginsbach /* 401661eb28dSginsbach * print_bfif_dd: 402661eb28dSginsbach * 403661eb28dSginsbach * Print a bytes from index format defect descriptor. 404661eb28dSginsbach */ 4059211d764Sjakllsch static void 4069bab889dSxtraeme print_bfif_dd(union scsi_defect_descriptor *dd) 407661eb28dSginsbach { 408661eb28dSginsbach u_int32_t cylinder; 409661eb28dSginsbach u_int32_t head; 410661eb28dSginsbach u_int32_t bytes_from_index; 411661eb28dSginsbach 412661eb28dSginsbach cylinder = _3btol(dd->bfif.cylinder); 413661eb28dSginsbach head = dd->bfif.head; 414661eb28dSginsbach bytes_from_index = _4btol(dd->bfif.bytes_from_index); 415661eb28dSginsbach 416661eb28dSginsbach printf("%8u %4u ", cylinder, head); 417661eb28dSginsbach 418661eb28dSginsbach if (bytes_from_index == DEFECTIVE_TRACK) 419661eb28dSginsbach printf("entire track defective\n"); 420661eb28dSginsbach else 421661eb28dSginsbach printf("%10u\n", bytes_from_index); 422661eb28dSginsbach } 423661eb28dSginsbach 424661eb28dSginsbach /* 425661eb28dSginsbach * print_psf_dd: 426661eb28dSginsbach * 427661eb28dSginsbach * Print a physical sector format defect descriptor. 428661eb28dSginsbach */ 4299211d764Sjakllsch static void 4309bab889dSxtraeme print_psf_dd(union scsi_defect_descriptor *dd) 431661eb28dSginsbach { 432661eb28dSginsbach u_int32_t cylinder; 433661eb28dSginsbach u_int32_t head; 434661eb28dSginsbach u_int32_t sector; 435661eb28dSginsbach 436661eb28dSginsbach cylinder = _3btol(dd->psf.cylinder); 437661eb28dSginsbach head = dd->psf.head; 438661eb28dSginsbach sector = _4btol(dd->psf.sector); 439661eb28dSginsbach 440661eb28dSginsbach printf("%8u %4u ", cylinder, head); 441661eb28dSginsbach 442661eb28dSginsbach if (sector == DEFECTIVE_TRACK) 443661eb28dSginsbach printf("entire track defective\n"); 444661eb28dSginsbach else 445661eb28dSginsbach printf("%10u\n", sector); 446661eb28dSginsbach } 447661eb28dSginsbach 448661eb28dSginsbach /* 449c1425134Sthorpej * device_format: 450c1425134Sthorpej * 451c1425134Sthorpej * Format a direct access device. 452c1425134Sthorpej */ 4539211d764Sjakllsch static void 4549bab889dSxtraeme device_format(int argc, char *argv[]) 455c1425134Sthorpej { 45641194c0eSmjacob u_int32_t blksize; 45741194c0eSmjacob int i, j, immediate; 45841194c0eSmjacob #define PC (65536/10) 45941194c0eSmjacob static int complete[] = { 46041194c0eSmjacob PC*1, PC*2, PC*3, PC*4, PC*5, PC*6, PC*7, PC*8, PC*9, 65536 46141194c0eSmjacob }; 46241194c0eSmjacob char *cp, buffer[64]; 463df9803ceSthorpej struct scsi_sense_data sense; 464c1425134Sthorpej struct scsi_format_unit cmd; 465c1425134Sthorpej struct { 46641194c0eSmjacob struct scsi_format_unit_defect_list_header header; 46741194c0eSmjacob /* optional initialization pattern */ 46841194c0eSmjacob /* optional defect list */ 46941194c0eSmjacob } dfl; 47041194c0eSmjacob struct { 471df9803ceSthorpej struct scsi_mode_parameter_header_6 header; 472df9803ceSthorpej struct scsi_general_block_descriptor blk_desc; 473c1425134Sthorpej struct page_disk_format format_page; 47441194c0eSmjacob } mode_page; 47541194c0eSmjacob struct { 476df9803ceSthorpej struct scsi_mode_parameter_header_6 header; 477df9803ceSthorpej struct scsi_general_block_descriptor blk_desc; 47841194c0eSmjacob } data_select; 479c1425134Sthorpej 48041194c0eSmjacob /* Blocksize is an optional argument. */ 48141194c0eSmjacob if (argc > 2) 482958ed46fShubertf usage(); 483c1425134Sthorpej 484c1425134Sthorpej /* 48541194c0eSmjacob * Loop doing Request Sense to clear any pending Unit Attention. 48641194c0eSmjacob * 48741194c0eSmjacob * Multiple conditions may exist on the drive which are returned 48841194c0eSmjacob * in priority order. 48941194c0eSmjacob */ 49041194c0eSmjacob for (i = 0; i < 8; i++) { 49141194c0eSmjacob scsi_request_sense(fd, &sense, sizeof (sense)); 492df9803ceSthorpej if ((j = SSD_SENSE_KEY(sense.flags)) == SKEY_NO_SENSE) 49341194c0eSmjacob break; 49441194c0eSmjacob } 49541194c0eSmjacob /* 49641194c0eSmjacob * Make sure we cleared any pending Unit Attention 49741194c0eSmjacob */ 49841194c0eSmjacob if (j != SKEY_NO_SENSE) { 49941194c0eSmjacob cp = scsi_decode_sense((const unsigned char *) &sense, 2, 50041194c0eSmjacob buffer, sizeof (buffer)); 5016742cb18Sgrant errx(1, "failed to clean Unit Attention: %s", cp); 50241194c0eSmjacob } 50341194c0eSmjacob 50441194c0eSmjacob /* 505c1425134Sthorpej * Get the DISK FORMAT mode page. SCSI-2 recommends specifying the 506c1425134Sthorpej * interleave read from this page in the FORMAT UNIT command. 507c1425134Sthorpej */ 50841194c0eSmjacob scsi_mode_sense(fd, 0x03, 0x00, &mode_page, sizeof(mode_page)); 50941194c0eSmjacob 51041194c0eSmjacob j = (mode_page.format_page.bytes_s[0] << 8) | 51141194c0eSmjacob (mode_page.format_page.bytes_s[1]); 51241194c0eSmjacob 51341194c0eSmjacob if (j != DEV_BSIZE) 51439a0f733Sjoerg printf("current disk sector size: %d\n", j); 515c1425134Sthorpej 516c1425134Sthorpej memset(&cmd, 0, sizeof(cmd)); 517c1425134Sthorpej 518c1425134Sthorpej cmd.opcode = SCSI_FORMAT_UNIT; 51941194c0eSmjacob memcpy(cmd.interleave, mode_page.format_page.interleave, 520c1425134Sthorpej sizeof(cmd.interleave)); 521c1425134Sthorpej 52241194c0eSmjacob /* 52341194c0eSmjacob * The blocksize on the device is only changed if the user 52441194c0eSmjacob * specified a new blocksize. If not specified the blocksize 52541194c0eSmjacob * used for the device will be the Default value in the device. 52641194c0eSmjacob * We don't specify the number of blocks since the format 52741194c0eSmjacob * command will always reformat the entire drive. Also by 52841194c0eSmjacob * not specifying a block count the drive will reset the 52941194c0eSmjacob * block count to the maximum available after the format 53041194c0eSmjacob * completes if the blocksize was changed in the format. 53141194c0eSmjacob * Finally, the new disk geometry will not but updated on 53241194c0eSmjacob * the drive in permanent storage until _AFTER_ the format 53341194c0eSmjacob * completes successfully. 53441194c0eSmjacob */ 53541194c0eSmjacob if (argc > 0) { 53641194c0eSmjacob blksize = strtoul(argv[0], &cp, 10); 53741194c0eSmjacob if (*cp != '\0') 5386742cb18Sgrant errx(1, "invalid block size: %s", argv[0]); 539c1425134Sthorpej 54041194c0eSmjacob memset(&data_select, 0, sizeof(data_select)); 54141194c0eSmjacob 542df9803ceSthorpej data_select.header.blk_desc_len = 543df9803ceSthorpej sizeof(struct scsi_general_block_descriptor); 54441194c0eSmjacob /* 54541194c0eSmjacob * blklen in desc is 3 bytes with a leading reserved byte 54641194c0eSmjacob */ 54741194c0eSmjacob _lto4b(blksize, &data_select.blk_desc.reserved); 54841194c0eSmjacob 54941194c0eSmjacob /* 55041194c0eSmjacob * Issue Mode Select to modify the device blocksize to be 55141194c0eSmjacob * used on the Format. The modified device geometry will 55241194c0eSmjacob * be stored as Current and Saved Page 3 parameters when 55341194c0eSmjacob * the Format completes. 55441194c0eSmjacob */ 55541194c0eSmjacob scsi_mode_select(fd, 0, &data_select, sizeof(data_select)); 55641194c0eSmjacob 55741194c0eSmjacob /* 55841194c0eSmjacob * Since user specified a specific block size make sure it 55941194c0eSmjacob * gets stored in the device when the format completes. 56041194c0eSmjacob * 56141194c0eSmjacob * Also scrub the defect list back to the manufacturers 56241194c0eSmjacob * original. 56341194c0eSmjacob */ 56441194c0eSmjacob cmd.flags = SFU_CMPLST | SFU_FMTDATA; 56541194c0eSmjacob } 56641194c0eSmjacob 56741194c0eSmjacob memset(&dfl, 0, sizeof(dfl)); 56841194c0eSmjacob 56941194c0eSmjacob if (argc > 1 && strncmp(argv[1], "imm", 3) == 0) { 57041194c0eSmjacob /* 57141194c0eSmjacob * Signal target for an immediate return from Format. 57241194c0eSmjacob * 57341194c0eSmjacob * We'll poll for completion status. 57441194c0eSmjacob */ 57541194c0eSmjacob dfl.header.flags = DLH_IMMED; 57641194c0eSmjacob immediate = 1; 57741194c0eSmjacob } else { 57841194c0eSmjacob immediate = 0; 57941194c0eSmjacob } 58041194c0eSmjacob 58141194c0eSmjacob scsi_command(fd, &cmd, sizeof(cmd), &dfl, sizeof(dfl), 5827528c09bSjakllsch 8 * 60 * 60 * 1000, SCCMD_WRITE); 58341194c0eSmjacob 58441194c0eSmjacob /* 58541194c0eSmjacob * Poll device for completion of Format 58641194c0eSmjacob */ 58741194c0eSmjacob if (immediate) { 58841194c0eSmjacob i = 0; 58941194c0eSmjacob printf("formatting."); 59041194c0eSmjacob fflush(stdout); 59141194c0eSmjacob do { 59241194c0eSmjacob scsireq_t req; 593df9803ceSthorpej struct scsi_test_unit_ready tcmd; 59441194c0eSmjacob 5958c34b2a3Sjakllsch memset(&tcmd, 0, sizeof(tcmd)); 596df9803ceSthorpej tcmd.opcode = SCSI_TEST_UNIT_READY; 59741194c0eSmjacob 59841194c0eSmjacob memset(&req, 0, sizeof(req)); 59941194c0eSmjacob memcpy(req.cmd, &tcmd, 6); 60041194c0eSmjacob req.cmdlen = 6; 60141194c0eSmjacob req.timeout = 10000; 60241194c0eSmjacob req.senselen = SENSEBUFLEN; 60341194c0eSmjacob 60441194c0eSmjacob if (ioctl(fd, SCIOCCOMMAND, &req) == -1) { 60541194c0eSmjacob err(1, "SCIOCCOMMAND"); 60641194c0eSmjacob } 60741194c0eSmjacob 60841194c0eSmjacob if (req.retsts == SCCMD_OK) { 60941194c0eSmjacob break; 61041194c0eSmjacob } else if (req.retsts == SCCMD_TIMEOUT) { 61141194c0eSmjacob fprintf(stderr, "%s: SCSI command timed out", 61241194c0eSmjacob dvname); 61341194c0eSmjacob break; 61441194c0eSmjacob } else if (req.retsts == SCCMD_BUSY) { 61541194c0eSmjacob fprintf(stderr, "%s: device is busy", 61641194c0eSmjacob dvname); 61741194c0eSmjacob break; 61841194c0eSmjacob } else if (req.retsts != SCCMD_SENSE) { 61941194c0eSmjacob fprintf(stderr, 62041194c0eSmjacob "%s: device had unknown status %x", dvname, 62141194c0eSmjacob req.retsts); 62241194c0eSmjacob break; 62341194c0eSmjacob } 62418d6bea5Schristos memcpy(&sense, req.sense, sizeof(sense)); 625df9803ceSthorpej if (sense.sks.sks_bytes[0] & SSD_SKSV) { 626df9803ceSthorpej j = (sense.sks.sks_bytes[1] << 8) | 627df9803ceSthorpej (sense.sks.sks_bytes[2]); 62841194c0eSmjacob if (j >= complete[i]) { 62941194c0eSmjacob printf(".%d0%%.", ++i); 63041194c0eSmjacob fflush(stdout); 63141194c0eSmjacob } 63241194c0eSmjacob } 63341194c0eSmjacob sleep(10); 634df9803ceSthorpej } while (SSD_SENSE_KEY(sense.flags) == SKEY_NOT_READY); 63541194c0eSmjacob printf(".100%%..done.\n"); 63641194c0eSmjacob } 637c1425134Sthorpej return; 638c1425134Sthorpej } 639c1425134Sthorpej 640f17194a0Smlelstv static void 641f17194a0Smlelstv print_designator(const char *pre, struct scsipi_inquiry_evpd_device_id *did) 642f17194a0Smlelstv { 643f17194a0Smlelstv char buf[252 * 4 + 1]; 644f17194a0Smlelstv unsigned assoc, proto, code, type; 645f17194a0Smlelstv static const char *typestr[] = { 646f17194a0Smlelstv "vendor", 647f17194a0Smlelstv "t10", 648f17194a0Smlelstv "eui64", 649f17194a0Smlelstv "naa", 650f17194a0Smlelstv "target port", 651f17194a0Smlelstv "port group", 652f17194a0Smlelstv "lun group", 653f17194a0Smlelstv "md5", 654f17194a0Smlelstv "scsi", 655f17194a0Smlelstv "res9", 656f17194a0Smlelstv "res10", 657f17194a0Smlelstv "res11", 658f17194a0Smlelstv "res12", 659f17194a0Smlelstv "res13", 660f17194a0Smlelstv "res14", 661f17194a0Smlelstv "res15" 662f17194a0Smlelstv }; 663f17194a0Smlelstv static const char *assocstr[] = { 664f17194a0Smlelstv "lun", 665f17194a0Smlelstv "port", 666f17194a0Smlelstv "target", 667f17194a0Smlelstv "reserved" 668f17194a0Smlelstv }; 669f17194a0Smlelstv static const char *protostr[] = { 670f17194a0Smlelstv "fibre channel", 671f17194a0Smlelstv "obsolete", 672f17194a0Smlelstv "ssa", 673f17194a0Smlelstv "ieee1394", 674f17194a0Smlelstv "rdma", 675f17194a0Smlelstv "iSCSI", 676f17194a0Smlelstv "SAS" 677f17194a0Smlelstv }; 678f17194a0Smlelstv const unsigned maxproto = __arraycount(protostr) - 1; 679f17194a0Smlelstv const unsigned isbinary = 680f17194a0Smlelstv __SHIFTOUT(SINQ_DEVICE_ID_CODESET_BINARY, SINQ_DEVICE_ID_CODESET); 681f17194a0Smlelstv unsigned k; 682f17194a0Smlelstv 683f17194a0Smlelstv assoc = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_ASSOCIATION); 684f17194a0Smlelstv proto = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_PROTOCOL); 685f17194a0Smlelstv code = __SHIFTOUT(did->pc, SINQ_DEVICE_ID_CODESET); 686f17194a0Smlelstv type = __SHIFTOUT(did->flags, SINQ_DEVICE_ID_TYPE); 687f17194a0Smlelstv 688f17194a0Smlelstv printf("%s%s", pre, assocstr[assoc]); 689f17194a0Smlelstv if (did->flags & SINQ_DEVICE_ID_PIV) { 690f17194a0Smlelstv if (proto > maxproto) 691f17194a0Smlelstv printf(" proto%u", proto); 692f17194a0Smlelstv else 693f17194a0Smlelstv printf(" %s", protostr[proto]); 694f17194a0Smlelstv } 695f17194a0Smlelstv printf(" %s: ", typestr[type]); 696f17194a0Smlelstv 697f17194a0Smlelstv if (code == isbinary) { 698*7eee88edSriastradh for (k = 0; k < did->designator_length; k++) { 699f17194a0Smlelstv printf("%02x", did->designator[k]); 700f17194a0Smlelstv } 701f17194a0Smlelstv printf("\n"); 702f17194a0Smlelstv } else { 703f17194a0Smlelstv scsi_strvis(buf, sizeof(buf), (char *)did->designator, 704f17194a0Smlelstv did->designator_length); 705f17194a0Smlelstv printf("%s\n", buf); 706f17194a0Smlelstv } 707f17194a0Smlelstv } 708f17194a0Smlelstv 709c1425134Sthorpej /* 710c9a47c22Sthorpej * device_identify: 711c9a47c22Sthorpej * 712f0a7346dSsnj * Display the identity of the device, including its SCSI bus, 713f0a7346dSsnj * target, lun, and its vendor/product/revision information. 714f17194a0Smlelstv * Optionally query and display vpd identification data. 715c9a47c22Sthorpej */ 7169211d764Sjakllsch static void 7179bab889dSxtraeme device_identify(int argc, char *argv[]) 718c9a47c22Sthorpej { 719c9a47c22Sthorpej struct scsipi_inquiry_data inqbuf; 720f17194a0Smlelstv struct { 721f17194a0Smlelstv struct scsipi_inquiry_evpd_header h; 722f17194a0Smlelstv uint8_t d[255 - sizeof(struct scsipi_inquiry_evpd_header)]; 723f17194a0Smlelstv } evpdbuf; 724c9a47c22Sthorpej struct scsipi_inquiry cmd; 725f17194a0Smlelstv unsigned len, rlen; 726f17194a0Smlelstv struct scsipi_inquiry_evpd_serial *ser; 727f17194a0Smlelstv struct scsipi_inquiry_evpd_device_id *did; 728f17194a0Smlelstv int has_serial; 729f17194a0Smlelstv int has_device_id; 730f17194a0Smlelstv bool getvpd = false; 731f17194a0Smlelstv int i; 732c9a47c22Sthorpej 733c9a47c22Sthorpej /* x4 in case every character is escaped, +1 for NUL. */ 734c9a47c22Sthorpej char vendor[(sizeof(inqbuf.vendor) * 4) + 1], 735c9a47c22Sthorpej product[(sizeof(inqbuf.product) * 4) + 1], 736f17194a0Smlelstv revision[(sizeof(inqbuf.revision) * 4) + 1], 737f17194a0Smlelstv ident[252 * 4 + 1]; 738c9a47c22Sthorpej 739f17194a0Smlelstv /* Check optional arguments */ 740f17194a0Smlelstv for (i = 0; i < argc; i++) { 741f17194a0Smlelstv if (strncmp("vpd", argv[i], 3) == 0) { 742f17194a0Smlelstv getvpd = true; 743f17194a0Smlelstv continue; 744f17194a0Smlelstv } 745958ed46fShubertf usage(); 746f17194a0Smlelstv } 747c9a47c22Sthorpej 748c9a47c22Sthorpej memset(&cmd, 0, sizeof(cmd)); 749c9a47c22Sthorpej memset(&inqbuf, 0, sizeof(inqbuf)); 750c9a47c22Sthorpej 751c9a47c22Sthorpej cmd.opcode = INQUIRY; 752c9a47c22Sthorpej cmd.length = sizeof(inqbuf); 753c9a47c22Sthorpej 754c9a47c22Sthorpej scsi_command(fd, &cmd, sizeof(cmd), &inqbuf, sizeof(inqbuf), 755c9a47c22Sthorpej 10000, SCCMD_READ); 756c9a47c22Sthorpej 757c9a47c22Sthorpej scsi_strvis(vendor, sizeof(vendor), inqbuf.vendor, 758c9a47c22Sthorpej sizeof(inqbuf.vendor)); 759c9a47c22Sthorpej scsi_strvis(product, sizeof(product), inqbuf.product, 760c9a47c22Sthorpej sizeof(inqbuf.product)); 761c9a47c22Sthorpej scsi_strvis(revision, sizeof(revision), inqbuf.revision, 762c9a47c22Sthorpej sizeof(inqbuf.revision)); 763c9a47c22Sthorpej 764c9a47c22Sthorpej printf("%s: scsibus%d target %d lun %d <%s, %s, %s>\n", 765c9a47c22Sthorpej dvname, dvaddr.addr.scsi.scbus, dvaddr.addr.scsi.target, 766c9a47c22Sthorpej dvaddr.addr.scsi.lun, vendor, product, revision); 767c9a47c22Sthorpej 768f17194a0Smlelstv if (!getvpd) 769f17194a0Smlelstv return; 770f17194a0Smlelstv 771f17194a0Smlelstv cmd.byte2 |= SINQ_EVPD; 772f17194a0Smlelstv cmd.pagecode = SINQ_VPD_PAGES; 773f17194a0Smlelstv 774f17194a0Smlelstv scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 775f17194a0Smlelstv 10000, SCCMD_READ); 776f17194a0Smlelstv 777f17194a0Smlelstv len = be16dec(evpdbuf.h.length); 778f17194a0Smlelstv if (len > sizeof(evpdbuf.d)) 779f17194a0Smlelstv len = 0; 780f17194a0Smlelstv 781f17194a0Smlelstv has_serial = memchr(evpdbuf.d, SINQ_VPD_SERIAL, len) != NULL; 782f17194a0Smlelstv has_device_id = memchr(evpdbuf.d, SINQ_VPD_DEVICE_ID, len) != NULL; 783f17194a0Smlelstv 784f17194a0Smlelstv if (has_serial) { 785f17194a0Smlelstv cmd.byte2 |= SINQ_EVPD; 786f17194a0Smlelstv cmd.pagecode = SINQ_VPD_SERIAL; 787f17194a0Smlelstv 788f17194a0Smlelstv scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 789f17194a0Smlelstv 10000, SCCMD_READ); 790f17194a0Smlelstv 791f17194a0Smlelstv len = be16dec(evpdbuf.h.length); 792f17194a0Smlelstv if (len > sizeof(evpdbuf.d)) 793f17194a0Smlelstv len = 0; 794f17194a0Smlelstv 795f17194a0Smlelstv ser = (struct scsipi_inquiry_evpd_serial *)&evpdbuf.d; 796*7eee88edSriastradh scsi_strvis(ident, sizeof(ident), (char *)ser->serial_number, 797*7eee88edSriastradh len); 798f17194a0Smlelstv printf("VPD Serial:\n"); 799f17194a0Smlelstv printf("\t%s\n", ident); 800f17194a0Smlelstv } 801f17194a0Smlelstv 802f17194a0Smlelstv if (has_device_id) { 803f17194a0Smlelstv cmd.byte2 |= SINQ_EVPD; 804f17194a0Smlelstv cmd.pagecode = SINQ_VPD_DEVICE_ID; 805f17194a0Smlelstv 806f17194a0Smlelstv scsi_command(fd, &cmd, sizeof(cmd), &evpdbuf, sizeof(evpdbuf), 807f17194a0Smlelstv 10000, SCCMD_READ); 808f17194a0Smlelstv 809f17194a0Smlelstv len = be16dec(evpdbuf.h.length); 810f17194a0Smlelstv if (len > sizeof(evpdbuf.d)) 811f17194a0Smlelstv len = 0; 812f17194a0Smlelstv 813f17194a0Smlelstv printf("VPD Device IDs:\n"); 814f17194a0Smlelstv 815f17194a0Smlelstv for (unsigned off = 0; off < len - sizeof(*did); off += rlen) { 816*7eee88edSriastradh void *p = &evpdbuf.d[off]; 817*7eee88edSriastradh did = (struct scsipi_inquiry_evpd_device_id *)p; 818f17194a0Smlelstv rlen = sizeof(*did) + did->designator_length - 1; 819f17194a0Smlelstv if (off + rlen > len) 820f17194a0Smlelstv break; 821f17194a0Smlelstv 822f17194a0Smlelstv print_designator("\t", did); 823f17194a0Smlelstv } 824f17194a0Smlelstv } 825f17194a0Smlelstv 826c9a47c22Sthorpej return; 827c9a47c22Sthorpej } 828c9a47c22Sthorpej 829c9a47c22Sthorpej /* 830c9a47c22Sthorpej * device_reassign: 831c9a47c22Sthorpej * 832c9a47c22Sthorpej * Reassign bad blocks on a direct access device. 833c9a47c22Sthorpej */ 8349211d764Sjakllsch static void 8359bab889dSxtraeme device_reassign(int argc, char *argv[]) 836c9a47c22Sthorpej { 837c9a47c22Sthorpej struct scsi_reassign_blocks cmd; 838c9a47c22Sthorpej struct scsi_reassign_blocks_data *data; 839c9a47c22Sthorpej size_t dlen; 840c9a47c22Sthorpej u_int32_t blkno; 841c9a47c22Sthorpej int i; 842c9a47c22Sthorpej char *cp; 843c9a47c22Sthorpej 844c9a47c22Sthorpej /* We get a list of block numbers. */ 845c9a47c22Sthorpej if (argc < 1) 846958ed46fShubertf usage(); 847c9a47c22Sthorpej 848c9a47c22Sthorpej /* 849c9a47c22Sthorpej * Allocate the reassign blocks descriptor. The 4 comes from the 850c9a47c22Sthorpej * size of the block address in the defect descriptor. 851c9a47c22Sthorpej */ 852c9a47c22Sthorpej dlen = sizeof(struct scsi_reassign_blocks_data) + ((argc - 1) * 4); 853c9a47c22Sthorpej data = malloc(dlen); 854c9a47c22Sthorpej if (data == NULL) 855c9a47c22Sthorpej errx(1, "unable to allocate defect descriptor"); 856c9a47c22Sthorpej memset(data, 0, dlen); 857c9a47c22Sthorpej 858c9a47c22Sthorpej cmd.opcode = SCSI_REASSIGN_BLOCKS; 859f6624fbbSmycroft cmd.byte2 = 0; 860f6624fbbSmycroft cmd.unused[0] = 0; 861f6624fbbSmycroft cmd.unused[1] = 0; 862f6624fbbSmycroft cmd.unused[2] = 0; 863f6624fbbSmycroft cmd.control = 0; 864c9a47c22Sthorpej 865c9a47c22Sthorpej /* Defect descriptor length. */ 866f6624fbbSmycroft _lto2b(argc * 4, data->length); 867c9a47c22Sthorpej 868c9a47c22Sthorpej /* Build the defect descriptor list. */ 869c9a47c22Sthorpej for (i = 0; i < argc; i++) { 870c9a47c22Sthorpej blkno = strtoul(argv[i], &cp, 10); 871c9a47c22Sthorpej if (*cp != '\0') 87293e37412Sad errx(1, "invalid block number: %s", argv[i]); 873f6624fbbSmycroft _lto4b(blkno, data->defect_descriptor[i].dlbaddr); 874c9a47c22Sthorpej } 875c9a47c22Sthorpej 876c9a47c22Sthorpej scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_WRITE); 877c9a47c22Sthorpej 878c9a47c22Sthorpej free(data); 879c9a47c22Sthorpej return; 880c9a47c22Sthorpej } 881c9a47c22Sthorpej 882c9a47c22Sthorpej /* 88341194c0eSmjacob * device_release: 88441194c0eSmjacob * 885e47fe82aSbouyer * Issue a RELEASE command to a SCSI device. 88641194c0eSmjacob */ 88741194c0eSmjacob #ifndef SCSI_RELEASE 88841194c0eSmjacob #define SCSI_RELEASE 0x17 88941194c0eSmjacob #endif 8909211d764Sjakllsch static void 8919bab889dSxtraeme device_release(int argc, char *argv[]) 89241194c0eSmjacob { 893df9803ceSthorpej struct scsi_test_unit_ready cmd; /* close enough */ 89441194c0eSmjacob 89541194c0eSmjacob /* No arguments. */ 89641194c0eSmjacob if (argc != 0) 89741194c0eSmjacob usage(); 89841194c0eSmjacob 89941194c0eSmjacob memset(&cmd, 0, sizeof(cmd)); 90041194c0eSmjacob 90141194c0eSmjacob cmd.opcode = SCSI_RELEASE; 90241194c0eSmjacob 90341194c0eSmjacob scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 90441194c0eSmjacob 90541194c0eSmjacob return; 90641194c0eSmjacob } 90741194c0eSmjacob 90841194c0eSmjacob /* 90941194c0eSmjacob * device_reserve: 91041194c0eSmjacob * 911e47fe82aSbouyer * Issue a RESERVE command to a SCSI device. 91241194c0eSmjacob */ 91341194c0eSmjacob #ifndef SCSI_RESERVE 91441194c0eSmjacob #define SCSI_RESERVE 0x16 91541194c0eSmjacob #endif 9169211d764Sjakllsch static void 9179bab889dSxtraeme device_reserve(int argc, char *argv[]) 91841194c0eSmjacob { 919df9803ceSthorpej struct scsi_test_unit_ready cmd; /* close enough */ 92041194c0eSmjacob 92141194c0eSmjacob /* No arguments. */ 92241194c0eSmjacob if (argc != 0) 92341194c0eSmjacob usage(); 92441194c0eSmjacob 92541194c0eSmjacob memset(&cmd, 0, sizeof(cmd)); 92641194c0eSmjacob 92741194c0eSmjacob cmd.opcode = SCSI_RESERVE; 92841194c0eSmjacob 92941194c0eSmjacob scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 93041194c0eSmjacob 93141194c0eSmjacob return; 93241194c0eSmjacob } 93341194c0eSmjacob 93441194c0eSmjacob /* 935c9a47c22Sthorpej * device_reset: 936c9a47c22Sthorpej * 937c9a47c22Sthorpej * Issue a reset to a SCSI device. 938c9a47c22Sthorpej */ 9399211d764Sjakllsch static void 9409bab889dSxtraeme device_reset(int argc, char *argv[]) 941c9a47c22Sthorpej { 942c9a47c22Sthorpej 943c9a47c22Sthorpej /* No arguments. */ 944c9a47c22Sthorpej if (argc != 0) 945958ed46fShubertf usage(); 946c9a47c22Sthorpej 947c9a47c22Sthorpej if (ioctl(fd, SCIOCRESET, NULL) != 0) 948c9a47c22Sthorpej err(1, "SCIOCRESET"); 949c9a47c22Sthorpej 950c9a47c22Sthorpej return; 951c9a47c22Sthorpej } 952c9a47c22Sthorpej 953c9a47c22Sthorpej /* 9541b08fb13Spetrov * device_debug: 9551b08fb13Spetrov * 9561b08fb13Spetrov * Set debug level to a SCSI device. 9571b08fb13Spetrov * scsipi will print anything iff SCSIPI_DEBUG set in config. 9581b08fb13Spetrov */ 9599211d764Sjakllsch static void 9609bab889dSxtraeme device_debug(int argc, char *argv[]) 9611b08fb13Spetrov { 9621b08fb13Spetrov int lvl; 9631b08fb13Spetrov 9641b08fb13Spetrov if (argc < 1) 9651b08fb13Spetrov usage(); 9661b08fb13Spetrov 9671b08fb13Spetrov lvl = atoi(argv[0]); 9681b08fb13Spetrov 9691b08fb13Spetrov if (ioctl(fd, SCIOCDEBUG, &lvl) != 0) 9701b08fb13Spetrov err(1, "SCIOCDEBUG"); 9711b08fb13Spetrov 9721b08fb13Spetrov return; 9731b08fb13Spetrov } 9741b08fb13Spetrov 9751b08fb13Spetrov /* 9760683880eSthorpej * device_getcache: 977c9a47c22Sthorpej * 9780683880eSthorpej * Get the caching parameters for a SCSI disk. 979c9a47c22Sthorpej */ 9809211d764Sjakllsch static void 9819bab889dSxtraeme device_getcache(int argc, char *argv[]) 982c9a47c22Sthorpej { 9830683880eSthorpej struct { 984df9803ceSthorpej struct scsi_mode_parameter_header_6 header; 985df9803ceSthorpej struct scsi_general_block_descriptor blk_desc; 9860683880eSthorpej struct page_caching caching_params; 9870683880eSthorpej } data; 988c9a47c22Sthorpej 989c9a47c22Sthorpej /* No arguments. */ 990c9a47c22Sthorpej if (argc != 0) 991958ed46fShubertf usage(); 992c9a47c22Sthorpej 9930683880eSthorpej scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 994c9a47c22Sthorpej 9950683880eSthorpej if ((data.caching_params.flags & (CACHING_RCD|CACHING_WCE)) == 9960683880eSthorpej CACHING_RCD) 9970683880eSthorpej printf("%s: no caches enabled\n", dvname); 9980683880eSthorpej else { 9990683880eSthorpej printf("%s: read cache %senabled\n", dvname, 10000683880eSthorpej (data.caching_params.flags & CACHING_RCD) ? "not " : ""); 10010683880eSthorpej printf("%s: write-back cache %senabled\n", dvname, 10020683880eSthorpej (data.caching_params.flags & CACHING_WCE) ? "" : "not "); 10030683880eSthorpej } 10040683880eSthorpej printf("%s: caching parameters are %ssavable\n", dvname, 10050683880eSthorpej (data.caching_params.pg_code & PGCODE_PS) ? "" : "not "); 10060683880eSthorpej } 10070683880eSthorpej 10080683880eSthorpej /* 10090683880eSthorpej * device_setcache: 10100683880eSthorpej * 10110683880eSthorpej * Set cache enables for a SCSI disk. 10120683880eSthorpej */ 10139211d764Sjakllsch static void 10149bab889dSxtraeme device_setcache(int argc, char *argv[]) 10150683880eSthorpej { 10160683880eSthorpej struct { 1017df9803ceSthorpej struct scsi_mode_parameter_header_6 header; 1018df9803ceSthorpej struct scsi_general_block_descriptor blk_desc; 10190683880eSthorpej struct page_caching caching_params; 10200683880eSthorpej } data; 10210683880eSthorpej int dlen; 10220683880eSthorpej u_int8_t flags, byte2; 10230683880eSthorpej 10240683880eSthorpej if (argc > 2 || argc == 0) 10250683880eSthorpej usage(); 10260683880eSthorpej 1027925bd672Slukem flags = 0; 1028925bd672Slukem byte2 = 0; 10290683880eSthorpej if (strcmp(argv[0], "none") == 0) 10300683880eSthorpej flags = CACHING_RCD; 10310683880eSthorpej else if (strcmp(argv[0], "r") == 0) 10320683880eSthorpej flags = 0; 10330683880eSthorpej else if (strcmp(argv[0], "w") == 0) 10340683880eSthorpej flags = CACHING_RCD|CACHING_WCE; 10350683880eSthorpej else if (strcmp(argv[0], "rw") == 0) 10360683880eSthorpej flags = CACHING_WCE; 10370683880eSthorpej else 10380683880eSthorpej usage(); 10390683880eSthorpej 10400683880eSthorpej if (argc == 2) { 10410683880eSthorpej if (strcmp(argv[1], "save") == 0) 10420683880eSthorpej byte2 = SMS_SP; 10430683880eSthorpej else 10440683880eSthorpej usage(); 10450683880eSthorpej } 10460683880eSthorpej 10470683880eSthorpej scsi_mode_sense(fd, 0x08, 0x00, &data, sizeof(data)); 10480683880eSthorpej 10490683880eSthorpej data.caching_params.pg_code &= PGCODE_MASK; 10500683880eSthorpej data.caching_params.flags = 10510683880eSthorpej (data.caching_params.flags & ~(CACHING_RCD|CACHING_WCE)) | flags; 10520683880eSthorpej 10530683880eSthorpej data.caching_params.cache_segment_size[0] = 0; 10540683880eSthorpej data.caching_params.cache_segment_size[1] = 0; 10550683880eSthorpej 10560683880eSthorpej data.header.data_length = 0; 10570683880eSthorpej 10580683880eSthorpej dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 10590683880eSthorpej data.caching_params.pg_length; 10600683880eSthorpej 10610683880eSthorpej scsi_mode_select(fd, byte2, &data, dlen); 1062c9a47c22Sthorpej } 1063c9a47c22Sthorpej 1064c9a47c22Sthorpej /* 10654950c2c9Smycroft * device_flushcache: 10664950c2c9Smycroft * 1067e47fe82aSbouyer * Issue a FLUSH CACHE command to a SCSI device. 10684950c2c9Smycroft */ 10694950c2c9Smycroft #ifndef SCSI_FLUSHCACHE 10704950c2c9Smycroft #define SCSI_FLUSHCACHE 0x35 10714950c2c9Smycroft #endif 10729211d764Sjakllsch static void 10739bab889dSxtraeme device_flushcache(int argc, char *argv[]) 10744950c2c9Smycroft { 1075df9803ceSthorpej struct scsi_test_unit_ready cmd; /* close enough */ 10764950c2c9Smycroft 10774950c2c9Smycroft /* No arguments. */ 10784950c2c9Smycroft if (argc != 0) 10794950c2c9Smycroft usage(); 10804950c2c9Smycroft 10814950c2c9Smycroft memset(&cmd, 0, sizeof(cmd)); 10824950c2c9Smycroft 10834950c2c9Smycroft cmd.opcode = SCSI_FLUSHCACHE; 10844950c2c9Smycroft 10854950c2c9Smycroft scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 10864950c2c9Smycroft 10874950c2c9Smycroft return; 10884950c2c9Smycroft } 10894950c2c9Smycroft 10904950c2c9Smycroft /* 1091e47fe82aSbouyer * device_setspeed: 1092e47fe82aSbouyer * 1093e47fe82aSbouyer * Set rotation speed to a CD/DVD drive. 1094e47fe82aSbouyer */ 10959211d764Sjakllsch static void 1096e47fe82aSbouyer device_setspeed(int argc, char *argv[]) 1097e47fe82aSbouyer { 1098e47fe82aSbouyer u_char cmd[11]; 1099e47fe82aSbouyer u_char pd[28]; 1100e47fe82aSbouyer u_int32_t speed; 1101e47fe82aSbouyer 1102e47fe82aSbouyer if (argc != 1) 1103e47fe82aSbouyer usage(); 1104e47fe82aSbouyer 1105e47fe82aSbouyer speed = atoi(argv[0]) * 177; 1106e47fe82aSbouyer 1107e47fe82aSbouyer memset(&pd, 0, sizeof(pd)); 1108e47fe82aSbouyer if (speed == 0) 1109e47fe82aSbouyer pd[0] = 4; /* restore drive defaults */ 1110e47fe82aSbouyer pd[8] = 0xff; 1111e47fe82aSbouyer pd[9] = 0xff; 1112e47fe82aSbouyer pd[10] = 0xff; 1113e47fe82aSbouyer pd[11] = 0xff; 1114e47fe82aSbouyer pd[12] = pd[20] = (speed >> 24) & 0xff; 1115e47fe82aSbouyer pd[13] = pd[21] = (speed >> 16) & 0xff; 1116e47fe82aSbouyer pd[14] = pd[22] = (speed >> 8) & 0xff; 1117e47fe82aSbouyer pd[15] = pd[23] = speed & 0xff; 1118e47fe82aSbouyer pd[18] = pd[26] = 1000 >> 8; 1119e47fe82aSbouyer pd[19] = pd[27] = 1000 & 0xff; 1120e47fe82aSbouyer 1121e47fe82aSbouyer memset(&cmd, 0, sizeof(cmd)); 1122e47fe82aSbouyer cmd[0] = 0xb6; 1123e47fe82aSbouyer cmd[10] = sizeof(pd); 1124e47fe82aSbouyer 1125e47fe82aSbouyer scsi_command(fd, &cmd, sizeof(cmd), pd, sizeof(pd), 10000, SCCMD_WRITE); 1126e47fe82aSbouyer 1127e47fe82aSbouyer return; 1128e47fe82aSbouyer } 1129e47fe82aSbouyer 1130e47fe82aSbouyer /* 1131100b2229Smlelstv * device_reportluns: 1132100b2229Smlelstv * 1133100b2229Smlelstv * Report the known LUNs to which the initiator can send commands 1134100b2229Smlelstv */ 1135100b2229Smlelstv static void 1136100b2229Smlelstv device_reportluns(int argc, char *argv[]) 1137100b2229Smlelstv { 1138100b2229Smlelstv struct scsi_report_luns cmd; 1139100b2229Smlelstv struct { 1140100b2229Smlelstv struct scsi_report_luns_header header; 1141100b2229Smlelstv struct scsi_report_luns_lun desc[1]; 1142100b2229Smlelstv } *data; 1143100b2229Smlelstv u_int32_t dlen, len; 1144100b2229Smlelstv u_int64_t lun; 1145100b2229Smlelstv size_t count, idx; 1146100b2229Smlelstv unsigned long sel; 1147100b2229Smlelstv char *endp; 1148100b2229Smlelstv int i; 1149100b2229Smlelstv 1150100b2229Smlelstv dlen = USHRT_MAX; /* good for > 8000 LUNs */ 1151100b2229Smlelstv data = malloc(dlen); 1152100b2229Smlelstv if (data == NULL) 1153100b2229Smlelstv errx(1, "unable to allocate lun report"); 1154100b2229Smlelstv 1155100b2229Smlelstv memset(&cmd, 0, sizeof(cmd)); 1156100b2229Smlelstv cmd.opcode = SCSI_REPORT_LUNS; 1157100b2229Smlelstv cmd.selectreport = SELECTREPORT_NORMAL; 1158100b2229Smlelstv 1159100b2229Smlelstv /* determine which report to read. */ 1160100b2229Smlelstv for (i = 0; i < argc; i++) { 1161100b2229Smlelstv if (strcmp("normal", argv[i]) == 0) { 1162100b2229Smlelstv cmd.selectreport = SELECTREPORT_NORMAL; 1163100b2229Smlelstv continue; 1164100b2229Smlelstv } 1165100b2229Smlelstv if (strcmp("wellknown", argv[i]) == 0) { 1166100b2229Smlelstv cmd.selectreport = SELECTREPORT_WELLKNOWN; 1167100b2229Smlelstv continue; 1168100b2229Smlelstv } 1169100b2229Smlelstv if (strcmp("all", argv[i]) == 0) { 1170100b2229Smlelstv cmd.selectreport = SELECTREPORT_ALL; 1171100b2229Smlelstv continue; 1172100b2229Smlelstv } 1173100b2229Smlelstv sel = strtoul(argv[i], &endp, 0); 1174100b2229Smlelstv if (*endp != '\0' || sel > 255) 1175100b2229Smlelstv errx(1, "Unknown select report '%s'", argv[i]); 1176100b2229Smlelstv cmd.selectreport = sel; 1177100b2229Smlelstv } 1178100b2229Smlelstv 1179100b2229Smlelstv _lto4b(dlen, &cmd.alloclen[0]); 1180100b2229Smlelstv cmd.control = 0x00; 1181100b2229Smlelstv 1182100b2229Smlelstv scsi_command(fd, &cmd, sizeof(cmd), data, dlen, 30000, SCCMD_READ); 1183100b2229Smlelstv 1184100b2229Smlelstv len = _4btol(data->header.length); 1185100b2229Smlelstv if (len > dlen) { 1186100b2229Smlelstv /* XXX reallocate and retry */ 1187100b2229Smlelstv printf("%s: report truncated %" PRIu32 "to %" PRIu32 "\n", 1188100b2229Smlelstv dvname, len, dlen); 1189100b2229Smlelstv len = dlen; 1190100b2229Smlelstv } 1191100b2229Smlelstv 1192100b2229Smlelstv count = len / sizeof(data->desc[0]); 1193100b2229Smlelstv 1194*7eee88edSriastradh for (idx = 0; idx < count; idx++) { 1195100b2229Smlelstv lun = _8btol(data->desc[idx].lun); 1196100b2229Smlelstv 1197100b2229Smlelstv /* 1198100b2229Smlelstv * swizzle bits so that LUNs 0..255 are 1199100b2229Smlelstv * mapped to numbers 0..255 1200100b2229Smlelstv */ 1201100b2229Smlelstv lun = (lun & 0xffff000000000000ull) >> 48 1202100b2229Smlelstv | (lun & 0x0000ffff00000000ull) >> 16 1203100b2229Smlelstv | (lun & 0x00000000ffff0000ull) << 16 1204100b2229Smlelstv | (lun & 0x000000000000ffffull) << 48; 1205100b2229Smlelstv 1206100b2229Smlelstv printf("%s: lun %" PRIu64 "\n", dvname, lun); 1207100b2229Smlelstv } 1208100b2229Smlelstv 1209100b2229Smlelstv free(data); 1210100b2229Smlelstv } 1211100b2229Smlelstv 1212100b2229Smlelstv /* 1213b0be1b67Sflxd * device_getrealloc: 1214b0be1b67Sflxd * 1215b0be1b67Sflxd * Get the automatic reallocation parameters for a SCSI disk. 1216b0be1b67Sflxd */ 1217b0be1b67Sflxd static void 1218b0be1b67Sflxd device_getrealloc(int argc, char *argv[]) 1219b0be1b67Sflxd { 1220b0be1b67Sflxd struct { 1221b0be1b67Sflxd struct scsi_mode_parameter_header_6 header; 1222b0be1b67Sflxd struct scsi_general_block_descriptor blk_desc; 1223b0be1b67Sflxd struct page_err_recov err_recov_params; 1224b0be1b67Sflxd } data; 1225b0be1b67Sflxd u_int8_t flags; 1226b0be1b67Sflxd 1227b0be1b67Sflxd /* No arguments. */ 1228b0be1b67Sflxd if (argc != 0) 1229b0be1b67Sflxd usage(); 1230b0be1b67Sflxd 1231b0be1b67Sflxd scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1232b0be1b67Sflxd 1233b0be1b67Sflxd flags = data.err_recov_params.flags; 1234b0be1b67Sflxd if ((flags & (ERR_RECOV_ARRE | ERR_RECOV_AWRE)) == 0) 1235b0be1b67Sflxd printf("%s: no automatic reallocation enabled\n", dvname); 1236b0be1b67Sflxd else { 1237b0be1b67Sflxd printf("%s: automatic read reallocation %senabled\n", dvname, 1238b0be1b67Sflxd (flags & ERR_RECOV_ARRE) ? "" : "not "); 1239b0be1b67Sflxd printf("%s: automatic write reallocation %senabled\n", dvname, 1240b0be1b67Sflxd (flags & ERR_RECOV_AWRE) ? "" : "not "); 1241b0be1b67Sflxd } 1242b0be1b67Sflxd printf("%s: error recovery parameters are %ssavable\n", dvname, 1243b0be1b67Sflxd (data.err_recov_params.pg_code & PGCODE_PS) ? "" : "not "); 1244b0be1b67Sflxd } 1245b0be1b67Sflxd 1246b0be1b67Sflxd /* 1247b0be1b67Sflxd * device_setrealloc: 1248b0be1b67Sflxd * 1249b0be1b67Sflxd * Set the automatic reallocation parameters for a SCSI disk. 1250b0be1b67Sflxd */ 1251b0be1b67Sflxd static void 1252b0be1b67Sflxd device_setrealloc(int argc, char *argv[]) 1253b0be1b67Sflxd { 1254b0be1b67Sflxd struct { 1255b0be1b67Sflxd struct scsi_mode_parameter_header_6 header; 1256b0be1b67Sflxd struct scsi_general_block_descriptor blk_desc; 1257b0be1b67Sflxd struct page_err_recov err_recov_params; 1258b0be1b67Sflxd } data; 1259b0be1b67Sflxd int dlen; 1260b0be1b67Sflxd u_int8_t flags, byte2; 1261b0be1b67Sflxd 1262b0be1b67Sflxd if (argc > 2 || argc == 0) 1263b0be1b67Sflxd usage(); 1264b0be1b67Sflxd 1265b0be1b67Sflxd flags = 0; 1266b0be1b67Sflxd byte2 = 0; 1267b0be1b67Sflxd if (strcmp(argv[0], "none") == 0) 1268b0be1b67Sflxd flags = 0; 1269b0be1b67Sflxd else if (strcmp(argv[0], "r") == 0) 1270b0be1b67Sflxd flags = ERR_RECOV_ARRE; 1271b0be1b67Sflxd else if (strcmp(argv[0], "w") == 0) 1272b0be1b67Sflxd flags = ERR_RECOV_AWRE; 1273b0be1b67Sflxd else if (strcmp(argv[0], "rw") == 0) 1274b0be1b67Sflxd flags = ERR_RECOV_ARRE | ERR_RECOV_AWRE; 1275b0be1b67Sflxd else 1276b0be1b67Sflxd usage(); 1277b0be1b67Sflxd 1278b0be1b67Sflxd if (argc == 2) { 1279b0be1b67Sflxd if (strcmp(argv[1], "save") == 0) 1280b0be1b67Sflxd byte2 = SMS_SP; 1281b0be1b67Sflxd else 1282b0be1b67Sflxd usage(); 1283b0be1b67Sflxd } 1284b0be1b67Sflxd 1285b0be1b67Sflxd scsi_mode_sense(fd, 0x01, 0x00, &data, sizeof(data)); 1286b0be1b67Sflxd 1287b0be1b67Sflxd data.err_recov_params.pg_code &= PGCODE_MASK; 1288b0be1b67Sflxd data.err_recov_params.flags &= ~(ERR_RECOV_ARRE | ERR_RECOV_AWRE); 1289b0be1b67Sflxd data.err_recov_params.flags |= flags; 1290b0be1b67Sflxd 1291b0be1b67Sflxd data.header.data_length = 0; 1292b0be1b67Sflxd 1293b0be1b67Sflxd dlen = sizeof(data.header) + sizeof(data.blk_desc) + 2 + 1294b0be1b67Sflxd data.err_recov_params.pg_length; 1295b0be1b67Sflxd 1296b0be1b67Sflxd scsi_mode_select(fd, byte2, &data, dlen); 1297b0be1b67Sflxd } 1298b0be1b67Sflxd 1299b0be1b67Sflxd /* 130046743e21Smycroft * device_prevent: 130146743e21Smycroft * 130246743e21Smycroft * Issue a prevent to a SCSI device. 130346743e21Smycroft */ 13049211d764Sjakllsch static void 13059bab889dSxtraeme device_prevent(int argc, char *argv[]) 130646743e21Smycroft { 1307df9803ceSthorpej struct scsi_prevent_allow_medium_removal cmd; 130846743e21Smycroft 130946743e21Smycroft /* No arguments. */ 131046743e21Smycroft if (argc != 0) 131146743e21Smycroft usage(); 131246743e21Smycroft 131346743e21Smycroft memset(&cmd, 0, sizeof(cmd)); 131446743e21Smycroft 1315df9803ceSthorpej cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1316df9803ceSthorpej cmd.how = SPAMR_PREVENT_DT; /* XXX SMAMR_PREVENT_ALL? */ 131746743e21Smycroft 131846743e21Smycroft scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 131946743e21Smycroft 132046743e21Smycroft return; 132146743e21Smycroft } 132246743e21Smycroft 132346743e21Smycroft /* 132446743e21Smycroft * device_allow: 132546743e21Smycroft * 132646743e21Smycroft * Issue a stop to a SCSI device. 132746743e21Smycroft */ 13289211d764Sjakllsch static void 13299bab889dSxtraeme device_allow(int argc, char *argv[]) 133046743e21Smycroft { 1331df9803ceSthorpej struct scsi_prevent_allow_medium_removal cmd; 133246743e21Smycroft 133346743e21Smycroft /* No arguments. */ 133446743e21Smycroft if (argc != 0) 133546743e21Smycroft usage(); 133646743e21Smycroft 133746743e21Smycroft memset(&cmd, 0, sizeof(cmd)); 133846743e21Smycroft 1339df9803ceSthorpej cmd.opcode = SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL; 1340df9803ceSthorpej cmd.how = SPAMR_ALLOW; 134146743e21Smycroft 134246743e21Smycroft scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 134346743e21Smycroft 134446743e21Smycroft return; 134546743e21Smycroft } 134646743e21Smycroft 134746743e21Smycroft /* 134841194c0eSmjacob * device_start: 134941194c0eSmjacob * 135041194c0eSmjacob * Issue a start to a SCSI device. 135141194c0eSmjacob */ 13529211d764Sjakllsch static void 13539bab889dSxtraeme device_start(int argc, char *argv[]) 135441194c0eSmjacob { 135541194c0eSmjacob struct scsipi_start_stop cmd; 135641194c0eSmjacob 135741194c0eSmjacob /* No arguments. */ 135841194c0eSmjacob if (argc != 0) 135941194c0eSmjacob usage(); 136041194c0eSmjacob 136141194c0eSmjacob memset(&cmd, 0, sizeof(cmd)); 136241194c0eSmjacob 136341194c0eSmjacob cmd.opcode = START_STOP; 136441194c0eSmjacob cmd.how = SSS_START; 136541194c0eSmjacob 136676b92d1aSfair scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 136741194c0eSmjacob 136841194c0eSmjacob return; 136941194c0eSmjacob } 137041194c0eSmjacob 137141194c0eSmjacob /* 137241194c0eSmjacob * device_stop: 137341194c0eSmjacob * 137441194c0eSmjacob * Issue a stop to a SCSI device. 137541194c0eSmjacob */ 13769211d764Sjakllsch static void 13779bab889dSxtraeme device_stop(int argc, char *argv[]) 137841194c0eSmjacob { 137941194c0eSmjacob struct scsipi_start_stop cmd; 138041194c0eSmjacob 138141194c0eSmjacob /* No arguments. */ 138241194c0eSmjacob if (argc != 0) 138341194c0eSmjacob usage(); 138441194c0eSmjacob 138541194c0eSmjacob memset(&cmd, 0, sizeof(cmd)); 138641194c0eSmjacob 138741194c0eSmjacob cmd.opcode = START_STOP; 138841194c0eSmjacob cmd.how = SSS_STOP; 138941194c0eSmjacob 139076b92d1aSfair scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 30000, 0); 139141194c0eSmjacob 139241194c0eSmjacob return; 139341194c0eSmjacob } 139441194c0eSmjacob 139541194c0eSmjacob /* 139641194c0eSmjacob * device_tur: 139741194c0eSmjacob * 1398e47fe82aSbouyer * Issue a TEST UNIT READY to a SCSI device. 139941194c0eSmjacob */ 14009211d764Sjakllsch static void 14019bab889dSxtraeme device_tur(int argc, char *argv[]) 140241194c0eSmjacob { 1403df9803ceSthorpej struct scsi_test_unit_ready cmd; 140441194c0eSmjacob 140541194c0eSmjacob /* No arguments. */ 140641194c0eSmjacob if (argc != 0) 140741194c0eSmjacob usage(); 140841194c0eSmjacob 140941194c0eSmjacob memset(&cmd, 0, sizeof(cmd)); 141041194c0eSmjacob 1411df9803ceSthorpej cmd.opcode = SCSI_TEST_UNIT_READY; 141241194c0eSmjacob 141341194c0eSmjacob scsi_command(fd, &cmd, sizeof(cmd), NULL, 0, 10000, 0); 141441194c0eSmjacob 141541194c0eSmjacob return; 141641194c0eSmjacob } 141741194c0eSmjacob 14180683880eSthorpej /* 14190683880eSthorpej * BUS COMMANDS 14200683880eSthorpej */ 142141194c0eSmjacob 14220683880eSthorpej /* 14230683880eSthorpej * bus_reset: 14240683880eSthorpej * 14250683880eSthorpej * Issue a reset to a SCSI bus. 14260683880eSthorpej */ 14279211d764Sjakllsch static void 14289bab889dSxtraeme bus_reset(int argc, char *argv[]) 14290683880eSthorpej { 14300683880eSthorpej 14310683880eSthorpej /* No arguments. */ 14320683880eSthorpej if (argc != 0) 14330683880eSthorpej usage(); 14340683880eSthorpej 14350683880eSthorpej if (ioctl(fd, SCBUSIORESET, NULL) != 0) 14360683880eSthorpej err(1, "SCBUSIORESET"); 14370683880eSthorpej 14380683880eSthorpej return; 14390683880eSthorpej } 144041194c0eSmjacob 144141194c0eSmjacob /* 1442c9a47c22Sthorpej * bus_scan: 1443c9a47c22Sthorpej * 1444c9a47c22Sthorpej * Rescan a SCSI bus for new devices. 1445c9a47c22Sthorpej */ 14469211d764Sjakllsch static void 14479bab889dSxtraeme bus_scan(int argc, char *argv[]) 1448c9a47c22Sthorpej { 1449c9a47c22Sthorpej struct scbusioscan_args args; 1450c9a47c22Sthorpej char *cp; 1451c9a47c22Sthorpej 1452c9a47c22Sthorpej /* Must have two args: target lun */ 1453c9a47c22Sthorpej if (argc != 2) 1454958ed46fShubertf usage(); 1455c9a47c22Sthorpej 14562829eb17Sad if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 1457c9a47c22Sthorpej args.sa_target = -1; 1458c9a47c22Sthorpej else { 1459c9a47c22Sthorpej args.sa_target = strtol(argv[0], &cp, 10); 1460c9a47c22Sthorpej if (*cp != '\0' || args.sa_target < 0) 146193e37412Sad errx(1, "invalid target: %s", argv[0]); 1462c9a47c22Sthorpej } 1463c9a47c22Sthorpej 14642829eb17Sad if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 1465c9a47c22Sthorpej args.sa_lun = -1; 1466c9a47c22Sthorpej else { 1467c9a47c22Sthorpej args.sa_lun = strtol(argv[1], &cp, 10); 1468c9a47c22Sthorpej if (*cp != '\0' || args.sa_lun < 0) 146993e37412Sad errx(1, "invalid lun: %s", argv[1]); 1470c9a47c22Sthorpej } 1471c9a47c22Sthorpej 1472c9a47c22Sthorpej if (ioctl(fd, SCBUSIOSCAN, &args) != 0) 1473c9a47c22Sthorpej err(1, "SCBUSIOSCAN"); 1474c9a47c22Sthorpej 1475c9a47c22Sthorpej return; 1476c9a47c22Sthorpej } 147703fd5e67Sbouyer 147803fd5e67Sbouyer /* 147903fd5e67Sbouyer * bus_detach: 148003fd5e67Sbouyer * 148103fd5e67Sbouyer * detach SCSI devices from a bus. 148203fd5e67Sbouyer */ 14839211d764Sjakllsch static void 14849bab889dSxtraeme bus_detach(int argc, char *argv[]) 148503fd5e67Sbouyer { 148603fd5e67Sbouyer struct scbusiodetach_args args; 148703fd5e67Sbouyer char *cp; 148803fd5e67Sbouyer 148903fd5e67Sbouyer /* Must have two args: target lun */ 149003fd5e67Sbouyer if (argc != 2) 149103fd5e67Sbouyer usage(); 149203fd5e67Sbouyer 149303fd5e67Sbouyer if (strcmp(argv[0], "any") == 0 || strcmp(argv[0], "all") == 0) 149403fd5e67Sbouyer args.sa_target = -1; 149503fd5e67Sbouyer else { 149603fd5e67Sbouyer args.sa_target = strtol(argv[0], &cp, 10); 149703fd5e67Sbouyer if (*cp != '\0' || args.sa_target < 0) 14986742cb18Sgrant errx(1, "invalid target: %s", argv[0]); 149903fd5e67Sbouyer } 150003fd5e67Sbouyer 150103fd5e67Sbouyer if (strcmp(argv[1], "any") == 0 || strcmp(argv[1], "all") == 0) 150203fd5e67Sbouyer args.sa_lun = -1; 150303fd5e67Sbouyer else { 150403fd5e67Sbouyer args.sa_lun = strtol(argv[1], &cp, 10); 150503fd5e67Sbouyer if (*cp != '\0' || args.sa_lun < 0) 15066742cb18Sgrant errx(1, "invalid lun: %s", argv[1]); 150703fd5e67Sbouyer } 150803fd5e67Sbouyer 150903fd5e67Sbouyer if (ioctl(fd, SCBUSIODETACH, &args) != 0) 151003fd5e67Sbouyer err(1, "SCBUSIODETACH"); 151103fd5e67Sbouyer 151203fd5e67Sbouyer return; 151303fd5e67Sbouyer } 1514