diff options
-rw-r--r-- | sys/cam/scsi/scsi_all.c | 35 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_all.h | 17 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_cd.c | 1191 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_cd.h | 85 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 97 | ||||
-rw-r--r-- | sys/dev/ata/atapi-cam.c | 30 | ||||
-rw-r--r-- | sys/dev/usb/umass.c | 27 |
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, ¶ms, 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(¶ms); + + page->audio.flags &= ~CD_PA_SOTC; + page->audio.flags |= CD_PA_IMMED; + error = cdsetmode(periph, ¶ms); + 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, ¶ms, 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(¶ms); + + page->audio.flags &= ~CD_PA_SOTC; + page->audio.flags |= CD_PA_IMMED; + error = cdsetmode(periph, ¶ms); + 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, ¶ms, 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(¶ms); + + page->audio.flags &= ~CD_PA_SOTC; + page->audio.flags |= CD_PA_IMMED; + error = cdsetmode(periph, ¶ms); + 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, ¶ms, AUDIO_PAGE); if (error) { - free(data, M_TEMP); + free(params.mode_buf, M_TEMP); break; } - data->page.audio.port[LEFT_PORT].channels = + page = cdgetpage(¶ms); + + 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, ¶ms); + 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, ¶ms, AUDIO_PAGE); if (error) { - free(data, M_TEMP); + free(params.mode_buf, M_TEMP); break; } + page = cdgetpage(¶ms); + 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, ¶ms, 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(¶ms); + + 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, ¶ms); + 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, ¶ms, AUDIO_PAGE); if (error) { - free(data, M_TEMP); + free(params.mode_buf, M_TEMP); break; } - data->page.audio.port[LEFT_PORT].channels = + page = cdgetpage(¶ms); + + 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, ¶ms); + 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, ¶ms, AUDIO_PAGE); if (error) { - free(data, M_TEMP); + free(params.mode_buf, M_TEMP); break; } - data->page.audio.port[LEFT_PORT].channels = + page = cdgetpage(¶ms); + + 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, ¶ms); + 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, ¶ms, AUDIO_PAGE); if (error) { - free(data, M_TEMP); + free(¶ms, 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(¶ms); + + 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, ¶ms); + 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, ¶ms, 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(¶ms); + + 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, ¶ms); + 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, ¶ms, 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(¶ms); + + 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, ¶ms); + 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]; |