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
This commit is contained in:
Kenneth D. Merry 2003-02-21 06:19:38 +00:00
parent b0d226932e
commit 56eac725a3
7 changed files with 1116 additions and 364 deletions

View File

@ -1950,6 +1950,7 @@ scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio,
#else /* !_KERNEL */
scsi_command_string(device, csio, sb);
#endif /* _KERNEL/!_KERNEL */
sbuf_printf(sb, "\n");
}
/*
@ -2068,9 +2069,9 @@ scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio,
/* Bit pointer is valid */
if (sense->sense_key_spec[0] & 0x08)
snprintf(tmpstr2, sizeof(tmpstr2),
"bit %d",
"bit %d ",
sense->sense_key_spec[0] & 0x7);
sbuf_printf(sb, ": %s byte %d %s is invalid",
sbuf_printf(sb, ": %s byte %d %sis invalid",
bad_command ? "Command" : "Data",
scsi_2btoul(
&sense->sense_key_spec[1]),
@ -2446,13 +2447,25 @@ scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries,
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) {
if ((param_len < 256)
&& (minimum_cmd_size < 10)) {
/*
* We can fit in a 6 byte cdb.
*/
@ -2499,13 +2512,27 @@ scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries,
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) {
if ((param_len < 256)
&& (minimum_cmd_size < 10)) {
/*
* We can fit in a 6 byte cdb.
*/

View File

@ -926,6 +926,15 @@ void scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries,
u_int8_t *param_buf, u_int32_t param_len,
u_int8_t sense_len, u_int32_t 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);
void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *,
union ccb *),
@ -934,6 +943,14 @@ void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries,
u_int32_t param_len, u_int8_t sense_len,
u_int32_t 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);
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,

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000 Kenneth D. Merry
* Copyright (c) 2000, 2002 Kenneth D. Merry
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -655,38 +655,47 @@ struct scsi_read_cd_cap_data
u_int8_t length_0; /* Least significant */
};
union cd_pages
struct cd_audio_page
{
struct audio_page
u_int8_t page_code;
#define CD_PAGE_CODE 0x3F
#define AUDIO_PAGE 0x0e
#define CD_PAGE_PS 0x80
u_int8_t param_len;
u_int8_t flags;
#define CD_PA_SOTC 0x02
#define CD_PA_IMMED 0x04
u_int8_t unused[2];
u_int8_t format_lba;
#define CD_PA_FORMAT_LBA 0x0F
#define CD_PA_APR_VALID 0x80
u_int8_t lb_per_sec[2];
struct port_control
{
u_int8_t page_code;
#define CD_PAGE_CODE 0x3F
#define AUDIO_PAGE 0x0e
#define CD_PAGE_PS 0x80
u_int8_t param_len;
u_int8_t flags;
#define CD_PA_SOTC 0x02
#define CD_PA_IMMED 0x04
u_int8_t unused[2];
u_int8_t format_lba;
#define CD_PA_FORMAT_LBA 0x0F
#define CD_PA_APR_VALID 0x80
u_int8_t lb_per_sec[2];
struct port_control
{
u_int8_t channels;
#define CHANNEL 0x0F
#define CHANNEL_0 1
#define CHANNEL_1 2
#define CHANNEL_2 4
#define CHANNEL_3 8
#define LEFT_CHANNEL CHANNEL_0
#define RIGHT_CHANNEL CHANNEL_1
u_int8_t volume;
} port[4];
#define LEFT_PORT 0
#define RIGHT_PORT 1
}audio;
u_int8_t channels;
#define CHANNEL 0x0F
#define CHANNEL_0 1
#define CHANNEL_1 2
#define CHANNEL_2 4
#define CHANNEL_3 8
#define LEFT_CHANNEL CHANNEL_0
#define RIGHT_CHANNEL CHANNEL_1
u_int8_t volume;
} port[4];
#define LEFT_PORT 0
#define RIGHT_PORT 1
};
union cd_pages
{
struct cd_audio_page audio;
};
struct cd_mode_data_10
{
struct scsi_mode_header_10 header;
struct scsi_mode_blk_desc blk_desc;
union cd_pages page;
};
struct cd_mode_data
@ -696,6 +705,20 @@ struct cd_mode_data
union cd_pages page;
};
union cd_mode_data_6_10
{
struct cd_mode_data mode_data_6;
struct cd_mode_data_10 mode_data_10;
};
struct cd_mode_params
{
STAILQ_ENTRY(cd_mode_params) links;
int cdb_size;
int alloc_len;
u_int8_t *mode_buf;
};
__BEGIN_DECLS
void scsi_report_key(struct ccb_scsiio *csio, u_int32_t retries,
void (*cbfcnp)(struct cam_periph *, union ccb *),

View File

@ -128,6 +128,8 @@ struct da_softc {
struct disk disk;
union ccb saved_ccb;
dev_t dev;
struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
};
struct da_quirk_entry {
@ -479,16 +481,15 @@ static void dashutdown(void *arg, int howto);
static int da_retry_count = DA_DEFAULT_RETRY;
static int da_default_timeout = DA_DEFAULT_TIMEOUT;
static int da_no_6_byte = 0;
SYSCTL_NODE(_kern_cam, OID_AUTO, da, CTLFLAG_RD, 0,
"CAM Direct Access Disk driver");
SYSCTL_INT(_kern_cam_da, OID_AUTO, retry_count, CTLFLAG_RW,
&da_retry_count, 0, "Normal I/O retry count");
TUNABLE_INT("kern.cam.da.retry_count", &da_retry_count);
SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RW,
&da_default_timeout, 0, "Normal I/O timeout (in seconds)");
SYSCTL_INT(_kern_cam_da, OID_AUTO, no_6_byte, CTLFLAG_RW,
&da_no_6_byte, 0, "No 6 bytes commands");
TUNABLE_INT("kern.cam.da.default_timeout", &da_default_timeout);
/*
* DA_ORDEREDTAG_INTERVAL determines how often, relative
@ -1075,6 +1076,38 @@ daasync(void *callback_arg, u_int32_t code,
}
}
static int
dacmdsizesysctl(SYSCTL_HANDLER_ARGS)
{
int error, value;
value = *(int *)arg1;
error = sysctl_handle_int(oidp, &value, 0, req);
if ((error != 0)
|| (req->newptr == NULL))
return (error);
/*
* Acceptable values here are 6, 10 or 12. It's possible we may
* support a 16 byte minimum command size in the future, since
* there are now READ(16) and WRITE(16) commands defined in the
* SBC-2 spec.
*/
if (value < 6)
value = 6;
else if ((value > 6)
&& (value <= 10))
value = 10;
else if (value > 10)
value = 12;
*(int *)arg1 = value;
return (0);
}
static cam_status
daregister(struct cam_periph *periph, void *arg)
{
@ -1082,6 +1115,7 @@ daregister(struct cam_periph *periph, void *arg)
struct da_softc *softc;
struct ccb_setasync csa;
struct ccb_getdev *cgd;
char tmpstr[80], tmpstr2[80];
caddr_t match;
cgd = (struct ccb_getdev *)arg;
@ -1127,11 +1161,52 @@ daregister(struct cam_periph *periph, void *arg)
else
softc->quirks = DA_Q_NONE;
snprintf(tmpstr, sizeof(tmpstr), "CAM DA unit %d", periph->unit_number);
snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number);
softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_kern_cam_da), OID_AUTO, tmpstr2,
CTLFLAG_RD, 0, tmpstr);
if (softc->sysctl_tree == NULL) {
printf("daregister: unable to allocate sysctl tree\n");
free(softc, M_DEVBUF);
return (CAM_REQ_CMP_ERR);
}
/*
* RBC devices don't have to support READ(6), only READ(10).
*/
if (softc->quirks & DA_Q_NO_6_BYTE || SID_TYPE(&cgd->inq_data) == T_RBC)
softc->minimum_cmd_size = 10;
else
softc->minimum_cmd_size = 6;
/*
* Load the user's default, if any.
*/
snprintf(tmpstr, sizeof(tmpstr), "kern.cam.da.%d.minimum_cmd_size",
periph->unit_number);
TUNABLE_INT_FETCH(tmpstr, &softc->minimum_cmd_size);
/*
* 6, 10 and 12 are the currently permissible values.
*/
if (softc->minimum_cmd_size < 6)
softc->minimum_cmd_size = 6;
else if ((softc->minimum_cmd_size > 6)
&& (softc->minimum_cmd_size <= 10))
softc->minimum_cmd_size = 10;
else if (softc->minimum_cmd_size > 12)
softc->minimum_cmd_size = 12;
/*
* Now register the sysctl handler, so the user can the value on
* the fly.
*/
SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree),
OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW,
&softc->minimum_cmd_size, 0, dacmdsizesysctl, "I",
"Minimum CDB size");
/*
* Block our timeout handler while we
* add this softc to the dev list.
@ -1233,8 +1308,6 @@ dastart(struct cam_periph *periph, union ccb *start_ccb)
} else {
tag_code = MSG_SIMPLE_Q_TAG;
}
if (da_no_6_byte && softc->minimum_cmd_size == 6)
softc->minimum_cmd_size = 10;
scsi_read_write(&start_ccb->csio,
/*retries*/da_retry_count,
dadone,
@ -1616,7 +1689,7 @@ daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
{
struct da_softc *softc;
struct cam_periph *periph;
int error, sense_key, error_code, asc, ascq;
int error;
periph = xpt_path_periph(ccb->ccb_h.path);
softc = (struct da_softc *)periph->softc;
@ -1626,8 +1699,16 @@ daerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
* READ(6)/WRITE(6) and upgrade to using 10 byte cdbs.
*/
error = 0;
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR
&& ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) {
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) {
error = cmd6workaround(ccb);
} else if (((ccb->ccb_h.status & CAM_STATUS_MASK) ==
CAM_SCSI_STATUS_ERROR)
&& (ccb->ccb_h.status & CAM_AUTOSNS_VALID)
&& (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND)
&& ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0)
&& ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) {
int sense_key, error_code, asc, ascq;
scsi_extract_sense(&ccb->csio.sense_data,
&error_code, &sense_key, &asc, &ascq);
if (sense_key == SSD_KEY_ILLEGAL_REQUEST)

View File

@ -419,36 +419,6 @@ atapi_action(struct cam_sim *sim, union ccb *ccb)
}
break;
}
case MODE_SELECT_6:
/* FALLTHROUGH */
case MODE_SENSE_6:
/*
* not supported by ATAPI/MMC devices (per SCSI MMC spec)
* translate to _10 equivalent.
* (actually we should do this only if we have tried
* MODE_foo_6 and received ILLEGAL_REQUEST or
* INVALID COMMAND OPERATION CODE)
* alternative fix: behave like a honest CAM transport,
* do not muck with CDB contents, and change scsi_cd to
* always use MODE_SENSE_10 in cdgetmode(), or let scsi_cd
* know that this specific unit is an ATAPI/MMC one,
* and in /that case/ use MODE_SENSE_10
*/
CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE,
("Translating %s into _10 equivalent\n",
(hcb->cmd[0] == MODE_SELECT_6) ?
"MODE_SELECT_6" : "MODE_SENSE_6"));
hcb->cmd[0] |= 0x40;
hcb->cmd[6] = 0;
hcb->cmd[7] = 0;
hcb->cmd[8] = hcb->cmd[4];
hcb->cmd[9] = hcb->cmd[5];
hcb->cmd[4] = 0;
hcb->cmd[5] = 0;
break;
case READ_6:
/* FALLTHROUGH */

View File

@ -2846,22 +2846,6 @@ umass_scsi_6_to_10(unsigned char *cmd, int cmdlen, unsigned char **rcmd,
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* -----------------------------------------
* OP |B2 | ADDRESS |RSV| LEN |CTRL
*
* For mode sense/select, the format is:
*
* 6 byte:
* -------------------------
* | 0 | 1 | 2 | 3 | 4 | 5 |
* -------------------------
* OP |B2 |PAG|UNU|LEN|CTRL
*
* 10 byte:
* -----------------------------------------
* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
* -----------------------------------------
* OP |B2 |PAG| UNUSED | LEN |CTRL
*
* with the exception that mode select does not have a page field.
*/
switch (cmd[0]) {
case READ_6:
@ -2870,12 +2854,6 @@ umass_scsi_6_to_10(unsigned char *cmd, int cmdlen, unsigned char **rcmd,
case WRITE_6:
(*rcmd)[0] = WRITE_10;
break;
case MODE_SENSE_6:
(*rcmd)[0] = MODE_SENSE_10;
break;
case MODE_SELECT_6:
(*rcmd)[0] = MODE_SELECT_6;
break;
default:
return (0);
}
@ -2885,11 +2863,6 @@ umass_scsi_6_to_10(unsigned char *cmd, int cmdlen, unsigned char **rcmd,
case WRITE_6:
memcpy(&(*rcmd)[3], &cmd[1], 3);
break;
case MODE_SENSE_6:
(*rcmd)[2] = cmd[2];
case MODE_SELECT_6:
(*rcmd)[1] = cmd[1];
break;
}
(*rcmd)[8] = cmd[4];
(*rcmd)[9] = cmd[5];