freebsd-skq/sys/cam/scsi/scsi_all.c
Kenneth D. Merry 56eac725a3 Fix ATAPI/USB/Firewire CDROM drive handling in cd(4) and hopefully fix
a number of related problems along the way.

 - Automatically detect CDROM drives that can't handle 6 byte mode
   sense and mode select, and adjust our command size accordingly.
   We have to handle this in the cd(4) driver (where the buffers are
   allocated), since the parameter list length is different for the
   6 and 10 byte mode sense commands.

 - Remove MODE_SENSE and MODE_SELECT translation removed in ATAPICAM
   and in the umass(4) driver, since there's no way for that to work
   properly.

 - Add a quirk entry for CDROM drives that just hang when they get a 6
   byte mode sense or mode select.  The reason for the quirk must be
   documented in a PR, and all quirks must be approved by
   ken@FreeBSD.org.  This is to make sure that we fully understand why
   each quirk is needed.  Once the CAM_NEW_TRAN_CODE is finished, we
   should be able to remove any such quirks, since we'll know what
   protocol the drive speaks (SCSI, ATAPI, etc.) and therefore whether
   we should use 6 or 10 byte mode sense/select commands.

 - Change the way the da(4) handles the no_6_byte sysctl.  There is
   now a per-drive sysctl to set the minimum command size for that
   particular disk.  (Since you could have multiple disks with
   multiple requirements in one system.)

 - Loader tunable support for all the sysctls in the da(4) and cd(4)
   drivers.

 - Add a CDIOCCLOSE ioctl for cd(4) (bde pointed this out a long
   time ago).

 - Add a media validation routine (cdcheckmedia()) to the cd(4)
   driver, to fix some problems bde pointed out a long time ago.  We
   now allow open() to succeed no matter what, but if we don't detect
   valid media, the user can only issue CDIOCCLOSE or CDIOCEJECT
   ioctls.

 - The media validation routine also reads the table of contents off
   the drive.  We use the table of contents to implement the
   CDIOCPLAYTRACKS ioctl using the PLAY AUDIO MSF command.  The
   PLAY AUDIO TRACK INDEX command that we previously used was
   deprecated after SCSI-2.  It works in every SCSI CDROM I've tried,
   but doesn't seem to work on ATAPI CDROM drives.  We still use the
   play audio track index command if we don't have a valid TOC, but
   I suppose it'll fail anyway in that case.

 - Add _len() versions of scsi_mode_sense() and scsi_mode_select() so
   that we can specify the minimum command length.

 - Fix a couple of formatting problems in the sense printing code.

MFC after: 	4 weeks
2003-02-21 06:19:38 +00:00

2988 lines
88 KiB
C

/*
* 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$
*/
#include <sys/param.h>
#ifdef _KERNEL
#include <opt_scsi.h>
#include <sys/systm.h>
#include <sys/libkern.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#else
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_xpt.h>
#include <cam/scsi/scsi_all.h>
#include <sys/sbuf.h>
#ifndef _KERNEL
#include <camlib.h>
#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 */
/* 89 */
/* 8A */
/* 8B */
/* 8C */
/* 8D */
/* 8E */
/* 8F */
/* 90 */
/* 91 */
/* 92 */
/* 93 */
/* 94 */
/* 95 */
/* 96 */
/* 97 */
/* 98 */
/* 99 */
/* 9A */
/* 9B */
/* 9C */
/* 9D */
/* 9E */
/* 9F */
/* 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 OM O READ(12) */
{0xA8, 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 WRITE(12) */
{0xAA, 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 <sys/param.h>
#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;
}
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)
{
return(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)
{
return(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_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_int32_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)) {
/*
* 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 {
/*
* The block count is too big for a 10 byte CDB, use a 12
* byte CDB. READ/WRITE(12) are currently only defined for
* optical devices.
*/
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));
}
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 */