summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/cam/scsi/scsi_all.c35
-rw-r--r--sys/cam/scsi/scsi_all.h17
-rw-r--r--sys/cam/scsi/scsi_cd.c1191
-rw-r--r--sys/cam/scsi/scsi_cd.h85
-rw-r--r--sys/cam/scsi/scsi_da.c97
-rw-r--r--sys/dev/ata/atapi-cam.c30
-rw-r--r--sys/dev/usb/umass.c27
7 files changed, 1117 insertions, 365 deletions
diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c
index 1ebbb2c..0f4100d 100644
--- a/sys/cam/scsi/scsi_all.c
+++ b/sys/cam/scsi/scsi_all.c
@@ -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]),
@@ -2447,12 +2448,24 @@ scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries,
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.
*/
@@ -2500,12 +2513,26 @@ scsi_mode_select(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)
{
+ 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.
*/
diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h
index 7841b3c..b972c5a 100644
--- a/sys/cam/scsi/scsi_all.h
+++ b/sys/cam/scsi/scsi_all.h
@@ -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,
diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c
index 543714f..f785962 100644
--- a/sys/cam/scsi/scsi_cd.c
+++ b/sys/cam/scsi/scsi_cd.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1997 Justin T. Gibbs.
- * Copyright (c) 1997, 1998, 1999, 2000, 2001 Kenneth D. Merry.
+ * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Kenneth D. Merry.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -79,11 +79,12 @@ struct cd_params {
};
typedef enum {
- CD_Q_NONE = 0x00,
- CD_Q_NO_TOUCH = 0x01,
- CD_Q_BCD_TRACKS = 0x02,
- CD_Q_NO_CHANGER = 0x04,
- CD_Q_CHANGER = 0x08
+ CD_Q_NONE = 0x00,
+ CD_Q_NO_TOUCH = 0x01,
+ CD_Q_BCD_TRACKS = 0x02,
+ CD_Q_NO_CHANGER = 0x04,
+ CD_Q_CHANGER = 0x08,
+ CD_Q_10_BYTE_ONLY = 0x10
} cd_quirks;
typedef enum {
@@ -95,7 +96,9 @@ typedef enum {
CD_FLAG_CHANGER = 0x040,
CD_FLAG_ACTIVE = 0x080,
CD_FLAG_SCHED_ON_COMP = 0x100,
- CD_FLAG_RETRY_UA = 0x200
+ CD_FLAG_RETRY_UA = 0x200,
+ CD_FLAG_VALID_MEDIA = 0x400,
+ CD_FLAG_VALID_TOC = 0x800
} cd_flags;
typedef enum {
@@ -116,6 +119,16 @@ typedef enum {
#define ccb_state ppriv_field0
#define ccb_bp ppriv_ptr1
+struct cd_tocdata {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entries[100];
+};
+
+struct cd_toc_single {
+ struct ioc_toc_header header;
+ struct cd_toc_entry entry;
+};
+
typedef enum {
CD_STATE_PROBE,
CD_STATE_NORMAL
@@ -137,6 +150,21 @@ struct cd_softc {
struct cam_periph *periph;
dev_t dev;
eventhandler_tag clonetag;
+ int minimum_command_size;
+ struct sysctl_ctx_list sysctl_ctx;
+ struct sysctl_oid *sysctl_tree;
+ STAILQ_HEAD(, cd_mode_params) mode_queue;
+ struct cd_tocdata toc;
+};
+
+struct cd_page_sizes {
+ int page;
+ int page_size;
+};
+
+static struct cd_page_sizes cd_page_size_table[] =
+{
+ { AUDIO_PAGE, sizeof(struct cd_audio_page)}
};
struct cd_quirk_entry {
@@ -145,12 +173,22 @@ struct cd_quirk_entry {
};
/*
- * These quirk entries aren't strictly necessary. Basically, what they do
- * is tell cdregister() up front that a device is a changer. Otherwise, it
- * will figure that fact out once it sees a LUN on the device that is
- * greater than 0. If it is known up front that a device is a changer, all
- * I/O to the device will go through the changer scheduling routines, as
+ * The changer quirk entries aren't strictly necessary. Basically, what
+ * they do is tell cdregister() up front that a device is a changer.
+ * Otherwise, it will figure that fact out once it sees a LUN on the device
+ * that is greater than 0. If it is known up front that a device is a changer,
+ * all I/O to the device will go through the changer scheduling routines, as
* opposed to the "normal" CD code.
+ *
+ * NOTE ON 10_BYTE_ONLY quirks: Any 10_BYTE_ONLY quirks MUST be because
+ * your device hangs when it gets a 10 byte command. Adding a quirk just
+ * to get rid of the informative diagnostic message is not acceptable. All
+ * 10_BYTE_ONLY quirks must be documented in full in a PR (which should be
+ * referenced in a comment along with the quirk) , and must be approved by
+ * ken@FreeBSD.org. Any quirks added that don't adhere to this policy may
+ * be removed until the submitter can explain why they are needed.
+ * 10_BYTE_ONLY quirks will be removed (as they will no longer be necessary)
+ * when the CAM_NEW_TRAN_CODE work is done.
*/
static struct cd_quirk_entry cd_quirk_table[] =
{
@@ -186,6 +224,7 @@ static periph_start_t cdstart;
static periph_oninv_t cdoninvalidate;
static void cdasync(void *callback_arg, u_int32_t code,
struct cam_path *path, void *arg);
+static int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS);
static void cdshorttimeout(void *arg);
static void cdschedule(struct cam_periph *periph, int priority);
static void cdrunchangerqueue(void *arg);
@@ -195,21 +234,25 @@ static int cdrunccb(union ccb *ccb,
u_int32_t cam_flags,
u_int32_t sense_flags),
u_int32_t cam_flags, u_int32_t sense_flags);
-static union ccb *cdgetccb(struct cam_periph *periph,
+static union ccb *cdgetccb(struct cam_periph *periph,
u_int32_t priority);
static void cddone(struct cam_periph *periph,
union ccb *start_ccb);
+static union cd_pages *cdgetpage(struct cd_mode_params *mode_params);
+static int cdgetpagesize(int page_num);
+static void cdprevent(struct cam_periph *periph, int action);
+static int cdcheckmedia(struct cam_periph *periph);
+static int cdsize(struct cam_periph *periph, u_int32_t *size);
+static int cd6byteworkaround(union ccb *ccb);
static int cderror(union ccb *ccb, u_int32_t cam_flags,
u_int32_t sense_flags);
-static void cdprevent(struct cam_periph *periph, int action);
-static int cdsize(dev_t dev, u_int32_t *size);
static int cdreadtoc(struct cam_periph *periph, u_int32_t mode,
- u_int32_t start, struct cd_toc_entry *data,
- u_int32_t len);
+ u_int32_t start, u_int8_t *data,
+ u_int32_t len, u_int32_t sense_flags);
static int cdgetmode(struct cam_periph *periph,
- struct cd_mode_data *data, u_int32_t page);
+ struct cd_mode_params *data, u_int32_t page);
static int cdsetmode(struct cam_periph *periph,
- struct cd_mode_data *data);
+ struct cd_mode_params *data);
static int cdplay(struct cam_periph *periph, u_int32_t blk,
u_int32_t len);
static int cdreadsubchannel(struct cam_periph *periph,
@@ -226,7 +269,7 @@ static int cdplaytracks(struct cam_periph *periph,
u_int32_t etrack, u_int32_t eindex);
static int cdpause(struct cam_periph *periph, u_int32_t go);
static int cdstopunit(struct cam_periph *periph, u_int32_t eject);
-static int cdstartunit(struct cam_periph *periph);
+static int cdstartunit(struct cam_periph *periph, int load);
static int cdsetspeed(struct cam_periph *periph,
u_int32_t rdspeed, u_int32_t wrspeed);
static int cdreportkey(struct cam_periph *periph,
@@ -276,8 +319,10 @@ SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");
SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer");
SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW,
&changer_min_busy_seconds, 0, "Minimum changer scheduling quantum");
+TUNABLE_INT("kern.cam.cd.changer.min_busy_seconds", &changer_min_busy_seconds);
SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW,
&changer_max_busy_seconds, 0, "Maximum changer scheduling quantum");
+TUNABLE_INT("kern.cam.cd.changer.max_busy_seconds", &changer_max_busy_seconds);
struct cdchanger {
path_id_t path_id;
@@ -554,12 +599,50 @@ cdasync(void *callback_arg, u_int32_t code,
}
}
+/*
+ * We have a handler function for this so we can check the values when the
+ * user sets them, instead of every time we look at them.
+ */
+static int
+cdcmdsizesysctl(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);
+
+ /*
+ * The only real values we can have here are 6 or 10. I don't
+ * really forsee having 12 be an option at any time in the future.
+ * So if the user sets something less than or equal to 6, we'll set
+ * it to 6. If he sets something greater than 6, we'll set it to 10.
+ *
+ * I suppose we could just return an error here for the wrong values,
+ * but I don't think it's necessary to do so, as long as we can
+ * determine the user's intent without too much trouble.
+ */
+ if (value < 6)
+ value = 6;
+ else if (value > 6)
+ value = 10;
+
+ *(int *)arg1 = value;
+
+ return (0);
+}
+
static cam_status
cdregister(struct cam_periph *periph, void *arg)
{
struct cd_softc *softc;
struct ccb_setasync csa;
struct ccb_getdev *cgd;
+ char tmpstr[80], tmpstr2[80];
caddr_t match;
cgd = (struct ccb_getdev *)arg;
@@ -582,6 +665,7 @@ cdregister(struct cam_periph *periph, void *arg)
bzero(softc, sizeof(*softc));
LIST_INIT(&softc->pending_ccbs);
+ STAILQ_INIT(&softc->mode_queue);
softc->state = CD_STATE_PROBE;
bioq_init(&softc->bio_queue);
if (SID_IS_REMOVABLE(&cgd->inq_data))
@@ -605,6 +689,45 @@ cdregister(struct cam_periph *periph, void *arg)
else
softc->quirks = CD_Q_NONE;
+ snprintf(tmpstr, sizeof(tmpstr), "CAM CD 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_cd), OID_AUTO,
+ tmpstr2, CTLFLAG_RD, 0, tmpstr);
+ if (softc->sysctl_tree == NULL) {
+ printf("cdregister: unable to allocate sysctl tree\n");
+ free(softc, M_DEVBUF);
+ return (CAM_REQ_CMP_ERR);
+ }
+
+ /* The default is 6 byte commands, unless quirked otherwise */
+ if (softc->quirks & CD_Q_10_BYTE_ONLY)
+ softc->minimum_command_size = 10;
+ else
+ softc->minimum_command_size = 6;
+
+ /*
+ * Load the user's default, if any.
+ */
+ snprintf(tmpstr, sizeof(tmpstr), "kern.cam.cd.%d.minimum_cmd_size",
+ periph->unit_number);
+ TUNABLE_INT_FETCH(tmpstr, &softc->minimum_command_size);
+
+ /* 6 and 10 are the only permissible values here. */
+ if (softc->minimum_command_size < 6)
+ softc->minimum_command_size = 6;
+ else if (softc->minimum_command_size > 6)
+ softc->minimum_command_size = 10;
+
+ /*
+ * 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_command_size, 0, cdcmdsizesysctl, "I",
+ "Minimum CDB size");
+
/*
* We need to register the statistics structure for this device,
* but we don't have the blocksize yet for it. So, we register
@@ -873,7 +996,6 @@ cdopen(dev_t dev, int flags, int fmt, struct thread *td)
{
struct cam_periph *periph;
struct cd_softc *softc;
- u_int32_t size;
int error;
int s;
@@ -902,26 +1024,12 @@ cdopen(dev_t dev, int flags, int fmt, struct thread *td)
if (cam_periph_acquire(periph) != CAM_REQ_CMP)
return(ENXIO);
- cdprevent(periph, PR_PREVENT);
-
- /* find out the size */
- if ((error = cdsize(dev, &size)) != 0) {
- cdprevent(periph, PR_ALLOW);
- cam_periph_unlock(periph);
- cam_periph_release(periph);
- return(error);
- }
-
/*
- * We unconditionally (re)set the blocksize each time the
- * CD device is opened. This is because the CD can change,
- * and therefore the blocksize might change.
- * XXX problems here if some slice or partition is still
- * open with the old size?
+ * Check for media, and set the appropriate flags. We don't bail
+ * if we don't have media, but then we don't allow anything but the
+ * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media.
*/
- if ((softc->device_stats.flags & DEVSTAT_BS_UNAVAILABLE) != 0)
- softc->device_stats.flags &= ~DEVSTAT_BS_UNAVAILABLE;
- softc->device_stats.block_size = softc->params.blksize;
+ cdcheckmedia(periph);
cam_periph_unlock(periph);
@@ -951,10 +1059,15 @@ cdclose(dev_t dev, int flag, int fmt, struct thread *td)
/*
* Since we're closing this CD, mark the blocksize as unavailable.
- * It will be marked as available whence the CD is opened again.
+ * It will be marked as available when the CD is opened again.
*/
softc->device_stats.flags |= DEVSTAT_BS_UNAVAILABLE;
+ /*
+ * We'll check the media and toc again at the next open().
+ */
+ softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
+
cam_periph_unlock(periph);
cam_periph_release(periph);
@@ -1330,6 +1443,21 @@ cdstrategy(struct bio *bp)
return;
}
+ /*
+ * If we don't have valid media, look for it before trying to
+ * schedule the I/O.
+ */
+ if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) {
+ int error;
+
+ error = cdcheckmedia(periph);
+ if (error != 0) {
+ splx(s);
+ biofinish(bp, NULL, error);
+ return;
+ }
+ }
+
/*
* Place it in the queue of disk activities for this disk
*/
@@ -1743,6 +1871,35 @@ cddone(struct cam_periph *periph, union ccb *done_ccb)
xpt_release_ccb(done_ccb);
}
+static union cd_pages *
+cdgetpage(struct cd_mode_params *mode_params)
+{
+ union cd_pages *page;
+
+ if (mode_params->cdb_size == 10)
+ page = (union cd_pages *)find_mode_page_10(
+ (struct scsi_mode_header_10 *)mode_params->mode_buf);
+ else
+ page = (union cd_pages *)find_mode_page_6(
+ (struct scsi_mode_header_6 *)mode_params->mode_buf);
+
+ return (page);
+}
+
+static int
+cdgetpagesize(int page_num)
+{
+ int i;
+
+ for (i = 0; i < (sizeof(cd_page_size_table)/
+ sizeof(cd_page_size_table[0])); i++) {
+ if (cd_page_size_table[i].page == page_num)
+ return (cd_page_size_table[i].page_size);
+ }
+
+ return (-1);
+}
+
static int
cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
{
@@ -1766,6 +1923,19 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
if (error != 0)
return(error);
+ /*
+ * If we don't have media loaded, check for it. If still don't
+ * have media loaded, we can only do a load or eject.
+ */
+ if (((softc->flags & CD_FLAG_VALID_MEDIA) == 0)
+ && ((cmd != CDIOCCLOSE)
+ && (cmd != CDIOCEJECT))) {
+ error = cdcheckmedia(periph);
+ if (error != 0) {
+ cam_periph_unlock(periph);
+ return (error);
+ }
+ }
switch (cmd) {
@@ -1781,57 +1951,117 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
{
struct ioc_play_track *args
= (struct ioc_play_track *) addr;
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCPLAYTRACKS\n"));
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.flags &= ~CD_PA_SOTC;
- data->page.audio.flags |= CD_PA_IMMED;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.flags &= ~CD_PA_SOTC;
+ page->audio.flags |= CD_PA_IMMED;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
if (error)
break;
- if (softc->quirks & CD_Q_BCD_TRACKS) {
- args->start_track = bin2bcd(args->start_track);
- args->end_track = bin2bcd(args->end_track);
+
+ /*
+ * This was originally implemented with the PLAY
+ * AUDIO TRACK INDEX command, but that command was
+ * deprecated after SCSI-2. Most (all?) SCSI CDROM
+ * drives support it but ATAPI and ATAPI-derivative
+ * drives don't seem to support it. So we keep a
+ * cache of the table of contents and translate
+ * track numbers to MSF format.
+ */
+ if (softc->flags & CD_FLAG_VALID_TOC) {
+ union msf_lba *sentry, *eentry;
+ int st, et;
+
+ if (args->end_track <
+ softc->toc.header.ending_track + 1)
+ args->end_track++;
+ if (args->end_track >
+ softc->toc.header.ending_track + 1)
+ args->end_track =
+ softc->toc.header.ending_track + 1;
+ st = args->start_track -
+ softc->toc.header.starting_track;
+ et = args->end_track -
+ softc->toc.header.starting_track;
+ if ((st < 0)
+ || (et < 0)
+ || (st > (softc->toc.header.ending_track -
+ softc->toc.header.starting_track))) {
+ error = EINVAL;
+ break;
+ }
+ sentry = &softc->toc.entries[st].addr;
+ eentry = &softc->toc.entries[et].addr;
+ error = cdplaymsf(periph,
+ sentry->msf.minute,
+ sentry->msf.second,
+ sentry->msf.frame,
+ eentry->msf.minute,
+ eentry->msf.second,
+ eentry->msf.frame);
+ } else {
+ /*
+ * If we don't have a valid TOC, try the
+ * play track index command. It is part of
+ * the SCSI-2 spec, but was removed in the
+ * MMC specs. ATAPI and ATAPI-derived
+ * drives don't support it.
+ */
+ if (softc->quirks & CD_Q_BCD_TRACKS) {
+ args->start_track =
+ bin2bcd(args->start_track);
+ args->end_track =
+ bin2bcd(args->end_track);
+ }
+ error = cdplaytracks(periph,
+ args->start_track,
+ args->start_index,
+ args->end_track,
+ args->end_index);
}
- error = cdplaytracks(periph,
- args->start_track,
- args->start_index,
- args->end_track,
- args->end_index);
}
break;
case CDIOCPLAYMSF:
{
struct ioc_play_msf *args
= (struct ioc_play_msf *) addr;
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCPLAYMSF\n"));
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.flags &= ~CD_PA_SOTC;
- data->page.audio.flags |= CD_PA_IMMED;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.flags &= ~CD_PA_SOTC;
+ page->audio.flags |= CD_PA_IMMED;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
if (error)
break;
error = cdplaymsf(periph,
@@ -1847,23 +2077,27 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
{
struct ioc_play_blocks *args
= (struct ioc_play_blocks *) addr;
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCPLAYBLOCKS\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.flags &= ~CD_PA_SOTC;
- data->page.audio.flags |= CD_PA_IMMED;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.flags &= ~CD_PA_SOTC;
+ page->audio.flags |= CD_PA_IMMED;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
if (error)
break;
error = cdplay(periph, args->blk, args->len);
@@ -1925,9 +2159,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
th = malloc(sizeof(struct ioc_toc_header), M_TEMP,
M_WAITOK);
- error = cdreadtoc(periph, 0, 0,
- (struct cd_toc_entry *)th,
- sizeof (*th));
+ error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
+ sizeof (*th), /*sense_flags*/0);
if (error) {
free(th, M_TEMP);
break;
@@ -1947,17 +2180,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
break;
case CDIOREADTOCENTRYS:
{
- typedef struct {
- struct ioc_toc_header header;
- struct cd_toc_entry entries[100];
- } data_t;
- typedef struct {
- struct ioc_toc_header header;
- struct cd_toc_entry entry;
- } lead_t;
-
- data_t *data;
- lead_t *lead;
+ struct cd_tocdata *data;
+ struct cd_toc_single *lead;
struct ioc_read_toc_entry *te =
(struct ioc_read_toc_entry *) addr;
struct ioc_toc_header *th;
@@ -1967,8 +2191,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOREADTOCENTRYS\n"));
- data = malloc(sizeof(data_t), M_TEMP, M_WAITOK);
- lead = malloc(sizeof(lead_t), M_TEMP, M_WAITOK);
+ data = malloc(sizeof(*data), M_TEMP, M_WAITOK);
+ lead = malloc(sizeof(*lead), M_TEMP, M_WAITOK);
if (te->data_len < sizeof(struct cd_toc_entry)
|| (te->data_len % sizeof(struct cd_toc_entry)) != 0
@@ -1983,9 +2207,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
}
th = &data->header;
- error = cdreadtoc(periph, 0, 0,
- (struct cd_toc_entry *)th,
- sizeof (*th));
+ error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
+ sizeof (*th), /*sense_flags*/0);
if (error) {
free(data, M_TEMP);
free(lead, M_TEMP);
@@ -2039,8 +2262,9 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
if (readlen > 0) {
error = cdreadtoc(periph, te->address_format,
starting_track,
- (struct cd_toc_entry *)data,
- readlen + sizeof (*th));
+ (u_int8_t *)data,
+ readlen + sizeof (*th),
+ /*sense_flags*/0);
if (error) {
free(data, M_TEMP);
free(lead, M_TEMP);
@@ -2054,9 +2278,9 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
th->ending_track = bcd2bin(th->ending_track);
if (idx == th->ending_track + 1) {
error = cdreadtoc(periph, te->address_format,
- LEADOUT,
- (struct cd_toc_entry *)lead,
- sizeof(*lead));
+ LEADOUT, (u_int8_t *)lead,
+ sizeof(*lead),
+ /*sense_flags*/0);
if (error) {
free(data, M_TEMP);
free(lead, M_TEMP);
@@ -2079,13 +2303,7 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
break;
case CDIOREADTOCENTRY:
{
- /* yeah yeah, this is ugly */
- typedef struct {
- struct ioc_toc_header header;
- struct cd_toc_entry entry;
- } data_t;
-
- data_t *data;
+ struct cd_toc_single *data;
struct ioc_read_toc_single_entry *te =
(struct ioc_read_toc_single_entry *) addr;
struct ioc_toc_header *th;
@@ -2094,7 +2312,7 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOREADTOCENTRY\n"));
- data = malloc(sizeof(data_t), M_TEMP, M_WAITOK);
+ data = malloc(sizeof(*data), M_TEMP, M_WAITOK);
if (te->address_format != CD_MSF_FORMAT
&& te->address_format != CD_LBA_FORMAT) {
@@ -2106,9 +2324,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
}
th = &data->header;
- error = cdreadtoc(periph, 0, 0,
- (struct cd_toc_entry *)th,
- sizeof (*th));
+ error = cdreadtoc(periph, 0, 0, (u_int8_t *)th,
+ sizeof (*th), /*sense_flags*/0);
if (error) {
free(data, M_TEMP);
break;
@@ -2137,8 +2354,8 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
}
error = cdreadtoc(periph, te->address_format, track,
- (struct cd_toc_entry *)data,
- sizeof(data_t));
+ (u_int8_t *)data, sizeof(*data),
+ /*sense_flags*/0);
if (error) {
free(data, M_TEMP);
break;
@@ -2153,196 +2370,226 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
break;
case CDIOCSETPATCH:
{
- struct ioc_patch *arg = (struct ioc_patch *) addr;
- struct cd_mode_data *data;
+ struct ioc_patch *arg = (struct ioc_patch *)addr;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETPATCH\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels =
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels =
arg->patch[0];
- data->page.audio.port[RIGHT_PORT].channels =
+ page->audio.port[RIGHT_PORT].channels =
arg->patch[1];
- data->page.audio.port[2].channels = arg->patch[2];
- data->page.audio.port[3].channels = arg->patch[3];
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page->audio.port[2].channels = arg->patch[2];
+ page->audio.port[3].channels = arg->patch[3];
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCGETVOL:
{
struct ioc_vol *arg = (struct ioc_vol *) addr;
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCGETVOL\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
+ page = cdgetpage(&params);
+
arg->vol[LEFT_PORT] =
- data->page.audio.port[LEFT_PORT].volume;
+ page->audio.port[LEFT_PORT].volume;
arg->vol[RIGHT_PORT] =
- data->page.audio.port[RIGHT_PORT].volume;
- arg->vol[2] = data->page.audio.port[2].volume;
- arg->vol[3] = data->page.audio.port[3].volume;
- free(data, M_TEMP);
+ page->audio.port[RIGHT_PORT].volume;
+ arg->vol[2] = page->audio.port[2].volume;
+ arg->vol[3] = page->audio.port[3].volume;
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETVOL:
{
struct ioc_vol *arg = (struct ioc_vol *) addr;
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETVOL\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels = CHANNEL_0;
- data->page.audio.port[LEFT_PORT].volume =
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels = CHANNEL_0;
+ page->audio.port[LEFT_PORT].volume =
arg->vol[LEFT_PORT];
- data->page.audio.port[RIGHT_PORT].channels = CHANNEL_1;
- data->page.audio.port[RIGHT_PORT].volume =
+ page->audio.port[RIGHT_PORT].channels = CHANNEL_1;
+ page->audio.port[RIGHT_PORT].volume =
arg->vol[RIGHT_PORT];
- data->page.audio.port[2].volume = arg->vol[2];
- data->page.audio.port[3].volume = arg->vol[3];
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page->audio.port[2].volume = arg->vol[2];
+ page->audio.port[3].volume = arg->vol[3];
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETMONO:
{
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETMONO\n"));
- data = malloc(sizeof(struct cd_mode_data),
- M_TEMP, M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels =
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels =
LEFT_CHANNEL | RIGHT_CHANNEL;
- data->page.audio.port[RIGHT_PORT].channels =
+ page->audio.port[RIGHT_PORT].channels =
LEFT_CHANNEL | RIGHT_CHANNEL;
- data->page.audio.port[2].channels = 0;
- data->page.audio.port[3].channels = 0;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page->audio.port[2].channels = 0;
+ page->audio.port[3].channels = 0;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETSTEREO:
{
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETSTEREO\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels =
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels =
LEFT_CHANNEL;
- data->page.audio.port[RIGHT_PORT].channels =
+ page->audio.port[RIGHT_PORT].channels =
RIGHT_CHANNEL;
- data->page.audio.port[2].channels = 0;
- data->page.audio.port[3].channels = 0;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page->audio.port[2].channels = 0;
+ page->audio.port[3].channels = 0;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETMUTE:
{
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETMUTE\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(&params, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels = 0;
- data->page.audio.port[RIGHT_PORT].channels = 0;
- data->page.audio.port[2].channels = 0;
- data->page.audio.port[3].channels = 0;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels = 0;
+ page->audio.port[RIGHT_PORT].channels = 0;
+ page->audio.port[2].channels = 0;
+ page->audio.port[3].channels = 0;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETLEFT:
{
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETLEFT\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels =
- LEFT_CHANNEL;
- data->page.audio.port[RIGHT_PORT].channels =
- LEFT_CHANNEL;
- data->page.audio.port[2].channels = 0;
- data->page.audio.port[3].channels = 0;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL;
+ page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL;
+ page->audio.port[2].channels = 0;
+ page->audio.port[3].channels = 0;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCSETRIGHT:
{
- struct cd_mode_data *data;
+ struct cd_mode_params params;
+ union cd_pages *page;
CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE,
("trying to do CDIOCSETRIGHT\n"));
- data = malloc(sizeof(struct cd_mode_data), M_TEMP,
- M_WAITOK);
- error = cdgetmode(periph, data, AUDIO_PAGE);
+ params.alloc_len = sizeof(union cd_mode_data_6_10);
+ params.mode_buf = malloc(params.alloc_len, M_TEMP,
+ M_WAITOK | M_ZERO);
+
+ error = cdgetmode(periph, &params, AUDIO_PAGE);
if (error) {
- free(data, M_TEMP);
+ free(params.mode_buf, M_TEMP);
break;
}
- data->page.audio.port[LEFT_PORT].channels =
- RIGHT_CHANNEL;
- data->page.audio.port[RIGHT_PORT].channels =
- RIGHT_CHANNEL;
- data->page.audio.port[2].channels = 0;
- data->page.audio.port[3].channels = 0;
- error = cdsetmode(periph, data);
- free(data, M_TEMP);
+ page = cdgetpage(&params);
+
+ page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL;
+ page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL;
+ page->audio.port[2].channels = 0;
+ page->audio.port[3].channels = 0;
+ error = cdsetmode(periph, &params);
+ free(params.mode_buf, M_TEMP);
}
break;
case CDIOCRESUME:
@@ -2352,7 +2599,10 @@ cdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td)
error = cdpause(periph, 0);
break;
case CDIOCSTART:
- error = cdstartunit(periph);
+ error = cdstartunit(periph, 0);
+ break;
+ case CDIOCCLOSE:
+ error = cdstartunit(periph, 1);
break;
case CDIOCSTOP:
error = cdstopunit(periph, 0);
@@ -2462,18 +2712,141 @@ cdprevent(struct cam_periph *periph, int action)
}
static int
-cdsize(dev_t dev, u_int32_t *size)
+cdcheckmedia(struct cam_periph *periph)
+{
+ struct cd_softc *softc;
+ struct ioc_toc_header *toch;
+ struct cd_toc_single leadout;
+ u_int32_t size, toclen;
+ int error, num_entries, cdindex;
+
+ softc = (struct cd_softc *)periph->softc;
+
+ cdprevent(periph, PR_PREVENT);
+
+ /*
+ * Get the disc size and block size. If we can't get it, we don't
+ * have media, most likely.
+ */
+ if ((error = cdsize(periph, &size)) != 0) {
+ softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
+ cdprevent(periph, PR_ALLOW);
+ return (error);
+ } else
+ softc->flags |= CD_FLAG_VALID_MEDIA;
+
+ /*
+ * Now we check the table of contents. This (currently) is only
+ * used for the CDIOCPLAYTRACKS ioctl. It may be used later to do
+ * things like present a separate entry in /dev for each track,
+ * like that acd(4) driver does.
+ */
+ bzero(&softc->toc, sizeof(softc->toc));
+ toch = &softc->toc.header;
+ /*
+ * We will get errors here for media that doesn't have a table of
+ * contents. According to the MMC-3 spec: "When a Read TOC/PMA/ATIP
+ * command is presented for a DDCD/CD-R/RW media, where the first TOC
+ * has not been recorded (no complete session) and the Format codes
+ * 0000b, 0001b, or 0010b are specified, this command shall be rejected
+ * with an INVALID FIELD IN CDB. Devices that are not capable of
+ * reading an incomplete session on DDC/CD-R/RW media shall report
+ * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT."
+ *
+ * So this isn't fatal if we can't read the table of contents, it
+ * just means that the user won't be able to issue the play tracks
+ * ioctl, and likely lots of other stuff won't work either. They
+ * need to burn the CD before we can do a whole lot with it. So
+ * we don't print anything here if we get an error back.
+ */
+ error = cdreadtoc(periph, 0, 0, (u_int8_t *)toch, sizeof(*toch),
+ SF_NO_PRINT);
+ /*
+ * Errors in reading the table of contents aren't fatal, we just
+ * won't have a valid table of contents cached.
+ */
+ if (error != 0) {
+ error = 0;
+ bzero(&softc->toc, sizeof(softc->toc));
+ goto bailout;
+ }
+
+ if (softc->quirks & CD_Q_BCD_TRACKS) {
+ toch->starting_track = bcd2bin(toch->starting_track);
+ toch->ending_track = bcd2bin(toch->ending_track);
+ }
+
+ /* Number of TOC entries, plus leadout */
+ num_entries = (toch->ending_track - toch->starting_track) + 2;
+
+ if (num_entries <= 0)
+ goto bailout;
+
+ toclen = num_entries * sizeof(struct cd_toc_entry);
+
+ error = cdreadtoc(periph, CD_MSF_FORMAT, toch->starting_track,
+ (u_int8_t *)&softc->toc, toclen + sizeof(*toch),
+ SF_NO_PRINT);
+ if (error != 0) {
+ error = 0;
+ bzero(&softc->toc, sizeof(softc->toc));
+ goto bailout;
+ }
+
+ if (softc->quirks & CD_Q_BCD_TRACKS) {
+ toch->starting_track = bcd2bin(toch->starting_track);
+ toch->ending_track = bcd2bin(toch->ending_track);
+ }
+ /*
+ * XXX KDM is this necessary? Probably only if the drive doesn't
+ * return leadout information with the table of contents.
+ */
+ cdindex = toch->starting_track + num_entries -1;
+ if (cdindex == toch->ending_track + 1) {
+
+ error = cdreadtoc(periph, CD_MSF_FORMAT, LEADOUT,
+ (u_int8_t *)&leadout, sizeof(leadout),
+ SF_NO_PRINT);
+ if (error != 0) {
+ error = 0;
+ goto bailout;
+ }
+ softc->toc.entries[cdindex - toch->starting_track] =
+ leadout.entry;
+ }
+ if (softc->quirks & CD_Q_BCD_TRACKS) {
+ for (cdindex = 0; cdindex < num_entries - 1; cdindex++) {
+ softc->toc.entries[cdindex].track =
+ bcd2bin(softc->toc.entries[cdindex].track);
+ }
+ }
+
+ softc->flags |= CD_FLAG_VALID_TOC;
+
+bailout:
+
+ /*
+ * We unconditionally (re)set the blocksize each time the
+ * CD device is opened. This is because the CD can change,
+ * and therefore the blocksize might change.
+ * XXX problems here if some slice or partition is still
+ * open with the old size?
+ */
+ if ((softc->device_stats.flags & DEVSTAT_BS_UNAVAILABLE) != 0)
+ softc->device_stats.flags &= ~DEVSTAT_BS_UNAVAILABLE;
+ softc->device_stats.block_size = softc->params.blksize;
+
+ return (error);
+}
+
+static int
+cdsize(struct cam_periph *periph, u_int32_t *size)
{
- struct cam_periph *periph;
struct cd_softc *softc;
union ccb *ccb;
struct scsi_read_capacity_data *rcap_buf;
int error;
- periph = (struct cam_periph *)dev->si_drv1;
- if (periph == NULL)
- return (ENXIO);
-
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n"));
softc = (struct cd_softc *)periph->softc;
@@ -2517,14 +2890,190 @@ cdsize(dev_t dev, u_int32_t *size)
}
static int
+cd6byteworkaround(union ccb *ccb)
+{
+ u_int8_t *cdb;
+ struct cam_periph *periph;
+ struct cd_softc *softc;
+ struct cd_mode_params *params;
+ int frozen, found;
+
+ periph = xpt_path_periph(ccb->ccb_h.path);
+ softc = (struct cd_softc *)periph->softc;
+
+ cdb = ccb->csio.cdb_io.cdb_bytes;
+
+ if ((ccb->ccb_h.flags & CAM_CDB_POINTER)
+ || ((cdb[0] != MODE_SENSE_6)
+ && (cdb[0] != MODE_SELECT_6)))
+ return (0);
+
+ /*
+ * Because there is no convenient place to stash the overall
+ * cd_mode_params structure pointer, we have to grab it like this.
+ * This means that ALL MODE_SENSE and MODE_SELECT requests in the
+ * cd(4) driver MUST go through cdgetmode() and cdsetmode()!
+ *
+ * XXX It would be nice if, at some point, we could increase the
+ * number of available peripheral private pointers. Both pointers
+ * are currently used in most every peripheral driver.
+ */
+ found = 0;
+
+ STAILQ_FOREACH(params, &softc->mode_queue, links) {
+ if (params->mode_buf == ccb->csio.data_ptr) {
+ found = 1;
+ break;
+ }
+ }
+
+ /*
+ * This shouldn't happen. All mode sense and mode select
+ * operations in the cd(4) driver MUST go through cdgetmode() and
+ * cdsetmode()!
+ */
+ if (found == 0) {
+ xpt_print_path(periph->path);
+ printf("mode buffer not found in mode queue!\n");
+ return (0);
+ }
+
+ params->cdb_size = 10;
+ softc->minimum_command_size = 10;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("%s(6) failed, increasing minimum CDB size to 10 bytes\n",
+ (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT");
+
+ if (cdb[0] == MODE_SENSE_6) {
+ struct scsi_mode_sense_10 ms10;
+ struct scsi_mode_sense_6 *ms6;
+ int len;
+
+ ms6 = (struct scsi_mode_sense_6 *)cdb;
+
+ bzero(&ms10, sizeof(ms10));
+ ms10.opcode = MODE_SENSE_10;
+ ms10.byte2 = ms6->byte2;
+ ms10.page = ms6->page;
+
+ /*
+ * 10 byte mode header, block descriptor,
+ * sizeof(union cd_pages)
+ */
+ len = sizeof(struct cd_mode_data_10);
+ ccb->csio.dxfer_len = len;
+
+ scsi_ulto2b(len, ms10.length);
+ ms10.control = ms6->control;
+ bcopy(&ms10, cdb, 10);
+ ccb->csio.cdb_len = 10;
+ } else {
+ struct scsi_mode_select_10 ms10;
+ struct scsi_mode_select_6 *ms6;
+ struct scsi_mode_header_6 *header6;
+ struct scsi_mode_header_10 *header10;
+ struct scsi_mode_page_header *page_header;
+ int blk_desc_len, page_num, page_size, len;
+
+ ms6 = (struct scsi_mode_select_6 *)cdb;
+
+ bzero(&ms10, sizeof(ms10));
+ ms10.opcode = MODE_SELECT_10;
+ ms10.byte2 = ms6->byte2;
+
+ header6 = (struct scsi_mode_header_6 *)params->mode_buf;
+ header10 = (struct scsi_mode_header_10 *)params->mode_buf;
+
+ page_header = find_mode_page_6(header6);
+ page_num = page_header->page_code;
+
+ blk_desc_len = header6->blk_desc_len;
+
+ page_size = cdgetpagesize(page_num);
+
+ if (page_size != (page_header->page_length +
+ sizeof(*page_header)))
+ page_size = page_header->page_length +
+ sizeof(*page_header);
+
+ len = sizeof(*header10) + blk_desc_len + page_size;
+
+ len = min(params->alloc_len, len);
+
+ /*
+ * Since the 6 byte parameter header is shorter than the 10
+ * byte parameter header, we need to copy the actual mode
+ * page data, and the block descriptor, if any, so things wind
+ * up in the right place. The regions will overlap, but
+ * bcopy() does the right thing.
+ */
+ bcopy(params->mode_buf + sizeof(*header6),
+ params->mode_buf + sizeof(*header10),
+ len - sizeof(*header10));
+
+ /* Make sure these fields are set correctly. */
+ scsi_ulto2b(0, header10->data_length);
+ header10->medium_type = 0;
+ scsi_ulto2b(blk_desc_len, header10->blk_desc_len);
+
+ ccb->csio.dxfer_len = len;
+
+ scsi_ulto2b(len, ms10.length);
+ ms10.control = ms6->control;
+ bcopy(&ms10, cdb, 10);
+ ccb->csio.cdb_len = 10;
+ }
+
+ frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0;
+ ccb->ccb_h.status = CAM_REQUEUE_REQ;
+ xpt_action(ccb);
+ if (frozen) {
+ cam_release_devq(ccb->ccb_h.path,
+ /*relsim_flags*/0,
+ /*openings*/0,
+ /*timeout*/0,
+ /*getcount_only*/0);
+ }
+
+ return (ERESTART);
+}
+
+static int
cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
{
struct cd_softc *softc;
struct cam_periph *periph;
+ int error;
periph = xpt_path_periph(ccb->ccb_h.path);
softc = (struct cd_softc *)periph->softc;
+ error = 0;
+
+ /*
+ * We use a status of CAM_REQ_INVALID as shorthand -- if a 6 byte
+ * CDB comes back with this particular error, try transforming it
+ * into the 10 byte version.
+ */
+ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) {
+ error = cd6byteworkaround(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)
+ error = cd6byteworkaround(ccb);
+ }
+
+ if (error == ERESTART)
+ return (error);
+
/*
* XXX
* Until we have a better way of doing pack validation,
@@ -2540,7 +3089,7 @@ cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags)
*/
static int
cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
- struct cd_toc_entry *data, u_int32_t len)
+ u_int8_t *data, u_int32_t len, u_int32_t sense_flags)
{
struct scsi_read_toc *scsi_cmd;
u_int32_t ntoc;
@@ -2560,7 +3109,7 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
/* cbfcnp */ cddone,
/* flags */ CAM_DIR_IN,
/* tag_action */ MSG_SIMPLE_Q_TAG,
- /* data_ptr */ (u_int8_t *)data,
+ /* data_ptr */ data,
/* dxfer_len */ len,
/* sense_len */ SSD_FULL_SIZE,
sizeof(struct scsi_read_toc),
@@ -2579,7 +3128,7 @@ cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start,
scsi_cmd->op_code = READ_TOC;
error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
- /*sense_flags*/SF_RETRY_UA);
+ /*sense_flags*/SF_RETRY_UA | sense_flags);
xpt_release_ccb(ccb);
@@ -2634,90 +3183,202 @@ cdreadsubchannel(struct cam_periph *periph, u_int32_t mode,
}
+/*
+ * All MODE_SENSE requests in the cd(4) driver MUST go through this
+ * routine. See comments in cd6byteworkaround() for details.
+ */
static int
-cdgetmode(struct cam_periph *periph, struct cd_mode_data *data, u_int32_t page)
+cdgetmode(struct cam_periph *periph, struct cd_mode_params *data,
+ u_int32_t page)
{
- struct scsi_mode_sense_6 *scsi_cmd;
- struct ccb_scsiio *csio;
+ struct ccb_scsiio *csio;
+ struct cd_softc *softc;
union ccb *ccb;
+ int param_len;
int error;
+ softc = (struct cd_softc *)periph->softc;
+
ccb = cdgetccb(periph, /* priority */ 1);
csio = &ccb->csio;
- bzero(data, sizeof(*data));
- cam_fill_csio(csio,
- /* retries */ 1,
- /* cbfcnp */ cddone,
- /* flags */ CAM_DIR_IN,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
- /* data_ptr */ (u_int8_t *)data,
- /* dxfer_len */ sizeof(*data),
- /* sense_len */ SSD_FULL_SIZE,
- sizeof(struct scsi_mode_sense_6),
- /* timeout */ 50000);
-
- scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes;
- bzero (scsi_cmd, sizeof(*scsi_cmd));
+ data->cdb_size = softc->minimum_command_size;
+ if (data->cdb_size < 10)
+ param_len = sizeof(struct cd_mode_data);
+ else
+ param_len = sizeof(struct cd_mode_data_10);
+
+ /* Don't say we've got more room than we actually allocated */
+ param_len = min(param_len, data->alloc_len);
+
+ scsi_mode_sense_len(csio,
+ /* retries */ 1,
+ /* cbfcnp */ cddone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* dbd */ 0,
+ /* page_code */ SMS_PAGE_CTRL_CURRENT,
+ /* page */ page,
+ /* param_buf */ data->mode_buf,
+ /* param_len */ param_len,
+ /* minimum_cmd_size */ softc->minimum_command_size,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ 50000);
- scsi_cmd->page = page;
- scsi_cmd->length = sizeof(*data) & 0xff;
- scsi_cmd->opcode = MODE_SENSE;
+ /*
+ * It would be nice not to have to do this, but there's no
+ * available pointer in the CCB that would allow us to stuff the
+ * mode params structure in there and retrieve it in
+ * cd6byteworkaround(), so we can set the cdb size. The cdb size
+ * lets the caller know what CDB size we ended up using, so they
+ * can find the actual mode page offset.
+ */
+ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links);
error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
/*sense_flags*/SF_RETRY_UA);
xpt_release_ccb(ccb);
- return(error);
+ STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links);
+
+ /*
+ * This is a bit of belt-and-suspenders checking, but if we run
+ * into a situation where the target sends back multiple block
+ * descriptors, we might not have enough space in the buffer to
+ * see the whole mode page. Better to return an error than
+ * potentially access memory beyond our malloced region.
+ */
+ if (error == 0) {
+ u_int32_t data_len;
+
+ if (data->cdb_size == 10) {
+ struct scsi_mode_header_10 *hdr10;
+
+ hdr10 = (struct scsi_mode_header_10 *)data->mode_buf;
+ data_len = scsi_2btoul(hdr10->data_length);
+ data_len += sizeof(hdr10->data_length);
+ } else {
+ struct scsi_mode_header_6 *hdr6;
+
+ hdr6 = (struct scsi_mode_header_6 *)data->mode_buf;
+ data_len = hdr6->data_length;
+ data_len += sizeof(hdr6->data_length);
+ }
+
+ /*
+ * Complain if there is more mode data available than we
+ * allocated space for. This could potentially happen if
+ * we miscalculated the page length for some reason, if the
+ * drive returns multiple block descriptors, or if it sets
+ * the data length incorrectly.
+ */
+ if (data_len > data->alloc_len) {
+ xpt_print_path(periph->path);
+ printf("allocated modepage %d length %d < returned "
+ "length %d\n", page, data->alloc_len, data_len);
+
+ error = ENOSPC;
+ }
+ }
+ return (error);
}
+/*
+ * All MODE_SELECT requests in the cd(4) driver MUST go through this
+ * routine. See comments in cd6byteworkaround() for details.
+ */
static int
-cdsetmode(struct cam_periph *periph, struct cd_mode_data *data)
+cdsetmode(struct cam_periph *periph, struct cd_mode_params *data)
{
- struct scsi_mode_select_6 *scsi_cmd;
- struct ccb_scsiio *csio;
+ struct ccb_scsiio *csio;
+ struct cd_softc *softc;
union ccb *ccb;
+ int cdb_size, param_len;
int error;
+ softc = (struct cd_softc *)periph->softc;
+
ccb = cdgetccb(periph, /* priority */ 1);
csio = &ccb->csio;
error = 0;
- cam_fill_csio(csio,
- /* retries */ 1,
- /* cbfcnp */ cddone,
- /* flags */ CAM_DIR_OUT,
- /* tag_action */ MSG_SIMPLE_Q_TAG,
- /* data_ptr */ (u_int8_t *)data,
- /* dxfer_len */ sizeof(*data),
- /* sense_len */ SSD_FULL_SIZE,
- sizeof(struct scsi_mode_select_6),
- /* timeout */ 50000);
-
- scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes;
-
- bzero(scsi_cmd, sizeof(*scsi_cmd));
- scsi_cmd->opcode = MODE_SELECT;
- scsi_cmd->byte2 |= SMS_PF;
- scsi_cmd->length = sizeof(*data) & 0xff;
- data->header.data_length = 0;
/*
- * SONY drives do not allow a mode select with a medium_type
- * value that has just been returned by a mode sense; use a
- * medium_type of 0 (Default) instead.
+ * If the data is formatted for the 10 byte version of the mode
+ * select parameter list, we need to use the 10 byte CDB.
+ * Otherwise, we use whatever the stored minimum command size.
*/
- data->header.medium_type = 0;
+ if (data->cdb_size == 10)
+ cdb_size = data->cdb_size;
+ else
+ cdb_size = softc->minimum_command_size;
+
+ if (cdb_size >= 10) {
+ struct scsi_mode_header_10 *mode_header;
+ u_int32_t data_len;
+
+ mode_header = (struct scsi_mode_header_10 *)data->mode_buf;
+
+ data_len = scsi_2btoul(mode_header->data_length);
+
+ scsi_ulto2b(0, mode_header->data_length);
+ /*
+ * SONY drives do not allow a mode select with a medium_type
+ * value that has just been returned by a mode sense; use a
+ * medium_type of 0 (Default) instead.
+ */
+ mode_header->medium_type = 0;
+
+ /*
+ * Pass back whatever the drive passed to us, plus the size
+ * of the data length field.
+ */
+ param_len = data_len + sizeof(mode_header->data_length);
+
+ } else {
+ struct scsi_mode_header_6 *mode_header;
+
+ mode_header = (struct scsi_mode_header_6 *)data->mode_buf;
+
+ param_len = mode_header->data_length + 1;
+
+ mode_header->data_length = 0;
+ /*
+ * SONY drives do not allow a mode select with a medium_type
+ * value that has just been returned by a mode sense; use a
+ * medium_type of 0 (Default) instead.
+ */
+ mode_header->medium_type = 0;
+ }
+
+ /* Don't say we've got more room than we actually allocated */
+ param_len = min(param_len, data->alloc_len);
+
+ scsi_mode_select_len(csio,
+ /* retries */ 1,
+ /* cbfcnp */ cddone,
+ /* tag_action */ MSG_SIMPLE_Q_TAG,
+ /* scsi_page_fmt */ 1,
+ /* save_pages */ 0,
+ /* param_buf */ data->mode_buf,
+ /* param_len */ param_len,
+ /* minimum_cmd_size */ cdb_size,
+ /* sense_len */ SSD_FULL_SIZE,
+ /* timeout */ 50000);
+
+ /* See comments in cdgetmode() and cd6byteworkaround(). */
+ STAILQ_INSERT_TAIL(&softc->mode_queue, data, links);
error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO,
/*sense_flags*/SF_RETRY_UA);
xpt_release_ccb(ccb);
- return(error);
+ STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links);
+
+ return (error);
}
@@ -2905,7 +3566,7 @@ cdpause(struct cam_periph *periph, u_int32_t go)
}
static int
-cdstartunit(struct cam_periph *periph)
+cdstartunit(struct cam_periph *periph, int load)
{
union ccb *ccb;
int error;
@@ -2919,7 +3580,7 @@ cdstartunit(struct cam_periph *periph)
/* cbfcnp */ cddone,
/* tag_action */ MSG_SIMPLE_Q_TAG,
/* start */ TRUE,
- /* load_eject */ FALSE,
+ /* load_eject */ load,
/* immediate */ FALSE,
/* sense_len */ SSD_FULL_SIZE,
/* timeout */ 50000);
diff --git a/sys/cam/scsi/scsi_cd.h b/sys/cam/scsi/scsi_cd.h
index f242139..f502d66 100644
--- a/sys/cam/scsi/scsi_cd.h
+++ b/sys/cam/scsi/scsi_cd.h
@@ -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 *),
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 65878a1..2f1338a 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -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,12 +1161,53 @@ 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)
diff --git a/sys/dev/ata/atapi-cam.c b/sys/dev/ata/atapi-cam.c
index 02e234b..eb603bb 100644
--- a/sys/dev/ata/atapi-cam.c
+++ b/sys/dev/ata/atapi-cam.c
@@ -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 */
diff --git a/sys/dev/usb/umass.c b/sys/dev/usb/umass.c
index df04e4e..afadc87 100644
--- a/sys/dev/usb/umass.c
+++ b/sys/dev/usb/umass.c
@@ -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];
OpenPOWER on IntegriCloud