/* * Implementation of Utility functions for all SCSI device types. * * Copyright (c) 1997, 1998, 1999 Justin T. Gibbs. * Copyright (c) 1997, 1998 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. * * $FreeBSD: src/sys/cam/scsi/scsi_all.c,v 1.38 2002/09/23 04:56:35 mjacob Exp $ * $Id$ */ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/version.h> /* Core SCSI definitions */ #include <scsi/scsi_host.h> #include "aiclib.h" #include "cam.h" #ifndef FALSE #define FALSE 0 #endif /* FALSE */ #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #ifndef ERESTART #define ERESTART -1 /* restart syscall */ #endif #ifndef EJUSTRETURN #define EJUSTRETURN -2 /* don't modify regs, just return */ #endif 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 **); static void * scsibsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); typedef int (cam_quirkmatch_t)(caddr_t, caddr_t); static int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, int str_len); static caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, int entry_size, cam_quirkmatch_t *comp_func); #define SCSI_NO_SENSE_STRINGS 1 #if !defined(SCSI_NO_SENSE_STRINGS) #define SST(asc, ascq, action, desc) \ asc, ascq, action, desc #else static const char empty_string[] = ""; #define SST(asc, ascq, action, desc) \ asc, ascq, action, empty_string #endif static 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" } }; static 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 } }; static 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_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO, "Logical unit not ready, cause not reportable") }, /* DTLPWRSOMCAE */{SST(0x04, 0x01, SS_TUR|SSQ_DELAY|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_RDEF, "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_RDEF, "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_NOP, "Medium not present") }, /* DT WR OM */{SST(0x3A, 0x01, SS_NOP, "Medium not present - tray closed") }, /* DT WR OM */{SST(0x3A, 0x01, SS_NOP, "Medium not present - tray open") }, /* DT WR OM */{SST(0x3A, 0x03, SS_NOP, "Medium not present - Loadable") }, /* DT WR OM */{SST(0x3A, 0x04, SS_NOP, "Medium not present - medium auxiliary " "memory accessible") }, /* DT WR OM */{SST(0x3A, 0xFF, SS_NOP, NULL) },/* Range 0x05->0xFF */ /* 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_INQ_REFRESH|SSQ_DECREMENT_COUNT, "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") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x0E, SS_RDEF, "Reported luns data has changed") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x0F, SS_RETRY|SSQ_DECREMENT_COUNT | SSQ_DELAY_RANDOM|EBUSY, "Echo buffer overwritten") }, /* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, "Medium Loadable") }, /* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, "Medium auxiliary memory accessible") }, /* 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|SSQ_FALLBACK, "SCSI parity error") }, /* DTLPWRSOMCAE */{SST(0x47, 0x01, SS_RDEF|SSQ_FALLBACK, "Data Phase CRC error detected") }, /* DTLPWRSOMCAE */{SST(0x47, 0x02, SS_RDEF|SSQ_FALLBACK, "SCSI parity error detected during ST data phase") }, /* DTLPWRSOMCAE */{SST(0x47, 0x03, SS_RDEF|SSQ_FALLBACK, "Information Unit iuCRC error") }, /* DTLPWRSOMCAE */{SST(0x47, 0x04, SS_RDEF|SSQ_FALLBACK, "Asynchronous information protection error detected") }, /* DTLPWRSOMCAE */{SST(0x47, 0x05, SS_RDEF|SSQ_FALLBACK, "Protocol server CRC error") }, /* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_RDEF|SSQ_FALLBACK, "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") } }; static 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) { void *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((void *)inq_data, (void *)sense_quirk_table, sense_quirk_table_size, sizeof(*sense_quirk_table), aic_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 = scsibsearch(&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 = scsibsearch(&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; } } } static void * scsibsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { const void *entry; u_int l; u_int u; u_int m; l = -1; u = nmemb; while (l + 1 != u) { m = (l + u) / 2; entry = base + m * size; if (compar(key, entry) > 0) l = m; else u = m; } entry = base + u * size; if (u == nmemb || compar(key, entry) != 0) return (NULL); return ((void *)entry); } /* * Compare string with pattern, returning 0 on match. * Short pattern matches trailing blanks in name, * wildcard '*' in pattern matches rest of name, * wildcard '?' matches a single non-space character. */ static int cam_strmatch(const uint8_t *str, const uint8_t *pattern, int str_len) { while (*pattern != '\0'&& str_len > 0) { if (*pattern == '*') { return (0); } if ((*pattern != *str) && (*pattern != '?' || *str == ' ')) { return (1); } pattern++; str++; str_len--; } while (str_len > 0 && *str++ == ' ') str_len--; return (str_len); } static caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, int entry_size, cam_quirkmatch_t *comp_func) { for (; num_entries > 0; num_entries--, quirk_table += entry_size) { if ((*comp_func)(target, quirk_table) == 0) return (quirk_table); } return (NULL); } void aic_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. */ aic_sense_action aic_sense_error_action(struct scsi_sense_data *sense_data, struct scsi_inquiry_data *inq_data, uint32_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; aic_sense_action action; scsi_extract_sense(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; } } } 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); } /* * Try make as good a match as possible with * available sub drivers */ int aic_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); } /* * 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 aic_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 speed in KB/s. */ u_int aic_calc_speed(u_int width, u_int period, u_int offset, u_int min_rate) { u_int freq; if (offset != 0 && period < min_rate) freq = aic_calc_syncsrate(period); else /* Roughly 3.3MB/s for async */ freq = 3300; freq <<= width; return (freq); } uint32_t aic_error_action(struct scsi_cmnd *cmd, struct scsi_inquiry_data *inq_data, cam_status status, u_int scsi_status) { aic_sense_action err_action; int sense; sense = (cmd->result >> 24) == DRIVER_SENSE; switch (status) { case CAM_REQ_CMP: err_action = SS_NOP; break; case CAM_AUTOSENSE_FAIL: case CAM_SCSI_STATUS_ERROR: switch (scsi_status) { case SCSI_STATUS_OK: case SCSI_STATUS_COND_MET: case SCSI_STATUS_INTERMED: case SCSI_STATUS_INTERMED_COND_MET: err_action = SS_NOP; break; case SCSI_STATUS_CMD_TERMINATED: case SCSI_STATUS_CHECK_COND: if (sense != 0) { struct scsi_sense_data *sense; sense = (struct scsi_sense_data *) &cmd->sense_buffer; err_action = aic_sense_error_action(sense, inq_data, 0); } else { err_action = SS_RETRY|SSQ_FALLBACK | SSQ_DECREMENT_COUNT|EIO; } break; case SCSI_STATUS_QUEUE_FULL: case SCSI_STATUS_BUSY: err_action = SS_RETRY|SSQ_DELAY|SSQ_MANY | SSQ_DECREMENT_COUNT|EBUSY; break; case SCSI_STATUS_RESERV_CONFLICT: default: err_action = SS_FAIL|EBUSY; break; } break; case CAM_CMD_TIMEOUT: case CAM_REQ_CMP_ERR: case CAM_UNEXP_BUSFREE: case CAM_UNCOR_PARITY: case CAM_DATA_RUN_ERR: err_action = SS_RETRY|SSQ_FALLBACK|EIO; break; case CAM_UA_ABORT: case CAM_UA_TERMIO: case CAM_MSG_REJECT_REC: case CAM_SEL_TIMEOUT: err_action = SS_FAIL|EIO; break; case CAM_REQ_INVALID: case CAM_PATH_INVALID: case CAM_DEV_NOT_THERE: case CAM_NO_HBA: case CAM_PROVIDE_FAIL: case CAM_REQ_TOO_BIG: case CAM_RESRC_UNAVAIL: case CAM_BUSY: default: /* panic?? These should never occur in our application. */ err_action = SS_FAIL|EIO; break; case CAM_SCSI_BUS_RESET: case CAM_BDR_SENT: case CAM_REQUEUE_REQ: /* Unconditional requeue */ err_action = SS_RETRY; break; } return (err_action); } char * aic_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, aic_option_callback_t *callback, u_long callback_arg) { char *tok_end; char *tok_end2; int i; int instance; int targ; int done; char tok_list[] = {'.', ',', '{', '}', '\0'}; /* All options use a ':' name/arg separator */ if (*opt_arg != ':') return (opt_arg); opt_arg++; instance = -1; targ = -1; done = FALSE; /* * Restore separator that may be in * the middle of our option argument. */ tok_end = strchr(opt_arg, '\0'); if (tok_end < end) *tok_end = ','; while (!done) { switch (*opt_arg) { case '{': if (instance == -1) { instance = 0; } else { if (depth > 1) { if (targ == -1) targ = 0; } else { printf("Malformed Option %s\n", opt_name); done = TRUE; } } opt_arg++; break; case '}': if (targ != -1) targ = -1; else if (instance != -1) instance = -1; opt_arg++; break; case ',': case '.': if (instance == -1) done = TRUE; else if (targ >= 0) targ++; else if (instance >= 0) instance++; opt_arg++; break; case '\0': done = TRUE; break; default: tok_end = end; for (i = 0; tok_list[i]; i++) { tok_end2 = strchr(opt_arg, tok_list[i]); if ((tok_end2) && (tok_end2 < tok_end)) tok_end = tok_end2; } callback(callback_arg, instance, targ, simple_strtol(opt_arg, NULL, 0)); opt_arg = tok_end; break; } } return (opt_arg); }