/* * Implementation of Utility functions for all SCSI device types. * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998, 2003 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #ifdef _KERNEL #include #include #include #include #include #else #include #include #include #include #endif #include #include #include #include #include #ifndef _KERNEL #include #ifndef FALSE #define FALSE 0 #endif /* FALSE */ #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #define ERESTART -1 /* restart syscall */ #define EJUSTRETURN -2 /* don't modify regs, just return */ #endif /* !_KERNEL */ /* * This is the default number of seconds we wait for devices to settle * after a SCSI bus reset. */ #ifndef SCSI_DELAY #define SCSI_DELAY 2000 #endif /* * All devices need _some_ sort of bus settle delay, so we'll set it to * a minimum value of 100ms. Note that this is pertinent only for SPI- * not transport like Fibre Channel or iSCSI where 'delay' is completely * meaningless. */ #ifndef SCSI_MIN_DELAY #define SCSI_MIN_DELAY 100 #endif /* * Make sure the user isn't using seconds instead of milliseconds. */ #if (SCSI_DELAY < SCSI_MIN_DELAY && SCSI_DELAY != 0) #error "SCSI_DELAY is in milliseconds, not seconds! Please use a larger value" #endif int scsi_delay; static int ascentrycomp(const void *key, const void *member); static int senseentrycomp(const void *key, const void *member); static void fetchtableentries(int sense_key, int asc, int ascq, struct scsi_inquiry_data *, const struct sense_key_table_entry **, const struct asc_table_entry **); #ifdef _KERNEL static void init_scsi_delay(void); static int sysctl_scsi_delay(SYSCTL_HANDLER_ARGS); static int set_scsi_delay(int delay); #endif #if !defined(SCSI_NO_OP_STRINGS) #define D 0x001 #define T 0x002 #define L 0x004 #define P 0x008 #define W 0x010 #define R 0x020 #define S 0x040 #define O 0x080 #define M 0x100 #define C 0x200 #define A 0x400 #define E 0x800 #define ALL 0xFFF static struct op_table_entry plextor_cd_ops[] = { {0xD8, R, "CD-DA READ"} }; static struct scsi_op_quirk_entry scsi_op_quirk_table[] = { { /* * I believe that 0xD8 is the Plextor proprietary command * to read CD-DA data. I'm not sure which Plextor CDROM * models support the command, though. I know for sure * that the 4X, 8X, and 12X models do, and presumably the * 12-20X does. I don't know about any earlier models, * though. If anyone has any more complete information, * feel free to change this quirk entry. */ {T_CDROM, SIP_MEDIA_REMOVABLE, "PLEXTOR", "CD-ROM PX*", "*"}, sizeof(plextor_cd_ops)/sizeof(struct op_table_entry), plextor_cd_ops } }; static struct op_table_entry scsi_op_codes[] = { /* * From: ftp://ftp.symbios.com/pub/standards/io/t10/drafts/spc/op-num.txt * Modifications by Kenneth Merry (ken@FreeBSD.ORG) * * Note: order is important in this table, scsi_op_desc() currently * depends on the opcodes in the table being in order to save search time. */ /* * File: OP-NUM.TXT * * SCSI Operation Codes * Numeric Sorted Listing * as of 11/13/96 * * D - DIRECT ACCESS DEVICE (SBC) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- * . L - PRINTER DEVICE (SSC) M = Mandatory * . P - PROCESSOR DEVICE (SPC) O = Optional * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) V = Vendor specific * . . R - CD DEVICE (MMC) R = Reserved * . . S - SCANNER DEVICE (SGC) Z = Obsolete * . . .O - OPTICAL MEMORY DEVICE (SBC) * . . . M - MEDIA CHANGER DEVICE (SMC) * . . . C - COMMUNICATION DEVICE (SSC) * . . . .A - STORAGE ARRAY DEVICE (SCC) * . . . . E - ENCLOSURE SERVICES DEVICE (SES) * OP DTLPWRSOMCAE Description * -- ------------ ---------------------------------------------------- */ /* 00 MMMMMMMMMMMM TEST UNIT READY */ {0x00, ALL, "TEST UNIT READY"}, /* 01 M REWIND */ {0x01, T, "REWIND"}, /* 01 Z V ZO ZO REZERO UNIT */ {0x01, D|L|W|O|M, "REZERO UNIT"}, /* 02 VVVVVV V */ /* 03 MMMMMMMMMMMM REQUEST SENSE */ {0x03, ALL, "REQUEST SENSE"}, /* 04 M O O FORMAT UNIT */ {0x04, D|R|O, "FORMAT UNIT"}, /* 04 O FORMAT MEDIUM */ {0x04, T, "FORMAT MEDIUM"}, /* 04 O FORMAT */ {0x04, L, "FORMAT"}, /* 05 VMVVVV V READ BLOCK LIMITS */ {0x05, T, "READ BLOCK LIMITS"}, /* 06 VVVVVV V */ /* 07 OVV O OV REASSIGN BLOCKS */ {0x07, D|W|O, "REASSIGN BLOCKS"}, /* 07 O INITIALIZE ELEMENT STATUS */ {0x07, M, "INITIALIZE ELEMENT STATUS"}, /* 08 OMV OO OV READ(06) */ {0x08, D|T|W|R|O, "READ(06)"}, /* 08 O RECEIVE */ {0x08, P, "RECEIVE"}, /* 08 M GET MESSAGE(06) */ {0x08, C, "GET MESSAGE(06)"}, /* 09 VVVVVV V */ /* 0A OM O OV WRITE(06) */ {0x0A, D|T|W|O, "WRITE(06)"}, /* 0A M SEND(06) */ {0x0A, P, "SEND(06)"}, /* 0A M SEND MESSAGE(06) */ {0x0A, C, "SEND MESSAGE(06)"}, /* 0A M PRINT */ {0x0A, L, "PRINT"}, /* 0B Z ZO ZV SEEK(06) */ {0x0B, D|W|R|O, "SEEK(06)"}, /* 0B O SLEW AND PRINT */ {0x0B, L, "SLEW AND PRINT"}, /* 0C VVVVVV V */ /* 0D VVVVVV V */ /* 0E VVVVVV V */ /* 0F VOVVVV V READ REVERSE */ {0x0F, T, "READ REVERSE"}, /* 10 VM VVV WRITE FILEMARKS */ {0x10, T, "WRITE FILEMARKS"}, /* 10 O O SYNCHRONIZE BUFFER */ {0x10, L|W, "SYNCHRONIZE BUFFER"}, /* 11 VMVVVV SPACE */ {0x11, T, "SPACE"}, /* 12 MMMMMMMMMMMM INQUIRY */ {0x12, ALL, "INQUIRY"}, /* 13 VOVVVV VERIFY(06) */ {0x13, T, "VERIFY(06)"}, /* 14 VOOVVV RECOVER BUFFERED DATA */ {0x14, T|L, "RECOVER BUFFERED DATA"}, /* 15 OMO OOOOOOOO MODE SELECT(06) */ {0x15, ALL & ~(P), "MODE SELECT(06)"}, /* 16 MMMOMMMM O RESERVE(06) */ {0x16, D|T|L|P|W|R|S|O|E, "RESERVE(06)"}, /* 16 M RESERVE ELEMENT(06) */ {0x16, M, "RESERVE ELEMENT(06)"}, /* 17 MMMOMMMM O RELEASE(06) */ {0x17, ALL & ~(M|C|A), "RELEASE(06)"}, /* 17 M RELEASE ELEMENT(06) */ {0x17, M, "RELEASE ELEMENT(06)"}, /* 18 OOOOOOOO COPY */ {0x18, ALL & ~(M|C|A|E), "COPY"}, /* 19 VMVVVV ERASE */ {0x19, T, "ERASE"}, /* 1A OMO OOOOOOOO MODE SENSE(06) */ {0x1A, ALL & ~(P), "MODE SENSE(06)"}, /* 1B O OM O STOP START UNIT */ {0x1B, D|W|R|O, "STOP START UNIT"}, /* 1B O LOAD UNLOAD */ {0x1B, T, "LOAD UNLOAD"}, /* 1B O SCAN */ {0x1B, S, "SCAN"}, /* 1B O STOP PRINT */ {0x1B, L, "STOP PRINT"}, /* 1C OOOOOOOOOO M RECEIVE DIAGNOSTIC RESULTS */ {0x1C, ALL & ~(A), "RECEIVE DIAGNOSTIC RESULTS"}, /* 1D MMMMMMMMMMMM SEND DIAGNOSTIC */ {0x1D, ALL, "SEND DIAGNOSTIC"}, /* 1E OO OM OO PREVENT ALLOW MEDIUM REMOVAL */ {0x1E, D|T|W|R|O|M, "PREVENT ALLOW MEDIUM REMOVAL"}, /* 1F */ /* 20 V VV V */ /* 21 V VV V */ /* 22 V VV V */ /* 23 V VV V */ /* 24 V VVM SET WINDOW */ {0x24, S, "SET WINDOW"}, /* 25 M M M READ CAPACITY */ {0x25, D|W|O, "READ CAPACITY"}, /* 25 M READ CD RECORDED CAPACITY */ {0x25, R, "READ CD RECORDED CAPACITY"}, /* 25 O GET WINDOW */ {0x25, S, "GET WINDOW"}, /* 26 V VV */ /* 27 V VV */ /* 28 M MMMM READ(10) */ {0x28, D|W|R|S|O, "READ(10)"}, /* 28 O GET MESSAGE(10) */ {0x28, C, "GET MESSAGE(10)"}, /* 29 V VV O READ GENERATION */ {0x29, O, "READ GENERATION"}, /* 2A M MM M WRITE(10) */ {0x2A, D|W|R|O, "WRITE(10)"}, /* 2A O SEND(10) */ {0x2A, S, "SEND(10)"}, /* 2A O SEND MESSAGE(10) */ {0x2A, C, "SEND MESSAGE(10)"}, /* 2B O OM O SEEK(10) */ {0x2B, D|W|R|O, "SEEK(10)"}, /* 2B O LOCATE */ {0x2B, T, "LOCATE"}, /* 2B O POSITION TO ELEMENT */ {0x2B, M, "POSITION TO ELEMENT"}, /* 2C V O ERASE(10) */ {0x2C, O, "ERASE(10)"}, /* 2D V O O READ UPDATED BLOCK */ {0x2D, W|O, "READ UPDATED BLOCK"}, /* 2E O O O WRITE AND VERIFY(10) */ {0x2E, D|W|O, "WRITE AND VERIFY(10)"}, /* 2F O OO O VERIFY(10) */ {0x2F, D|W|R|O, "VERIFY(10)"}, /* 30 Z ZO Z SEARCH DATA HIGH(10) */ {0x30, D|W|R|O, "SEARCH DATA HIGH(10)"}, /* 31 Z ZO Z SEARCH DATA EQUAL(10) */ {0x31, D|W|R|O, "SEARCH DATA EQUAL(10)"}, /* 31 O OBJECT POSITION */ {0x31, S, "OBJECT POSITION"}, /* 32 Z ZO Z SEARCH DATA LOW(10) */ {0x32, D|W|R|O, "SEARCH DATA LOW(10"}, /* 33 O OO O SET LIMITS(10) */ {0x33, D|W|R|O, "SET LIMITS(10)"}, /* 34 O OO O PRE-FETCH */ {0x34, D|W|R|O, "PRE-FETCH"}, /* 34 O READ POSITION */ {0x34, T, "READ POSITION"}, /* 34 O GET DATA BUFFER STATUS */ {0x34, S, "GET DATA BUFFER STATUS"}, /* 35 O OM O SYNCHRONIZE CACHE */ {0x35, D|W|R|O, "SYNCHRONIZE CACHE"}, /* 36 O OO O LOCK UNLOCK CACHE */ {0x36, D|W|R|O, "LOCK UNLOCK CACHE"}, /* 37 O O READ DEFECT DATA(10) */ {0x37, D|O, "READ DEFECT DATA(10)"}, /* 38 O O MEDIUM SCAN */ {0x38, W|O, "MEDIUM SCAN"}, /* 39 OOOOOOOO COMPARE */ {0x39, ALL & ~(M|C|A|E), "COMPARE"}, /* 3A OOOOOOOO COPY AND VERIFY */ {0x3A, ALL & ~(M|C|A|E), "COPY AND VERIFY"}, /* 3B OOOOOOOOOO O WRITE BUFFER */ {0x3B, ALL & ~(A), "WRITE BUFFER"}, /* 3C OOOOOOOOOO READ BUFFER */ {0x3C, ALL & ~(A|E),"READ BUFFER"}, /* 3D O O UPDATE BLOCK */ {0x3D, W|O, "UPDATE BLOCK"}, /* 3E O OO O READ LONG */ {0x3E, D|W|R|O, "READ LONG"}, /* 3F O O O WRITE LONG */ {0x3F, D|W|O, "WRITE LONG"}, /* 40 OOOOOOOOOO CHANGE DEFINITION */ {0x40, ALL & ~(A|E),"CHANGE DEFINITION"}, /* 41 O WRITE SAME */ {0x41, D, "WRITE SAME"}, /* 42 M READ SUB-CHANNEL */ {0x42, R, "READ SUB-CHANNEL"}, /* 43 M READ TOC/PMA/ATIP {MMC Proposed} */ {0x43, R, "READ TOC/PMA/ATIP {MMC Proposed}"}, /* 44 M REPORT DENSITY SUPPORT */ {0x44, T, "REPORT DENSITY SUPPORT"}, /* 44 M READ HEADER */ {0x44, R, "READ HEADER"}, /* 45 O PLAY AUDIO(10) */ {0x45, R, "PLAY AUDIO(10)"}, /* 46 */ /* 47 O PLAY AUDIO MSF */ {0x47, R, "PLAY AUDIO MSF"}, /* 48 O PLAY AUDIO TRACK INDEX */ {0x48, R, "PLAY AUDIO TRACK INDEX"}, /* 49 O PLAY TRACK RELATIVE(10) */ {0x49, R, "PLAY TRACK RELATIVE(10)"}, /* 4A */ /* 4B O PAUSE/RESUME */ {0x4B, R, "PAUSE/RESUME"}, /* 4C OOOOOOOOOOO LOG SELECT */ {0x4C, ALL & ~(E), "LOG SELECT"}, /* 4D OOOOOOOOOOO LOG SENSE */ {0x4D, ALL & ~(E), "LOG SENSE"}, /* 4E O STOP PLAY/SCAN {MMC Proposed} */ {0x4E, R, "STOP PLAY/SCAN {MMC Proposed}"}, /* 4F */ /* 50 O XDWRITE(10) */ {0x50, D, "XDWRITE(10)"}, /* 51 O XPWRITE(10) */ {0x51, D, "XPWRITE(10)"}, /* 51 M READ DISC INFORMATION {MMC Proposed} */ {0x51, R, "READ DISC INFORMATION {MMC Proposed}"}, /* 52 O XDREAD(10) */ {0x52, D, "XDREAD(10)"}, /* 52 M READ TRACK INFORMATION {MMC Proposed} */ {0x52, R, "READ TRACK INFORMATION {MMC Proposed}"}, /* 53 M RESERVE TRACK {MMC Proposed} */ {0x53, R, "RESERVE TRACK {MMC Proposed}"}, /* 54 O SEND OPC INFORMATION {MMC Proposed} */ {0x54, R, "SEND OPC INFORMATION {MMC Proposed}"}, /* 55 OOO OOOOOOOO MODE SELECT(10) */ {0x55, ALL & ~(P), "MODE SELECT(10)"}, /* 56 MMMOMMMM O RESERVE(10) */ {0x56, ALL & ~(M|C|A), "RESERVE(10)"}, /* 56 M RESERVE ELEMENT(10) */ {0x56, M, "RESERVE ELEMENT(10)"}, /* 57 MMMOMMMM O RELEASE(10) */ {0x57, ALL & ~(M|C|A), "RELEASE(10"}, /* 57 M RELEASE ELEMENT(10) */ {0x57, M, "RELEASE ELEMENT(10)"}, /* 58 O REPAIR TRACK {MMC Proposed} */ {0x58, R, "REPAIR TRACK {MMC Proposed}"}, /* 59 O READ MASTER CUE {MMC Proposed} */ {0x59, R, "READ MASTER CUE {MMC Proposed}"}, /* 5A OOO OOOOOOOO MODE SENSE(10) */ {0x5A, ALL & ~(P), "MODE SENSE(10)"}, /* 5B M CLOSE TRACK/SESSION {MMC Proposed} */ {0x5B, R, "CLOSE TRACK/SESSION {MMC Proposed}"}, /* 5C O READ BUFFER CAPACITY {MMC Proposed} */ {0x5C, R, "READ BUFFER CAPACITY {MMC Proposed}"}, /* 5D O SEND CUE SHEET {MMC Proposed} */ {0x5D, R, "SEND CUE SHEET {MMC Proposed}"}, /* 5E OOOOOOOOO O PERSISTENT RESERVE IN */ {0x5E, ALL & ~(C|A),"PERSISTENT RESERVE IN"}, /* 5F OOOOOOOOO O PERSISTENT RESERVE OUT */ {0x5F, ALL & ~(C|A),"PERSISTENT RESERVE OUT"}, /* 80 O XDWRITE EXTENDED(16) */ {0x80, D, "XDWRITE EXTENDED(16)"}, /* 81 O REBUILD(16) */ {0x81, D, "REBUILD(16)"}, /* 82 O REGENERATE(16) */ {0x82, D, "REGENERATE(16)"}, /* 83 */ /* 84 */ /* 85 */ /* 86 */ /* 87 */ /* 88 MM OO O O READ(16) */ {0x88, D|T|W|R|O, "READ(16)"}, /* 89 */ /* 8A OM O O O WRITE(16) */ {0x8A, D|T|W|R|O, "WRITE(16)"}, /* 8B */ /* 8C */ /* 8D */ /* 8E */ /* 8F */ /* 90 */ /* 91 */ /* 92 */ /* 93 */ /* 94 */ /* 95 */ /* 96 */ /* 97 */ /* 98 */ /* 99 */ /* 9A */ /* 9B */ /* 9C */ /* 9D */ /* XXX KDM ALL for these? op-num.txt defines them for none.. */ /* 9E SERVICE ACTION IN(16) */ {0x9E, ALL, "SERVICE ACTION IN(16)"}, /* 9F SERVICE ACTION OUT(16) */ {0x9F, ALL, "SERVICE ACTION OUT(16)"}, /* A0 OOOOOOOOOOO REPORT LUNS */ {0xA0, ALL & ~(E), "REPORT LUNS"}, /* A1 O BLANK {MMC Proposed} */ {0xA1, R, "BLANK {MMC Proposed}"}, /* A2 O WRITE CD MSF {MMC Proposed} */ {0xA2, R, "WRITE CD MSF {MMC Proposed}"}, /* A3 M MAINTENANCE (IN) */ {0xA3, A, "MAINTENANCE (IN)"}, /* A4 O MAINTENANCE (OUT) */ {0xA4, A, "MAINTENANCE (OUT)"}, /* A5 O M MOVE MEDIUM */ {0xA5, T|M, "MOVE MEDIUM"}, /* A5 O PLAY AUDIO(12) */ {0xA5, R, "PLAY AUDIO(12)"}, /* A6 O EXCHANGE MEDIUM */ {0xA6, M, "EXCHANGE MEDIUM"}, /* A6 O LOAD/UNLOAD CD {MMC Proposed} */ {0xA6, R, "LOAD/UNLOAD CD {MMC Proposed}"}, /* A7 OO OO OO MOVE MEDIUM ATTACHED */ {0xA7, D|T|W|R|O|M, "MOVE MEDIUM ATTACHED"}, /* A8 O OM O READ(12) */ {0xA8,D|W|R|O, "READ(12)"}, /* A8 O GET MESSAGE(12) */ {0xA8, C, "GET MESSAGE(12)"}, /* A9 O PLAY TRACK RELATIVE(12) */ {0xA9, R, "PLAY TRACK RELATIVE(12)"}, /* AA O O O WRITE(12) */ {0xAA,D|W|O, "WRITE(12)"}, /* AA O WRITE CD(12) {MMC Proposed} */ {0xAA, R, "WRITE CD(12) {MMC Proposed}"}, /* AA O SEND MESSAGE(12) */ {0xAA, C, "SEND MESSAGE(12)"}, /* AB */ /* AC O ERASE(12) */ {0xAC, O, "ERASE(12)"}, /* AD */ /* AE O O WRITE AND VERIFY(12) */ {0xAE, W|O, "WRITE AND VERIFY(12)"}, /* AF OO O VERIFY(12) */ {0xAF, W|R|O, "VERIFY(12)"}, /* B0 ZO Z SEARCH DATA HIGH(12) */ {0xB0, W|R|O, "SEARCH DATA HIGH(12)"}, /* B1 ZO Z SEARCH DATA EQUAL(12) */ {0xB1, W|R|O, "SEARCH DATA EQUAL(12)"}, /* B2 ZO Z SEARCH DATA LOW(12) */ {0xB2, W|R|O, "SEARCH DATA LOW(12)"}, /* B3 OO O SET LIMITS(12) */ {0xB3, W|R|O, "SET LIMITS(12)"}, /* B4 OO OO OO READ ELEMENT STATUS ATTACHED */ {0xB4, D|T|W|R|O|M, "READ ELEMENT STATUS ATTACHED"}, /* B5 O REQUEST VOLUME ELEMENT ADDRESS */ {0xB5, M, "REQUEST VOLUME ELEMENT ADDRESS"}, /* B6 O SEND VOLUME TAG */ {0xB6, M, "SEND VOLUME TAG"}, /* B7 O READ DEFECT DATA(12) */ {0xB7, O, "READ DEFECT DATA(12)"}, /* B8 O M READ ELEMENT STATUS */ {0xB8, T|M, "READ ELEMENT STATUS"}, /* B8 O SET CD SPEED {MMC Proposed} */ {0xB8, R, "SET CD SPEED {MMC Proposed}"}, /* B9 M READ CD MSF {MMC Proposed} */ {0xB9, R, "READ CD MSF {MMC Proposed}"}, /* BA O SCAN {MMC Proposed} */ {0xBA, R, "SCAN {MMC Proposed}"}, /* BA M REDUNDANCY GROUP (IN) */ {0xBA, A, "REDUNDANCY GROUP (IN)"}, /* BB O SET CD-ROM SPEED {proposed} */ {0xBB, R, "SET CD-ROM SPEED {proposed}"}, /* BB O REDUNDANCY GROUP (OUT) */ {0xBB, A, "REDUNDANCY GROUP (OUT)"}, /* BC O PLAY CD {MMC Proposed} */ {0xBC, R, "PLAY CD {MMC Proposed}"}, /* BC M SPARE (IN) */ {0xBC, A, "SPARE (IN)"}, /* BD M MECHANISM STATUS {MMC Proposed} */ {0xBD, R, "MECHANISM STATUS {MMC Proposed}"}, /* BD O SPARE (OUT) */ {0xBD, A, "SPARE (OUT)"}, /* BE O READ CD {MMC Proposed} */ {0xBE, R, "READ CD {MMC Proposed}"}, /* BE M VOLUME SET (IN) */ {0xBE, A, "VOLUME SET (IN)"}, /* BF O VOLUME SET (OUT) */ {0xBF, A, "VOLUME SET (OUT)"} }; const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { caddr_t match; int i, j; u_int16_t opmask; u_int16_t pd_type; int num_ops[2]; struct op_table_entry *table[2]; int num_tables; pd_type = SID_TYPE(inq_data); match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)scsi_op_quirk_table, sizeof(scsi_op_quirk_table)/ sizeof(*scsi_op_quirk_table), sizeof(*scsi_op_quirk_table), scsi_inquiry_match); if (match != NULL) { table[0] = ((struct scsi_op_quirk_entry *)match)->op_table; num_ops[0] = ((struct scsi_op_quirk_entry *)match)->num_ops; table[1] = scsi_op_codes; num_ops[1] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]); num_tables = 2; } else { /* * If this is true, we have a vendor specific opcode that * wasn't covered in the quirk table. */ if ((opcode > 0xBF) || ((opcode > 0x5F) && (opcode < 0x80))) return("Vendor Specific Command"); table[0] = scsi_op_codes; num_ops[0] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]); num_tables = 1; } /* RBC is 'Simplified' Direct Access Device */ if (pd_type == T_RBC) pd_type = T_DIRECT; opmask = 1 << pd_type; for (j = 0; j < num_tables; j++) { for (i = 0;i < num_ops[j] && table[j][i].opcode <= opcode; i++){ if ((table[j][i].opcode == opcode) && ((table[j][i].opmask & opmask) != 0)) return(table[j][i].desc); } } /* * If we can't find a match for the command in the table, we just * assume it's a vendor specifc command. */ return("Vendor Specific Command"); } #else /* SCSI_NO_OP_STRINGS */ const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { return(""); } #endif #include #if !defined(SCSI_NO_SENSE_STRINGS) #define SST(asc, ascq, action, desc) \ asc, ascq, action, desc #else const char empty_string[] = ""; #define SST(asc, ascq, action, desc) \ asc, ascq, action, empty_string #endif const struct sense_key_table_entry sense_key_table[] = { { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" }, { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" }, { SSD_KEY_NOT_READY, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, "NOT READY" }, { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" }, { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" }, { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" }, { SSD_KEY_UNIT_ATTENTION, SS_FATAL|ENXIO, "UNIT ATTENTION" }, { SSD_KEY_DATA_PROTECT, SS_FATAL|EACCES, "DATA PROTECT" }, { SSD_KEY_BLANK_CHECK, SS_FATAL|ENOSPC, "BLANK CHECK" }, { SSD_KEY_Vendor_Specific, SS_FATAL|EIO, "Vendor Specific" }, { SSD_KEY_COPY_ABORTED, SS_FATAL|EIO, "COPY ABORTED" }, { SSD_KEY_ABORTED_COMMAND, SS_RDEF, "ABORTED COMMAND" }, { SSD_KEY_EQUAL, SS_NOP, "EQUAL" }, { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" }, { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" }, { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" } }; const int sense_key_table_size = sizeof(sense_key_table)/sizeof(sense_key_table[0]); static struct asc_table_entry quantum_fireball_entries[] = { {SST(0x04, 0x0b, SS_START|SSQ_DECREMENT_COUNT|ENXIO, "Logical unit not ready, initializing cmd. required")} }; static struct asc_table_entry sony_mo_entries[] = { {SST(0x04, 0x00, SS_START|SSQ_DECREMENT_COUNT|ENXIO, "Logical unit not ready, cause not reportable")} }; static struct scsi_sense_quirk_entry sense_quirk_table[] = { { /* * The Quantum Fireball ST and SE like to return 0x04 0x0b when * they really should return 0x04 0x02. 0x04,0x0b isn't * defined in any SCSI spec, and it isn't mentioned in the * hardware manual for these drives. */ {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"}, /*num_sense_keys*/0, sizeof(quantum_fireball_entries)/sizeof(struct asc_table_entry), /*sense key entries*/NULL, quantum_fireball_entries }, { /* * This Sony MO drive likes to return 0x04, 0x00 when it * isn't spun up. */ {T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"}, /*num_sense_keys*/0, sizeof(sony_mo_entries)/sizeof(struct asc_table_entry), /*sense key entries*/NULL, sony_mo_entries } }; const int sense_quirk_table_size = sizeof(sense_quirk_table)/sizeof(sense_quirk_table[0]); static struct asc_table_entry asc_table[] = { /* * From File: ASC-NUM.TXT * SCSI ASC/ASCQ Assignments * Numeric Sorted Listing * as of 5/12/97 * * D - DIRECT ACCESS DEVICE (SBC) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- * . L - PRINTER DEVICE (SSC) blank = reserved * . P - PROCESSOR DEVICE (SPC) not blank = allowed * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) * . . R - CD DEVICE (MMC) * . . S - SCANNER DEVICE (SGC) * . . .O - OPTICAL MEMORY DEVICE (SBC) * . . . M - MEDIA CHANGER DEVICE (SMC) * . . . C - COMMUNICATION DEVICE (SSC) * . . . .A - STORAGE ARRAY DEVICE (SCC) * . . . . E - ENCLOSURE SERVICES DEVICE (SES) * DTLPWRSOMCAE ASC ASCQ Action Description * ------------ ---- ---- ------ -----------------------------------*/ /* DTLPWRSOMCAE */{SST(0x00, 0x00, SS_NOP, "No additional sense information") }, /* T S */{SST(0x00, 0x01, SS_RDEF, "Filemark detected") }, /* T S */{SST(0x00, 0x02, SS_RDEF, "End-of-partition/medium detected") }, /* T */{SST(0x00, 0x03, SS_RDEF, "Setmark detected") }, /* T S */{SST(0x00, 0x04, SS_RDEF, "Beginning-of-partition/medium detected") }, /* T S */{SST(0x00, 0x05, SS_RDEF, "End-of-data detected") }, /* DTLPWRSOMCAE */{SST(0x00, 0x06, SS_RDEF, "I/O process terminated") }, /* R */{SST(0x00, 0x11, SS_FATAL|EBUSY, "Audio play operation in progress") }, /* R */{SST(0x00, 0x12, SS_NOP, "Audio play operation paused") }, /* R */{SST(0x00, 0x13, SS_NOP, "Audio play operation successfully completed") }, /* R */{SST(0x00, 0x14, SS_RDEF, "Audio play operation stopped due to error") }, /* R */{SST(0x00, 0x15, SS_NOP, "No current audio status to return") }, /* DTLPWRSOMCAE */{SST(0x00, 0x16, SS_FATAL|EBUSY, "Operation in progress") }, /* DTL WRSOM AE */{SST(0x00, 0x17, SS_RDEF, "Cleaning requested") }, /* D W O */{SST(0x01, 0x00, SS_RDEF, "No index/sector signal") }, /* D WR OM */{SST(0x02, 0x00, SS_RDEF, "No seek complete") }, /* DTL W SO */{SST(0x03, 0x00, SS_RDEF, "Peripheral device write fault") }, /* T */{SST(0x03, 0x01, SS_RDEF, "No write current") }, /* T */{SST(0x03, 0x02, SS_RDEF, "Excessive write errors") }, /* DTLPWRSOMCAE */{SST(0x04, 0x00, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO, "Logical unit not ready, cause not reportable") }, /* DTLPWRSOMCAE */{SST(0x04, 0x01, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, "Logical unit is in process of becoming ready") }, /* DTLPWRSOMCAE */{SST(0x04, 0x02, SS_START|SSQ_DECREMENT_COUNT|ENXIO, "Logical unit not ready, initializing cmd. required") }, /* DTLPWRSOMCAE */{SST(0x04, 0x03, SS_FATAL|ENXIO, "Logical unit not ready, manual intervention required")}, /* DTL O */{SST(0x04, 0x04, SS_FATAL|EBUSY, "Logical unit not ready, format in progress") }, /* DT W OMCA */{SST(0x04, 0x05, SS_FATAL|EBUSY, "Logical unit not ready, rebuild in progress") }, /* DT W OMCA */{SST(0x04, 0x06, SS_FATAL|EBUSY, "Logical unit not ready, recalculation in progress") }, /* DTLPWRSOMCAE */{SST(0x04, 0x07, SS_FATAL|EBUSY, "Logical unit not ready, operation in progress") }, /* R */{SST(0x04, 0x08, SS_FATAL|EBUSY, "Logical unit not ready, long write in progress") }, /* DTL WRSOMCAE */{SST(0x05, 0x00, SS_RDEF, "Logical unit does not respond to selection") }, /* D WR OM */{SST(0x06, 0x00, SS_RDEF, "No reference position found") }, /* DTL WRSOM */{SST(0x07, 0x00, SS_RDEF, "Multiple peripheral devices selected") }, /* DTL WRSOMCAE */{SST(0x08, 0x00, SS_RDEF, "Logical unit communication failure") }, /* DTL WRSOMCAE */{SST(0x08, 0x01, SS_RDEF, "Logical unit communication time-out") }, /* DTL WRSOMCAE */{SST(0x08, 0x02, SS_RDEF, "Logical unit communication parity error") }, /* DT R OM */{SST(0x08, 0x03, SS_RDEF, "Logical unit communication crc error (ultra-dma/32)")}, /* DT WR O */{SST(0x09, 0x00, SS_RDEF, "Track following error") }, /* WR O */{SST(0x09, 0x01, SS_RDEF, "Tracking servo failure") }, /* WR O */{SST(0x09, 0x02, SS_RDEF, "Focus servo failure") }, /* WR O */{SST(0x09, 0x03, SS_RDEF, "Spindle servo failure") }, /* DT WR O */{SST(0x09, 0x04, SS_RDEF, "Head select fault") }, /* DTLPWRSOMCAE */{SST(0x0A, 0x00, SS_FATAL|ENOSPC, "Error log overflow") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x00, SS_RDEF, "Warning") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x01, SS_RDEF, "Specified temperature exceeded") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x02, SS_RDEF, "Enclosure degraded") }, /* T RS */{SST(0x0C, 0x00, SS_RDEF, "Write error") }, /* D W O */{SST(0x0C, 0x01, SS_NOP|SSQ_PRINT_SENSE, "Write error - recovered with auto reallocation") }, /* D W O */{SST(0x0C, 0x02, SS_RDEF, "Write error - auto reallocation failed") }, /* D W O */{SST(0x0C, 0x03, SS_RDEF, "Write error - recommend reassignment") }, /* DT W O */{SST(0x0C, 0x04, SS_RDEF, "Compression check miscompare error") }, /* DT W O */{SST(0x0C, 0x05, SS_RDEF, "Data expansion occurred during compression") }, /* DT W O */{SST(0x0C, 0x06, SS_RDEF, "Block not compressible") }, /* R */{SST(0x0C, 0x07, SS_RDEF, "Write error - recovery needed") }, /* R */{SST(0x0C, 0x08, SS_RDEF, "Write error - recovery failed") }, /* R */{SST(0x0C, 0x09, SS_RDEF, "Write error - loss of streaming") }, /* R */{SST(0x0C, 0x0A, SS_RDEF, "Write error - padding blocks added") }, /* D W O */{SST(0x10, 0x00, SS_RDEF, "ID CRC or ECC error") }, /* DT WRSO */{SST(0x11, 0x00, SS_RDEF, "Unrecovered read error") }, /* DT W SO */{SST(0x11, 0x01, SS_RDEF, "Read retries exhausted") }, /* DT W SO */{SST(0x11, 0x02, SS_RDEF, "Error too long to correct") }, /* DT W SO */{SST(0x11, 0x03, SS_RDEF, "Multiple read errors") }, /* D W O */{SST(0x11, 0x04, SS_RDEF, "Unrecovered read error - auto reallocate failed") }, /* WR O */{SST(0x11, 0x05, SS_RDEF, "L-EC uncorrectable error") }, /* WR O */{SST(0x11, 0x06, SS_RDEF, "CIRC unrecovered error") }, /* W O */{SST(0x11, 0x07, SS_RDEF, "Data re-synchronization error") }, /* T */{SST(0x11, 0x08, SS_RDEF, "Incomplete block read") }, /* T */{SST(0x11, 0x09, SS_RDEF, "No gap found") }, /* DT O */{SST(0x11, 0x0A, SS_RDEF, "Miscorrected error") }, /* D W O */{SST(0x11, 0x0B, SS_RDEF, "Unrecovered read error - recommend reassignment") }, /* D W O */{SST(0x11, 0x0C, SS_RDEF, "Unrecovered read error - recommend rewrite the data")}, /* DT WR O */{SST(0x11, 0x0D, SS_RDEF, "De-compression CRC error") }, /* DT WR O */{SST(0x11, 0x0E, SS_RDEF, "Cannot decompress using declared algorithm") }, /* R */{SST(0x11, 0x0F, SS_RDEF, "Error reading UPC/EAN number") }, /* R */{SST(0x11, 0x10, SS_RDEF, "Error reading ISRC number") }, /* R */{SST(0x11, 0x11, SS_RDEF, "Read error - loss of streaming") }, /* D W O */{SST(0x12, 0x00, SS_RDEF, "Address mark not found for id field") }, /* D W O */{SST(0x13, 0x00, SS_RDEF, "Address mark not found for data field") }, /* DTL WRSO */{SST(0x14, 0x00, SS_RDEF, "Recorded entity not found") }, /* DT WR O */{SST(0x14, 0x01, SS_RDEF, "Record not found") }, /* T */{SST(0x14, 0x02, SS_RDEF, "Filemark or setmark not found") }, /* T */{SST(0x14, 0x03, SS_RDEF, "End-of-data not found") }, /* T */{SST(0x14, 0x04, SS_RDEF, "Block sequence error") }, /* DT W O */{SST(0x14, 0x05, SS_RDEF, "Record not found - recommend reassignment") }, /* DT W O */{SST(0x14, 0x06, SS_RDEF, "Record not found - data auto-reallocated") }, /* DTL WRSOM */{SST(0x15, 0x00, SS_RDEF, "Random positioning error") }, /* DTL WRSOM */{SST(0x15, 0x01, SS_RDEF, "Mechanical positioning error") }, /* DT WR O */{SST(0x15, 0x02, SS_RDEF, "Positioning error detected by read of medium") }, /* D W O */{SST(0x16, 0x00, SS_RDEF, "Data synchronization mark error") }, /* D W O */{SST(0x16, 0x01, SS_RDEF, "Data sync error - data rewritten") }, /* D W O */{SST(0x16, 0x02, SS_RDEF, "Data sync error - recommend rewrite") }, /* D W O */{SST(0x16, 0x03, SS_NOP|SSQ_PRINT_SENSE, "Data sync error - data auto-reallocated") }, /* D W O */{SST(0x16, 0x04, SS_RDEF, "Data sync error - recommend reassignment") }, /* DT WRSO */{SST(0x17, 0x00, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with no error correction applied") }, /* DT WRSO */{SST(0x17, 0x01, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with retries") }, /* DT WR O */{SST(0x17, 0x02, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with positive head offset") }, /* DT WR O */{SST(0x17, 0x03, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with negative head offset") }, /* WR O */{SST(0x17, 0x04, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with retries and/or CIRC applied") }, /* D WR O */{SST(0x17, 0x05, SS_NOP|SSQ_PRINT_SENSE, "Recovered data using previous sector id") }, /* D W O */{SST(0x17, 0x06, SS_NOP|SSQ_PRINT_SENSE, "Recovered data without ECC - data auto-reallocated") }, /* D W O */{SST(0x17, 0x07, SS_NOP|SSQ_PRINT_SENSE, "Recovered data without ECC - recommend reassignment")}, /* D W O */{SST(0x17, 0x08, SS_NOP|SSQ_PRINT_SENSE, "Recovered data without ECC - recommend rewrite") }, /* D W O */{SST(0x17, 0x09, SS_NOP|SSQ_PRINT_SENSE, "Recovered data without ECC - data rewritten") }, /* D W O */{SST(0x18, 0x00, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with error correction applied") }, /* D WR O */{SST(0x18, 0x01, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with error corr. & retries applied") }, /* D WR O */{SST(0x18, 0x02, SS_NOP|SSQ_PRINT_SENSE, "Recovered data - data auto-reallocated") }, /* R */{SST(0x18, 0x03, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with CIRC") }, /* R */{SST(0x18, 0x04, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with L-EC") }, /* D WR O */{SST(0x18, 0x05, SS_NOP|SSQ_PRINT_SENSE, "Recovered data - recommend reassignment") }, /* D WR O */{SST(0x18, 0x06, SS_NOP|SSQ_PRINT_SENSE, "Recovered data - recommend rewrite") }, /* D W O */{SST(0x18, 0x07, SS_NOP|SSQ_PRINT_SENSE, "Recovered data with ECC - data rewritten") }, /* D O */{SST(0x19, 0x00, SS_RDEF, "Defect list error") }, /* D O */{SST(0x19, 0x01, SS_RDEF, "Defect list not available") }, /* D O */{SST(0x19, 0x02, SS_RDEF, "Defect list error in primary list") }, /* D O */{SST(0x19, 0x03, SS_RDEF, "Defect list error in grown list") }, /* DTLPWRSOMCAE */{SST(0x1A, 0x00, SS_RDEF, "Parameter list length error") }, /* DTLPWRSOMCAE */{SST(0x1B, 0x00, SS_RDEF, "Synchronous data transfer error") }, /* D O */{SST(0x1C, 0x00, SS_RDEF, "Defect list not found") }, /* D O */{SST(0x1C, 0x01, SS_RDEF, "Primary defect list not found") }, /* D O */{SST(0x1C, 0x02, SS_RDEF, "Grown defect list not found") }, /* D W O */{SST(0x1D, 0x00, SS_FATAL, "Miscompare during verify operation" )}, /* D W O */{SST(0x1E, 0x00, SS_NOP|SSQ_PRINT_SENSE, "Recovered id with ecc correction") }, /* D O */{SST(0x1F, 0x00, SS_RDEF, "Partial defect list transfer") }, /* DTLPWRSOMCAE */{SST(0x20, 0x00, SS_FATAL|EINVAL, "Invalid command operation code") }, /* DT WR OM */{SST(0x21, 0x00, SS_FATAL|EINVAL, "Logical block address out of range" )}, /* DT WR OM */{SST(0x21, 0x01, SS_FATAL|EINVAL, "Invalid element address") }, /* D */{SST(0x22, 0x00, SS_FATAL|EINVAL, "Illegal function") }, /* Deprecated. Use 20 00, 24 00, or 26 00 instead */ /* DTLPWRSOMCAE */{SST(0x24, 0x00, SS_FATAL|EINVAL, "Invalid field in CDB") }, /* DTLPWRSOMCAE */{SST(0x25, 0x00, SS_FATAL|ENXIO, "Logical unit not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x00, SS_FATAL|EINVAL, "Invalid field in parameter list") }, /* DTLPWRSOMCAE */{SST(0x26, 0x01, SS_FATAL|EINVAL, "Parameter not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x02, SS_FATAL|EINVAL, "Parameter value invalid") }, /* DTLPWRSOMCAE */{SST(0x26, 0x03, SS_FATAL|EINVAL, "Threshold parameters not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x04, SS_FATAL|EINVAL, "Invalid release of active persistent reservation") }, /* DT W O */{SST(0x27, 0x00, SS_FATAL|EACCES, "Write protected") }, /* DT W O */{SST(0x27, 0x01, SS_FATAL|EACCES, "Hardware write protected") }, /* DT W O */{SST(0x27, 0x02, SS_FATAL|EACCES, "Logical unit software write protected") }, /* T */{SST(0x27, 0x03, SS_FATAL|EACCES, "Associated write protect") }, /* T */{SST(0x27, 0x04, SS_FATAL|EACCES, "Persistent write protect") }, /* T */{SST(0x27, 0x05, SS_FATAL|EACCES, "Permanent write protect") }, /* DTLPWRSOMCAE */{SST(0x28, 0x00, SS_FATAL|ENXIO, "Not ready to ready change, medium may have changed") }, /* DTLPWRSOMCAE */{SST(0x28, 0x01, SS_FATAL|ENXIO, "Import or export element accessed") }, /* * XXX JGibbs - All of these should use the same errno, but I don't think * ENXIO is the correct choice. Should we borrow from the networking * errnos? ECONNRESET anyone? */ /* DTLPWRSOMCAE */{SST(0x29, 0x00, SS_FATAL|ENXIO, "Power on, reset, or bus device reset occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x01, SS_RDEF, "Power on occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x02, SS_RDEF, "Scsi bus reset occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x03, SS_RDEF, "Bus device reset function occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x04, SS_RDEF, "Device internal reset") }, /* DTLPWRSOMCAE */{SST(0x29, 0x05, SS_RDEF, "Transceiver mode changed to single-ended") }, /* DTLPWRSOMCAE */{SST(0x29, 0x06, SS_RDEF, "Transceiver mode changed to LVD") }, /* DTL WRSOMCAE */{SST(0x2A, 0x00, SS_RDEF, "Parameters changed") }, /* DTL WRSOMCAE */{SST(0x2A, 0x01, SS_RDEF, "Mode parameters changed") }, /* DTL WRSOMCAE */{SST(0x2A, 0x02, SS_RDEF, "Log parameters changed") }, /* DTLPWRSOMCAE */{SST(0x2A, 0x03, SS_RDEF, "Reservations preempted") }, /* DTLPWRSO C */{SST(0x2B, 0x00, SS_RDEF, "Copy cannot execute since host cannot disconnect") }, /* DTLPWRSOMCAE */{SST(0x2C, 0x00, SS_RDEF, "Command sequence error") }, /* S */{SST(0x2C, 0x01, SS_RDEF, "Too many windows specified") }, /* S */{SST(0x2C, 0x02, SS_RDEF, "Invalid combination of windows specified") }, /* R */{SST(0x2C, 0x03, SS_RDEF, "Current program area is not empty") }, /* R */{SST(0x2C, 0x04, SS_RDEF, "Current program area is empty") }, /* T */{SST(0x2D, 0x00, SS_RDEF, "Overwrite error on update in place") }, /* DTLPWRSOMCAE */{SST(0x2F, 0x00, SS_RDEF, "Commands cleared by another initiator") }, /* DT WR OM */{SST(0x30, 0x00, SS_RDEF, "Incompatible medium installed") }, /* DT WR O */{SST(0x30, 0x01, SS_RDEF, "Cannot read medium - unknown format") }, /* DT WR O */{SST(0x30, 0x02, SS_RDEF, "Cannot read medium - incompatible format") }, /* DT */{SST(0x30, 0x03, SS_RDEF, "Cleaning cartridge installed") }, /* DT WR O */{SST(0x30, 0x04, SS_RDEF, "Cannot write medium - unknown format") }, /* DT WR O */{SST(0x30, 0x05, SS_RDEF, "Cannot write medium - incompatible format") }, /* DT W O */{SST(0x30, 0x06, SS_RDEF, "Cannot format medium - incompatible medium") }, /* DTL WRSOM AE */{SST(0x30, 0x07, SS_RDEF, "Cleaning failure") }, /* R */{SST(0x30, 0x08, SS_RDEF, "Cannot write - application code mismatch") }, /* R */{SST(0x30, 0x09, SS_RDEF, "Current session not fixated for append") }, /* DT WR O */{SST(0x31, 0x00, SS_RDEF, "Medium format corrupted") }, /* D L R O */{SST(0x31, 0x01, SS_RDEF, "Format command failed") }, /* D W O */{SST(0x32, 0x00, SS_RDEF, "No defect spare location available") }, /* D W O */{SST(0x32, 0x01, SS_RDEF, "Defect list update failure") }, /* T */{SST(0x33, 0x00, SS_RDEF, "Tape length error") }, /* DTLPWRSOMCAE */{SST(0x34, 0x00, SS_RDEF, "Enclosure failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x00, SS_RDEF, "Enclosure services failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x01, SS_RDEF, "Unsupported enclosure function") }, /* DTLPWRSOMCAE */{SST(0x35, 0x02, SS_RDEF, "Enclosure services unavailable") }, /* DTLPWRSOMCAE */{SST(0x35, 0x03, SS_RDEF, "Enclosure services transfer failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x04, SS_RDEF, "Enclosure services transfer refused") }, /* L */{SST(0x36, 0x00, SS_RDEF, "Ribbon, ink, or toner failure") }, /* DTL WRSOMCAE */{SST(0x37, 0x00, SS_RDEF, "Rounded parameter") }, /* DTL WRSOMCAE */{SST(0x39, 0x00, SS_RDEF, "Saving parameters not supported") }, /* DTL WRSOM */{SST(0x3A, 0x00, SS_FATAL|ENXIO, "Medium not present") }, /* DT WR OM */{SST(0x3A, 0x01, SS_FATAL|ENXIO, "Medium not present - tray closed") }, /* DT WR OM */{SST(0x3A, 0x02, SS_FATAL|ENXIO, "Medium not present - tray open") }, /* TL */{SST(0x3B, 0x00, SS_RDEF, "Sequential positioning error") }, /* T */{SST(0x3B, 0x01, SS_RDEF, "Tape position error at beginning-of-medium") }, /* T */{SST(0x3B, 0x02, SS_RDEF, "Tape position error at end-of-medium") }, /* L */{SST(0x3B, 0x03, SS_RDEF, "Tape or electronic vertical forms unit not ready") }, /* L */{SST(0x3B, 0x04, SS_RDEF, "Slew failure") }, /* L */{SST(0x3B, 0x05, SS_RDEF, "Paper jam") }, /* L */{SST(0x3B, 0x06, SS_RDEF, "Failed to sense top-of-form") }, /* L */{SST(0x3B, 0x07, SS_RDEF, "Failed to sense bottom-of-form") }, /* T */{SST(0x3B, 0x08, SS_RDEF, "Reposition error") }, /* S */{SST(0x3B, 0x09, SS_RDEF, "Read past end of medium") }, /* S */{SST(0x3B, 0x0A, SS_RDEF, "Read past beginning of medium") }, /* S */{SST(0x3B, 0x0B, SS_RDEF, "Position past end of medium") }, /* T S */{SST(0x3B, 0x0C, SS_RDEF, "Position past beginning of medium") }, /* DT WR OM */{SST(0x3B, 0x0D, SS_FATAL|ENOSPC, "Medium destination element full") }, /* DT WR OM */{SST(0x3B, 0x0E, SS_RDEF, "Medium source element empty") }, /* R */{SST(0x3B, 0x0F, SS_RDEF, "End of medium reached") }, /* DT WR OM */{SST(0x3B, 0x11, SS_RDEF, "Medium magazine not accessible") }, /* DT WR OM */{SST(0x3B, 0x12, SS_RDEF, "Medium magazine removed") }, /* DT WR OM */{SST(0x3B, 0x13, SS_RDEF, "Medium magazine inserted") }, /* DT WR OM */{SST(0x3B, 0x14, SS_RDEF, "Medium magazine locked") }, /* DT WR OM */{SST(0x3B, 0x15, SS_RDEF, "Medium magazine unlocked") }, /* DTLPWRSOMCAE */{SST(0x3D, 0x00, SS_RDEF, "Invalid bits in identify message") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x00, SS_RDEF, "Logical unit has not self-configured yet") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x01, SS_RDEF, "Logical unit failure") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x02, SS_RDEF, "Timeout on logical unit") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x00, SS_RDEF, "Target operating conditions have changed") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x01, SS_RDEF, "Microcode has been changed") }, /* DTLPWRSOMC */{SST(0x3F, 0x02, SS_RDEF, "Changed operating definition") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x03, SS_RDEF, "Inquiry data has changed") }, /* DT WR OMCAE */{SST(0x3F, 0x04, SS_RDEF, "Component device attached") }, /* DT WR OMCAE */{SST(0x3F, 0x05, SS_RDEF, "Device identifier changed") }, /* DT WR OMCAE */{SST(0x3F, 0x06, SS_RDEF, "Redundancy group created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x07, SS_RDEF, "Redundancy group deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x08, SS_RDEF, "Spare created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x09, SS_RDEF, "Spare deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x0A, SS_RDEF, "Volume set created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x0B, SS_RDEF, "Volume set deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x0C, SS_RDEF, "Volume set deassigned") }, /* DT WR OMCAE */{SST(0x3F, 0x0D, SS_RDEF, "Volume set reassigned") }, /* D */{SST(0x40, 0x00, SS_RDEF, "Ram failure") }, /* deprecated - use 40 NN instead */ /* DTLPWRSOMCAE */{SST(0x40, 0x80, SS_RDEF, "Diagnostic failure: ASCQ = Component ID") }, /* DTLPWRSOMCAE */{SST(0x40, 0xFF, SS_RDEF|SSQ_RANGE, NULL) },/* Range 0x80->0xFF */ /* D */{SST(0x41, 0x00, SS_RDEF, "Data path failure") }, /* deprecated - use 40 NN instead */ /* D */{SST(0x42, 0x00, SS_RDEF, "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */ /* DTLPWRSOMCAE */{SST(0x43, 0x00, SS_RDEF, "Message error") }, /* DTLPWRSOMCAE */{SST(0x44, 0x00, SS_RDEF, "Internal target failure") }, /* DTLPWRSOMCAE */{SST(0x45, 0x00, SS_RDEF, "Select or reselect failure") }, /* DTLPWRSOMC */{SST(0x46, 0x00, SS_RDEF, "Unsuccessful soft reset") }, /* DTLPWRSOMCAE */{SST(0x47, 0x00, SS_RDEF, "SCSI parity error") }, /* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_RDEF, "Initiator detected error message received") }, /* DTLPWRSOMCAE */{SST(0x49, 0x00, SS_RDEF, "Invalid message error") }, /* DTLPWRSOMCAE */{SST(0x4A, 0x00, SS_RDEF, "Command phase error") }, /* DTLPWRSOMCAE */{SST(0x4B, 0x00, SS_RDEF, "Data phase error") }, /* DTLPWRSOMCAE */{SST(0x4C, 0x00, SS_RDEF, "Logical unit failed self-configuration") }, /* DTLPWRSOMCAE */{SST(0x4D, 0x00, SS_RDEF, "Tagged overlapped commands: ASCQ = Queue tag ID") }, /* DTLPWRSOMCAE */{SST(0x4D, 0xFF, SS_RDEF|SSQ_RANGE, NULL)}, /* Range 0x00->0xFF */ /* DTLPWRSOMCAE */{SST(0x4E, 0x00, SS_RDEF, "Overlapped commands attempted") }, /* T */{SST(0x50, 0x00, SS_RDEF, "Write append error") }, /* T */{SST(0x50, 0x01, SS_RDEF, "Write append position error") }, /* T */{SST(0x50, 0x02, SS_RDEF, "Position error related to timing") }, /* T O */{SST(0x51, 0x00, SS_RDEF, "Erase failure") }, /* T */{SST(0x52, 0x00, SS_RDEF, "Cartridge fault") }, /* DTL WRSOM */{SST(0x53, 0x00, SS_RDEF, "Media load or eject failed") }, /* T */{SST(0x53, 0x01, SS_RDEF, "Unload tape failure") }, /* DT WR OM */{SST(0x53, 0x02, SS_RDEF, "Medium removal prevented") }, /* P */{SST(0x54, 0x00, SS_RDEF, "Scsi to host system interface failure") }, /* P */{SST(0x55, 0x00, SS_RDEF, "System resource failure") }, /* D O */{SST(0x55, 0x01, SS_FATAL|ENOSPC, "System buffer full") }, /* R */{SST(0x57, 0x00, SS_RDEF, "Unable to recover table-of-contents") }, /* O */{SST(0x58, 0x00, SS_RDEF, "Generation does not exist") }, /* O */{SST(0x59, 0x00, SS_RDEF, "Updated block read") }, /* DTLPWRSOM */{SST(0x5A, 0x00, SS_RDEF, "Operator request or state change input") }, /* DT WR OM */{SST(0x5A, 0x01, SS_RDEF, "Operator medium removal request") }, /* DT W O */{SST(0x5A, 0x02, SS_RDEF, "Operator selected write protect") }, /* DT W O */{SST(0x5A, 0x03, SS_RDEF, "Operator selected write permit") }, /* DTLPWRSOM */{SST(0x5B, 0x00, SS_RDEF, "Log exception") }, /* DTLPWRSOM */{SST(0x5B, 0x01, SS_RDEF, "Threshold condition met") }, /* DTLPWRSOM */{SST(0x5B, 0x02, SS_RDEF, "Log counter at maximum") }, /* DTLPWRSOM */{SST(0x5B, 0x03, SS_RDEF, "Log list codes exhausted") }, /* D O */{SST(0x5C, 0x00, SS_RDEF, "RPL status change") }, /* D O */{SST(0x5C, 0x01, SS_NOP|SSQ_PRINT_SENSE, "Spindles synchronized") }, /* D O */{SST(0x5C, 0x02, SS_RDEF, "Spindles not synchronized") }, /* DTLPWRSOMCAE */{SST(0x5D, 0x00, SS_RDEF, "Failure prediction threshold exceeded") }, /* DTLPWRSOMCAE */{SST(0x5D, 0xFF, SS_RDEF, "Failure prediction threshold exceeded (false)") }, /* DTLPWRSO CA */{SST(0x5E, 0x00, SS_RDEF, "Low power condition on") }, /* DTLPWRSO CA */{SST(0x5E, 0x01, SS_RDEF, "Idle condition activated by timer") }, /* DTLPWRSO CA */{SST(0x5E, 0x02, SS_RDEF, "Standby condition activated by timer") }, /* DTLPWRSO CA */{SST(0x5E, 0x03, SS_RDEF, "Idle condition activated by command") }, /* DTLPWRSO CA */{SST(0x5E, 0x04, SS_RDEF, "Standby condition activated by command") }, /* S */{SST(0x60, 0x00, SS_RDEF, "Lamp failure") }, /* S */{SST(0x61, 0x00, SS_RDEF, "Video acquisition error") }, /* S */{SST(0x61, 0x01, SS_RDEF, "Unable to acquire video") }, /* S */{SST(0x61, 0x02, SS_RDEF, "Out of focus") }, /* S */{SST(0x62, 0x00, SS_RDEF, "Scan head positioning error") }, /* R */{SST(0x63, 0x00, SS_RDEF, "End of user area encountered on this track") }, /* R */{SST(0x63, 0x01, SS_FATAL|ENOSPC, "Packet does not fit in available space") }, /* R */{SST(0x64, 0x00, SS_RDEF, "Illegal mode for this track") }, /* R */{SST(0x64, 0x01, SS_RDEF, "Invalid packet size") }, /* DTLPWRSOMCAE */{SST(0x65, 0x00, SS_RDEF, "Voltage fault") }, /* S */{SST(0x66, 0x00, SS_RDEF, "Automatic document feeder cover up") }, /* S */{SST(0x66, 0x01, SS_RDEF, "Automatic document feeder lift up") }, /* S */{SST(0x66, 0x02, SS_RDEF, "Document jam in automatic document feeder") }, /* S */{SST(0x66, 0x03, SS_RDEF, "Document miss feed automatic in document feeder") }, /* A */{SST(0x67, 0x00, SS_RDEF, "Configuration failure") }, /* A */{SST(0x67, 0x01, SS_RDEF, "Configuration of incapable logical units failed") }, /* A */{SST(0x67, 0x02, SS_RDEF, "Add logical unit failed") }, /* A */{SST(0x67, 0x03, SS_RDEF, "Modification of logical unit failed") }, /* A */{SST(0x67, 0x04, SS_RDEF, "Exchange of logical unit failed") }, /* A */{SST(0x67, 0x05, SS_RDEF, "Remove of logical unit failed") }, /* A */{SST(0x67, 0x06, SS_RDEF, "Attachment of logical unit failed") }, /* A */{SST(0x67, 0x07, SS_RDEF, "Creation of logical unit failed") }, /* A */{SST(0x68, 0x00, SS_RDEF, "Logical unit not configured") }, /* A */{SST(0x69, 0x00, SS_RDEF, "Data loss on logical unit") }, /* A */{SST(0x69, 0x01, SS_RDEF, "Multiple logical unit failures") }, /* A */{SST(0x69, 0x02, SS_RDEF, "Parity/data mismatch") }, /* A */{SST(0x6A, 0x00, SS_RDEF, "Informational, refer to log") }, /* A */{SST(0x6B, 0x00, SS_RDEF, "State change has occurred") }, /* A */{SST(0x6B, 0x01, SS_RDEF, "Redundancy level got better") }, /* A */{SST(0x6B, 0x02, SS_RDEF, "Redundancy level got worse") }, /* A */{SST(0x6C, 0x00, SS_RDEF, "Rebuild failure occurred") }, /* A */{SST(0x6D, 0x00, SS_RDEF, "Recalculate failure occurred") }, /* A */{SST(0x6E, 0x00, SS_RDEF, "Command to logical unit failed") }, /* T */{SST(0x70, 0x00, SS_RDEF, "Decompression exception short: ASCQ = Algorithm ID") }, /* T */{SST(0x70, 0xFF, SS_RDEF|SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* T */{SST(0x71, 0x00, SS_RDEF, "Decompression exception long: ASCQ = Algorithm ID") }, /* T */{SST(0x71, 0xFF, SS_RDEF|SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* R */{SST(0x72, 0x00, SS_RDEF, "Session fixation error") }, /* R */{SST(0x72, 0x01, SS_RDEF, "Session fixation error writing lead-in") }, /* R */{SST(0x72, 0x02, SS_RDEF, "Session fixation error writing lead-out") }, /* R */{SST(0x72, 0x03, SS_RDEF, "Session fixation error - incomplete track in session") }, /* R */{SST(0x72, 0x04, SS_RDEF, "Empty or partially written reserved track") }, /* R */{SST(0x73, 0x00, SS_RDEF, "CD control error") }, /* R */{SST(0x73, 0x01, SS_RDEF, "Power calibration area almost full") }, /* R */{SST(0x73, 0x02, SS_FATAL|ENOSPC, "Power calibration area is full") }, /* R */{SST(0x73, 0x03, SS_RDEF, "Power calibration area error") }, /* R */{SST(0x73, 0x04, SS_RDEF, "Program memory area update failure") }, /* R */{SST(0x73, 0x05, SS_RDEF, "program memory area is full") } }; const int asc_table_size = sizeof(asc_table)/sizeof(asc_table[0]); struct asc_key { int asc; int ascq; }; static int ascentrycomp(const void *key, const void *member) { int asc; int ascq; const struct asc_table_entry *table_entry; asc = ((const struct asc_key *)key)->asc; ascq = ((const struct asc_key *)key)->ascq; table_entry = (const struct asc_table_entry *)member; if (asc >= table_entry->asc) { if (asc > table_entry->asc) return (1); if (ascq <= table_entry->ascq) { /* Check for ranges */ if (ascq == table_entry->ascq || ((table_entry->action & SSQ_RANGE) != 0 && ascq >= (table_entry - 1)->ascq)) return (0); return (-1); } return (1); } return (-1); } static int senseentrycomp(const void *key, const void *member) { int sense_key; const struct sense_key_table_entry *table_entry; sense_key = *((const int *)key); table_entry = (const struct sense_key_table_entry *)member; if (sense_key >= table_entry->sense_key) { if (sense_key == table_entry->sense_key) return (0); return (1); } return (-1); } static void fetchtableentries(int sense_key, int asc, int ascq, struct scsi_inquiry_data *inq_data, const struct sense_key_table_entry **sense_entry, const struct asc_table_entry **asc_entry) { caddr_t match; const struct asc_table_entry *asc_tables[2]; const struct sense_key_table_entry *sense_tables[2]; struct asc_key asc_ascq; size_t asc_tables_size[2]; size_t sense_tables_size[2]; int num_asc_tables; int num_sense_tables; int i; /* Default to failure */ *sense_entry = NULL; *asc_entry = NULL; match = NULL; if (inq_data != NULL) match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)sense_quirk_table, sense_quirk_table_size, sizeof(*sense_quirk_table), scsi_inquiry_match); if (match != NULL) { struct scsi_sense_quirk_entry *quirk; quirk = (struct scsi_sense_quirk_entry *)match; asc_tables[0] = quirk->asc_info; asc_tables_size[0] = quirk->num_ascs; asc_tables[1] = asc_table; asc_tables_size[1] = asc_table_size; num_asc_tables = 2; sense_tables[0] = quirk->sense_key_info; sense_tables_size[0] = quirk->num_sense_keys; sense_tables[1] = sense_key_table; sense_tables_size[1] = sense_key_table_size; num_sense_tables = 2; } else { asc_tables[0] = asc_table; asc_tables_size[0] = asc_table_size; num_asc_tables = 1; sense_tables[0] = sense_key_table; sense_tables_size[0] = sense_key_table_size; num_sense_tables = 1; } asc_ascq.asc = asc; asc_ascq.ascq = ascq; for (i = 0; i < num_asc_tables; i++) { void *found_entry; found_entry = bsearch(&asc_ascq, asc_tables[i], asc_tables_size[i], sizeof(**asc_tables), ascentrycomp); if (found_entry) { *asc_entry = (struct asc_table_entry *)found_entry; break; } } for (i = 0; i < num_sense_tables; i++) { void *found_entry; found_entry = bsearch(&sense_key, sense_tables[i], sense_tables_size[i], sizeof(**sense_tables), senseentrycomp); if (found_entry) { *sense_entry = (struct sense_key_table_entry *)found_entry; break; } } } void scsi_sense_desc(int sense_key, int asc, int ascq, struct scsi_inquiry_data *inq_data, const char **sense_key_desc, const char **asc_desc) { const struct asc_table_entry *asc_entry; const struct sense_key_table_entry *sense_entry; fetchtableentries(sense_key, asc, ascq, inq_data, &sense_entry, &asc_entry); *sense_key_desc = sense_entry->desc; if (asc_entry != NULL) *asc_desc = asc_entry->desc; else if (asc >= 0x80 && asc <= 0xff) *asc_desc = "Vendor Specific ASC"; else if (ascq >= 0x80 && ascq <= 0xff) *asc_desc = "Vendor Specific ASCQ"; else *asc_desc = "Reserved ASC/ASCQ pair"; } /* * Given sense and device type information, return the appropriate action. * If we do not understand the specific error as identified by the ASC/ASCQ * pair, fall back on the more generic actions derived from the sense key. */ scsi_sense_action scsi_error_action(struct ccb_scsiio *csio, struct scsi_inquiry_data *inq_data, u_int32_t sense_flags) { const struct asc_table_entry *asc_entry; const struct sense_key_table_entry *sense_entry; int error_code, sense_key, asc, ascq; scsi_sense_action action; scsi_extract_sense(&csio->sense_data, &error_code, &sense_key, &asc, &ascq); if (error_code == SSD_DEFERRED_ERROR) { /* * XXX dufault@FreeBSD.org * This error doesn't relate to the command associated * with this request sense. A deferred error is an error * for a command that has already returned GOOD status * (see SCSI2 8.2.14.2). * * By my reading of that section, it looks like the current * command has been cancelled, we should now clean things up * (hopefully recovering any lost data) and then retry the * current command. There are two easy choices, both wrong: * * 1. Drop through (like we had been doing), thus treating * this as if the error were for the current command and * return and stop the current command. * * 2. Issue a retry (like I made it do) thus hopefully * recovering the current transfer, and ignoring the * fact that we've dropped a command. * * These should probably be handled in a device specific * sense handler or punted back up to a user mode daemon */ action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; } else { fetchtableentries(sense_key, asc, ascq, inq_data, &sense_entry, &asc_entry); /* * Override the 'No additional Sense' entry (0,0) * with the error action of the sense key. */ if (asc_entry != NULL && (asc != 0 || ascq != 0)) action = asc_entry->action; else action = sense_entry->action; if (sense_key == SSD_KEY_RECOVERED_ERROR) { /* * The action succeeded but the device wants * the user to know that some recovery action * was required. */ action &= ~(SS_MASK|SSQ_MASK|SS_ERRMASK); action |= SS_NOP|SSQ_PRINT_SENSE; } else if (sense_key == SSD_KEY_ILLEGAL_REQUEST) { if ((sense_flags & SF_QUIET_IR) != 0) action &= ~SSQ_PRINT_SENSE; } else if (sense_key == SSD_KEY_UNIT_ATTENTION) { if ((sense_flags & SF_RETRY_UA) != 0 && (action & SS_MASK) == SS_FAIL) { action &= ~(SS_MASK|SSQ_MASK); action |= SS_RETRY|SSQ_DECREMENT_COUNT| SSQ_PRINT_SENSE; } } } #ifdef KERNEL if (bootverbose) sense_flags |= SF_PRINT_ALWAYS; #endif if ((sense_flags & SF_PRINT_ALWAYS) != 0) action |= SSQ_PRINT_SENSE; else if ((sense_flags & SF_NO_PRINT) != 0) action &= ~SSQ_PRINT_SENSE; return (action); } char * scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len) { u_int8_t cdb_len; int i; if (cdb_ptr == NULL) return(""); /* Silence warnings */ cdb_len = 0; /* * This is taken from the SCSI-3 draft spec. * (T10/1157D revision 0.3) * The top 3 bits of an opcode are the group code. The next 5 bits * are the command code. * Group 0: six byte commands * Group 1: ten byte commands * Group 2: ten byte commands * Group 3: reserved * Group 4: sixteen byte commands * Group 5: twelve byte commands * Group 6: vendor specific * Group 7: vendor specific */ switch((*cdb_ptr >> 5) & 0x7) { case 0: cdb_len = 6; break; case 1: case 2: cdb_len = 10; break; case 3: case 6: case 7: /* in this case, just print out the opcode */ cdb_len = 1; break; case 4: cdb_len = 16; break; case 5: cdb_len = 12; break; } *cdb_string = '\0'; for (i = 0; i < cdb_len; i++) snprintf(cdb_string + strlen(cdb_string), len - strlen(cdb_string), "%x ", cdb_ptr[i]); return(cdb_string); } const char * scsi_status_string(struct ccb_scsiio *csio) { switch(csio->scsi_status) { case SCSI_STATUS_OK: return("OK"); case SCSI_STATUS_CHECK_COND: return("Check Condition"); case SCSI_STATUS_BUSY: return("Busy"); case SCSI_STATUS_INTERMED: return("Intermediate"); case SCSI_STATUS_INTERMED_COND_MET: return("Intermediate-Condition Met"); case SCSI_STATUS_RESERV_CONFLICT: return("Reservation Conflict"); case SCSI_STATUS_CMD_TERMINATED: return("Command Terminated"); case SCSI_STATUS_QUEUE_FULL: return("Queue Full"); case SCSI_STATUS_ACA_ACTIVE: return("ACA Active"); case SCSI_STATUS_TASK_ABORTED: return("Task Aborted"); default: { static char unkstr[64]; snprintf(unkstr, sizeof(unkstr), "Unknown %#x", csio->scsi_status); return(unkstr); } } } /* * scsi_command_string() returns 0 for success and -1 for failure. */ #ifdef _KERNEL int scsi_command_string(struct ccb_scsiio *csio, struct sbuf *sb) #else /* !_KERNEL */ int scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio, struct sbuf *sb) #endif /* _KERNEL/!_KERNEL */ { struct scsi_inquiry_data *inq_data; char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; #ifdef _KERNEL struct ccb_getdev cgd; #endif /* _KERNEL */ #ifdef _KERNEL /* * Get the device information. */ xpt_setup_ccb(&cgd.ccb_h, csio->ccb_h.path, /*priority*/ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); /* * If the device is unconfigured, just pretend that it is a hard * drive. scsi_op_desc() needs this. */ if (cgd.ccb_h.status == CAM_DEV_NOT_THERE) cgd.inq_data.device = T_DIRECT; inq_data = &cgd.inq_data; #else /* !_KERNEL */ inq_data = &device->inq_data; #endif /* _KERNEL/!_KERNEL */ if ((csio->ccb_h.flags & CAM_CDB_POINTER) != 0) { sbuf_printf(sb, "%s. CDB: %s", scsi_op_desc(csio->cdb_io.cdb_ptr[0], inq_data), scsi_cdb_string(csio->cdb_io.cdb_ptr, cdb_str, sizeof(cdb_str))); } else { sbuf_printf(sb, "%s. CDB: %s", scsi_op_desc(csio->cdb_io.cdb_bytes[0], inq_data), scsi_cdb_string(csio->cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str))); } return(0); } /* * scsi_sense_sbuf() returns 0 for success and -1 for failure. */ #ifdef _KERNEL int scsi_sense_sbuf(struct ccb_scsiio *csio, struct sbuf *sb, scsi_sense_string_flags flags) #else /* !_KERNEL */ int scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio, struct sbuf *sb, scsi_sense_string_flags flags) #endif /* _KERNEL/!_KERNEL */ { struct scsi_sense_data *sense; struct scsi_inquiry_data *inq_data; #ifdef _KERNEL struct ccb_getdev cgd; #endif /* _KERNEL */ u_int32_t info; int error_code; int sense_key; int asc, ascq; char path_str[64]; #ifndef _KERNEL if (device == NULL) return(-1); #endif /* !_KERNEL */ if ((csio == NULL) || (sb == NULL)) return(-1); /* * If the CDB is a physical address, we can't deal with it.. */ if ((csio->ccb_h.flags & CAM_CDB_PHYS) != 0) flags &= ~SSS_FLAG_PRINT_COMMAND; #ifdef _KERNEL xpt_path_string(csio->ccb_h.path, path_str, sizeof(path_str)); #else /* !_KERNEL */ cam_path_string(device, path_str, sizeof(path_str)); #endif /* _KERNEL/!_KERNEL */ #ifdef _KERNEL /* * Get the device information. */ xpt_setup_ccb(&cgd.ccb_h, csio->ccb_h.path, /*priority*/ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); /* * If the device is unconfigured, just pretend that it is a hard * drive. scsi_op_desc() needs this. */ if (cgd.ccb_h.status == CAM_DEV_NOT_THERE) cgd.inq_data.device = T_DIRECT; inq_data = &cgd.inq_data; #else /* !_KERNEL */ inq_data = &device->inq_data; #endif /* _KERNEL/!_KERNEL */ sense = NULL; if (flags & SSS_FLAG_PRINT_COMMAND) { sbuf_cat(sb, path_str); #ifdef _KERNEL scsi_command_string(csio, sb); #else /* !_KERNEL */ scsi_command_string(device, csio, sb); #endif /* _KERNEL/!_KERNEL */ sbuf_printf(sb, "\n"); } /* * If the sense data is a physical pointer, forget it. */ if (csio->ccb_h.flags & CAM_SENSE_PTR) { if (csio->ccb_h.flags & CAM_SENSE_PHYS) return(-1); else { /* * bcopy the pointer to avoid unaligned access * errors on finicky architectures. We don't * ensure that the sense data is pointer aligned. */ bcopy(&csio->sense_data, sense, sizeof(struct scsi_sense_data *)); } } else { /* * If the physical sense flag is set, but the sense pointer * is not also set, we assume that the user is an idiot and * return. (Well, okay, it could be that somehow, the * entire csio is physical, but we would have probably core * dumped on one of the bogus pointer deferences above * already.) */ if (csio->ccb_h.flags & CAM_SENSE_PHYS) return(-1); else sense = &csio->sense_data; } sbuf_cat(sb, path_str); error_code = sense->error_code & SSD_ERRCODE; sense_key = sense->flags & SSD_KEY; switch (error_code) { case SSD_DEFERRED_ERROR: sbuf_printf(sb, "Deferred Error: "); /* FALLTHROUGH */ case SSD_CURRENT_ERROR: { const char *sense_key_desc; const char *asc_desc; asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; scsi_sense_desc(sense_key, asc, ascq, inq_data, &sense_key_desc, &asc_desc); sbuf_cat(sb, sense_key_desc); info = scsi_4btoul(sense->info); if (sense->error_code & SSD_ERRCODE_VALID) { switch (sense_key) { case SSD_KEY_NOT_READY: case SSD_KEY_ILLEGAL_REQUEST: case SSD_KEY_UNIT_ATTENTION: case SSD_KEY_DATA_PROTECT: break; case SSD_KEY_BLANK_CHECK: sbuf_printf(sb, " req sz: %d (decimal)", info); break; default: if (info) { if (sense->flags & SSD_ILI) { sbuf_printf(sb, " ILI (length " "mismatch): %d", info); } else { sbuf_printf(sb, " info:%x", info); } } } } else if (info) { sbuf_printf(sb, " info?:%x", info); } if (sense->extra_len >= 4) { if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) { sbuf_printf(sb, " csi:%x,%x,%x,%x", sense->cmd_spec_info[0], sense->cmd_spec_info[1], sense->cmd_spec_info[2], sense->cmd_spec_info[3]); } } sbuf_printf(sb, " asc:%x,%x\n%s%s", asc, ascq, path_str, asc_desc); if (sense->extra_len >= 7 && sense->fru) { sbuf_printf(sb, " field replaceable unit: %x", sense->fru); } if ((sense->extra_len >= 10) && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) { switch(sense_key) { case SSD_KEY_ILLEGAL_REQUEST: { int bad_command; char tmpstr2[40]; if (sense->sense_key_spec[0] & 0x40) bad_command = 1; else bad_command = 0; tmpstr2[0] = '\0'; /* Bit pointer is valid */ if (sense->sense_key_spec[0] & 0x08) snprintf(tmpstr2, sizeof(tmpstr2), "bit %d ", sense->sense_key_spec[0] & 0x7); sbuf_printf(sb, ": %s byte %d %sis invalid", bad_command ? "Command" : "Data", scsi_2btoul( &sense->sense_key_spec[1]), tmpstr2); break; } case SSD_KEY_RECOVERED_ERROR: case SSD_KEY_HARDWARE_ERROR: case SSD_KEY_MEDIUM_ERROR: sbuf_printf(sb, " actual retry count: %d", scsi_2btoul( &sense->sense_key_spec[1])); break; default: sbuf_printf(sb, " sks:%#x,%#x", sense->sense_key_spec[0], scsi_2btoul( &sense->sense_key_spec[1])); break; } } break; } default: sbuf_printf(sb, "error code %d", sense->error_code & SSD_ERRCODE); if (sense->error_code & SSD_ERRCODE_VALID) { sbuf_printf(sb, " at block no. %d (decimal)", info = scsi_4btoul(sense->info)); } } sbuf_printf(sb, "\n"); return(0); } #ifdef _KERNEL char * scsi_sense_string(struct ccb_scsiio *csio, char *str, int str_len) #else /* !_KERNEL */ char * scsi_sense_string(struct cam_device *device, struct ccb_scsiio *csio, char *str, int str_len) #endif /* _KERNEL/!_KERNEL */ { struct sbuf sb; sbuf_new(&sb, str, str_len, 0); #ifdef _KERNEL scsi_sense_sbuf(csio, &sb, SSS_FLAG_PRINT_COMMAND); #else /* !_KERNEL */ scsi_sense_sbuf(device, csio, &sb, SSS_FLAG_PRINT_COMMAND); #endif /* _KERNEL/!_KERNEL */ sbuf_finish(&sb); return(sbuf_data(&sb)); } #ifdef _KERNEL void scsi_sense_print(struct ccb_scsiio *csio) { struct sbuf sb; char str[512]; sbuf_new(&sb, str, sizeof(str), 0); scsi_sense_sbuf(csio, &sb, SSS_FLAG_PRINT_COMMAND); sbuf_finish(&sb); printf("%s", sbuf_data(&sb)); } #else /* !_KERNEL */ void scsi_sense_print(struct cam_device *device, struct ccb_scsiio *csio, FILE *ofile) { struct sbuf sb; char str[512]; if ((device == NULL) || (csio == NULL) || (ofile == NULL)) return; sbuf_new(&sb, str, sizeof(str), 0); scsi_sense_sbuf(device, csio, &sb, SSS_FLAG_PRINT_COMMAND); sbuf_finish(&sb); fprintf(ofile, "%s", sbuf_data(&sb)); } #endif /* _KERNEL/!_KERNEL */ /* * This function currently requires at least 36 bytes, or * SHORT_INQUIRY_LENGTH, worth of data to function properly. If this * function needs more or less data in the future, another length should be * defined in scsi_all.h to indicate the minimum amount of data necessary * for this routine to function properly. */ void scsi_print_inquiry(struct scsi_inquiry_data *inq_data) { u_int8_t type; char *dtype, *qtype; char vendor[16], product[48], revision[16], rstr[4]; type = SID_TYPE(inq_data); /* * Figure out basic device type and qualifier. */ if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) { qtype = "(vendor-unique qualifier)"; } else { switch (SID_QUAL(inq_data)) { case SID_QUAL_LU_CONNECTED: qtype = ""; break; case SID_QUAL_LU_OFFLINE: qtype = "(offline)"; break; case SID_QUAL_RSVD: qtype = "(reserved qualifier)"; break; default: case SID_QUAL_BAD_LU: qtype = "(lun not supported)"; break; } } switch (type) { case T_DIRECT: dtype = "Direct Access"; break; case T_SEQUENTIAL: dtype = "Sequential Access"; break; case T_PRINTER: dtype = "Printer"; break; case T_PROCESSOR: dtype = "Processor"; break; case T_CDROM: dtype = "CD-ROM"; break; case T_WORM: dtype = "Worm"; break; case T_SCANNER: dtype = "Scanner"; break; case T_OPTICAL: dtype = "Optical"; break; case T_CHANGER: dtype = "Changer"; break; case T_COMM: dtype = "Communication"; break; case T_STORARRAY: dtype = "Storage Array"; break; case T_ENCLOSURE: dtype = "Enclosure Services"; break; case T_RBC: dtype = "Simplified Direct Access"; break; case T_OCRW: dtype = "Optical Card Read/Write"; break; case T_NODEVICE: dtype = "Uninstalled"; default: dtype = "unknown"; break; } cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), sizeof(vendor)); cam_strvis(product, inq_data->product, sizeof(inq_data->product), sizeof(product)); cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), sizeof(revision)); if (SID_ANSI_REV(inq_data) == SCSI_REV_CCS) bcopy("CCS", rstr, 4); else snprintf(rstr, sizeof (rstr), "%d", SID_ANSI_REV(inq_data)); printf("<%s %s %s> %s %s SCSI-%s device %s\n", vendor, product, revision, SID_IS_REMOVABLE(inq_data) ? "Removable" : "Fixed", dtype, rstr, qtype); } /* * Table of syncrates that don't follow the "divisible by 4" * rule. This table will be expanded in future SCSI specs. */ static struct { u_int period_factor; u_int period; /* in 100ths of ns */ } scsi_syncrates[] = { { 0x08, 625 }, /* FAST-160 */ { 0x09, 1250 }, /* FAST-80 */ { 0x0a, 2500 }, /* FAST-40 40MHz */ { 0x0b, 3030 }, /* FAST-40 33MHz */ { 0x0c, 5000 } /* FAST-20 */ }; /* * Return the frequency in kHz corresponding to the given * sync period factor. */ u_int scsi_calc_syncsrate(u_int period_factor) { int i; int num_syncrates; num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period_factor == scsi_syncrates[i].period_factor) { /* Period in kHz */ return (100000000 / scsi_syncrates[i].period); } } /* * Wasn't in the table, so use the standard * 4 times conversion. */ return (10000000 / (period_factor * 4 * 10)); } /* * Return the SCSI sync parameter that corresponsd to * the passed in period in 10ths of ns. */ u_int scsi_calc_syncparam(u_int period) { int i; int num_syncrates; if (period == 0) return (~0); /* Async */ /* Adjust for exception table being in 100ths. */ period *= 10; num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period <= scsi_syncrates[i].period) { /* Period in 100ths of ns */ return (scsi_syncrates[i].period_factor); } } /* * Wasn't in the table, so use the standard * 1/4 period in ns conversion. */ return (period/400); } void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_test_unit_ready *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_test_unit_ready *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = TEST_UNIT_READY; } void scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), void *data_ptr, u_int8_t dxfer_len, u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_request_sense *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_request_sense *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REQUEST_SENSE; scsi_cmd->length = dxfer_len; } void scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t *inq_buf, u_int32_t inq_len, int evpd, u_int8_t page_code, u_int8_t sense_len, u_int32_t timeout) { struct scsi_inquiry *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/inq_buf, /*dxfer_len*/inq_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_inquiry *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = INQUIRY; if (evpd) { scsi_cmd->byte2 |= SI_EVPD; scsi_cmd->page_code = page_code; } /* * A 'transfer units' count of 256 is coded as * zero for all commands with a single byte count * field. */ if (inq_len == 256) inq_len = 0; scsi_cmd->length = inq_len; } void scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int dbd, u_int8_t page_code, u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { scsi_mode_sense_len(csio, retries, cbfcnp, tag_action, dbd, page_code, page, param_buf, param_len, 0, sense_len, timeout); } void scsi_mode_sense_len(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int dbd, u_int8_t page_code, u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if ((param_len < 256) && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_sense_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_6; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = page_code | page; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_sense_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_10; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = page_code | page; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { scsi_mode_select_len(csio, retries, cbfcnp, tag_action, scsi_page_fmt, save_pages, param_buf, param_len, 0, sense_len, timeout); } void scsi_mode_select_len(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, int minimum_cmd_size, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if ((param_len < 256) && (minimum_cmd_size < 10)) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_select_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_6; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_select_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_10; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_OUT, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } void scsi_log_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t page_code, u_int8_t page, int save_pages, int ppc, u_int32_t paramptr, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_log_sense *scsi_cmd; u_int8_t cdb_len; scsi_cmd = (struct scsi_log_sense *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = LOG_SENSE; scsi_cmd->page = page_code | page; if (save_pages != 0) scsi_cmd->byte2 |= SLS_SP; if (ppc != 0) scsi_cmd->byte2 |= SLS_PPC; scsi_ulto2b(paramptr, scsi_cmd->paramptr); scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/param_buf, /*dxfer_len*/param_len, sense_len, cdb_len, timeout); } void scsi_log_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t page_code, int save_pages, int pc_reset, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_log_select *scsi_cmd; u_int8_t cdb_len; scsi_cmd = (struct scsi_log_select *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = LOG_SELECT; scsi_cmd->page = page_code & SLS_PAGE_CODE; if (save_pages != 0) scsi_cmd->byte2 |= SLS_SP; if (pc_reset != 0) scsi_cmd->byte2 |= SLS_PCR; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_OUT, tag_action, /*data_ptr*/param_buf, /*dxfer_len*/param_len, sense_len, cdb_len, timeout); } /* * Prevent or allow the user to remove the media */ void scsi_prevent(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_prevent *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_prevent *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = PREVENT_ALLOW; scsi_cmd->how = action; } /* XXX allow specification of address and PMI bit and LBA */ void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, struct scsi_read_capacity_data *rcap_buf, u_int8_t sense_len, u_int32_t timeout) { struct scsi_read_capacity *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rcap_buf, /*dxfer_len*/sizeof(*rcap_buf), sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_read_capacity *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_CAPACITY; } void scsi_read_capacity_16(struct ccb_scsiio *csio, uint32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), uint8_t tag_action, uint64_t lba, int reladr, int pmi, struct scsi_read_capacity_data_long *rcap_buf, uint8_t sense_len, uint32_t timeout) { struct scsi_read_capacity_16 *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rcap_buf, /*dxfer_len*/sizeof(*rcap_buf), sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_read_capacity_16 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SERVICE_ACTION_IN; scsi_cmd->service_action = SRC16_SERVICE_ACTION; scsi_u64to8b(lba, scsi_cmd->addr); scsi_ulto4b(sizeof(*rcap_buf), scsi_cmd->alloc_len); if (pmi) reladr |= SRC16_PMI; if (reladr) reladr |= SRC16_RELADR; } void scsi_report_luns(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, struct scsi_report_luns_data *rpl_buf, u_int32_t alloc_len, u_int8_t sense_len, u_int32_t timeout) { struct scsi_report_luns *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rpl_buf, /*dxfer_len*/alloc_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_report_luns *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REPORT_LUNS; scsi_ulto4b(alloc_len, scsi_cmd->addr); } /* * Syncronize the media to the contents of the cache for * the given lba/count pair. Specifying 0/0 means sync * the whole cache. */ void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t begin_lba, u_int16_t lb_count, u_int8_t sense_len, u_int32_t timeout) { struct scsi_sync_cache *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_sync_cache *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SYNCHRONIZE_CACHE; scsi_ulto4b(begin_lba, scsi_cmd->begin_lba); scsi_ulto2b(lb_count, scsi_cmd->lb_count); } void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int readop, u_int8_t byte2, int minimum_cmd_size, u_int64_t lba, u_int32_t block_count, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation * as some legacy hardware does not support the 10 byte commands. * If any of the bits in byte2 is set, we have to go with a larger * command. */ if ((minimum_cmd_size < 10) && ((lba & 0x1fffff) == lba) && ((block_count & 0xff) == block_count) && (byte2 == 0)) { /* * We can fit in a 6 byte cdb. */ struct scsi_rw_6 *scsi_cmd; scsi_cmd = (struct scsi_rw_6 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_6 : WRITE_6; scsi_ulto3b(lba, scsi_cmd->addr); scsi_cmd->length = block_count & 0xff; scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("6byte: %x%x%x:%d:%d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->length, dxfer_len)); } else if ((minimum_cmd_size < 12) && ((block_count & 0xffff) == block_count) && ((lba & 0xffffffff) == lba)) { /* * Need a 10 byte cdb. */ struct scsi_rw_10 *scsi_cmd; scsi_cmd = (struct scsi_rw_10 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_10 : WRITE_10; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto2b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], dxfer_len)); } else if ((minimum_cmd_size < 16) && ((block_count & 0xffffffff) == block_count) && ((lba & 0xffffffff) == lba)) { /* * The block count is too big for a 10 byte CDB, use a 12 * byte CDB. */ struct scsi_rw_12 *scsi_cmd; scsi_cmd = (struct scsi_rw_12 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_12 : WRITE_12; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("12byte: %x%x%x%x:%x%x%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], scsi_cmd->length[2], scsi_cmd->length[3], dxfer_len)); } else { /* * 16 byte CDB. We'll only get here if the LBA is larger * than 2^32, or if the user asks for a 16 byte command. */ struct scsi_rw_16 *scsi_cmd; scsi_cmd = (struct scsi_rw_16 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_16 : WRITE_16; scsi_cmd->byte2 = byte2; scsi_u64to8b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, sense_len, cdb_len, timeout); } void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout) { struct scsi_start_stop_unit *scsi_cmd; int extra_flags = 0; scsi_cmd = (struct scsi_start_stop_unit *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = START_STOP_UNIT; if (start != 0) { scsi_cmd->how |= SSS_START; /* it takes a lot of power to start a drive */ extra_flags |= CAM_HIGH_POWER; } if (load_eject != 0) scsi_cmd->how |= SSS_LOEJ; if (immediate != 0) scsi_cmd->byte2 |= SSS_IMMED; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE | extra_flags, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_static_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_static_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } #ifdef _KERNEL static void init_scsi_delay(void) { int delay; delay = SCSI_DELAY; TUNABLE_INT_FETCH("kern.cam.scsi_delay", &delay); if (set_scsi_delay(delay) != 0) { printf("cam: invalid value for tunable kern.cam.scsi_delay\n"); set_scsi_delay(SCSI_DELAY); } } SYSINIT(scsi_delay, SI_SUB_TUNABLES, SI_ORDER_ANY, init_scsi_delay, NULL); static int sysctl_scsi_delay(SYSCTL_HANDLER_ARGS) { int error, delay; delay = scsi_delay; error = sysctl_handle_int(oidp, &delay, sizeof(delay), req); if (error != 0 || req->newptr == NULL) return (error); return (set_scsi_delay(delay)); } SYSCTL_PROC(_kern_cam, OID_AUTO, scsi_delay, CTLTYPE_INT|CTLFLAG_RW, 0, 0, sysctl_scsi_delay, "I", "Delay to allow devices to settle after a SCSI bus reset (ms)"); static int set_scsi_delay(int delay) { /* * If someone sets this to 0, we assume that they want the * minimum allowable bus settle delay. */ if (delay == 0) { printf("cam: using minimum scsi_delay (%dms)\n", SCSI_MIN_DELAY); delay = SCSI_MIN_DELAY; } if (delay < SCSI_MIN_DELAY) return (EINVAL); scsi_delay = delay; return (0); } #endif /* _KERNEL */