X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/1f2de5d41c9be614e9a1cba7cf16de309a2ea210..e4c9c0c82658e0eca11c3e516c41021ba48b0093:/sys/bus/cam/scsi/scsi_cd.c diff --git a/sys/bus/cam/scsi/scsi_cd.c b/sys/bus/cam/scsi/scsi_cd.c index f5c001348b..1a073df5dd 100644 --- a/sys/bus/cam/scsi/scsi_cd.c +++ b/sys/bus/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, 2003, 2003 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/cam/scsi/scsi_cd.c,v 1.31.2.13 2002/11/25 05:30:31 njl Exp $ - * $DragonFly: src/sys/bus/cam/scsi/scsi_cd.c,v 1.9 2003/08/07 21:16:44 dillon Exp $ + * $FreeBSD: src/sys/cam/scsi/scsi_cd.c,v 1.31.2.16 2003/10/21 22:26:11 thomas Exp $ + * $DragonFly: src/sys/bus/cam/scsi/scsi_cd.c,v 1.15 2004/05/19 22:52:38 dillon Exp $ */ /* * Portions of this driver taken from the original FreeBSD cd driver. @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -84,23 +85,28 @@ 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 { - CD_FLAG_INVALID = 0x001, - CD_FLAG_NEW_DISC = 0x002, - CD_FLAG_DISC_LOCKED = 0x004, - CD_FLAG_DISC_REMOVABLE = 0x008, - CD_FLAG_TAGGED_QUEUING = 0x010, - CD_FLAG_CHANGER = 0x040, - CD_FLAG_ACTIVE = 0x080, - CD_FLAG_SCHED_ON_COMP = 0x100, - CD_FLAG_RETRY_UA = 0x200 + CD_FLAG_INVALID = 0x0001, + CD_FLAG_NEW_DISC = 0x0002, + CD_FLAG_DISC_LOCKED = 0x0004, + CD_FLAG_DISC_REMOVABLE = 0x0008, + CD_FLAG_TAGGED_QUEUING = 0x0010, + CD_FLAG_CHANGER = 0x0040, + CD_FLAG_ACTIVE = 0x0080, + CD_FLAG_SCHED_ON_COMP = 0x0100, + CD_FLAG_RETRY_UA = 0x0200, + CD_FLAG_VALID_MEDIA = 0x0400, + CD_FLAG_VALID_TOC = 0x0800, + CD_FLAG_OPEN = 0x1000, + CD_FLAG_SCTX_INIT = 0x2000 } cd_flags; typedef enum { @@ -121,6 +127,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 @@ -141,6 +157,22 @@ struct cd_softc { struct cdchanger *changer; int bufs_left; struct cam_periph *periph; + int minimum_command_size; + struct task sysctl_task; + 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 { @@ -149,12 +181,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[] = { @@ -188,6 +230,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); @@ -203,16 +246,19 @@ static void cddone(struct cam_periph *periph, union ccb *start_ccb); static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags); +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 cdsize(dev_t dev, u_int32_t *size); -static int cdfirsttrackisdata(struct cam_periph *periph); +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 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, @@ -229,7 +275,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, @@ -256,7 +302,7 @@ static struct cdevsw cd_cdevsw = { /* maj */ SCSICD_CDEV_MAJOR, /* flags */ D_DISK, /* port */ NULL, - /* autoq */ 0, + /* clone */ NULL, /* open */ cdopen, /* close */ cdclose, @@ -287,13 +333,14 @@ static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS; * XXX KDM this CAM node should be moved if we ever get more CAM sysctl * variables. */ -SYSCTL_NODE(_kern, OID_AUTO, cam, CTLFLAG_RD, 0, "CAM Subsystem"); 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; @@ -420,6 +467,12 @@ cdcleanup(struct cam_periph *periph) xpt_print_path(periph->path); printf("removing device entry\n"); + if ((softc->flags & CD_FLAG_SCTX_INIT) != 0 + && sysctl_ctx_free(&softc->sysctl_ctx) != 0) { + xpt_print_path(periph->path); + printf("can't remove sysctl context\n"); + } + s = splsoftcam(); /* * In the queued, non-active case, the device in question @@ -492,7 +545,7 @@ cdcleanup(struct cam_periph *periph) } devstat_remove_entry(&softc->device_stats); cam_extend_release(cdperiphs, periph->unit_number); - if (softc->disk.d_dev) { + if (softc->disk.d_rawdev) { disk_destroy(&softc->disk); } free(softc, M_DEVBUF); @@ -562,12 +615,85 @@ cdasync(void *callback_arg, u_int32_t code, } } +static void +cdsysctlinit(void *context, int pending) +{ + struct cam_periph *periph; + struct cd_softc *softc; + char tmpstr[80], tmpstr2[80]; + + periph = (struct cam_periph *)context; + softc = (struct cd_softc *)periph->softc; + + snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); + snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); + + sysctl_ctx_init(&softc->sysctl_ctx); + softc->flags |= CD_FLAG_SCTX_INIT; + 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("cdsysctlinit: unable to allocate sysctl tree\n"); + return; + } + + /* + * 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 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_pathinq cpi; struct ccb_getdev *cgd; + char tmpstr[80]; caddr_t match; cgd = (struct ccb_getdev *)arg; @@ -580,16 +706,9 @@ cdregister(struct cam_periph *periph, void *arg) return(CAM_REQ_CMP_ERR); } - softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); - - if (softc == NULL) { - printf("cdregister: Unable to probe new device. " - "Unable to allocate softc\n"); - return(CAM_REQ_CMP_ERR); - } - - bzero(softc, sizeof(*softc)); + softc = malloc(sizeof(*softc), M_DEVBUF, M_INTWAIT | M_ZERO); LIST_INIT(&softc->pending_ccbs); + STAILQ_INIT(&softc->mode_queue); softc->state = CD_STATE_PROBE; bufq_init(&softc->buf_queue); if (SID_IS_REMOVABLE(&cgd->inq_data)) @@ -615,6 +734,34 @@ cdregister(struct cam_periph *periph, void *arg) else softc->quirks = CD_Q_NONE; + /* Check if the SIM does not want 6 byte commands */ + xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + if (cpi.ccb_h.status == CAM_REQ_CMP && (cpi.hba_misc & PIM_NO_6_BYTE)) + softc->quirks |= CD_Q_10_BYTE_ONLY; + + TASK_INIT(&softc->sysctl_task, 0, cdsysctlinit, periph); + + /* 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; + /* * We need to register the statistics structure for this device, * but we don't have the blocksize yet for it. So, we register @@ -765,23 +912,7 @@ cdregister(struct cam_periph *periph, void *arg) */ else { nchanger = malloc(sizeof(struct cdchanger), - M_DEVBUF, M_NOWAIT); - - if (nchanger == NULL) { - softc->flags &= ~CD_FLAG_CHANGER; - printf("cdregister: unable to malloc " - "changer structure\ncdregister: " - "changer support disabled\n"); - - /* - * Yes, gotos can be gross but in this case - * I think it's justified.. - */ - goto cdregisterexit; - } - - /* zero the structure */ - bzero(nchanger, sizeof(struct cdchanger)); + M_DEVBUF, M_INTWAIT | M_ZERO); if (camq_init(&nchanger->devq, 1) != 0) { softc->flags &= ~CD_FLAG_CHANGER; @@ -878,11 +1009,8 @@ cdregisterexit: static int cdopen(dev_t dev, int flags, int fmt, struct thread *td) { - struct disklabel *label; struct cam_periph *periph; struct cd_softc *softc; - struct ccb_getdev cgd; - u_int32_t size; int unit, error; int s; @@ -913,72 +1041,19 @@ 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); - } - - /* - * If we get a non-zero return, revert back to not reading the - * label off the disk. The first track is likely audio, which - * won't have a disklabel. - */ - if ((error = cdfirsttrackisdata(periph)) != 0) { - softc->disk.d_dsflags &= ~DSO_COMPATLABEL; - softc->disk.d_dsflags |= DSO_NOLABELS; - error = 0; - } - - /* - * Build prototype label for whole disk. - * Should take information about different data tracks from the - * TOC and put it in the partition table. - */ - label = &softc->disk.d_label; - bzero(label, sizeof(*label)); - label->d_type = DTYPE_SCSI; - /* - * Grab the inquiry data to get the vendor and product names. - * Put them in the typename and packname for the label. - */ - xpt_setup_ccb(&cgd.ccb_h, periph->path, /*priority*/ 1); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); - - strncpy(label->d_typename, cgd.inq_data.vendor, - min(SID_VENDOR_SIZE, sizeof(label->d_typename))); - strncpy(label->d_packname, cgd.inq_data.product, - min(SID_PRODUCT_SIZE, sizeof(label->d_packname))); - - label->d_secsize = softc->params.blksize; - label->d_secperunit = softc->params.disksize; - label->d_flags = D_REMOVABLE; - /* - * Make partition 'a' cover the whole disk. This is a temporary - * compatibility hack. The 'a' partition should not exist, so - * the slice code won't create it. The slice code will make - * partition (RAW_PART + 'a') cover the whole disk and fill in - * some more defaults. + * 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. + * + * XXX KDM for now, we do fail the open if we don't have media. We + * can change this once we've figured out how to make the slice + * code work well with media changing underneath it. */ - label->d_partitions[0].p_size = label->d_secperunit; - label->d_partitions[0].p_fstype = FS_OTHER; + error = cdcheckmedia(periph); - /* - * 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; + if (error == 0) + softc->flags |= CD_FLAG_OPEN; cam_periph_unlock(periph); @@ -1016,10 +1091,17 @@ 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); + + softc->flags &= ~CD_FLAG_OPEN; + cam_periph_unlock(periph); cam_periph_release(periph); @@ -1398,6 +1480,21 @@ cdstrategy(struct buf *bp) goto bad; } + /* + * 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); + bp->b_error = error; + goto bad; + } + } + /* * Place it in the queue of disk activities for this disk */ @@ -1509,15 +1606,7 @@ cdstart(struct cam_periph *periph, union ccb *start_ccb) case CD_STATE_PROBE: { - rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap), - M_TEMP, - M_NOWAIT); - if (rcap == NULL) { - xpt_print_path(periph->path); - printf("cdstart: Couldn't malloc read_capacity data\n"); - /* cd_free_periph??? */ - break; - } + rcap = malloc(sizeof(*rcap), M_TEMP, M_INTWAIT); csio = &start_ccb->csio; scsi_read_capacity(csio, /*retries*/1, @@ -1785,6 +1874,11 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) xpt_announce_periph(periph, announce_buf); if (softc->flags & CD_FLAG_CHANGER) cdchangerschedule(softc); + /* + * Create our sysctl variables, now that we know + * we have successfully attached. + */ + taskqueue_enqueue(taskqueue_thread,&softc->sysctl_task); } softc->state = CD_STATE_NORMAL; /* @@ -1814,6 +1908,34 @@ 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) { @@ -1840,63 +1962,137 @@ 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) { case CDIOCPLAYTRACKS: { 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, @@ -1912,23 +2108,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); @@ -1990,9 +2190,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; @@ -2012,17 +2211,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; @@ -2032,8 +2222,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 @@ -2048,9 +2238,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); @@ -2104,8 +2293,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); @@ -2119,9 +2309,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); @@ -2144,13 +2334,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; @@ -2159,7 +2343,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) { @@ -2171,9 +2355,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; @@ -2202,8 +2385,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; @@ -2218,196 +2401,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: @@ -2417,13 +2630,40 @@ 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); + +#ifdef notyet + if (error != 0) + break; + + /* + * The user successfully closed the tray, run + * cdcheckmedia() again so we can re-sync the disklabel + * information. + */ + cdcheckmedia(periph); +#endif /* notyet */ break; case CDIOCSTOP: error = cdstopunit(periph, 0); break; case CDIOCEJECT: error = cdstopunit(periph, 1); + +#ifdef notyet + if (error != 0) + break; + + /* + * Since we've successfully ejected the media, run + * cdcheckmedia() again so we re-sync the disklabel + * information. + */ + cdcheckmedia(periph); +#endif /* notyet */ break; case CDIOCALLOW: cdprevent(periph, PR_ALLOW); @@ -2523,20 +2763,295 @@ cdprevent(struct cam_periph *periph, int action) } } +int +cdcheckmedia(struct cam_periph *periph) +{ + struct cd_softc *softc; + struct ioc_toc_header *toch; + struct cd_toc_single leadout; + struct ccb_getdev cgd; + u_int32_t size, toclen; + int error, num_entries, cdindex; + int first_track_audio; + struct disklabel *label; + + softc = (struct cd_softc *)periph->softc; + + first_track_audio = -1; + error = 0; + + cdprevent(periph, PR_PREVENT); + + /* + * Build prototype label for whole disk. + * Should take information about different data tracks from the + * TOC and put it in the partition table. + */ + label = &softc->disk.d_label; + bzero(label, sizeof(*label)); + label->d_type = DTYPE_SCSI; + + /* + * Grab the inquiry data to get the vendor and product names. + * Put them in the typename and packname for the label. + */ + xpt_setup_ccb(&cgd.ccb_h, periph->path, /*priority*/ 1); + cgd.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)&cgd); + + strncpy(label->d_typename, cgd.inq_data.vendor, + min(SID_VENDOR_SIZE, sizeof(label->d_typename))); + strncpy(label->d_packname, cgd.inq_data.product, + min(SID_PRODUCT_SIZE, sizeof(label->d_packname))); + + label->d_flags = D_REMOVABLE; + /* + * Make partition 'a' cover the whole disk. This is a temporary + * compatibility hack. The 'a' partition should not exist, so + * the slice code won't create it. The slice code will make + * partition (RAW_PART + 'a') cover the whole disk and fill in + * some more defaults. + */ + label->d_partitions[0].p_size = label->d_secperunit; + label->d_partitions[0].p_fstype = FS_OTHER; + + /* + * Default to not reading the disklabel off the disk, until we can + * verify that we have media, that we have a table of contents, and + * that the first track is a data track (which could theoretically + * contain a disklabel). + */ + softc->disk.d_dsflags &= ~DSO_COMPATLABEL; + softc->disk.d_dsflags |= DSO_NOLABELS; + + /* + * Clear the valid media and TOC flags until we've verified that we + * have both. + */ + softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); + + /* + * 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) { + /* + * Set a bogus sector size, so the slice code won't try to + * divide by 0 and panic the kernel. + */ + label->d_secsize = 2048; + + label->d_secperunit = 0; + + /* + * XXX KDM is this a good idea? Seems to cause more + * problems. + */ + if (softc->flags & CD_FLAG_OPEN) { + int force; + + force = 1; + + /* + * We don't bother checking the return value here, + * since we already have an error... + */ + dsioctl(softc->disk.d_cdev, DIOCSYNCSLICEINFO, + /*data*/(caddr_t)&force, /*flags*/ 0, + &softc->disk.d_slice); + } + + /* + * Tell devstat(9) we don't have a blocksize. + */ + softc->device_stats.flags |= DEVSTAT_BS_UNAVAILABLE; + + cdprevent(periph, PR_ALLOW); + + return (error); + } else { + + label->d_secsize = softc->params.blksize; + label->d_secperunit = softc->params.disksize; + + /* + * Force a re-sync of slice information, like the blocksize, + * now that we know it. It isn't pretty...but according to + * Bruce Evans, this is probably the best way to do it in + * -stable. We only do this if we're already open, and + * therefore dsopen() has already run. If CD_FLAG_OPEN + * isn't set, this isn't necessary. + */ + if (softc->flags & CD_FLAG_OPEN) { + int force; + + force = 1; + + error = dsioctl(softc->disk.d_cdev, DIOCSYNCSLICEINFO, + /*data*/(caddr_t)&force, /*flags*/ 0, + &softc->disk.d_slice); + if (error != 0) { + /* + * Set a bogus sector size, so the slice code + * won't try to divide by 0 and panic the + * kernel. + */ + label->d_secsize = 2048; + + label->d_secperunit = 0; + + /* + * Tell devstat(9) we don't have a blocksize. + */ + softc->device_stats.flags |= + DEVSTAT_BS_UNAVAILABLE; + + cdprevent(periph, PR_ALLOW); + } + } + + /* + * 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; + + 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); + } + toch->len = scsi_2btoul((uint8_t *)&toch->len); + + /* + * 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); + } + } + + /* + * Run through the TOC entries, find the first entry and determine + * whether it is an audio or data track. + */ + for (cdindex = 0; cdindex < (num_entries - 1); cdindex++) { + if (softc->toc.entries[cdindex].track == toch->starting_track) { + if (softc->toc.entries[cdindex].control & 0x04) + first_track_audio = 0; + else + first_track_audio = 1; + break; + } + } + + /* + * If first_track_audio is non-zero, we either have an error (e.g. + * couldn't find the starting track) or the first track is an audio + * track. If first_track_audio is 0, the first track is a data + * track that could have a disklabel. Attempt to read the + * disklabel off the media, just in case the user put one there. + */ + if (first_track_audio == 0) { + softc->disk.d_dsflags |= DSO_COMPATLABEL; + softc->disk.d_dsflags &= ~DSO_NOLABELS; + } + softc->flags |= CD_FLAG_VALID_TOC; + +bailout: + return (error); +} + static int -cdsize(dev_t dev, u_int32_t *size) +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 = cam_extend_get(cdperiphs, dkunit(dev)); - - if (periph == NULL) - return (ENXIO); - CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n")); softc = (struct cd_softc *)periph->softc; @@ -2544,7 +3059,7 @@ cdsize(dev_t dev, u_int32_t *size) ccb = cdgetccb(periph, /* priority */ 1); rcap_buf = malloc(sizeof(struct scsi_read_capacity_data), - M_TEMP, M_WAITOK); + M_TEMP, M_INTWAIT | M_ZERO); scsi_read_capacity(&ccb->csio, /*retries*/ 1, @@ -2579,78 +3094,153 @@ cdsize(dev_t dev, u_int32_t *size) } -/* - * The idea here is to try to figure out whether the first track is data or - * audio. If it is data, we can at least attempt to read a disklabel off - * the first sector of the disk. If it is audio, there won't be a - * disklabel. - * - * This routine returns 0 if the first track is data, and non-zero if there - * is an error or the first track is audio. (If either non-zero case, we - * should not attempt to read the disklabel.) - */ static int -cdfirsttrackisdata(struct cam_periph *periph) +cd6byteworkaround(union ccb *ccb) { - struct cdtocdata { - struct ioc_toc_header header; - struct cd_toc_entry entries[100]; - }; + u_int8_t *cdb; + struct cam_periph *periph; struct cd_softc *softc; - struct ioc_toc_header *th; - struct cdtocdata *data; - int num_entries, i; - int error, first_track_audio; - - error = 0; - first_track_audio = -1; + struct cd_mode_params *params; + int frozen, found; + periph = xpt_path_periph(ccb->ccb_h.path); softc = (struct cd_softc *)periph->softc; - data = malloc(sizeof(struct cdtocdata), M_TEMP, M_WAITOK); + cdb = ccb->csio.cdb_io.cdb_bytes; - th = &data->header; - error = cdreadtoc(periph, 0, 0, (struct cd_toc_entry *)data, - sizeof(*data)); + if ((ccb->ccb_h.flags & CAM_CDB_POINTER) + || ((cdb[0] != MODE_SENSE_6) + && (cdb[0] != MODE_SELECT_6))) + return (0); - if (error) - goto bailout; + /* + * 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; - if (softc->quirks & CD_Q_BCD_TRACKS) { - /* we are going to have to convert the BCD - * encoding on the cd to what is expected - */ - th->starting_track = - bcd2bin(th->starting_track); - th->ending_track = bcd2bin(th->ending_track); + STAILQ_FOREACH(params, &softc->mode_queue, links) { + if (params->mode_buf == ccb->csio.data_ptr) { + found = 1; + break; + } } - th->len = scsi_2btoul((u_int8_t *)&th->len); - if ((th->len - 2) > 0) - num_entries = (th->len - 2) / sizeof(struct cd_toc_entry); - else - num_entries = 0; + /* + * 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); + } - for (i = 0; i < num_entries; i++) { - if (data->entries[i].track == th->starting_track) { - if (data->entries[i].control & 0x4) - first_track_audio = 0; - else - first_track_audio = 1; - break; - } + 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; } - if (first_track_audio == -1) - error = ENOENT; - else if (first_track_audio == 1) - error = EINVAL; - else - error = 0; -bailout: - free(data, M_TEMP); + 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(error); + return (ERESTART); } static int @@ -2658,10 +3248,37 @@ 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, @@ -2677,7 +3294,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; @@ -2697,7 +3314,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), @@ -2716,7 +3333,8 @@ 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*/0, - /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO); + /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO | + sense_flags); xpt_release_ccb(ccb); @@ -2770,93 +3388,205 @@ cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, return(error); } - +/* + * 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*/0, /*sense_flags*/SF_RETRY_UA|SF_RETRY_SELTO); 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*/0, /*sense_flags*/SF_RETRY_UA | SF_RETRY_SELTO); xpt_release_ccb(ccb); - return(error); + STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); + + return (error); } + static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len) @@ -3042,7 +3772,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; @@ -3056,7 +3786,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); @@ -3187,9 +3917,10 @@ cdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) } if (length != 0) { - databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); - } else + databuf = malloc(length, M_DEVBUF, M_INTWAIT | M_ZERO); + } else { databuf = NULL; + } scsi_report_key(&ccb->csio, @@ -3320,7 +4051,7 @@ cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) length = sizeof(*challenge_data); - challenge_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); + challenge_data = malloc(length, M_DEVBUF, M_INTWAIT | M_ZERO); databuf = (u_int8_t *)challenge_data; @@ -3337,7 +4068,7 @@ cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) length = sizeof(*key2_data); - key2_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); + key2_data = malloc(length, M_DEVBUF, M_INTWAIT | M_ZERO); databuf = (u_int8_t *)key2_data; @@ -3354,7 +4085,7 @@ cdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) length = sizeof(*rpc_data); - rpc_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); + rpc_data = malloc(length, M_DEVBUF, M_INTWAIT | M_ZERO); databuf = (u_int8_t *)rpc_data; @@ -3496,9 +4227,10 @@ cdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct) } if (length != 0) { - databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); - } else + databuf = malloc(length, M_DEVBUF, M_INTWAIT | M_ZERO); + } else { databuf = NULL; + } scsi_read_dvd_structure(&ccb->csio, /* retries */ 1,