scsi_cd.c revision 116162
139213Sgibbs/* 239213Sgibbs * Copyright (c) 1997 Justin T. Gibbs. 3111206Sken * Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003 Kenneth D. Merry. 439213Sgibbs * All rights reserved. 539213Sgibbs * 639213Sgibbs * Redistribution and use in source and binary forms, with or without 739213Sgibbs * modification, are permitted provided that the following conditions 839213Sgibbs * are met: 939213Sgibbs * 1. Redistributions of source code must retain the above copyright 1039213Sgibbs * notice, this list of conditions, and the following disclaimer, 1139213Sgibbs * without modification, immediately at the beginning of the file. 1239213Sgibbs * 2. The name of the author may not be used to endorse or promote products 1339213Sgibbs * derived from this software without specific prior written permission. 1439213Sgibbs * 1539213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1639213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1739213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1839213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 1939213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2039213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2139213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2239213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2339213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2439213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2539213Sgibbs * SUCH DAMAGE. 2639213Sgibbs */ 27116162Sobrien 2839213Sgibbs/* 2939213Sgibbs * Portions of this driver taken from the original FreeBSD cd driver. 3039213Sgibbs * Written by Julian Elischer (julian@tfs.com) 3139213Sgibbs * for TRW Financial Systems for use under the MACH(2.5) operating system. 3239213Sgibbs * 3339213Sgibbs * TRW Financial Systems, in accordance with their agreement with Carnegie 3439213Sgibbs * Mellon University, makes this software available to CMU to distribute 3539213Sgibbs * or use in any manner that they see fit as long as this message is kept with 3639213Sgibbs * the software. For this reason TFS also grants any other persons or 3739213Sgibbs * organisations permission to use or modify this software. 3839213Sgibbs * 3939213Sgibbs * TFS supplies this software to be publicly redistributed 4039213Sgibbs * on the understanding that TFS is not responsible for the correct 4139213Sgibbs * functioning of this software in any circumstances. 4239213Sgibbs * 4339213Sgibbs * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 4439213Sgibbs * 4539213Sgibbs * from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $ 4639213Sgibbs */ 4739213Sgibbs 48116162Sobrien#include <sys/cdefs.h> 49116162Sobrien__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_cd.c 116162 2003-06-10 18:14:05Z obrien $"); 50116162Sobrien 5140020Sken#include "opt_cd.h" 5239213Sgibbs 5339213Sgibbs#include <sys/param.h> 5439213Sgibbs#include <sys/systm.h> 5539213Sgibbs#include <sys/kernel.h> 5660041Sphk#include <sys/bio.h> 5751836Sphk#include <sys/conf.h> 5851836Sphk#include <sys/disk.h> 5939213Sgibbs#include <sys/malloc.h> 6039213Sgibbs#include <sys/cdio.h> 61105421Snjl#include <sys/cdrio.h> 6260422Sken#include <sys/dvdio.h> 6339213Sgibbs#include <sys/devicestat.h> 6439213Sgibbs#include <sys/sysctl.h> 6539213Sgibbs 6639213Sgibbs#include <cam/cam.h> 6739213Sgibbs#include <cam/cam_ccb.h> 6839213Sgibbs#include <cam/cam_periph.h> 6939213Sgibbs#include <cam/cam_xpt_periph.h> 7039213Sgibbs#include <cam/cam_queue.h> 7139213Sgibbs 7239213Sgibbs#include <cam/scsi/scsi_message.h> 7339213Sgibbs#include <cam/scsi/scsi_da.h> 7439213Sgibbs#include <cam/scsi/scsi_cd.h> 7539213Sgibbs 7639213Sgibbs#define LEADOUT 0xaa /* leadout toc entry */ 7739213Sgibbs 7839213Sgibbsstruct cd_params { 7939213Sgibbs u_int32_t blksize; 8039213Sgibbs u_long disksize; 8139213Sgibbs}; 8239213Sgibbs 8339213Sgibbstypedef enum { 84111206Sken CD_Q_NONE = 0x00, 85111206Sken CD_Q_NO_TOUCH = 0x01, 86111206Sken CD_Q_BCD_TRACKS = 0x02, 87111206Sken CD_Q_NO_CHANGER = 0x04, 88111206Sken CD_Q_CHANGER = 0x08, 89111206Sken CD_Q_10_BYTE_ONLY = 0x10 9039213Sgibbs} cd_quirks; 9139213Sgibbs 9239213Sgibbstypedef enum { 9339213Sgibbs CD_FLAG_INVALID = 0x001, 9439213Sgibbs CD_FLAG_NEW_DISC = 0x002, 9539213Sgibbs CD_FLAG_DISC_LOCKED = 0x004, 9639213Sgibbs CD_FLAG_DISC_REMOVABLE = 0x008, 9739213Sgibbs CD_FLAG_TAGGED_QUEUING = 0x010, 9839213Sgibbs CD_FLAG_CHANGER = 0x040, 9939213Sgibbs CD_FLAG_ACTIVE = 0x080, 10039213Sgibbs CD_FLAG_SCHED_ON_COMP = 0x100, 101111206Sken CD_FLAG_RETRY_UA = 0x200, 102111206Sken CD_FLAG_VALID_MEDIA = 0x400, 103111206Sken CD_FLAG_VALID_TOC = 0x800 10439213Sgibbs} cd_flags; 10539213Sgibbs 10639213Sgibbstypedef enum { 10739213Sgibbs CD_CCB_PROBE = 0x01, 10839213Sgibbs CD_CCB_BUFFER_IO = 0x02, 10939213Sgibbs CD_CCB_WAITING = 0x03, 11039213Sgibbs CD_CCB_TYPE_MASK = 0x0F, 11139213Sgibbs CD_CCB_RETRY_UA = 0x10 11239213Sgibbs} cd_ccb_state; 11339213Sgibbs 11439213Sgibbstypedef enum { 11539213Sgibbs CHANGER_TIMEOUT_SCHED = 0x01, 11639213Sgibbs CHANGER_SHORT_TMOUT_SCHED = 0x02, 11739213Sgibbs CHANGER_MANUAL_CALL = 0x04, 11839213Sgibbs CHANGER_NEED_TIMEOUT = 0x08 11939213Sgibbs} cd_changer_flags; 12039213Sgibbs 12139213Sgibbs#define ccb_state ppriv_field0 12239213Sgibbs#define ccb_bp ppriv_ptr1 12339213Sgibbs 124111206Skenstruct cd_tocdata { 125111206Sken struct ioc_toc_header header; 126111206Sken struct cd_toc_entry entries[100]; 127111206Sken}; 128111206Sken 129111206Skenstruct cd_toc_single { 130111206Sken struct ioc_toc_header header; 131111206Sken struct cd_toc_entry entry; 132111206Sken}; 133111206Sken 13439213Sgibbstypedef enum { 13539213Sgibbs CD_STATE_PROBE, 13639213Sgibbs CD_STATE_NORMAL 13739213Sgibbs} cd_state; 13839213Sgibbs 13939213Sgibbsstruct cd_softc { 14039213Sgibbs cam_pinfo pinfo; 14139213Sgibbs cd_state state; 14246581Sken volatile cd_flags flags; 14359249Sphk struct bio_queue_head bio_queue; 14460938Sjake LIST_HEAD(, ccb_hdr) pending_ccbs; 14539213Sgibbs struct cd_params params; 14639213Sgibbs union ccb saved_ccb; 14739213Sgibbs cd_quirks quirks; 148112006Sphk struct devstat *device_stats; 14960938Sjake STAILQ_ENTRY(cd_softc) changer_links; 15039213Sgibbs struct cdchanger *changer; 15139213Sgibbs int bufs_left; 15239213Sgibbs struct cam_periph *periph; 153104456Sphk dev_t dev; 154104880Sphk eventhandler_tag clonetag; 155111206Sken int minimum_command_size; 156112262Sphk int outstanding_cmds; 157111206Sken struct sysctl_ctx_list sysctl_ctx; 158111206Sken struct sysctl_oid *sysctl_tree; 159111206Sken STAILQ_HEAD(, cd_mode_params) mode_queue; 160111206Sken struct cd_tocdata toc; 16139213Sgibbs}; 16239213Sgibbs 163111206Skenstruct cd_page_sizes { 164111206Sken int page; 165111206Sken int page_size; 166111206Sken}; 167111206Sken 168111206Skenstatic struct cd_page_sizes cd_page_size_table[] = 169111206Sken{ 170111206Sken { AUDIO_PAGE, sizeof(struct cd_audio_page)} 171111206Sken}; 172111206Sken 17339213Sgibbsstruct cd_quirk_entry { 17439213Sgibbs struct scsi_inquiry_pattern inq_pat; 17539213Sgibbs cd_quirks quirks; 17639213Sgibbs}; 17739213Sgibbs 17839213Sgibbs/* 179111206Sken * The changer quirk entries aren't strictly necessary. Basically, what 180111206Sken * they do is tell cdregister() up front that a device is a changer. 181111206Sken * Otherwise, it will figure that fact out once it sees a LUN on the device 182111206Sken * that is greater than 0. If it is known up front that a device is a changer, 183111206Sken * all I/O to the device will go through the changer scheduling routines, as 18439213Sgibbs * opposed to the "normal" CD code. 185111206Sken * 186111206Sken * NOTE ON 10_BYTE_ONLY quirks: Any 10_BYTE_ONLY quirks MUST be because 187111206Sken * your device hangs when it gets a 10 byte command. Adding a quirk just 188111206Sken * to get rid of the informative diagnostic message is not acceptable. All 189111206Sken * 10_BYTE_ONLY quirks must be documented in full in a PR (which should be 190111206Sken * referenced in a comment along with the quirk) , and must be approved by 191111206Sken * ken@FreeBSD.org. Any quirks added that don't adhere to this policy may 192111206Sken * be removed until the submitter can explain why they are needed. 193111206Sken * 10_BYTE_ONLY quirks will be removed (as they will no longer be necessary) 194111206Sken * when the CAM_NEW_TRAN_CODE work is done. 19539213Sgibbs */ 19639213Sgibbsstatic struct cd_quirk_entry cd_quirk_table[] = 19739213Sgibbs{ 19839213Sgibbs { 19939213Sgibbs { T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"}, 20039213Sgibbs /*quirks*/ CD_Q_CHANGER 20139213Sgibbs }, 20239213Sgibbs { 20354451Sken { T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM*", 20439213Sgibbs "*"}, /* quirks */ CD_Q_CHANGER 20540262Sken }, 20640262Sken { 20767752Sken { T_CDROM, SIP_MEDIA_REMOVABLE, "NAKAMICH", "MJ-*", "*"}, 20867752Sken /* quirks */ CD_Q_CHANGER 20967752Sken }, 21067752Sken { 21140262Sken { T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"}, 21240262Sken /* quirks */ CD_Q_BCD_TRACKS 21339213Sgibbs } 21439213Sgibbs}; 21539213Sgibbs 21639213Sgibbs#define CD_CDEV_MAJOR 15 21739213Sgibbs 21839213Sgibbsstatic d_open_t cdopen; 21939213Sgibbsstatic d_close_t cdclose; 22039213Sgibbsstatic d_ioctl_t cdioctl; 22139213Sgibbsstatic d_strategy_t cdstrategy; 22239213Sgibbs 22339213Sgibbsstatic periph_init_t cdinit; 22439213Sgibbsstatic periph_ctor_t cdregister; 22539213Sgibbsstatic periph_dtor_t cdcleanup; 22639213Sgibbsstatic periph_start_t cdstart; 22740603Skenstatic periph_oninv_t cdoninvalidate; 22839213Sgibbsstatic void cdasync(void *callback_arg, u_int32_t code, 22939213Sgibbs struct cam_path *path, void *arg); 230111206Skenstatic int cdcmdsizesysctl(SYSCTL_HANDLER_ARGS); 23139213Sgibbsstatic void cdshorttimeout(void *arg); 23239213Sgibbsstatic void cdschedule(struct cam_periph *periph, int priority); 23339213Sgibbsstatic void cdrunchangerqueue(void *arg); 23439213Sgibbsstatic void cdchangerschedule(struct cd_softc *softc); 23539213Sgibbsstatic int cdrunccb(union ccb *ccb, 23639213Sgibbs int (*error_routine)(union ccb *ccb, 23739213Sgibbs u_int32_t cam_flags, 23839213Sgibbs u_int32_t sense_flags), 23939213Sgibbs u_int32_t cam_flags, u_int32_t sense_flags); 240111206Skenstatic union ccb *cdgetccb(struct cam_periph *periph, 24139213Sgibbs u_int32_t priority); 24239213Sgibbsstatic void cddone(struct cam_periph *periph, 24339213Sgibbs union ccb *start_ccb); 244111206Skenstatic union cd_pages *cdgetpage(struct cd_mode_params *mode_params); 245111206Skenstatic int cdgetpagesize(int page_num); 246111206Skenstatic void cdprevent(struct cam_periph *periph, int action); 247111206Skenstatic int cdcheckmedia(struct cam_periph *periph); 248111206Skenstatic int cdsize(struct cam_periph *periph, u_int32_t *size); 249111206Skenstatic int cd6byteworkaround(union ccb *ccb); 25039213Sgibbsstatic int cderror(union ccb *ccb, u_int32_t cam_flags, 25139213Sgibbs u_int32_t sense_flags); 25239213Sgibbsstatic int cdreadtoc(struct cam_periph *periph, u_int32_t mode, 253111206Sken u_int32_t start, u_int8_t *data, 254111206Sken u_int32_t len, u_int32_t sense_flags); 25539213Sgibbsstatic int cdgetmode(struct cam_periph *periph, 256111206Sken struct cd_mode_params *data, u_int32_t page); 25739213Sgibbsstatic int cdsetmode(struct cam_periph *periph, 258111206Sken struct cd_mode_params *data); 25939213Sgibbsstatic int cdplay(struct cam_periph *periph, u_int32_t blk, 26039213Sgibbs u_int32_t len); 26139213Sgibbsstatic int cdreadsubchannel(struct cam_periph *periph, 26239213Sgibbs u_int32_t mode, u_int32_t format, 26339213Sgibbs int track, 26439213Sgibbs struct cd_sub_channel_info *data, 26539213Sgibbs u_int32_t len); 26639213Sgibbsstatic int cdplaymsf(struct cam_periph *periph, u_int32_t startm, 26739213Sgibbs u_int32_t starts, u_int32_t startf, 26839213Sgibbs u_int32_t endm, u_int32_t ends, 26939213Sgibbs u_int32_t endf); 27039213Sgibbsstatic int cdplaytracks(struct cam_periph *periph, 27139213Sgibbs u_int32_t strack, u_int32_t sindex, 27239213Sgibbs u_int32_t etrack, u_int32_t eindex); 27339213Sgibbsstatic int cdpause(struct cam_periph *periph, u_int32_t go); 27439213Sgibbsstatic int cdstopunit(struct cam_periph *periph, u_int32_t eject); 275111206Skenstatic int cdstartunit(struct cam_periph *periph, int load); 276105421Snjlstatic int cdsetspeed(struct cam_periph *periph, 277105421Snjl u_int32_t rdspeed, u_int32_t wrspeed); 27860422Skenstatic int cdreportkey(struct cam_periph *periph, 27960422Sken struct dvd_authinfo *authinfo); 28060422Skenstatic int cdsendkey(struct cam_periph *periph, 28160422Sken struct dvd_authinfo *authinfo); 28260422Skenstatic int cdreaddvdstructure(struct cam_periph *periph, 28360422Sken struct dvd_struct *dvdstruct); 28439213Sgibbs 28539213Sgibbsstatic struct periph_driver cddriver = 28639213Sgibbs{ 28739213Sgibbs cdinit, "cd", 28839213Sgibbs TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0 28939213Sgibbs}; 29039213Sgibbs 29172119SpeterPERIPHDRIVER_DECLARE(cd, cddriver); 29239213Sgibbs 29347625Sphkstatic struct cdevsw cd_cdevsw = { 294111815Sphk .d_open = cdopen, 295111815Sphk .d_close = cdclose, 296111815Sphk .d_read = physread, 297111815Sphk .d_write = physwrite, 298111815Sphk .d_ioctl = cdioctl, 299111815Sphk .d_strategy = cdstrategy, 300111815Sphk .d_name = "cd", 301111815Sphk .d_maj = CD_CDEV_MAJOR, 302111815Sphk .d_flags = D_DISK, 30339213Sgibbs}; 30439213Sgibbs 30539213Sgibbsstatic int num_changers; 30639213Sgibbs 30739213Sgibbs#ifndef CHANGER_MIN_BUSY_SECONDS 30846747Sken#define CHANGER_MIN_BUSY_SECONDS 5 30939213Sgibbs#endif 31039213Sgibbs#ifndef CHANGER_MAX_BUSY_SECONDS 31146747Sken#define CHANGER_MAX_BUSY_SECONDS 15 31239213Sgibbs#endif 31339213Sgibbs 31439213Sgibbsstatic int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS; 31539213Sgibbsstatic int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS; 31639213Sgibbs 31739213SgibbsSYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver"); 31839213SgibbsSYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer"); 31939213SgibbsSYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW, 32039213Sgibbs &changer_min_busy_seconds, 0, "Minimum changer scheduling quantum"); 321111206SkenTUNABLE_INT("kern.cam.cd.changer.min_busy_seconds", &changer_min_busy_seconds); 32239213SgibbsSYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW, 32339213Sgibbs &changer_max_busy_seconds, 0, "Maximum changer scheduling quantum"); 324111206SkenTUNABLE_INT("kern.cam.cd.changer.max_busy_seconds", &changer_max_busy_seconds); 32539213Sgibbs 32639213Sgibbsstruct cdchanger { 32739213Sgibbs path_id_t path_id; 32839213Sgibbs target_id_t target_id; 32939213Sgibbs int num_devices; 33039213Sgibbs struct camq devq; 33139213Sgibbs struct timeval start_time; 33239213Sgibbs struct cd_softc *cur_device; 33339213Sgibbs struct callout_handle short_handle; 33439213Sgibbs struct callout_handle long_handle; 33546581Sken volatile cd_changer_flags flags; 33660938Sjake STAILQ_ENTRY(cdchanger) changer_links; 33760938Sjake STAILQ_HEAD(chdevlist, cd_softc) chluns; 33839213Sgibbs}; 33939213Sgibbs 34060938Sjakestatic STAILQ_HEAD(changerlist, cdchanger) changerq; 34139213Sgibbs 342104094Sphkstatic void 343104880Sphkcdclone(void *arg, char *name, int namelen, dev_t *dev) 344104880Sphk{ 345104880Sphk struct cd_softc *softc; 346104880Sphk const char *p; 347104880Sphk int l; 348104880Sphk 349104880Sphk softc = arg; 350104880Sphk p = devtoname(softc->dev); 351104880Sphk l = strlen(p); 352104880Sphk if (bcmp(name, p, l)) 353104880Sphk return; 354104880Sphk if (name[l] != 'a' && name[l] != 'c') 355104880Sphk return; 356104880Sphk if (name[l + 1] != '\0') 357104880Sphk return; 358104880Sphk *dev = softc->dev; 359104880Sphk return; 360104880Sphk} 361104880Sphk 362104880Sphkstatic void 36339213Sgibbscdinit(void) 36439213Sgibbs{ 36539213Sgibbs cam_status status; 36639213Sgibbs struct cam_path *path; 36739213Sgibbs 36839213Sgibbs /* 36939213Sgibbs * Install a global async callback. This callback will 37039213Sgibbs * receive async callbacks like "new device found". 37139213Sgibbs */ 37239213Sgibbs status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, 37339213Sgibbs CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 37439213Sgibbs 37539213Sgibbs if (status == CAM_REQ_CMP) { 37639213Sgibbs struct ccb_setasync csa; 37739213Sgibbs 37839213Sgibbs xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); 37939213Sgibbs csa.ccb_h.func_code = XPT_SASYNC_CB; 38039213Sgibbs csa.event_enable = AC_FOUND_DEVICE; 38139213Sgibbs csa.callback = cdasync; 38239213Sgibbs csa.callback_arg = NULL; 38339213Sgibbs xpt_action((union ccb *)&csa); 38439213Sgibbs status = csa.ccb_h.status; 38539213Sgibbs xpt_free_path(path); 38639213Sgibbs } 38739213Sgibbs 38839213Sgibbs if (status != CAM_REQ_CMP) { 38939213Sgibbs printf("cd: Failed to attach master async callback " 39039213Sgibbs "due to status 0x%x!\n", status); 39139213Sgibbs } 39239213Sgibbs} 39339213Sgibbs 39439213Sgibbsstatic void 39540603Skencdoninvalidate(struct cam_periph *periph) 39640603Sken{ 39740603Sken int s; 39840603Sken struct cd_softc *softc; 39940603Sken struct ccb_setasync csa; 40040603Sken 40140603Sken softc = (struct cd_softc *)periph->softc; 40240603Sken 40340603Sken /* 40440603Sken * De-register any async callbacks. 40540603Sken */ 40640603Sken xpt_setup_ccb(&csa.ccb_h, periph->path, 40740603Sken /* priority */ 5); 40840603Sken csa.ccb_h.func_code = XPT_SASYNC_CB; 40940603Sken csa.event_enable = 0; 41040603Sken csa.callback = cdasync; 41140603Sken csa.callback_arg = periph; 41240603Sken xpt_action((union ccb *)&csa); 41340603Sken 41440603Sken softc->flags |= CD_FLAG_INVALID; 41540603Sken 41640603Sken /* 41740603Sken * Although the oninvalidate() routines are always called at 41840603Sken * splsoftcam, we need to be at splbio() here to keep the buffer 41940603Sken * queue from being modified while we traverse it. 42040603Sken */ 42140603Sken s = splbio(); 42240603Sken 42340603Sken /* 42440603Sken * Return all queued I/O with ENXIO. 42540603Sken * XXX Handle any transactions queued to the card 42640603Sken * with XPT_ABORT_CCB. 42740603Sken */ 428112946Sphk bioq_flush(&softc->bio_queue, NULL, ENXIO); 42940603Sken splx(s); 43040603Sken 43140603Sken /* 43240603Sken * If this device is part of a changer, and it was scheduled 43340603Sken * to run, remove it from the run queue since we just nuked 43440603Sken * all of its scheduled I/O. 43540603Sken */ 43640603Sken if ((softc->flags & CD_FLAG_CHANGER) 43740603Sken && (softc->pinfo.index != CAM_UNQUEUED_INDEX)) 43840603Sken camq_remove(&softc->changer->devq, softc->pinfo.index); 43940603Sken 44040603Sken xpt_print_path(periph->path); 44140603Sken printf("lost device\n"); 44240603Sken} 44340603Sken 44440603Skenstatic void 44539213Sgibbscdcleanup(struct cam_periph *periph) 44639213Sgibbs{ 44739213Sgibbs struct cd_softc *softc; 44840603Sken int s; 44939213Sgibbs 45039213Sgibbs softc = (struct cd_softc *)periph->softc; 45139213Sgibbs 45239213Sgibbs xpt_print_path(periph->path); 45339213Sgibbs printf("removing device entry\n"); 45440603Sken 455112668Sken if (sysctl_ctx_free(&softc->sysctl_ctx) != 0) { 456112668Sken xpt_print_path(periph->path); 457112668Sken printf("can't remove sysctl context\n"); 458112668Sken } 459112668Sken 46040603Sken s = splsoftcam(); 46139213Sgibbs /* 46239213Sgibbs * In the queued, non-active case, the device in question 46339213Sgibbs * has already been removed from the changer run queue. Since this 46439213Sgibbs * device is active, we need to de-activate it, and schedule 46539213Sgibbs * another device to run. (if there is another one to run) 46639213Sgibbs */ 46739213Sgibbs if ((softc->flags & CD_FLAG_CHANGER) 46839213Sgibbs && (softc->flags & CD_FLAG_ACTIVE)) { 46939213Sgibbs 47039213Sgibbs /* 47139213Sgibbs * The purpose of the short timeout is soley to determine 47239213Sgibbs * whether the current device has finished or not. Well, 47339213Sgibbs * since we're removing the active device, we know that it 47439213Sgibbs * is finished. So, get rid of the short timeout. 47539213Sgibbs * Otherwise, if we're in the time period before the short 47639213Sgibbs * timeout fires, and there are no other devices in the 47739213Sgibbs * queue to run, there won't be any other device put in the 47839213Sgibbs * active slot. i.e., when we call cdrunchangerqueue() 47939213Sgibbs * below, it won't do anything. Then, when the short 48039213Sgibbs * timeout fires, it'll look at the "current device", which 48139213Sgibbs * we are free below, and possibly panic the kernel on a 48239213Sgibbs * bogus pointer reference. 48339213Sgibbs * 48439213Sgibbs * The long timeout doesn't really matter, since we 48539213Sgibbs * decrement the qfrozen_cnt to indicate that there is 48639213Sgibbs * nothing in the active slot now. Therefore, there won't 48739213Sgibbs * be any bogus pointer references there. 48839213Sgibbs */ 48939213Sgibbs if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { 49039213Sgibbs untimeout(cdshorttimeout, softc->changer, 49139213Sgibbs softc->changer->short_handle); 49239213Sgibbs softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; 49339213Sgibbs } 49439213Sgibbs softc->changer->devq.qfrozen_cnt--; 49539213Sgibbs softc->changer->flags |= CHANGER_MANUAL_CALL; 49639213Sgibbs cdrunchangerqueue(softc->changer); 49739213Sgibbs } 49839213Sgibbs 49939213Sgibbs /* 50039213Sgibbs * If we're removing the last device on the changer, go ahead and 50139213Sgibbs * remove the changer device structure. 50239213Sgibbs */ 50339213Sgibbs if ((softc->flags & CD_FLAG_CHANGER) 50439213Sgibbs && (--softc->changer->num_devices == 0)) { 50539213Sgibbs 50639213Sgibbs /* 50739213Sgibbs * Theoretically, there shouldn't be any timeouts left, but 50839213Sgibbs * I'm not completely sure that that will be the case. So, 50939213Sgibbs * it won't hurt to check and see if there are any left. 51039213Sgibbs */ 51139213Sgibbs if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) { 51239213Sgibbs untimeout(cdrunchangerqueue, softc->changer, 51339213Sgibbs softc->changer->long_handle); 51439213Sgibbs softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED; 51539213Sgibbs } 51639213Sgibbs 51739213Sgibbs if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { 51839213Sgibbs untimeout(cdshorttimeout, softc->changer, 51939213Sgibbs softc->changer->short_handle); 52039213Sgibbs softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; 52139213Sgibbs } 52239213Sgibbs 52360938Sjake STAILQ_REMOVE(&changerq, softc->changer, cdchanger, 52439213Sgibbs changer_links); 52539213Sgibbs xpt_print_path(periph->path); 52639213Sgibbs printf("removing changer entry\n"); 52739213Sgibbs free(softc->changer, M_DEVBUF); 52839213Sgibbs num_changers--; 52939213Sgibbs } 530112006Sphk devstat_remove_entry(softc->device_stats); 531104456Sphk destroy_dev(softc->dev); 532104880Sphk EVENTHANDLER_DEREGISTER(dev_clone, softc->clonetag); 53340603Sken free(softc, M_DEVBUF); 53440603Sken splx(s); 53539213Sgibbs} 53639213Sgibbs 53739213Sgibbsstatic void 53839213Sgibbscdasync(void *callback_arg, u_int32_t code, 53939213Sgibbs struct cam_path *path, void *arg) 54039213Sgibbs{ 54139213Sgibbs struct cam_periph *periph; 54239213Sgibbs 54339213Sgibbs periph = (struct cam_periph *)callback_arg; 54439213Sgibbs switch (code) { 54539213Sgibbs case AC_FOUND_DEVICE: 54639213Sgibbs { 54739213Sgibbs struct ccb_getdev *cgd; 54839213Sgibbs cam_status status; 54939213Sgibbs 55039213Sgibbs cgd = (struct ccb_getdev *)arg; 55179177Smjacob if (cgd == NULL) 55279177Smjacob break; 55339213Sgibbs 55456148Smjacob if (SID_TYPE(&cgd->inq_data) != T_CDROM 55556148Smjacob && SID_TYPE(&cgd->inq_data) != T_WORM) 55639213Sgibbs break; 55739213Sgibbs 55839213Sgibbs /* 55939213Sgibbs * Allocate a peripheral instance for 56039213Sgibbs * this device and start the probe 56139213Sgibbs * process. 56239213Sgibbs */ 56340603Sken status = cam_periph_alloc(cdregister, cdoninvalidate, 56440603Sken cdcleanup, cdstart, 56540603Sken "cd", CAM_PERIPH_BIO, 56640603Sken cgd->ccb_h.path, cdasync, 56740603Sken AC_FOUND_DEVICE, cgd); 56839213Sgibbs 56939213Sgibbs if (status != CAM_REQ_CMP 57039213Sgibbs && status != CAM_REQ_INPROG) 57139213Sgibbs printf("cdasync: Unable to attach new device " 57239213Sgibbs "due to status 0x%x\n", status); 57339213Sgibbs 57439213Sgibbs break; 57539213Sgibbs } 57639213Sgibbs case AC_SENT_BDR: 57739213Sgibbs case AC_BUS_RESET: 57839213Sgibbs { 57939213Sgibbs struct cd_softc *softc; 58039213Sgibbs struct ccb_hdr *ccbh; 58139213Sgibbs int s; 58239213Sgibbs 58339213Sgibbs softc = (struct cd_softc *)periph->softc; 58439213Sgibbs s = splsoftcam(); 58539213Sgibbs /* 58639213Sgibbs * Don't fail on the expected unit attention 58739213Sgibbs * that will occur. 58839213Sgibbs */ 58939213Sgibbs softc->flags |= CD_FLAG_RETRY_UA; 59071999Sphk LIST_FOREACH(ccbh, &softc->pending_ccbs, periph_links.le) 59139213Sgibbs ccbh->ccb_state |= CD_CCB_RETRY_UA; 59239213Sgibbs splx(s); 59347413Sgibbs /* FALLTHROUGH */ 59439213Sgibbs } 59539213Sgibbs default: 59647413Sgibbs cam_periph_async(periph, code, path, arg); 59739213Sgibbs break; 59839213Sgibbs } 59939213Sgibbs} 60039213Sgibbs 601111206Sken/* 602111206Sken * We have a handler function for this so we can check the values when the 603111206Sken * user sets them, instead of every time we look at them. 604111206Sken */ 605111206Skenstatic int 606111206Skencdcmdsizesysctl(SYSCTL_HANDLER_ARGS) 607111206Sken{ 608111206Sken int error, value; 609111206Sken 610111206Sken value = *(int *)arg1; 611111206Sken 612111206Sken error = sysctl_handle_int(oidp, &value, 0, req); 613111206Sken 614111206Sken if ((error != 0) 615111206Sken || (req->newptr == NULL)) 616111206Sken return (error); 617111206Sken 618111206Sken /* 619111206Sken * The only real values we can have here are 6 or 10. I don't 620111206Sken * really forsee having 12 be an option at any time in the future. 621111206Sken * So if the user sets something less than or equal to 6, we'll set 622111206Sken * it to 6. If he sets something greater than 6, we'll set it to 10. 623111206Sken * 624111206Sken * I suppose we could just return an error here for the wrong values, 625111206Sken * but I don't think it's necessary to do so, as long as we can 626111206Sken * determine the user's intent without too much trouble. 627111206Sken */ 628111206Sken if (value < 6) 629111206Sken value = 6; 630111206Sken else if (value > 6) 631111206Sken value = 10; 632111206Sken 633111206Sken *(int *)arg1 = value; 634111206Sken 635111206Sken return (0); 636111206Sken} 637111206Sken 63839213Sgibbsstatic cam_status 63939213Sgibbscdregister(struct cam_periph *periph, void *arg) 64039213Sgibbs{ 64139213Sgibbs struct cd_softc *softc; 64239213Sgibbs struct ccb_setasync csa; 64339213Sgibbs struct ccb_getdev *cgd; 644111206Sken char tmpstr[80], tmpstr2[80]; 64539213Sgibbs caddr_t match; 64639213Sgibbs 64739213Sgibbs cgd = (struct ccb_getdev *)arg; 64839213Sgibbs if (periph == NULL) { 64939213Sgibbs printf("cdregister: periph was NULL!!\n"); 65039213Sgibbs return(CAM_REQ_CMP_ERR); 65139213Sgibbs } 65239213Sgibbs if (cgd == NULL) { 65339213Sgibbs printf("cdregister: no getdev CCB, can't register device\n"); 65439213Sgibbs return(CAM_REQ_CMP_ERR); 65539213Sgibbs } 65639213Sgibbs 65739213Sgibbs softc = (struct cd_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); 65839213Sgibbs 65939213Sgibbs if (softc == NULL) { 66039213Sgibbs printf("cdregister: Unable to probe new device. " 66139213Sgibbs "Unable to allocate softc\n"); 66239213Sgibbs return(CAM_REQ_CMP_ERR); 66339213Sgibbs } 66439213Sgibbs 66539213Sgibbs bzero(softc, sizeof(*softc)); 66639213Sgibbs LIST_INIT(&softc->pending_ccbs); 667111206Sken STAILQ_INIT(&softc->mode_queue); 66839213Sgibbs softc->state = CD_STATE_PROBE; 66959249Sphk bioq_init(&softc->bio_queue); 67039213Sgibbs if (SID_IS_REMOVABLE(&cgd->inq_data)) 67139213Sgibbs softc->flags |= CD_FLAG_DISC_REMOVABLE; 67239213Sgibbs if ((cgd->inq_data.flags & SID_CmdQue) != 0) 67339213Sgibbs softc->flags |= CD_FLAG_TAGGED_QUEUING; 67439213Sgibbs 67539213Sgibbs periph->softc = softc; 67639213Sgibbs softc->periph = periph; 67739213Sgibbs 67839213Sgibbs /* 67939213Sgibbs * See if this device has any quirks. 68039213Sgibbs */ 68139213Sgibbs match = cam_quirkmatch((caddr_t)&cgd->inq_data, 68239213Sgibbs (caddr_t)cd_quirk_table, 68339213Sgibbs sizeof(cd_quirk_table)/sizeof(*cd_quirk_table), 68439213Sgibbs sizeof(*cd_quirk_table), scsi_inquiry_match); 68539213Sgibbs 68639213Sgibbs if (match != NULL) 68739213Sgibbs softc->quirks = ((struct cd_quirk_entry *)match)->quirks; 68839213Sgibbs else 68939213Sgibbs softc->quirks = CD_Q_NONE; 69039213Sgibbs 691111206Sken snprintf(tmpstr, sizeof(tmpstr), "CAM CD unit %d", periph->unit_number); 692111206Sken snprintf(tmpstr2, sizeof(tmpstr2), "%d", periph->unit_number); 693112668Sken sysctl_ctx_init(&softc->sysctl_ctx); 694111206Sken softc->sysctl_tree = SYSCTL_ADD_NODE(&softc->sysctl_ctx, 695111206Sken SYSCTL_STATIC_CHILDREN(_kern_cam_cd), OID_AUTO, 696111206Sken tmpstr2, CTLFLAG_RD, 0, tmpstr); 697111206Sken if (softc->sysctl_tree == NULL) { 698111206Sken printf("cdregister: unable to allocate sysctl tree\n"); 699111206Sken free(softc, M_DEVBUF); 700111206Sken return (CAM_REQ_CMP_ERR); 701111206Sken } 702111206Sken 703111206Sken /* The default is 6 byte commands, unless quirked otherwise */ 704111206Sken if (softc->quirks & CD_Q_10_BYTE_ONLY) 705111206Sken softc->minimum_command_size = 10; 706111206Sken else 707111206Sken softc->minimum_command_size = 6; 708111206Sken 70939213Sgibbs /* 710111206Sken * Load the user's default, if any. 711111206Sken */ 712111206Sken snprintf(tmpstr, sizeof(tmpstr), "kern.cam.cd.%d.minimum_cmd_size", 713111206Sken periph->unit_number); 714111206Sken TUNABLE_INT_FETCH(tmpstr, &softc->minimum_command_size); 715111206Sken 716111206Sken /* 6 and 10 are the only permissible values here. */ 717111206Sken if (softc->minimum_command_size < 6) 718111206Sken softc->minimum_command_size = 6; 719111206Sken else if (softc->minimum_command_size > 6) 720111206Sken softc->minimum_command_size = 10; 721111206Sken 722111206Sken /* 723111206Sken * Now register the sysctl handler, so the user can the value on 724111206Sken * the fly. 725111206Sken */ 726111206Sken SYSCTL_ADD_PROC(&softc->sysctl_ctx,SYSCTL_CHILDREN(softc->sysctl_tree), 727111206Sken OID_AUTO, "minimum_cmd_size", CTLTYPE_INT | CTLFLAG_RW, 728111206Sken &softc->minimum_command_size, 0, cdcmdsizesysctl, "I", 729111206Sken "Minimum CDB size"); 730111206Sken 731111206Sken /* 73239213Sgibbs * We need to register the statistics structure for this device, 73339213Sgibbs * but we don't have the blocksize yet for it. So, we register 73439213Sgibbs * the structure and indicate that we don't have the blocksize 73539213Sgibbs * yet. Unlike other SCSI peripheral drivers, we explicitly set 73639213Sgibbs * the device type here to be CDROM, rather than just ORing in 73756148Smjacob * the device type. This is because this driver can attach to either 73839213Sgibbs * CDROM or WORM devices, and we want this peripheral driver to 73939213Sgibbs * show up in the devstat list as a CD peripheral driver, not a 74039213Sgibbs * WORM peripheral driver. WORM drives will also have the WORM 74139213Sgibbs * driver attached to them. 74239213Sgibbs */ 743112006Sphk softc->device_stats = devstat_new_entry("cd", 74439213Sgibbs periph->unit_number, 0, 74539213Sgibbs DEVSTAT_BS_UNAVAILABLE, 74643819Sken DEVSTAT_TYPE_CDROM | DEVSTAT_TYPE_IF_SCSI, 74743819Sken DEVSTAT_PRIORITY_CD); 748104456Sphk softc->dev = make_dev(&cd_cdevsw, periph->unit_number, 749104456Sphk UID_ROOT, GID_OPERATOR, 0640, "cd%d", periph->unit_number); 750104456Sphk softc->dev->si_drv1 = periph; 751104880Sphk softc->clonetag = 752104880Sphk EVENTHANDLER_REGISTER(dev_clone, cdclone, softc, 1000); 75339213Sgibbs 75439213Sgibbs /* 75539213Sgibbs * Add an async callback so that we get 75639213Sgibbs * notified if this device goes away. 75739213Sgibbs */ 75839213Sgibbs xpt_setup_ccb(&csa.ccb_h, periph->path, 75939213Sgibbs /* priority */ 5); 76039213Sgibbs csa.ccb_h.func_code = XPT_SASYNC_CB; 76139213Sgibbs csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE; 76239213Sgibbs csa.callback = cdasync; 76339213Sgibbs csa.callback_arg = periph; 76439213Sgibbs xpt_action((union ccb *)&csa); 76539213Sgibbs 76639213Sgibbs /* 76739213Sgibbs * If the target lun is greater than 0, we most likely have a CD 76839213Sgibbs * changer device. Check the quirk entries as well, though, just 76939213Sgibbs * in case someone has a CD tower with one lun per drive or 77039213Sgibbs * something like that. Also, if we know up front that a 77139213Sgibbs * particular device is a changer, we can mark it as such starting 77239213Sgibbs * with lun 0, instead of lun 1. It shouldn't be necessary to have 77339213Sgibbs * a quirk entry to define something as a changer, however. 77439213Sgibbs */ 77539213Sgibbs if (((cgd->ccb_h.target_lun > 0) 77639213Sgibbs && ((softc->quirks & CD_Q_NO_CHANGER) == 0)) 77739213Sgibbs || ((softc->quirks & CD_Q_CHANGER) != 0)) { 77839213Sgibbs struct cdchanger *nchanger; 77939213Sgibbs struct cam_periph *nperiph; 78039213Sgibbs struct cam_path *path; 78139213Sgibbs cam_status status; 78239213Sgibbs int found; 78339213Sgibbs 78439213Sgibbs /* Set the changer flag in the current device's softc */ 78539213Sgibbs softc->flags |= CD_FLAG_CHANGER; 78639213Sgibbs 78739213Sgibbs if (num_changers == 0) 78839213Sgibbs STAILQ_INIT(&changerq); 78939213Sgibbs 79039213Sgibbs /* 79139213Sgibbs * Now, look around for an existing changer device with the 79239213Sgibbs * same path and target ID as the current device. 79339213Sgibbs */ 79439213Sgibbs for (found = 0, 79539213Sgibbs nchanger = (struct cdchanger *)STAILQ_FIRST(&changerq); 79639213Sgibbs nchanger != NULL; 79739213Sgibbs nchanger = STAILQ_NEXT(nchanger, changer_links)){ 79839213Sgibbs if ((nchanger->path_id == cgd->ccb_h.path_id) 79939213Sgibbs && (nchanger->target_id == cgd->ccb_h.target_id)) { 80039213Sgibbs found = 1; 80139213Sgibbs break; 80239213Sgibbs } 80339213Sgibbs } 80439213Sgibbs 80539213Sgibbs /* 80639213Sgibbs * If we found a matching entry, just add this device to 80739213Sgibbs * the list of devices on this changer. 80839213Sgibbs */ 80939213Sgibbs if (found == 1) { 81039213Sgibbs struct chdevlist *chlunhead; 81139213Sgibbs 81239213Sgibbs chlunhead = &nchanger->chluns; 81339213Sgibbs 81439213Sgibbs /* 81539213Sgibbs * XXX KDM look at consolidating this code with the 81639213Sgibbs * code below in a separate function. 81739213Sgibbs */ 81839213Sgibbs 81939213Sgibbs /* 82039213Sgibbs * Create a path with lun id 0, and see if we can 82139213Sgibbs * find a matching device 82239213Sgibbs */ 82339213Sgibbs status = xpt_create_path(&path, /*periph*/ periph, 82439213Sgibbs cgd->ccb_h.path_id, 82539213Sgibbs cgd->ccb_h.target_id, 0); 82639213Sgibbs 82739213Sgibbs if ((status == CAM_REQ_CMP) 82839213Sgibbs && ((nperiph = cam_periph_find(path, "cd")) != NULL)){ 82939213Sgibbs struct cd_softc *nsoftc; 83039213Sgibbs 83139213Sgibbs nsoftc = (struct cd_softc *)nperiph->softc; 83239213Sgibbs 83339213Sgibbs if ((nsoftc->flags & CD_FLAG_CHANGER) == 0){ 83439213Sgibbs nsoftc->flags |= CD_FLAG_CHANGER; 83539213Sgibbs nchanger->num_devices++; 83639213Sgibbs if (camq_resize(&nchanger->devq, 83739213Sgibbs nchanger->num_devices)!=CAM_REQ_CMP){ 83839213Sgibbs printf("cdregister: " 83939213Sgibbs "camq_resize " 84039213Sgibbs "failed, changer " 84139213Sgibbs "support may " 84239213Sgibbs "be messed up\n"); 84339213Sgibbs } 84439213Sgibbs nsoftc->changer = nchanger; 84539213Sgibbs nsoftc->pinfo.index =CAM_UNQUEUED_INDEX; 84639213Sgibbs 84739213Sgibbs STAILQ_INSERT_TAIL(&nchanger->chluns, 84839213Sgibbs nsoftc,changer_links); 84939213Sgibbs } 85067928Sken xpt_free_path(path); 85139213Sgibbs } else if (status == CAM_REQ_CMP) 85239213Sgibbs xpt_free_path(path); 85339213Sgibbs else { 85439213Sgibbs printf("cdregister: unable to allocate path\n" 85539213Sgibbs "cdregister: changer support may be " 85639213Sgibbs "broken\n"); 85739213Sgibbs } 85839213Sgibbs 85939213Sgibbs nchanger->num_devices++; 86039213Sgibbs 86139213Sgibbs softc->changer = nchanger; 86239213Sgibbs softc->pinfo.index = CAM_UNQUEUED_INDEX; 86339213Sgibbs 86439213Sgibbs if (camq_resize(&nchanger->devq, 86539213Sgibbs nchanger->num_devices) != CAM_REQ_CMP) { 86639213Sgibbs printf("cdregister: camq_resize " 86739213Sgibbs "failed, changer support may " 86839213Sgibbs "be messed up\n"); 86939213Sgibbs } 87039213Sgibbs 87139213Sgibbs STAILQ_INSERT_TAIL(chlunhead, softc, changer_links); 87239213Sgibbs } 87339213Sgibbs /* 87439213Sgibbs * In this case, we don't already have an entry for this 87539213Sgibbs * particular changer, so we need to create one, add it to 87639213Sgibbs * the queue, and queue this device on the list for this 87739213Sgibbs * changer. Before we queue this device, however, we need 87839213Sgibbs * to search for lun id 0 on this target, and add it to the 87939213Sgibbs * queue first, if it exists. (and if it hasn't already 88039213Sgibbs * been marked as part of the changer.) 88139213Sgibbs */ 88239213Sgibbs else { 88339213Sgibbs nchanger = malloc(sizeof(struct cdchanger), 88439213Sgibbs M_DEVBUF, M_NOWAIT); 88539213Sgibbs 88639213Sgibbs if (nchanger == NULL) { 88739213Sgibbs softc->flags &= ~CD_FLAG_CHANGER; 88839213Sgibbs printf("cdregister: unable to malloc " 88939213Sgibbs "changer structure\ncdregister: " 89039213Sgibbs "changer support disabled\n"); 89139213Sgibbs 89239213Sgibbs /* 89339213Sgibbs * Yes, gotos can be gross but in this case 89439213Sgibbs * I think it's justified.. 89539213Sgibbs */ 89639213Sgibbs goto cdregisterexit; 89739213Sgibbs } 89839213Sgibbs 89939213Sgibbs /* zero the structure */ 90039213Sgibbs bzero(nchanger, sizeof(struct cdchanger)); 90139213Sgibbs 90239213Sgibbs if (camq_init(&nchanger->devq, 1) != 0) { 90339213Sgibbs softc->flags &= ~CD_FLAG_CHANGER; 90439213Sgibbs printf("cdregister: changer support " 90539213Sgibbs "disabled\n"); 90639213Sgibbs goto cdregisterexit; 90739213Sgibbs } 90839213Sgibbs 90939213Sgibbs num_changers++; 91039213Sgibbs 91139213Sgibbs nchanger->path_id = cgd->ccb_h.path_id; 91239213Sgibbs nchanger->target_id = cgd->ccb_h.target_id; 91339213Sgibbs 91439213Sgibbs /* this is superfluous, but it makes things clearer */ 91539213Sgibbs nchanger->num_devices = 0; 91639213Sgibbs 91739213Sgibbs STAILQ_INIT(&nchanger->chluns); 91839213Sgibbs 91939213Sgibbs STAILQ_INSERT_TAIL(&changerq, nchanger, 92039213Sgibbs changer_links); 92139213Sgibbs 92239213Sgibbs /* 92339213Sgibbs * Create a path with lun id 0, and see if we can 92439213Sgibbs * find a matching device 92539213Sgibbs */ 92639213Sgibbs status = xpt_create_path(&path, /*periph*/ periph, 92739213Sgibbs cgd->ccb_h.path_id, 92839213Sgibbs cgd->ccb_h.target_id, 0); 92939213Sgibbs 93039213Sgibbs /* 93139213Sgibbs * If we were able to allocate the path, and if we 93239213Sgibbs * find a matching device and it isn't already 93339213Sgibbs * marked as part of a changer, then we add it to 93439213Sgibbs * the current changer. 93539213Sgibbs */ 93639213Sgibbs if ((status == CAM_REQ_CMP) 93739213Sgibbs && ((nperiph = cam_periph_find(path, "cd")) != NULL) 93839213Sgibbs && ((((struct cd_softc *)periph->softc)->flags & 93939213Sgibbs CD_FLAG_CHANGER) == 0)) { 94039213Sgibbs struct cd_softc *nsoftc; 94139213Sgibbs 94239213Sgibbs nsoftc = (struct cd_softc *)nperiph->softc; 94339213Sgibbs 94439213Sgibbs nsoftc->flags |= CD_FLAG_CHANGER; 94539213Sgibbs nchanger->num_devices++; 94639213Sgibbs if (camq_resize(&nchanger->devq, 94739213Sgibbs nchanger->num_devices) != CAM_REQ_CMP) { 94839213Sgibbs printf("cdregister: camq_resize " 94939213Sgibbs "failed, changer support may " 95039213Sgibbs "be messed up\n"); 95139213Sgibbs } 95239213Sgibbs nsoftc->changer = nchanger; 95339213Sgibbs nsoftc->pinfo.index = CAM_UNQUEUED_INDEX; 95439213Sgibbs 95539213Sgibbs STAILQ_INSERT_TAIL(&nchanger->chluns, 95639213Sgibbs nsoftc, changer_links); 95767928Sken xpt_free_path(path); 95839213Sgibbs } else if (status == CAM_REQ_CMP) 95939213Sgibbs xpt_free_path(path); 96039213Sgibbs else { 96139213Sgibbs printf("cdregister: unable to allocate path\n" 96239213Sgibbs "cdregister: changer support may be " 96339213Sgibbs "broken\n"); 96439213Sgibbs } 96539213Sgibbs 96639213Sgibbs softc->changer = nchanger; 96739213Sgibbs softc->pinfo.index = CAM_UNQUEUED_INDEX; 96839213Sgibbs nchanger->num_devices++; 96939213Sgibbs if (camq_resize(&nchanger->devq, 97039213Sgibbs nchanger->num_devices) != CAM_REQ_CMP) { 97139213Sgibbs printf("cdregister: camq_resize " 97239213Sgibbs "failed, changer support may " 97339213Sgibbs "be messed up\n"); 97439213Sgibbs } 97539213Sgibbs STAILQ_INSERT_TAIL(&nchanger->chluns, softc, 97639213Sgibbs changer_links); 97739213Sgibbs } 97839213Sgibbs } 97939213Sgibbs 98039213Sgibbscdregisterexit: 98139213Sgibbs 98239213Sgibbs /* Lock this peripheral until we are setup */ 98339213Sgibbs /* Can't block */ 98439213Sgibbs cam_periph_lock(periph, PRIBIO); 98539213Sgibbs 98639213Sgibbs if ((softc->flags & CD_FLAG_CHANGER) == 0) 98739213Sgibbs xpt_schedule(periph, /*priority*/5); 98839213Sgibbs else 98939213Sgibbs cdschedule(periph, /*priority*/ 5); 99039213Sgibbs 99139213Sgibbs return(CAM_REQ_CMP); 99239213Sgibbs} 99339213Sgibbs 99439213Sgibbsstatic int 99583366Sjuliancdopen(dev_t dev, int flags, int fmt, struct thread *td) 99639213Sgibbs{ 99739213Sgibbs struct cam_periph *periph; 99839213Sgibbs struct cd_softc *softc; 999101940Snjl int error; 100040603Sken int s; 100139213Sgibbs 1002101940Snjl periph = (struct cam_periph *)dev->si_drv1; 100339213Sgibbs if (periph == NULL) 100439213Sgibbs return (ENXIO); 100539213Sgibbs 100639213Sgibbs softc = (struct cd_softc *)periph->softc; 100739213Sgibbs 100841297Sken /* 100941297Sken * Grab splsoftcam and hold it until we lock the peripheral. 101041297Sken */ 101140603Sken s = splsoftcam(); 101240603Sken if (softc->flags & CD_FLAG_INVALID) { 101340603Sken splx(s); 101439213Sgibbs return(ENXIO); 101540603Sken } 101639213Sgibbs 101741297Sken if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { 101841297Sken splx(s); 101939213Sgibbs return (error); 102041297Sken } 102139213Sgibbs 102241297Sken splx(s); 102341297Sken 102451836Sphk if (cam_periph_acquire(periph) != CAM_REQ_CMP) 102551836Sphk return(ENXIO); 102640020Sken 102740020Sken /* 1028111206Sken * Check for media, and set the appropriate flags. We don't bail 1029111206Sken * if we don't have media, but then we don't allow anything but the 1030111206Sken * CDIOCEJECT/CDIOCCLOSE ioctls if there is no media. 103151836Sphk */ 1032111206Sken cdcheckmedia(periph); 103340020Sken 103439213Sgibbs cam_periph_unlock(periph); 103539213Sgibbs 103639213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n")); 103739213Sgibbs 103839213Sgibbs return (error); 103939213Sgibbs} 104039213Sgibbs 104139213Sgibbsstatic int 104283366Sjuliancdclose(dev_t dev, int flag, int fmt, struct thread *td) 104339213Sgibbs{ 104439213Sgibbs struct cam_periph *periph; 104539213Sgibbs struct cd_softc *softc; 1046101940Snjl int error; 104739213Sgibbs 1048101940Snjl periph = (struct cam_periph *)dev->si_drv1; 104939213Sgibbs if (periph == NULL) 105039213Sgibbs return (ENXIO); 105139213Sgibbs 105239213Sgibbs softc = (struct cd_softc *)periph->softc; 105339213Sgibbs 105439213Sgibbs if ((error = cam_periph_lock(periph, PRIBIO)) != 0) 105539213Sgibbs return (error); 105639213Sgibbs 105739213Sgibbs if ((softc->flags & CD_FLAG_DISC_REMOVABLE) != 0) 105839213Sgibbs cdprevent(periph, PR_ALLOW); 105939213Sgibbs 106039213Sgibbs /* 106139213Sgibbs * Since we're closing this CD, mark the blocksize as unavailable. 1062111206Sken * It will be marked as available when the CD is opened again. 106339213Sgibbs */ 1064112006Sphk softc->device_stats->flags |= DEVSTAT_BS_UNAVAILABLE; 106539213Sgibbs 1066111206Sken /* 1067111206Sken * We'll check the media and toc again at the next open(). 1068111206Sken */ 1069111206Sken softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); 1070111206Sken 107139213Sgibbs cam_periph_unlock(periph); 107239213Sgibbs cam_periph_release(periph); 107339213Sgibbs 107439213Sgibbs return (0); 107539213Sgibbs} 107639213Sgibbs 107739213Sgibbsstatic void 107839213Sgibbscdshorttimeout(void *arg) 107939213Sgibbs{ 108039213Sgibbs struct cdchanger *changer; 108139213Sgibbs int s; 108239213Sgibbs 108339213Sgibbs s = splsoftcam(); 108439213Sgibbs 108539213Sgibbs changer = (struct cdchanger *)arg; 108639213Sgibbs 108739213Sgibbs /* Always clear the short timeout flag, since that's what we're in */ 108839213Sgibbs changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; 108939213Sgibbs 109039213Sgibbs /* 109139213Sgibbs * Check to see if there is any more pending or outstanding I/O for 109239213Sgibbs * this device. If not, move it out of the active slot. 109339213Sgibbs */ 109459249Sphk if ((bioq_first(&changer->cur_device->bio_queue) == NULL) 1095112262Sphk && (changer->cur_device->outstanding_cmds == 0)) { 109639213Sgibbs changer->flags |= CHANGER_MANUAL_CALL; 109739213Sgibbs cdrunchangerqueue(changer); 109839213Sgibbs } 109939213Sgibbs 110039213Sgibbs splx(s); 110139213Sgibbs} 110239213Sgibbs 110339213Sgibbs/* 110439213Sgibbs * This is a wrapper for xpt_schedule. It only applies to changers. 110539213Sgibbs */ 110639213Sgibbsstatic void 110739213Sgibbscdschedule(struct cam_periph *periph, int priority) 110839213Sgibbs{ 110939213Sgibbs struct cd_softc *softc; 111039213Sgibbs int s; 111139213Sgibbs 111239213Sgibbs s = splsoftcam(); 111339213Sgibbs 111439213Sgibbs softc = (struct cd_softc *)periph->softc; 111539213Sgibbs 111639213Sgibbs /* 111739213Sgibbs * If this device isn't currently queued, and if it isn't 111839213Sgibbs * the active device, then we queue this device and run the 111939213Sgibbs * changer queue if there is no timeout scheduled to do it. 112039213Sgibbs * If this device is the active device, just schedule it 112139213Sgibbs * to run again. If this device is queued, there should be 112239213Sgibbs * a timeout in place already that will make sure it runs. 112339213Sgibbs */ 112439213Sgibbs if ((softc->pinfo.index == CAM_UNQUEUED_INDEX) 112539213Sgibbs && ((softc->flags & CD_FLAG_ACTIVE) == 0)) { 112639213Sgibbs /* 112739213Sgibbs * We don't do anything with the priority here. 112839213Sgibbs * This is strictly a fifo queue. 112939213Sgibbs */ 113039213Sgibbs softc->pinfo.priority = 1; 113145442Sgibbs softc->pinfo.generation = ++softc->changer->devq.generation; 113239213Sgibbs camq_insert(&softc->changer->devq, (cam_pinfo *)softc); 113339213Sgibbs 113439213Sgibbs /* 113539213Sgibbs * Since we just put a device in the changer queue, 113639213Sgibbs * check and see if there is a timeout scheduled for 113739213Sgibbs * this changer. If so, let the timeout handle 113839213Sgibbs * switching this device into the active slot. If 113939213Sgibbs * not, manually call the timeout routine to 114039213Sgibbs * bootstrap things. 114139213Sgibbs */ 114239213Sgibbs if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0) 114346581Sken && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0) 114446581Sken && ((softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED)==0)){ 114539213Sgibbs softc->changer->flags |= CHANGER_MANUAL_CALL; 114639213Sgibbs cdrunchangerqueue(softc->changer); 114739213Sgibbs } 114839213Sgibbs } else if ((softc->flags & CD_FLAG_ACTIVE) 114939213Sgibbs && ((softc->flags & CD_FLAG_SCHED_ON_COMP) == 0)) 115039213Sgibbs xpt_schedule(periph, priority); 115139213Sgibbs 115239213Sgibbs splx(s); 115339213Sgibbs 115439213Sgibbs} 115539213Sgibbs 115639213Sgibbsstatic void 115739213Sgibbscdrunchangerqueue(void *arg) 115839213Sgibbs{ 115939213Sgibbs struct cd_softc *softc; 116039213Sgibbs struct cdchanger *changer; 116139213Sgibbs int called_from_timeout; 116239213Sgibbs int s; 116339213Sgibbs 116439213Sgibbs s = splsoftcam(); 116539213Sgibbs 116639213Sgibbs changer = (struct cdchanger *)arg; 116739213Sgibbs 116839213Sgibbs /* 116939213Sgibbs * If we have NOT been called from cdstrategy() or cddone(), and 117039213Sgibbs * instead from a timeout routine, go ahead and clear the 117139213Sgibbs * timeout flag. 117239213Sgibbs */ 117339213Sgibbs if ((changer->flags & CHANGER_MANUAL_CALL) == 0) { 117439213Sgibbs changer->flags &= ~CHANGER_TIMEOUT_SCHED; 117539213Sgibbs called_from_timeout = 1; 117639213Sgibbs } else 117739213Sgibbs called_from_timeout = 0; 117839213Sgibbs 117939213Sgibbs /* Always clear the manual call flag */ 118039213Sgibbs changer->flags &= ~CHANGER_MANUAL_CALL; 118139213Sgibbs 118239213Sgibbs /* nothing to do if the queue is empty */ 118339213Sgibbs if (changer->devq.entries <= 0) { 118439213Sgibbs splx(s); 118539213Sgibbs return; 118639213Sgibbs } 118739213Sgibbs 118839213Sgibbs /* 118939213Sgibbs * If the changer queue is frozen, that means we have an active 119039213Sgibbs * device. 119139213Sgibbs */ 119239213Sgibbs if (changer->devq.qfrozen_cnt > 0) { 119339213Sgibbs 1194112262Sphk if (changer->cur_device->outstanding_cmds > 0) { 119539213Sgibbs changer->cur_device->flags |= CD_FLAG_SCHED_ON_COMP; 119639213Sgibbs changer->cur_device->bufs_left = 1197112262Sphk changer->cur_device->outstanding_cmds; 119839213Sgibbs if (called_from_timeout) { 119939213Sgibbs changer->long_handle = 120039213Sgibbs timeout(cdrunchangerqueue, changer, 120139213Sgibbs changer_max_busy_seconds * hz); 120239213Sgibbs changer->flags |= CHANGER_TIMEOUT_SCHED; 120339213Sgibbs } 120439213Sgibbs splx(s); 120539213Sgibbs return; 120639213Sgibbs } 120739213Sgibbs 120839213Sgibbs /* 120939213Sgibbs * We always need to reset the frozen count and clear the 121039213Sgibbs * active flag. 121139213Sgibbs */ 121239213Sgibbs changer->devq.qfrozen_cnt--; 121339213Sgibbs changer->cur_device->flags &= ~CD_FLAG_ACTIVE; 121439213Sgibbs changer->cur_device->flags &= ~CD_FLAG_SCHED_ON_COMP; 121539213Sgibbs 121639213Sgibbs /* 121739213Sgibbs * Check to see whether the current device has any I/O left 121839213Sgibbs * to do. If so, requeue it at the end of the queue. If 121939213Sgibbs * not, there is no need to requeue it. 122039213Sgibbs */ 122159249Sphk if (bioq_first(&changer->cur_device->bio_queue) != NULL) { 122239213Sgibbs 122339213Sgibbs changer->cur_device->pinfo.generation = 122445442Sgibbs ++changer->devq.generation; 122539213Sgibbs camq_insert(&changer->devq, 122639213Sgibbs (cam_pinfo *)changer->cur_device); 122739213Sgibbs } 122839213Sgibbs } 122939213Sgibbs 123045845Sgibbs softc = (struct cd_softc *)camq_remove(&changer->devq, CAMQ_HEAD); 123139213Sgibbs 123239213Sgibbs changer->cur_device = softc; 123339213Sgibbs 123439213Sgibbs changer->devq.qfrozen_cnt++; 123539213Sgibbs softc->flags |= CD_FLAG_ACTIVE; 123639213Sgibbs 123739213Sgibbs /* Just in case this device is waiting */ 123839213Sgibbs wakeup(&softc->changer); 123939213Sgibbs xpt_schedule(softc->periph, /*priority*/ 1); 124039213Sgibbs 124139213Sgibbs /* 124239213Sgibbs * Get rid of any pending timeouts, and set a flag to schedule new 124339213Sgibbs * ones so this device gets its full time quantum. 124439213Sgibbs */ 124539213Sgibbs if (changer->flags & CHANGER_TIMEOUT_SCHED) { 124639213Sgibbs untimeout(cdrunchangerqueue, changer, changer->long_handle); 124739213Sgibbs changer->flags &= ~CHANGER_TIMEOUT_SCHED; 124839213Sgibbs } 124939213Sgibbs 125039213Sgibbs if (changer->flags & CHANGER_SHORT_TMOUT_SCHED) { 125139213Sgibbs untimeout(cdshorttimeout, changer, changer->short_handle); 125239213Sgibbs changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; 125339213Sgibbs } 125439213Sgibbs 125539213Sgibbs /* 125639213Sgibbs * We need to schedule timeouts, but we only do this after the 125739213Sgibbs * first transaction has completed. This eliminates the changer 125839213Sgibbs * switch time. 125939213Sgibbs */ 126039213Sgibbs changer->flags |= CHANGER_NEED_TIMEOUT; 126139213Sgibbs 126239213Sgibbs splx(s); 126339213Sgibbs} 126439213Sgibbs 126539213Sgibbsstatic void 126639213Sgibbscdchangerschedule(struct cd_softc *softc) 126739213Sgibbs{ 126839213Sgibbs struct cdchanger *changer; 126939213Sgibbs int s; 127039213Sgibbs 127139213Sgibbs s = splsoftcam(); 127239213Sgibbs 127339213Sgibbs changer = softc->changer; 127439213Sgibbs 127539213Sgibbs /* 127639213Sgibbs * If this is a changer, and this is the current device, 127739213Sgibbs * and this device has at least the minimum time quantum to 127839213Sgibbs * run, see if we can switch it out. 127939213Sgibbs */ 128039213Sgibbs if ((softc->flags & CD_FLAG_ACTIVE) 128139213Sgibbs && ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) 128239213Sgibbs && ((changer->flags & CHANGER_NEED_TIMEOUT) == 0)) { 128339213Sgibbs /* 128439213Sgibbs * We try three things here. The first is that we 128539213Sgibbs * check to see whether the schedule on completion 128639213Sgibbs * flag is set. If it is, we decrement the number 128739213Sgibbs * of buffers left, and if it's zero, we reschedule. 128839213Sgibbs * Next, we check to see whether the pending buffer 128939213Sgibbs * queue is empty and whether there are no 129039213Sgibbs * outstanding transactions. If so, we reschedule. 129139213Sgibbs * Next, we see if the pending buffer queue is empty. 129239213Sgibbs * If it is, we set the number of buffers left to 129339213Sgibbs * the current active buffer count and set the 129439213Sgibbs * schedule on complete flag. 129539213Sgibbs */ 129639213Sgibbs if (softc->flags & CD_FLAG_SCHED_ON_COMP) { 129739213Sgibbs if (--softc->bufs_left == 0) { 129839213Sgibbs softc->changer->flags |= 129939213Sgibbs CHANGER_MANUAL_CALL; 130039213Sgibbs softc->flags &= ~CD_FLAG_SCHED_ON_COMP; 130139213Sgibbs cdrunchangerqueue(softc->changer); 130239213Sgibbs } 130359249Sphk } else if ((bioq_first(&softc->bio_queue) == NULL) 1304112262Sphk && (softc->outstanding_cmds == 0)) { 130539213Sgibbs softc->changer->flags |= CHANGER_MANUAL_CALL; 130639213Sgibbs cdrunchangerqueue(softc->changer); 130739213Sgibbs } 130839213Sgibbs } else if ((softc->changer->flags & CHANGER_NEED_TIMEOUT) 130939213Sgibbs && (softc->flags & CD_FLAG_ACTIVE)) { 131039213Sgibbs 131139213Sgibbs /* 131239213Sgibbs * Now that the first transaction to this 131339213Sgibbs * particular device has completed, we can go ahead 131439213Sgibbs * and schedule our timeouts. 131539213Sgibbs */ 131639213Sgibbs if ((changer->flags & CHANGER_TIMEOUT_SCHED) == 0) { 131739213Sgibbs changer->long_handle = 131839213Sgibbs timeout(cdrunchangerqueue, changer, 131939213Sgibbs changer_max_busy_seconds * hz); 132039213Sgibbs changer->flags |= CHANGER_TIMEOUT_SCHED; 132139213Sgibbs } else 132239213Sgibbs printf("cdchangerschedule: already have a long" 132339213Sgibbs " timeout!\n"); 132439213Sgibbs 132539213Sgibbs if ((changer->flags & CHANGER_SHORT_TMOUT_SCHED) == 0) { 132639213Sgibbs changer->short_handle = 132739213Sgibbs timeout(cdshorttimeout, changer, 132839213Sgibbs changer_min_busy_seconds * hz); 132939213Sgibbs changer->flags |= CHANGER_SHORT_TMOUT_SCHED; 133039213Sgibbs } else 133139213Sgibbs printf("cdchangerschedule: already have a short " 133239213Sgibbs "timeout!\n"); 133339213Sgibbs 133439213Sgibbs /* 133539213Sgibbs * We just scheduled timeouts, no need to schedule 133639213Sgibbs * more. 133739213Sgibbs */ 133839213Sgibbs changer->flags &= ~CHANGER_NEED_TIMEOUT; 133939213Sgibbs 134039213Sgibbs } 134139213Sgibbs splx(s); 134239213Sgibbs} 134339213Sgibbs 134439213Sgibbsstatic int 134539213Sgibbscdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, 134639213Sgibbs u_int32_t cam_flags, 134739213Sgibbs u_int32_t sense_flags), 134839213Sgibbs u_int32_t cam_flags, u_int32_t sense_flags) 134939213Sgibbs{ 135039213Sgibbs struct cd_softc *softc; 135139213Sgibbs struct cam_periph *periph; 135239213Sgibbs int error; 135339213Sgibbs 135439213Sgibbs periph = xpt_path_periph(ccb->ccb_h.path); 135539213Sgibbs softc = (struct cd_softc *)periph->softc; 135639213Sgibbs 135739213Sgibbs error = cam_periph_runccb(ccb, error_routine, cam_flags, sense_flags, 1358112006Sphk softc->device_stats); 135939213Sgibbs 136039213Sgibbs if (softc->flags & CD_FLAG_CHANGER) 136139213Sgibbs cdchangerschedule(softc); 136239213Sgibbs 136339213Sgibbs return(error); 136439213Sgibbs} 136539213Sgibbs 136642017Seivindstatic union ccb * 136739213Sgibbscdgetccb(struct cam_periph *periph, u_int32_t priority) 136839213Sgibbs{ 136939213Sgibbs struct cd_softc *softc; 137039213Sgibbs int s; 137139213Sgibbs 137239213Sgibbs softc = (struct cd_softc *)periph->softc; 137339213Sgibbs 137439213Sgibbs if (softc->flags & CD_FLAG_CHANGER) { 137539213Sgibbs 137639213Sgibbs s = splsoftcam(); 137739213Sgibbs 137839213Sgibbs /* 137939213Sgibbs * This should work the first time this device is woken up, 138039213Sgibbs * but just in case it doesn't, we use a while loop. 138139213Sgibbs */ 138246581Sken while ((softc->flags & CD_FLAG_ACTIVE) == 0) { 138339213Sgibbs /* 138439213Sgibbs * If this changer isn't already queued, queue it up. 138539213Sgibbs */ 138639213Sgibbs if (softc->pinfo.index == CAM_UNQUEUED_INDEX) { 138739213Sgibbs softc->pinfo.priority = 1; 138839213Sgibbs softc->pinfo.generation = 138945442Sgibbs ++softc->changer->devq.generation; 139039213Sgibbs camq_insert(&softc->changer->devq, 139139213Sgibbs (cam_pinfo *)softc); 139239213Sgibbs } 139346581Sken if (((softc->changer->flags & CHANGER_TIMEOUT_SCHED)==0) 139446581Sken && ((softc->changer->flags & CHANGER_NEED_TIMEOUT)==0) 139546581Sken && ((softc->changer->flags 139646581Sken & CHANGER_SHORT_TMOUT_SCHED)==0)) { 139739213Sgibbs softc->changer->flags |= CHANGER_MANUAL_CALL; 139839213Sgibbs cdrunchangerqueue(softc->changer); 139939213Sgibbs } else 140039213Sgibbs tsleep(&softc->changer, PRIBIO, "cgticb", 0); 140139213Sgibbs } 140239213Sgibbs splx(s); 140339213Sgibbs } 140439213Sgibbs return(cam_periph_getccb(periph, priority)); 140539213Sgibbs} 140639213Sgibbs 140739213Sgibbs 140839213Sgibbs/* 140939213Sgibbs * Actually translate the requested transfer into one the physical driver 141039213Sgibbs * can understand. The transfer is described by a buf and will include 141139213Sgibbs * only one physical transfer. 141239213Sgibbs */ 141339213Sgibbsstatic void 141459249Sphkcdstrategy(struct bio *bp) 141539213Sgibbs{ 141639213Sgibbs struct cam_periph *periph; 141739213Sgibbs struct cd_softc *softc; 141839213Sgibbs int s; 141939213Sgibbs 1420101940Snjl periph = (struct cam_periph *)bp->bio_dev->si_drv1; 142139213Sgibbs if (periph == NULL) { 142276362Sphk biofinish(bp, NULL, ENXIO); 142376362Sphk return; 142439213Sgibbs } 142539213Sgibbs 142639213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstrategy\n")); 142739213Sgibbs 142839213Sgibbs softc = (struct cd_softc *)periph->softc; 142939213Sgibbs 143039213Sgibbs /* 143139213Sgibbs * Mask interrupts so that the pack cannot be invalidated until 143239213Sgibbs * after we are in the queue. Otherwise, we might not properly 143339213Sgibbs * clean up one of the buffers. 143439213Sgibbs */ 143539213Sgibbs s = splbio(); 143639213Sgibbs 143739213Sgibbs /* 143839213Sgibbs * If the device has been made invalid, error out 143939213Sgibbs */ 144039213Sgibbs if ((softc->flags & CD_FLAG_INVALID)) { 144139213Sgibbs splx(s); 144276362Sphk biofinish(bp, NULL, ENXIO); 144376362Sphk return; 144439213Sgibbs } 144539213Sgibbs 1446111206Sken /* 1447111206Sken * If we don't have valid media, look for it before trying to 1448111206Sken * schedule the I/O. 1449111206Sken */ 1450111206Sken if ((softc->flags & CD_FLAG_VALID_MEDIA) == 0) { 1451111206Sken int error; 1452111206Sken 1453111206Sken error = cdcheckmedia(periph); 1454111206Sken if (error != 0) { 1455111206Sken splx(s); 1456111206Sken biofinish(bp, NULL, error); 1457111206Sken return; 1458111206Sken } 1459111206Sken } 1460111206Sken 146139213Sgibbs /* 146239213Sgibbs * Place it in the queue of disk activities for this disk 146339213Sgibbs */ 1464112946Sphk bioq_disksort(&softc->bio_queue, bp); 146539213Sgibbs 146639213Sgibbs splx(s); 146739213Sgibbs 146839213Sgibbs /* 146939213Sgibbs * Schedule ourselves for performing the work. We do things 147039213Sgibbs * differently for changers. 147139213Sgibbs */ 147239213Sgibbs if ((softc->flags & CD_FLAG_CHANGER) == 0) 147339213Sgibbs xpt_schedule(periph, /* XXX priority */1); 147439213Sgibbs else 147539213Sgibbs cdschedule(periph, /* priority */ 1); 147639213Sgibbs 147739213Sgibbs return; 147839213Sgibbs} 147939213Sgibbs 148039213Sgibbsstatic void 148139213Sgibbscdstart(struct cam_periph *periph, union ccb *start_ccb) 148239213Sgibbs{ 148339213Sgibbs struct cd_softc *softc; 148459249Sphk struct bio *bp; 148539213Sgibbs struct ccb_scsiio *csio; 148639213Sgibbs struct scsi_read_capacity_data *rcap; 148739213Sgibbs int s; 148839213Sgibbs 148939213Sgibbs softc = (struct cd_softc *)periph->softc; 149039213Sgibbs 149139213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdstart\n")); 149239213Sgibbs 149339213Sgibbs switch (softc->state) { 149439213Sgibbs case CD_STATE_NORMAL: 149539213Sgibbs { 149639213Sgibbs int oldspl; 149739213Sgibbs 149839213Sgibbs s = splbio(); 149959249Sphk bp = bioq_first(&softc->bio_queue); 150039213Sgibbs if (periph->immediate_priority <= periph->pinfo.priority) { 150139213Sgibbs start_ccb->ccb_h.ccb_state = CD_CCB_WAITING; 150239213Sgibbs 150339213Sgibbs SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, 150439213Sgibbs periph_links.sle); 150539213Sgibbs periph->immediate_priority = CAM_PRIORITY_NONE; 150639213Sgibbs splx(s); 150739213Sgibbs wakeup(&periph->ccb_list); 150839213Sgibbs } else if (bp == NULL) { 150939213Sgibbs splx(s); 151039213Sgibbs xpt_release_ccb(start_ccb); 151139213Sgibbs } else { 151259249Sphk bioq_remove(&softc->bio_queue, bp); 151339213Sgibbs 1514112260Sphk devstat_start_transaction_bio(softc->device_stats, bp); 151539213Sgibbs 151639213Sgibbs scsi_read_write(&start_ccb->csio, 151739213Sgibbs /*retries*/4, 151839213Sgibbs /* cbfcnp */ cddone, 151991062Sphk MSG_SIMPLE_Q_TAG, 152059249Sphk /* read */bp->bio_cmd == BIO_READ, 152139213Sgibbs /* byte2 */ 0, 152239213Sgibbs /* minimum_cmd_size */ 10, 1523104611Sphk /* lba */ bp->bio_blkno / 1524104611Sphk (softc->params.blksize / DEV_BSIZE), 152559249Sphk bp->bio_bcount / softc->params.blksize, 152659249Sphk /* data_ptr */ bp->bio_data, 152759249Sphk /* dxfer_len */ bp->bio_bcount, 152839213Sgibbs /* sense_len */ SSD_FULL_SIZE, 152939213Sgibbs /* timeout */ 30000); 153039213Sgibbs start_ccb->ccb_h.ccb_state = CD_CCB_BUFFER_IO; 153139213Sgibbs 153239213Sgibbs 153339213Sgibbs /* 153439213Sgibbs * Block out any asyncronous callbacks 153539213Sgibbs * while we touch the pending ccb list. 153639213Sgibbs */ 153739213Sgibbs oldspl = splcam(); 153839213Sgibbs LIST_INSERT_HEAD(&softc->pending_ccbs, 153939213Sgibbs &start_ccb->ccb_h, periph_links.le); 1540112262Sphk softc->outstanding_cmds++; 154139213Sgibbs splx(oldspl); 154239213Sgibbs 154339213Sgibbs /* We expect a unit attention from this device */ 154439213Sgibbs if ((softc->flags & CD_FLAG_RETRY_UA) != 0) { 154539213Sgibbs start_ccb->ccb_h.ccb_state |= CD_CCB_RETRY_UA; 154639213Sgibbs softc->flags &= ~CD_FLAG_RETRY_UA; 154739213Sgibbs } 154839213Sgibbs 154939213Sgibbs start_ccb->ccb_h.ccb_bp = bp; 155059249Sphk bp = bioq_first(&softc->bio_queue); 155139213Sgibbs splx(s); 155239213Sgibbs 155339213Sgibbs xpt_action(start_ccb); 155439213Sgibbs } 155539213Sgibbs if (bp != NULL) { 155639213Sgibbs /* Have more work to do, so ensure we stay scheduled */ 155739213Sgibbs xpt_schedule(periph, /* XXX priority */1); 155839213Sgibbs } 155939213Sgibbs break; 156039213Sgibbs } 156139213Sgibbs case CD_STATE_PROBE: 156239213Sgibbs { 156339213Sgibbs 156439213Sgibbs rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap), 156539213Sgibbs M_TEMP, 156639213Sgibbs M_NOWAIT); 156739213Sgibbs if (rcap == NULL) { 156839213Sgibbs xpt_print_path(periph->path); 156939213Sgibbs printf("cdstart: Couldn't malloc read_capacity data\n"); 157039213Sgibbs /* cd_free_periph??? */ 157139213Sgibbs break; 157239213Sgibbs } 157339213Sgibbs csio = &start_ccb->csio; 157439213Sgibbs scsi_read_capacity(csio, 157539213Sgibbs /*retries*/1, 157639213Sgibbs cddone, 157739213Sgibbs MSG_SIMPLE_Q_TAG, 157839213Sgibbs rcap, 157939213Sgibbs SSD_FULL_SIZE, 158039213Sgibbs /*timeout*/20000); 158139213Sgibbs start_ccb->ccb_h.ccb_bp = NULL; 158239213Sgibbs start_ccb->ccb_h.ccb_state = CD_CCB_PROBE; 158339213Sgibbs xpt_action(start_ccb); 158439213Sgibbs break; 158539213Sgibbs } 158639213Sgibbs } 158739213Sgibbs} 158839213Sgibbs 158939213Sgibbsstatic void 159039213Sgibbscddone(struct cam_periph *periph, union ccb *done_ccb) 159139213Sgibbs{ 159239213Sgibbs struct cd_softc *softc; 159339213Sgibbs struct ccb_scsiio *csio; 159439213Sgibbs 159539213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cddone\n")); 159639213Sgibbs 159739213Sgibbs softc = (struct cd_softc *)periph->softc; 159839213Sgibbs csio = &done_ccb->csio; 159939213Sgibbs 160039213Sgibbs switch (csio->ccb_h.ccb_state & CD_CCB_TYPE_MASK) { 160139213Sgibbs case CD_CCB_BUFFER_IO: 160239213Sgibbs { 160359249Sphk struct bio *bp; 160439213Sgibbs int error; 160539213Sgibbs int oldspl; 160639213Sgibbs 160759249Sphk bp = (struct bio *)done_ccb->ccb_h.ccb_bp; 160839213Sgibbs error = 0; 160939213Sgibbs 161039213Sgibbs if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 161139213Sgibbs int sf; 161239213Sgibbs 161339213Sgibbs if ((done_ccb->ccb_h.ccb_state & CD_CCB_RETRY_UA) != 0) 161439213Sgibbs sf = SF_RETRY_UA; 161539213Sgibbs else 161639213Sgibbs sf = 0; 161746747Sken 161874840Sken error = cderror(done_ccb, CAM_RETRY_SELTO, sf); 161974840Sken if (error == ERESTART) { 162039213Sgibbs /* 162139213Sgibbs * A retry was scheuled, so 162239213Sgibbs * just return. 162339213Sgibbs */ 162439213Sgibbs return; 162539213Sgibbs } 162639213Sgibbs } 162739213Sgibbs 162839213Sgibbs if (error != 0) { 162939213Sgibbs int s; 163039213Sgibbs 163139213Sgibbs xpt_print_path(periph->path); 163239213Sgibbs printf("cddone: got error %#x back\n", error); 163339213Sgibbs s = splbio(); 1634112946Sphk bioq_flush(&softc->bio_queue, NULL, EIO); 163539213Sgibbs splx(s); 163659249Sphk bp->bio_resid = bp->bio_bcount; 163759249Sphk bp->bio_error = error; 163859249Sphk bp->bio_flags |= BIO_ERROR; 163939213Sgibbs cam_release_devq(done_ccb->ccb_h.path, 164039213Sgibbs /*relsim_flags*/0, 164139213Sgibbs /*reduction*/0, 164239213Sgibbs /*timeout*/0, 164339213Sgibbs /*getcount_only*/0); 164439213Sgibbs 164539213Sgibbs } else { 164659249Sphk bp->bio_resid = csio->resid; 164759249Sphk bp->bio_error = 0; 164859249Sphk if (bp->bio_resid != 0) { 1649104456Sphk /* 1650104456Sphk * Short transfer ??? 1651104456Sphk * XXX: not sure this is correct for partial 1652104456Sphk * transfers at EOM 1653104456Sphk */ 165459249Sphk bp->bio_flags |= BIO_ERROR; 165539213Sgibbs } 165639213Sgibbs } 165739213Sgibbs 165839213Sgibbs /* 165939213Sgibbs * Block out any asyncronous callbacks 166039213Sgibbs * while we touch the pending ccb list. 166139213Sgibbs */ 166239213Sgibbs oldspl = splcam(); 166339213Sgibbs LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); 1664112262Sphk softc->outstanding_cmds--; 166539213Sgibbs splx(oldspl); 166639213Sgibbs 166739213Sgibbs if (softc->flags & CD_FLAG_CHANGER) 166839213Sgibbs cdchangerschedule(softc); 166939213Sgibbs 1670112006Sphk biofinish(bp, softc->device_stats, 0); 167139213Sgibbs break; 167239213Sgibbs } 167339213Sgibbs case CD_CCB_PROBE: 167439213Sgibbs { 167539213Sgibbs struct scsi_read_capacity_data *rdcap; 167639213Sgibbs char announce_buf[120]; /* 167739213Sgibbs * Currently (9/30/97) the 167839213Sgibbs * longest possible announce 167939213Sgibbs * buffer is 108 bytes, for the 168039213Sgibbs * first error case below. 168139213Sgibbs * That is 39 bytes for the 168239213Sgibbs * basic string, 16 bytes for the 168339213Sgibbs * biggest sense key (hardware 168439213Sgibbs * error), 52 bytes for the 168539213Sgibbs * text of the largest sense 168639213Sgibbs * qualifier valid for a CDROM, 168739213Sgibbs * (0x72, 0x03 or 0x04, 168839213Sgibbs * 0x03), and one byte for the 168939213Sgibbs * null terminating character. 169039213Sgibbs * To allow for longer strings, 169139213Sgibbs * the announce buffer is 120 169239213Sgibbs * bytes. 169339213Sgibbs */ 169439213Sgibbs struct cd_params *cdp; 169539213Sgibbs 169639213Sgibbs cdp = &softc->params; 169739213Sgibbs 169839213Sgibbs rdcap = (struct scsi_read_capacity_data *)csio->data_ptr; 169939213Sgibbs 170039213Sgibbs cdp->disksize = scsi_4btoul (rdcap->addr) + 1; 170139213Sgibbs cdp->blksize = scsi_4btoul (rdcap->length); 170239213Sgibbs 170339213Sgibbs if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { 170439213Sgibbs 170541514Sarchie snprintf(announce_buf, sizeof(announce_buf), 170640020Sken "cd present [%lu x %lu byte records]", 170740020Sken cdp->disksize, (u_long)cdp->blksize); 170839213Sgibbs 170939213Sgibbs } else { 171039213Sgibbs int error; 171139213Sgibbs /* 171239213Sgibbs * Retry any UNIT ATTENTION type errors. They 171339213Sgibbs * are expected at boot. 171439213Sgibbs */ 171574840Sken error = cderror(done_ccb, CAM_RETRY_SELTO, 171674840Sken SF_RETRY_UA | SF_NO_PRINT); 171739213Sgibbs if (error == ERESTART) { 171839213Sgibbs /* 171939213Sgibbs * A retry was scheuled, so 172039213Sgibbs * just return. 172139213Sgibbs */ 172239213Sgibbs return; 172339213Sgibbs } else if (error != 0) { 172439213Sgibbs 172539213Sgibbs struct scsi_sense_data *sense; 172639213Sgibbs int asc, ascq; 172739213Sgibbs int sense_key, error_code; 172839213Sgibbs int have_sense; 172939213Sgibbs cam_status status; 173039213Sgibbs struct ccb_getdev cgd; 173139213Sgibbs 173239213Sgibbs /* Don't wedge this device's queue */ 173339213Sgibbs cam_release_devq(done_ccb->ccb_h.path, 173439213Sgibbs /*relsim_flags*/0, 173539213Sgibbs /*reduction*/0, 173639213Sgibbs /*timeout*/0, 173739213Sgibbs /*getcount_only*/0); 173839213Sgibbs 173939213Sgibbs status = done_ccb->ccb_h.status; 174039213Sgibbs 174139213Sgibbs xpt_setup_ccb(&cgd.ccb_h, 174239213Sgibbs done_ccb->ccb_h.path, 174339213Sgibbs /* priority */ 1); 174439213Sgibbs cgd.ccb_h.func_code = XPT_GDEV_TYPE; 174539213Sgibbs xpt_action((union ccb *)&cgd); 174639213Sgibbs 174739213Sgibbs if (((csio->ccb_h.flags & CAM_SENSE_PHYS) != 0) 174839213Sgibbs || ((csio->ccb_h.flags & CAM_SENSE_PTR) != 0) 174939213Sgibbs || ((status & CAM_AUTOSNS_VALID) == 0)) 175039213Sgibbs have_sense = FALSE; 175139213Sgibbs else 175239213Sgibbs have_sense = TRUE; 175339213Sgibbs 175439213Sgibbs if (have_sense) { 175539213Sgibbs sense = &csio->sense_data; 175639213Sgibbs scsi_extract_sense(sense, &error_code, 175739213Sgibbs &sense_key, 175839213Sgibbs &asc, &ascq); 175939213Sgibbs } 176039213Sgibbs /* 176146581Sken * Attach to anything that claims to be a 176246581Sken * CDROM or WORM device, as long as it 176346581Sken * doesn't return a "Logical unit not 176446581Sken * supported" (0x25) error. 176539213Sgibbs */ 176646581Sken if ((have_sense) && (asc != 0x25) 176774840Sken && (error_code == SSD_CURRENT_ERROR)) { 176874840Sken const char *sense_key_desc; 176974840Sken const char *asc_desc; 177074840Sken 177174840Sken scsi_sense_desc(sense_key, asc, ascq, 177274840Sken &cgd.inq_data, 177374840Sken &sense_key_desc, 177474840Sken &asc_desc); 177541514Sarchie snprintf(announce_buf, 177641514Sarchie sizeof(announce_buf), 177739213Sgibbs "Attempt to query device " 177839213Sgibbs "size failed: %s, %s", 177974840Sken sense_key_desc, 178074840Sken asc_desc); 178182850Sken } else if ((have_sense == 0) 178282850Sken && ((status & CAM_STATUS_MASK) == 178382850Sken CAM_SCSI_STATUS_ERROR) 178482850Sken && (csio->scsi_status == 178582850Sken SCSI_STATUS_BUSY)) { 178682850Sken snprintf(announce_buf, 178782850Sken sizeof(announce_buf), 178882850Sken "Attempt to query device " 178982850Sken "size failed: SCSI Status: %s", 179082850Sken scsi_status_string(csio)); 179174840Sken } else if (SID_TYPE(&cgd.inq_data) == T_CDROM) { 179239213Sgibbs /* 179339213Sgibbs * We only print out an error for 179439213Sgibbs * CDROM type devices. For WORM 179539213Sgibbs * devices, we don't print out an 179639213Sgibbs * error since a few WORM devices 179739213Sgibbs * don't support CDROM commands. 179839213Sgibbs * If we have sense information, go 179939213Sgibbs * ahead and print it out. 180039213Sgibbs * Otherwise, just say that we 180139213Sgibbs * couldn't attach. 180239213Sgibbs */ 180339213Sgibbs 180439213Sgibbs /* 180539213Sgibbs * Just print out the error, not 180639213Sgibbs * the full probe message, when we 180739213Sgibbs * don't attach. 180839213Sgibbs */ 180940020Sken if (have_sense) 181040020Sken scsi_sense_print( 181140020Sken &done_ccb->csio); 181240020Sken else { 181340020Sken xpt_print_path(periph->path); 181440020Sken printf("got CAM status %#x\n", 181540020Sken done_ccb->ccb_h.status); 181640020Sken } 181740020Sken xpt_print_path(periph->path); 181840020Sken printf("fatal error, failed" 181940603Sken " to attach to device\n"); 182039213Sgibbs 182139213Sgibbs /* 182240603Sken * Invalidate this peripheral. 182339213Sgibbs */ 182439213Sgibbs cam_periph_invalidate(periph); 182540020Sken 182640020Sken announce_buf[0] = '\0'; 182739213Sgibbs } else { 182840603Sken 182939213Sgibbs /* 183040603Sken * Invalidate this peripheral. 183139213Sgibbs */ 183239213Sgibbs cam_periph_invalidate(periph); 183340020Sken announce_buf[0] = '\0'; 183439213Sgibbs } 183539213Sgibbs } 183639213Sgibbs } 183739213Sgibbs free(rdcap, M_TEMP); 183842378Smjacob if (announce_buf[0] != '\0') { 183939213Sgibbs xpt_announce_periph(periph, announce_buf); 184042378Smjacob if (softc->flags & CD_FLAG_CHANGER) 184142378Smjacob cdchangerschedule(softc); 184242378Smjacob } 184340020Sken softc->state = CD_STATE_NORMAL; 184442378Smjacob /* 184542378Smjacob * Since our peripheral may be invalidated by an error 184642378Smjacob * above or an external event, we must release our CCB 184742378Smjacob * before releasing the probe lock on the peripheral. 184842378Smjacob * The peripheral will only go away once the last lock 184942378Smjacob * is removed, and we need it around for the CCB release 185042378Smjacob * operation. 185142378Smjacob */ 185242378Smjacob xpt_release_ccb(done_ccb); 185340020Sken cam_periph_unlock(periph); 185442378Smjacob return; 185539213Sgibbs } 185639213Sgibbs case CD_CCB_WAITING: 185739213Sgibbs { 185839213Sgibbs /* Caller will release the CCB */ 185939213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 186039213Sgibbs ("trying to wakeup ccbwait\n")); 186139213Sgibbs 186239213Sgibbs wakeup(&done_ccb->ccb_h.cbfcnp); 186339213Sgibbs return; 186439213Sgibbs } 186542378Smjacob default: 186642378Smjacob break; 186739213Sgibbs } 186839213Sgibbs xpt_release_ccb(done_ccb); 186939213Sgibbs} 187039213Sgibbs 1871111206Skenstatic union cd_pages * 1872111206Skencdgetpage(struct cd_mode_params *mode_params) 1873111206Sken{ 1874111206Sken union cd_pages *page; 1875111206Sken 1876111206Sken if (mode_params->cdb_size == 10) 1877111206Sken page = (union cd_pages *)find_mode_page_10( 1878111206Sken (struct scsi_mode_header_10 *)mode_params->mode_buf); 1879111206Sken else 1880111206Sken page = (union cd_pages *)find_mode_page_6( 1881111206Sken (struct scsi_mode_header_6 *)mode_params->mode_buf); 1882111206Sken 1883111206Sken return (page); 1884111206Sken} 1885111206Sken 188639213Sgibbsstatic int 1887111206Skencdgetpagesize(int page_num) 1888111206Sken{ 1889111206Sken int i; 1890111206Sken 1891111206Sken for (i = 0; i < (sizeof(cd_page_size_table)/ 1892111206Sken sizeof(cd_page_size_table[0])); i++) { 1893111206Sken if (cd_page_size_table[i].page == page_num) 1894111206Sken return (cd_page_size_table[i].page_size); 1895111206Sken } 1896111206Sken 1897111206Sken return (-1); 1898111206Sken} 1899111206Sken 1900111206Skenstatic int 190183366Sjuliancdioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct thread *td) 190239213Sgibbs{ 190339213Sgibbs 190439213Sgibbs struct cam_periph *periph; 190539213Sgibbs struct cd_softc *softc; 1906101940Snjl int error; 190739213Sgibbs 1908101940Snjl periph = (struct cam_periph *)dev->si_drv1; 190939213Sgibbs if (periph == NULL) 191039213Sgibbs return(ENXIO); 191139213Sgibbs 191239213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdioctl\n")); 191339213Sgibbs 191439213Sgibbs softc = (struct cd_softc *)periph->softc; 191539213Sgibbs 191639213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 191740020Sken ("trying to do ioctl %#lx\n", cmd)); 191839213Sgibbs 191940020Sken error = cam_periph_lock(periph, PRIBIO | PCATCH); 192039213Sgibbs 192140020Sken if (error != 0) 192240020Sken return(error); 1923111206Sken /* 1924111206Sken * If we don't have media loaded, check for it. If still don't 1925111206Sken * have media loaded, we can only do a load or eject. 1926111206Sken */ 1927111206Sken if (((softc->flags & CD_FLAG_VALID_MEDIA) == 0) 1928111206Sken && ((cmd != CDIOCCLOSE) 1929111206Sken && (cmd != CDIOCEJECT))) { 1930111206Sken error = cdcheckmedia(periph); 1931111206Sken if (error != 0) { 1932111206Sken cam_periph_unlock(periph); 1933111206Sken return (error); 1934111206Sken } 1935111206Sken } 193640020Sken 193739213Sgibbs switch (cmd) { 193839213Sgibbs 1939104456Sphk case DIOCGMEDIASIZE: 1940104456Sphk *(off_t *)addr = 1941104456Sphk (off_t)softc->params.blksize * softc->params.disksize; 1942104456Sphk break; 1943104456Sphk case DIOCGSECTORSIZE: 1944104456Sphk *(u_int *)addr = softc->params.blksize; 1945104456Sphk break; 1946104456Sphk 194739213Sgibbs case CDIOCPLAYTRACKS: 194839213Sgibbs { 194939213Sgibbs struct ioc_play_track *args 195039213Sgibbs = (struct ioc_play_track *) addr; 1951111206Sken struct cd_mode_params params; 1952111206Sken union cd_pages *page; 195339213Sgibbs 1954111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 1955111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 1956111206Sken M_WAITOK | M_ZERO); 195739213Sgibbs 195839213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 195939213Sgibbs ("trying to do CDIOCPLAYTRACKS\n")); 196039213Sgibbs 1961111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 196239213Sgibbs if (error) { 1963111206Sken free(params.mode_buf, M_TEMP); 196439213Sgibbs break; 196539213Sgibbs } 1966111206Sken page = cdgetpage(¶ms); 1967111206Sken 1968111206Sken page->audio.flags &= ~CD_PA_SOTC; 1969111206Sken page->audio.flags |= CD_PA_IMMED; 1970111206Sken error = cdsetmode(periph, ¶ms); 1971111206Sken free(params.mode_buf, M_TEMP); 197239213Sgibbs if (error) 197339213Sgibbs break; 1974111206Sken 1975111206Sken /* 1976111206Sken * This was originally implemented with the PLAY 1977111206Sken * AUDIO TRACK INDEX command, but that command was 1978111206Sken * deprecated after SCSI-2. Most (all?) SCSI CDROM 1979111206Sken * drives support it but ATAPI and ATAPI-derivative 1980111206Sken * drives don't seem to support it. So we keep a 1981111206Sken * cache of the table of contents and translate 1982111206Sken * track numbers to MSF format. 1983111206Sken */ 1984111206Sken if (softc->flags & CD_FLAG_VALID_TOC) { 1985111206Sken union msf_lba *sentry, *eentry; 1986111206Sken int st, et; 1987111206Sken 1988111206Sken if (args->end_track < 1989111206Sken softc->toc.header.ending_track + 1) 1990111206Sken args->end_track++; 1991111206Sken if (args->end_track > 1992111206Sken softc->toc.header.ending_track + 1) 1993111206Sken args->end_track = 1994111206Sken softc->toc.header.ending_track + 1; 1995111206Sken st = args->start_track - 1996111206Sken softc->toc.header.starting_track; 1997111206Sken et = args->end_track - 1998111206Sken softc->toc.header.starting_track; 1999111206Sken if ((st < 0) 2000111206Sken || (et < 0) 2001111206Sken || (st > (softc->toc.header.ending_track - 2002111206Sken softc->toc.header.starting_track))) { 2003111206Sken error = EINVAL; 2004111206Sken break; 2005111206Sken } 2006111206Sken sentry = &softc->toc.entries[st].addr; 2007111206Sken eentry = &softc->toc.entries[et].addr; 2008111206Sken error = cdplaymsf(periph, 2009111206Sken sentry->msf.minute, 2010111206Sken sentry->msf.second, 2011111206Sken sentry->msf.frame, 2012111206Sken eentry->msf.minute, 2013111206Sken eentry->msf.second, 2014111206Sken eentry->msf.frame); 2015111206Sken } else { 2016111206Sken /* 2017111206Sken * If we don't have a valid TOC, try the 2018111206Sken * play track index command. It is part of 2019111206Sken * the SCSI-2 spec, but was removed in the 2020111206Sken * MMC specs. ATAPI and ATAPI-derived 2021111206Sken * drives don't support it. 2022111206Sken */ 2023111206Sken if (softc->quirks & CD_Q_BCD_TRACKS) { 2024111206Sken args->start_track = 2025111206Sken bin2bcd(args->start_track); 2026111206Sken args->end_track = 2027111206Sken bin2bcd(args->end_track); 2028111206Sken } 2029111206Sken error = cdplaytracks(periph, 2030111206Sken args->start_track, 2031111206Sken args->start_index, 2032111206Sken args->end_track, 2033111206Sken args->end_index); 203439213Sgibbs } 203539213Sgibbs } 203639213Sgibbs break; 203739213Sgibbs case CDIOCPLAYMSF: 203839213Sgibbs { 203939213Sgibbs struct ioc_play_msf *args 204039213Sgibbs = (struct ioc_play_msf *) addr; 2041111206Sken struct cd_mode_params params; 2042111206Sken union cd_pages *page; 204339213Sgibbs 2044111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2045111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2046111206Sken M_WAITOK | M_ZERO); 204739213Sgibbs 204839213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 204939213Sgibbs ("trying to do CDIOCPLAYMSF\n")); 205039213Sgibbs 2051111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 205239213Sgibbs if (error) { 2053111206Sken free(params.mode_buf, M_TEMP); 205439213Sgibbs break; 205539213Sgibbs } 2056111206Sken page = cdgetpage(¶ms); 2057111206Sken 2058111206Sken page->audio.flags &= ~CD_PA_SOTC; 2059111206Sken page->audio.flags |= CD_PA_IMMED; 2060111206Sken error = cdsetmode(periph, ¶ms); 2061111206Sken free(params.mode_buf, M_TEMP); 206239213Sgibbs if (error) 206339213Sgibbs break; 206439213Sgibbs error = cdplaymsf(periph, 206539213Sgibbs args->start_m, 206639213Sgibbs args->start_s, 206739213Sgibbs args->start_f, 206839213Sgibbs args->end_m, 206939213Sgibbs args->end_s, 207039213Sgibbs args->end_f); 207139213Sgibbs } 207239213Sgibbs break; 207339213Sgibbs case CDIOCPLAYBLOCKS: 207439213Sgibbs { 207539213Sgibbs struct ioc_play_blocks *args 207639213Sgibbs = (struct ioc_play_blocks *) addr; 2077111206Sken struct cd_mode_params params; 2078111206Sken union cd_pages *page; 207939213Sgibbs 208039213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 208139213Sgibbs ("trying to do CDIOCPLAYBLOCKS\n")); 208239213Sgibbs 2083111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2084111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2085111206Sken M_WAITOK | M_ZERO); 208639213Sgibbs 2087111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 208839213Sgibbs if (error) { 2089111206Sken free(params.mode_buf, M_TEMP); 209039213Sgibbs break; 209139213Sgibbs } 2092111206Sken page = cdgetpage(¶ms); 2093111206Sken 2094111206Sken page->audio.flags &= ~CD_PA_SOTC; 2095111206Sken page->audio.flags |= CD_PA_IMMED; 2096111206Sken error = cdsetmode(periph, ¶ms); 2097111206Sken free(params.mode_buf, M_TEMP); 209839213Sgibbs if (error) 209939213Sgibbs break; 210039213Sgibbs error = cdplay(periph, args->blk, args->len); 210139213Sgibbs } 210239213Sgibbs break; 210339213Sgibbs case CDIOCREADSUBCHANNEL: 210439213Sgibbs { 210539213Sgibbs struct ioc_read_subchannel *args 210639213Sgibbs = (struct ioc_read_subchannel *) addr; 210739213Sgibbs struct cd_sub_channel_info *data; 210839213Sgibbs u_int32_t len = args->data_len; 210939213Sgibbs 211039213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 211139213Sgibbs ("trying to do CDIOCREADSUBCHANNEL\n")); 211239213Sgibbs 211339213Sgibbs data = malloc(sizeof(struct cd_sub_channel_info), 2114111119Simp M_TEMP, M_WAITOK); 211539213Sgibbs 211639213Sgibbs if ((len > sizeof(struct cd_sub_channel_info)) || 211739213Sgibbs (len < sizeof(struct cd_sub_channel_header))) { 211839213Sgibbs printf( 211939213Sgibbs "scsi_cd: cdioctl: " 212039213Sgibbs "cdioreadsubchannel: error, len=%d\n", 212139213Sgibbs len); 212239213Sgibbs error = EINVAL; 212339213Sgibbs free(data, M_TEMP); 212439213Sgibbs break; 212539213Sgibbs } 212639213Sgibbs 212739213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) 212839213Sgibbs args->track = bin2bcd(args->track); 212939213Sgibbs 213039213Sgibbs error = cdreadsubchannel(periph, args->address_format, 213139213Sgibbs args->data_format, args->track, data, len); 213239213Sgibbs 213339213Sgibbs if (error) { 213439213Sgibbs free(data, M_TEMP); 213539213Sgibbs break; 213639213Sgibbs } 213739213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) 213839213Sgibbs data->what.track_info.track_number = 213939213Sgibbs bcd2bin(data->what.track_info.track_number); 214039213Sgibbs len = min(len, ((data->header.data_len[0] << 8) + 214139213Sgibbs data->header.data_len[1] + 214239213Sgibbs sizeof(struct cd_sub_channel_header))); 214339213Sgibbs if (copyout(data, args->data, len) != 0) { 214439213Sgibbs error = EFAULT; 214539213Sgibbs } 214639213Sgibbs free(data, M_TEMP); 214739213Sgibbs } 214839213Sgibbs break; 214939213Sgibbs 215039213Sgibbs case CDIOREADTOCHEADER: 215139213Sgibbs { 215239213Sgibbs struct ioc_toc_header *th; 215339213Sgibbs 215439213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 215539213Sgibbs ("trying to do CDIOREADTOCHEADER\n")); 215639213Sgibbs 215739213Sgibbs th = malloc(sizeof(struct ioc_toc_header), M_TEMP, 2158111119Simp M_WAITOK); 2159111206Sken error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, 2160111206Sken sizeof (*th), /*sense_flags*/0); 216139213Sgibbs if (error) { 216239213Sgibbs free(th, M_TEMP); 216339213Sgibbs break; 216439213Sgibbs } 216539213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) { 216639213Sgibbs /* we are going to have to convert the BCD 216739213Sgibbs * encoding on the cd to what is expected 216839213Sgibbs */ 216939213Sgibbs th->starting_track = 217039213Sgibbs bcd2bin(th->starting_track); 217139213Sgibbs th->ending_track = bcd2bin(th->ending_track); 217239213Sgibbs } 217390868Smike th->len = ntohs(th->len); 217439213Sgibbs bcopy(th, addr, sizeof(*th)); 217539213Sgibbs free(th, M_TEMP); 217639213Sgibbs } 217739213Sgibbs break; 217839213Sgibbs case CDIOREADTOCENTRYS: 217939213Sgibbs { 2180111206Sken struct cd_tocdata *data; 2181111206Sken struct cd_toc_single *lead; 218239213Sgibbs struct ioc_read_toc_entry *te = 218339213Sgibbs (struct ioc_read_toc_entry *) addr; 218439213Sgibbs struct ioc_toc_header *th; 218539213Sgibbs u_int32_t len, readlen, idx, num; 218639213Sgibbs u_int32_t starting_track = te->starting_track; 218739213Sgibbs 218839213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 218939213Sgibbs ("trying to do CDIOREADTOCENTRYS\n")); 219039213Sgibbs 2191111206Sken data = malloc(sizeof(*data), M_TEMP, M_WAITOK); 2192111206Sken lead = malloc(sizeof(*lead), M_TEMP, M_WAITOK); 219339213Sgibbs 219439213Sgibbs if (te->data_len < sizeof(struct cd_toc_entry) 219539213Sgibbs || (te->data_len % sizeof(struct cd_toc_entry)) != 0 219639213Sgibbs || (te->address_format != CD_MSF_FORMAT 219739213Sgibbs && te->address_format != CD_LBA_FORMAT)) { 219839213Sgibbs error = EINVAL; 219939213Sgibbs printf("scsi_cd: error in readtocentries, " 220039213Sgibbs "returning EINVAL\n"); 220139213Sgibbs free(data, M_TEMP); 220239213Sgibbs free(lead, M_TEMP); 220339213Sgibbs break; 220439213Sgibbs } 220539213Sgibbs 220639213Sgibbs th = &data->header; 2207111206Sken error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, 2208111206Sken sizeof (*th), /*sense_flags*/0); 220939213Sgibbs if (error) { 221039213Sgibbs free(data, M_TEMP); 221139213Sgibbs free(lead, M_TEMP); 221239213Sgibbs break; 221339213Sgibbs } 221439213Sgibbs 221539213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) { 221639213Sgibbs /* we are going to have to convert the BCD 221739213Sgibbs * encoding on the cd to what is expected 221839213Sgibbs */ 221939213Sgibbs th->starting_track = 222039213Sgibbs bcd2bin(th->starting_track); 222139213Sgibbs th->ending_track = bcd2bin(th->ending_track); 222239213Sgibbs } 222339213Sgibbs 222439213Sgibbs if (starting_track == 0) 222539213Sgibbs starting_track = th->starting_track; 222639213Sgibbs else if (starting_track == LEADOUT) 222739213Sgibbs starting_track = th->ending_track + 1; 222839213Sgibbs else if (starting_track < th->starting_track || 222939213Sgibbs starting_track > th->ending_track + 1) { 223039213Sgibbs printf("scsi_cd: error in readtocentries, " 223139213Sgibbs "returning EINVAL\n"); 223239213Sgibbs free(data, M_TEMP); 223339213Sgibbs free(lead, M_TEMP); 223439213Sgibbs error = EINVAL; 223539213Sgibbs break; 223639213Sgibbs } 223739213Sgibbs 223839213Sgibbs /* calculate reading length without leadout entry */ 223939213Sgibbs readlen = (th->ending_track - starting_track + 1) * 224039213Sgibbs sizeof(struct cd_toc_entry); 224139213Sgibbs 224239213Sgibbs /* and with leadout entry */ 224339213Sgibbs len = readlen + sizeof(struct cd_toc_entry); 224439213Sgibbs if (te->data_len < len) { 224539213Sgibbs len = te->data_len; 224639213Sgibbs if (readlen > len) 224739213Sgibbs readlen = len; 224839213Sgibbs } 224939213Sgibbs if (len > sizeof(data->entries)) { 225039213Sgibbs printf("scsi_cd: error in readtocentries, " 225139213Sgibbs "returning EINVAL\n"); 225239213Sgibbs error = EINVAL; 225339213Sgibbs free(data, M_TEMP); 225439213Sgibbs free(lead, M_TEMP); 225539213Sgibbs break; 225639213Sgibbs } 225739213Sgibbs num = len / sizeof(struct cd_toc_entry); 225839213Sgibbs 225939213Sgibbs if (readlen > 0) { 226039213Sgibbs error = cdreadtoc(periph, te->address_format, 226139213Sgibbs starting_track, 2262111206Sken (u_int8_t *)data, 2263111206Sken readlen + sizeof (*th), 2264111206Sken /*sense_flags*/0); 226539213Sgibbs if (error) { 226639213Sgibbs free(data, M_TEMP); 226739213Sgibbs free(lead, M_TEMP); 226839213Sgibbs break; 226939213Sgibbs } 227039213Sgibbs } 227139213Sgibbs 227239213Sgibbs /* make leadout entry if needed */ 227339213Sgibbs idx = starting_track + num - 1; 227439213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) 227539213Sgibbs th->ending_track = bcd2bin(th->ending_track); 227639213Sgibbs if (idx == th->ending_track + 1) { 227739213Sgibbs error = cdreadtoc(periph, te->address_format, 2278111206Sken LEADOUT, (u_int8_t *)lead, 2279111206Sken sizeof(*lead), 2280111206Sken /*sense_flags*/0); 228139213Sgibbs if (error) { 228239213Sgibbs free(data, M_TEMP); 228339213Sgibbs free(lead, M_TEMP); 228439213Sgibbs break; 228539213Sgibbs } 228639213Sgibbs data->entries[idx - starting_track] = 228739213Sgibbs lead->entry; 228839213Sgibbs } 228939213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) { 229039213Sgibbs for (idx = 0; idx < num - 1; idx++) { 229139213Sgibbs data->entries[idx].track = 229239213Sgibbs bcd2bin(data->entries[idx].track); 229339213Sgibbs } 229439213Sgibbs } 229539213Sgibbs 229639213Sgibbs error = copyout(data->entries, te->data, len); 229739213Sgibbs free(data, M_TEMP); 229839213Sgibbs free(lead, M_TEMP); 229939213Sgibbs } 230039213Sgibbs break; 230139213Sgibbs case CDIOREADTOCENTRY: 230239213Sgibbs { 2303111206Sken struct cd_toc_single *data; 230439213Sgibbs struct ioc_read_toc_single_entry *te = 230539213Sgibbs (struct ioc_read_toc_single_entry *) addr; 230639213Sgibbs struct ioc_toc_header *th; 230739213Sgibbs u_int32_t track; 230839213Sgibbs 230939213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 231039213Sgibbs ("trying to do CDIOREADTOCENTRY\n")); 231139213Sgibbs 2312111206Sken data = malloc(sizeof(*data), M_TEMP, M_WAITOK); 231339213Sgibbs 231439213Sgibbs if (te->address_format != CD_MSF_FORMAT 231539213Sgibbs && te->address_format != CD_LBA_FORMAT) { 231639213Sgibbs printf("error in readtocentry, " 231739213Sgibbs " returning EINVAL\n"); 231839213Sgibbs free(data, M_TEMP); 231939213Sgibbs error = EINVAL; 232039213Sgibbs break; 232139213Sgibbs } 232239213Sgibbs 232339213Sgibbs th = &data->header; 2324111206Sken error = cdreadtoc(periph, 0, 0, (u_int8_t *)th, 2325111206Sken sizeof (*th), /*sense_flags*/0); 232639213Sgibbs if (error) { 232739213Sgibbs free(data, M_TEMP); 232839213Sgibbs break; 232939213Sgibbs } 233039213Sgibbs 233139213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) { 233239213Sgibbs /* we are going to have to convert the BCD 233339213Sgibbs * encoding on the cd to what is expected 233439213Sgibbs */ 233539213Sgibbs th->starting_track = 233639213Sgibbs bcd2bin(th->starting_track); 233739213Sgibbs th->ending_track = bcd2bin(th->ending_track); 233839213Sgibbs } 233939213Sgibbs track = te->track; 234039213Sgibbs if (track == 0) 234139213Sgibbs track = th->starting_track; 234239213Sgibbs else if (track == LEADOUT) 234339213Sgibbs /* OK */; 234439213Sgibbs else if (track < th->starting_track || 234539213Sgibbs track > th->ending_track + 1) { 234639213Sgibbs printf("error in readtocentry, " 234739213Sgibbs " returning EINVAL\n"); 234839213Sgibbs free(data, M_TEMP); 234939213Sgibbs error = EINVAL; 235039213Sgibbs break; 235139213Sgibbs } 235239213Sgibbs 235339213Sgibbs error = cdreadtoc(periph, te->address_format, track, 2354111206Sken (u_int8_t *)data, sizeof(*data), 2355111206Sken /*sense_flags*/0); 235639213Sgibbs if (error) { 235739213Sgibbs free(data, M_TEMP); 235839213Sgibbs break; 235939213Sgibbs } 236039213Sgibbs 236139213Sgibbs if (softc->quirks & CD_Q_BCD_TRACKS) 236239213Sgibbs data->entry.track = bcd2bin(data->entry.track); 236339213Sgibbs bcopy(&data->entry, &te->entry, 236439213Sgibbs sizeof(struct cd_toc_entry)); 236539213Sgibbs free(data, M_TEMP); 236639213Sgibbs } 236739213Sgibbs break; 236839213Sgibbs case CDIOCSETPATCH: 236939213Sgibbs { 2370111206Sken struct ioc_patch *arg = (struct ioc_patch *)addr; 2371111206Sken struct cd_mode_params params; 2372111206Sken union cd_pages *page; 237339213Sgibbs 237439213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 237539213Sgibbs ("trying to do CDIOCSETPATCH\n")); 237639213Sgibbs 2377111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2378111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2379111206Sken M_WAITOK | M_ZERO); 2380111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 238139213Sgibbs if (error) { 2382111206Sken free(params.mode_buf, M_TEMP); 238339213Sgibbs break; 238439213Sgibbs } 2385111206Sken page = cdgetpage(¶ms); 2386111206Sken 2387111206Sken page->audio.port[LEFT_PORT].channels = 238839213Sgibbs arg->patch[0]; 2389111206Sken page->audio.port[RIGHT_PORT].channels = 239039213Sgibbs arg->patch[1]; 2391111206Sken page->audio.port[2].channels = arg->patch[2]; 2392111206Sken page->audio.port[3].channels = arg->patch[3]; 2393111206Sken error = cdsetmode(periph, ¶ms); 2394111206Sken free(params.mode_buf, M_TEMP); 239539213Sgibbs } 239639213Sgibbs break; 239739213Sgibbs case CDIOCGETVOL: 239839213Sgibbs { 239939213Sgibbs struct ioc_vol *arg = (struct ioc_vol *) addr; 2400111206Sken struct cd_mode_params params; 2401111206Sken union cd_pages *page; 240239213Sgibbs 240339213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 240439213Sgibbs ("trying to do CDIOCGETVOL\n")); 240539213Sgibbs 2406111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2407111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2408111206Sken M_WAITOK | M_ZERO); 2409111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 241039213Sgibbs if (error) { 2411111206Sken free(params.mode_buf, M_TEMP); 241239213Sgibbs break; 241339213Sgibbs } 2414111206Sken page = cdgetpage(¶ms); 2415111206Sken 241639213Sgibbs arg->vol[LEFT_PORT] = 2417111206Sken page->audio.port[LEFT_PORT].volume; 241839213Sgibbs arg->vol[RIGHT_PORT] = 2419111206Sken page->audio.port[RIGHT_PORT].volume; 2420111206Sken arg->vol[2] = page->audio.port[2].volume; 2421111206Sken arg->vol[3] = page->audio.port[3].volume; 2422111206Sken free(params.mode_buf, M_TEMP); 242339213Sgibbs } 242439213Sgibbs break; 242539213Sgibbs case CDIOCSETVOL: 242639213Sgibbs { 242739213Sgibbs struct ioc_vol *arg = (struct ioc_vol *) addr; 2428111206Sken struct cd_mode_params params; 2429111206Sken union cd_pages *page; 243039213Sgibbs 243139213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 243239213Sgibbs ("trying to do CDIOCSETVOL\n")); 243339213Sgibbs 2434111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2435111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2436111206Sken M_WAITOK | M_ZERO); 2437111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 243839213Sgibbs if (error) { 2439111206Sken free(params.mode_buf, M_TEMP); 244039213Sgibbs break; 244139213Sgibbs } 2442111206Sken page = cdgetpage(¶ms); 2443111206Sken 2444111206Sken page->audio.port[LEFT_PORT].channels = CHANNEL_0; 2445111206Sken page->audio.port[LEFT_PORT].volume = 244639213Sgibbs arg->vol[LEFT_PORT]; 2447111206Sken page->audio.port[RIGHT_PORT].channels = CHANNEL_1; 2448111206Sken page->audio.port[RIGHT_PORT].volume = 244939213Sgibbs arg->vol[RIGHT_PORT]; 2450111206Sken page->audio.port[2].volume = arg->vol[2]; 2451111206Sken page->audio.port[3].volume = arg->vol[3]; 2452111206Sken error = cdsetmode(periph, ¶ms); 2453111206Sken free(params.mode_buf, M_TEMP); 245439213Sgibbs } 245539213Sgibbs break; 245639213Sgibbs case CDIOCSETMONO: 245739213Sgibbs { 2458111206Sken struct cd_mode_params params; 2459111206Sken union cd_pages *page; 246039213Sgibbs 246139213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 246239213Sgibbs ("trying to do CDIOCSETMONO\n")); 246339213Sgibbs 2464111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2465111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2466111206Sken M_WAITOK | M_ZERO); 2467111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 246839213Sgibbs if (error) { 2469111206Sken free(params.mode_buf, M_TEMP); 247039213Sgibbs break; 247139213Sgibbs } 2472111206Sken page = cdgetpage(¶ms); 2473111206Sken 2474111206Sken page->audio.port[LEFT_PORT].channels = 247539213Sgibbs LEFT_CHANNEL | RIGHT_CHANNEL; 2476111206Sken page->audio.port[RIGHT_PORT].channels = 247739213Sgibbs LEFT_CHANNEL | RIGHT_CHANNEL; 2478111206Sken page->audio.port[2].channels = 0; 2479111206Sken page->audio.port[3].channels = 0; 2480111206Sken error = cdsetmode(periph, ¶ms); 2481111206Sken free(params.mode_buf, M_TEMP); 248239213Sgibbs } 248339213Sgibbs break; 248439213Sgibbs case CDIOCSETSTEREO: 248539213Sgibbs { 2486111206Sken struct cd_mode_params params; 2487111206Sken union cd_pages *page; 248839213Sgibbs 248939213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 249039213Sgibbs ("trying to do CDIOCSETSTEREO\n")); 249139213Sgibbs 2492111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2493111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2494111206Sken M_WAITOK | M_ZERO); 2495111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 249639213Sgibbs if (error) { 2497111206Sken free(params.mode_buf, M_TEMP); 249839213Sgibbs break; 249939213Sgibbs } 2500111206Sken page = cdgetpage(¶ms); 2501111206Sken 2502111206Sken page->audio.port[LEFT_PORT].channels = 250339213Sgibbs LEFT_CHANNEL; 2504111206Sken page->audio.port[RIGHT_PORT].channels = 250539213Sgibbs RIGHT_CHANNEL; 2506111206Sken page->audio.port[2].channels = 0; 2507111206Sken page->audio.port[3].channels = 0; 2508111206Sken error = cdsetmode(periph, ¶ms); 2509111206Sken free(params.mode_buf, M_TEMP); 251039213Sgibbs } 251139213Sgibbs break; 251239213Sgibbs case CDIOCSETMUTE: 251339213Sgibbs { 2514111206Sken struct cd_mode_params params; 2515111206Sken union cd_pages *page; 251639213Sgibbs 251739213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 251839213Sgibbs ("trying to do CDIOCSETMUTE\n")); 251939213Sgibbs 2520111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2521111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2522111206Sken M_WAITOK | M_ZERO); 2523111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 252439213Sgibbs if (error) { 2525111206Sken free(¶ms, M_TEMP); 252639213Sgibbs break; 252739213Sgibbs } 2528111206Sken page = cdgetpage(¶ms); 2529111206Sken 2530111206Sken page->audio.port[LEFT_PORT].channels = 0; 2531111206Sken page->audio.port[RIGHT_PORT].channels = 0; 2532111206Sken page->audio.port[2].channels = 0; 2533111206Sken page->audio.port[3].channels = 0; 2534111206Sken error = cdsetmode(periph, ¶ms); 2535111206Sken free(params.mode_buf, M_TEMP); 253639213Sgibbs } 253739213Sgibbs break; 253839213Sgibbs case CDIOCSETLEFT: 253939213Sgibbs { 2540111206Sken struct cd_mode_params params; 2541111206Sken union cd_pages *page; 254239213Sgibbs 254339213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 254439213Sgibbs ("trying to do CDIOCSETLEFT\n")); 254539213Sgibbs 2546111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2547111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2548111206Sken M_WAITOK | M_ZERO); 2549111206Sken 2550111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 255139213Sgibbs if (error) { 2552111206Sken free(params.mode_buf, M_TEMP); 255339213Sgibbs break; 255439213Sgibbs } 2555111206Sken page = cdgetpage(¶ms); 2556111206Sken 2557111206Sken page->audio.port[LEFT_PORT].channels = LEFT_CHANNEL; 2558111206Sken page->audio.port[RIGHT_PORT].channels = LEFT_CHANNEL; 2559111206Sken page->audio.port[2].channels = 0; 2560111206Sken page->audio.port[3].channels = 0; 2561111206Sken error = cdsetmode(periph, ¶ms); 2562111206Sken free(params.mode_buf, M_TEMP); 256339213Sgibbs } 256439213Sgibbs break; 256539213Sgibbs case CDIOCSETRIGHT: 256639213Sgibbs { 2567111206Sken struct cd_mode_params params; 2568111206Sken union cd_pages *page; 256939213Sgibbs 257039213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_SUBTRACE, 257139213Sgibbs ("trying to do CDIOCSETRIGHT\n")); 257239213Sgibbs 2573111206Sken params.alloc_len = sizeof(union cd_mode_data_6_10); 2574111206Sken params.mode_buf = malloc(params.alloc_len, M_TEMP, 2575111206Sken M_WAITOK | M_ZERO); 2576111206Sken 2577111206Sken error = cdgetmode(periph, ¶ms, AUDIO_PAGE); 257839213Sgibbs if (error) { 2579111206Sken free(params.mode_buf, M_TEMP); 258039213Sgibbs break; 258139213Sgibbs } 2582111206Sken page = cdgetpage(¶ms); 2583111206Sken 2584111206Sken page->audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; 2585111206Sken page->audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; 2586111206Sken page->audio.port[2].channels = 0; 2587111206Sken page->audio.port[3].channels = 0; 2588111206Sken error = cdsetmode(periph, ¶ms); 2589111206Sken free(params.mode_buf, M_TEMP); 259039213Sgibbs } 259139213Sgibbs break; 259239213Sgibbs case CDIOCRESUME: 259339213Sgibbs error = cdpause(periph, 1); 259439213Sgibbs break; 259539213Sgibbs case CDIOCPAUSE: 259639213Sgibbs error = cdpause(periph, 0); 259739213Sgibbs break; 259839213Sgibbs case CDIOCSTART: 2599111206Sken error = cdstartunit(periph, 0); 260039213Sgibbs break; 2601111206Sken case CDIOCCLOSE: 2602111206Sken error = cdstartunit(periph, 1); 2603111206Sken break; 260439213Sgibbs case CDIOCSTOP: 260539213Sgibbs error = cdstopunit(periph, 0); 260639213Sgibbs break; 260739213Sgibbs case CDIOCEJECT: 260839213Sgibbs error = cdstopunit(periph, 1); 260939213Sgibbs break; 261039213Sgibbs case CDIOCALLOW: 261139213Sgibbs cdprevent(periph, PR_ALLOW); 261239213Sgibbs break; 261339213Sgibbs case CDIOCPREVENT: 261439213Sgibbs cdprevent(periph, PR_PREVENT); 261539213Sgibbs break; 261639213Sgibbs case CDIOCSETDEBUG: 261739213Sgibbs /* sc_link->flags |= (SDEV_DB1 | SDEV_DB2); */ 261839213Sgibbs error = ENOTTY; 261939213Sgibbs break; 262039213Sgibbs case CDIOCCLRDEBUG: 262139213Sgibbs /* sc_link->flags &= ~(SDEV_DB1 | SDEV_DB2); */ 262239213Sgibbs error = ENOTTY; 262339213Sgibbs break; 262439213Sgibbs case CDIOCRESET: 262539213Sgibbs /* return (cd_reset(periph)); */ 262639213Sgibbs error = ENOTTY; 262739213Sgibbs break; 2628105421Snjl case CDRIOCREADSPEED: 2629105421Snjl error = cdsetspeed(periph, *(u_int32_t *)addr, CDR_MAX_SPEED); 2630105421Snjl break; 2631105421Snjl case CDRIOCWRITESPEED: 2632105421Snjl error = cdsetspeed(periph, CDR_MAX_SPEED, *(u_int32_t *)addr); 2633105421Snjl break; 263460422Sken case DVDIOCSENDKEY: 263560422Sken case DVDIOCREPORTKEY: { 263660422Sken struct dvd_authinfo *authinfo; 263760422Sken 263860422Sken authinfo = (struct dvd_authinfo *)addr; 263960422Sken 264060422Sken if (cmd == DVDIOCREPORTKEY) 264160422Sken error = cdreportkey(periph, authinfo); 264260422Sken else 264360422Sken error = cdsendkey(periph, authinfo); 264460422Sken break; 2645104456Sphk } 264660422Sken case DVDIOCREADSTRUCTURE: { 264760422Sken struct dvd_struct *dvdstruct; 264860422Sken 264960422Sken dvdstruct = (struct dvd_struct *)addr; 265060422Sken 265160422Sken error = cdreaddvdstructure(periph, dvdstruct); 265260422Sken 265360422Sken break; 265460422Sken } 265539213Sgibbs default: 265639213Sgibbs error = cam_periph_ioctl(periph, cmd, addr, cderror); 265739213Sgibbs break; 265839213Sgibbs } 265939213Sgibbs 266040020Sken cam_periph_unlock(periph); 266140020Sken 266239213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdioctl\n")); 2663104456Sphk if (error && bootverbose) { 2664104456Sphk printf("scsi_cd.c::ioctl cmd=%08lx error=%d\n", cmd, error); 2665104456Sphk } 266639213Sgibbs 266739213Sgibbs return (error); 266839213Sgibbs} 266939213Sgibbs 267039213Sgibbsstatic void 267139213Sgibbscdprevent(struct cam_periph *periph, int action) 267239213Sgibbs{ 267339213Sgibbs union ccb *ccb; 267439213Sgibbs struct cd_softc *softc; 267539213Sgibbs int error; 267639213Sgibbs 267739213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdprevent\n")); 267839213Sgibbs 267939213Sgibbs softc = (struct cd_softc *)periph->softc; 268039213Sgibbs 268139213Sgibbs if (((action == PR_ALLOW) 268239213Sgibbs && (softc->flags & CD_FLAG_DISC_LOCKED) == 0) 268339213Sgibbs || ((action == PR_PREVENT) 268439213Sgibbs && (softc->flags & CD_FLAG_DISC_LOCKED) != 0)) { 268539213Sgibbs return; 268639213Sgibbs } 268739213Sgibbs 268839213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 268939213Sgibbs 269039213Sgibbs scsi_prevent(&ccb->csio, 269139213Sgibbs /*retries*/ 1, 269239213Sgibbs cddone, 269339213Sgibbs MSG_SIMPLE_Q_TAG, 269439213Sgibbs action, 269539213Sgibbs SSD_FULL_SIZE, 269639213Sgibbs /* timeout */60000); 269739213Sgibbs 269874840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 269974840Sken /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); 270039213Sgibbs 270139213Sgibbs xpt_release_ccb(ccb); 270239213Sgibbs 270339213Sgibbs if (error == 0) { 270439213Sgibbs if (action == PR_ALLOW) 270539213Sgibbs softc->flags &= ~CD_FLAG_DISC_LOCKED; 270639213Sgibbs else 270739213Sgibbs softc->flags |= CD_FLAG_DISC_LOCKED; 270839213Sgibbs } 270939213Sgibbs} 271039213Sgibbs 271139213Sgibbsstatic int 2712111206Skencdcheckmedia(struct cam_periph *periph) 271339213Sgibbs{ 271439213Sgibbs struct cd_softc *softc; 2715111206Sken struct ioc_toc_header *toch; 2716111206Sken struct cd_toc_single leadout; 2717111206Sken u_int32_t size, toclen; 2718111206Sken int error, num_entries, cdindex; 2719111206Sken 2720111206Sken softc = (struct cd_softc *)periph->softc; 2721111206Sken 2722111206Sken cdprevent(periph, PR_PREVENT); 2723111206Sken 2724111206Sken /* 2725111206Sken * Get the disc size and block size. If we can't get it, we don't 2726111206Sken * have media, most likely. 2727111206Sken */ 2728111206Sken if ((error = cdsize(periph, &size)) != 0) { 2729111206Sken softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC); 2730111206Sken cdprevent(periph, PR_ALLOW); 2731111206Sken return (error); 2732111206Sken } else 2733111206Sken softc->flags |= CD_FLAG_VALID_MEDIA; 2734111206Sken 2735111206Sken /* 2736111206Sken * Now we check the table of contents. This (currently) is only 2737111206Sken * used for the CDIOCPLAYTRACKS ioctl. It may be used later to do 2738111206Sken * things like present a separate entry in /dev for each track, 2739111206Sken * like that acd(4) driver does. 2740111206Sken */ 2741111206Sken bzero(&softc->toc, sizeof(softc->toc)); 2742111206Sken toch = &softc->toc.header; 2743111206Sken /* 2744111206Sken * We will get errors here for media that doesn't have a table of 2745111206Sken * contents. According to the MMC-3 spec: "When a Read TOC/PMA/ATIP 2746111206Sken * command is presented for a DDCD/CD-R/RW media, where the first TOC 2747111206Sken * has not been recorded (no complete session) and the Format codes 2748111206Sken * 0000b, 0001b, or 0010b are specified, this command shall be rejected 2749111206Sken * with an INVALID FIELD IN CDB. Devices that are not capable of 2750111206Sken * reading an incomplete session on DDC/CD-R/RW media shall report 2751111206Sken * CANNOT READ MEDIUM - INCOMPATIBLE FORMAT." 2752111206Sken * 2753111206Sken * So this isn't fatal if we can't read the table of contents, it 2754111206Sken * just means that the user won't be able to issue the play tracks 2755111206Sken * ioctl, and likely lots of other stuff won't work either. They 2756111206Sken * need to burn the CD before we can do a whole lot with it. So 2757111206Sken * we don't print anything here if we get an error back. 2758111206Sken */ 2759111206Sken error = cdreadtoc(periph, 0, 0, (u_int8_t *)toch, sizeof(*toch), 2760111206Sken SF_NO_PRINT); 2761111206Sken /* 2762111206Sken * Errors in reading the table of contents aren't fatal, we just 2763111206Sken * won't have a valid table of contents cached. 2764111206Sken */ 2765111206Sken if (error != 0) { 2766111206Sken error = 0; 2767111206Sken bzero(&softc->toc, sizeof(softc->toc)); 2768111206Sken goto bailout; 2769111206Sken } 2770111206Sken 2771111206Sken if (softc->quirks & CD_Q_BCD_TRACKS) { 2772111206Sken toch->starting_track = bcd2bin(toch->starting_track); 2773111206Sken toch->ending_track = bcd2bin(toch->ending_track); 2774111206Sken } 2775111206Sken 2776111206Sken /* Number of TOC entries, plus leadout */ 2777111206Sken num_entries = (toch->ending_track - toch->starting_track) + 2; 2778111206Sken 2779111206Sken if (num_entries <= 0) 2780111206Sken goto bailout; 2781111206Sken 2782111206Sken toclen = num_entries * sizeof(struct cd_toc_entry); 2783111206Sken 2784111206Sken error = cdreadtoc(periph, CD_MSF_FORMAT, toch->starting_track, 2785111206Sken (u_int8_t *)&softc->toc, toclen + sizeof(*toch), 2786111206Sken SF_NO_PRINT); 2787111206Sken if (error != 0) { 2788111206Sken error = 0; 2789111206Sken bzero(&softc->toc, sizeof(softc->toc)); 2790111206Sken goto bailout; 2791111206Sken } 2792111206Sken 2793111206Sken if (softc->quirks & CD_Q_BCD_TRACKS) { 2794111206Sken toch->starting_track = bcd2bin(toch->starting_track); 2795111206Sken toch->ending_track = bcd2bin(toch->ending_track); 2796111206Sken } 2797111206Sken /* 2798111206Sken * XXX KDM is this necessary? Probably only if the drive doesn't 2799111206Sken * return leadout information with the table of contents. 2800111206Sken */ 2801111206Sken cdindex = toch->starting_track + num_entries -1; 2802111206Sken if (cdindex == toch->ending_track + 1) { 2803111206Sken 2804111206Sken error = cdreadtoc(periph, CD_MSF_FORMAT, LEADOUT, 2805111206Sken (u_int8_t *)&leadout, sizeof(leadout), 2806111206Sken SF_NO_PRINT); 2807111206Sken if (error != 0) { 2808111206Sken error = 0; 2809111206Sken goto bailout; 2810111206Sken } 2811111206Sken softc->toc.entries[cdindex - toch->starting_track] = 2812111206Sken leadout.entry; 2813111206Sken } 2814111206Sken if (softc->quirks & CD_Q_BCD_TRACKS) { 2815111206Sken for (cdindex = 0; cdindex < num_entries - 1; cdindex++) { 2816111206Sken softc->toc.entries[cdindex].track = 2817111206Sken bcd2bin(softc->toc.entries[cdindex].track); 2818111206Sken } 2819111206Sken } 2820111206Sken 2821111206Sken softc->flags |= CD_FLAG_VALID_TOC; 2822111206Sken 2823111206Skenbailout: 2824111206Sken 2825111206Sken /* 2826111206Sken * We unconditionally (re)set the blocksize each time the 2827111206Sken * CD device is opened. This is because the CD can change, 2828111206Sken * and therefore the blocksize might change. 2829111206Sken * XXX problems here if some slice or partition is still 2830111206Sken * open with the old size? 2831111206Sken */ 2832112006Sphk if ((softc->device_stats->flags & DEVSTAT_BS_UNAVAILABLE) != 0) 2833112006Sphk softc->device_stats->flags &= ~DEVSTAT_BS_UNAVAILABLE; 2834112006Sphk softc->device_stats->block_size = softc->params.blksize; 2835111206Sken 2836111206Sken return (error); 2837111206Sken} 2838111206Sken 2839111206Skenstatic int 2840111206Skencdsize(struct cam_periph *periph, u_int32_t *size) 2841111206Sken{ 2842111206Sken struct cd_softc *softc; 284339213Sgibbs union ccb *ccb; 284439213Sgibbs struct scsi_read_capacity_data *rcap_buf; 284539213Sgibbs int error; 284639213Sgibbs 284739213Sgibbs CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdsize\n")); 284839213Sgibbs 284939213Sgibbs softc = (struct cd_softc *)periph->softc; 285039213Sgibbs 285139213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 285239213Sgibbs 285339213Sgibbs rcap_buf = malloc(sizeof(struct scsi_read_capacity_data), 2854111119Simp M_TEMP, M_WAITOK); 285539213Sgibbs 285639213Sgibbs scsi_read_capacity(&ccb->csio, 285739213Sgibbs /*retries*/ 1, 285839213Sgibbs cddone, 285939213Sgibbs MSG_SIMPLE_Q_TAG, 286039213Sgibbs rcap_buf, 286139213Sgibbs SSD_FULL_SIZE, 286239213Sgibbs /* timeout */20000); 286339213Sgibbs 286474840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 286574840Sken /*sense_flags*/SF_RETRY_UA|SF_NO_PRINT); 286639213Sgibbs 286739213Sgibbs xpt_release_ccb(ccb); 286839213Sgibbs 286939213Sgibbs softc->params.disksize = scsi_4btoul(rcap_buf->addr) + 1; 287039213Sgibbs softc->params.blksize = scsi_4btoul(rcap_buf->length); 287160806Sjoerg /* 287260806Sjoerg * SCSI-3 mandates that the reported blocksize shall be 2048. 287360806Sjoerg * Older drives sometimes report funny values, trim it down to 287460806Sjoerg * 2048, or other parts of the kernel will get confused. 287560806Sjoerg * 287660806Sjoerg * XXX we leave drives alone that might report 512 bytes, as 287760806Sjoerg * well as drives reporting more weird sizes like perhaps 4K. 287860806Sjoerg */ 287960806Sjoerg if (softc->params.blksize > 2048 && softc->params.blksize <= 2352) 288060806Sjoerg softc->params.blksize = 2048; 288139213Sgibbs 288239213Sgibbs free(rcap_buf, M_TEMP); 288339213Sgibbs *size = softc->params.disksize; 288439213Sgibbs 288539213Sgibbs return (error); 288639213Sgibbs 288739213Sgibbs} 288839213Sgibbs 288939213Sgibbsstatic int 2890111206Skencd6byteworkaround(union ccb *ccb) 2891111206Sken{ 2892111206Sken u_int8_t *cdb; 2893111206Sken struct cam_periph *periph; 2894111206Sken struct cd_softc *softc; 2895111206Sken struct cd_mode_params *params; 2896111206Sken int frozen, found; 2897111206Sken 2898111206Sken periph = xpt_path_periph(ccb->ccb_h.path); 2899111206Sken softc = (struct cd_softc *)periph->softc; 2900111206Sken 2901111206Sken cdb = ccb->csio.cdb_io.cdb_bytes; 2902111206Sken 2903111206Sken if ((ccb->ccb_h.flags & CAM_CDB_POINTER) 2904111206Sken || ((cdb[0] != MODE_SENSE_6) 2905111206Sken && (cdb[0] != MODE_SELECT_6))) 2906111206Sken return (0); 2907111206Sken 2908111206Sken /* 2909111206Sken * Because there is no convenient place to stash the overall 2910111206Sken * cd_mode_params structure pointer, we have to grab it like this. 2911111206Sken * This means that ALL MODE_SENSE and MODE_SELECT requests in the 2912111206Sken * cd(4) driver MUST go through cdgetmode() and cdsetmode()! 2913111206Sken * 2914111206Sken * XXX It would be nice if, at some point, we could increase the 2915111206Sken * number of available peripheral private pointers. Both pointers 2916111206Sken * are currently used in most every peripheral driver. 2917111206Sken */ 2918111206Sken found = 0; 2919111206Sken 2920111206Sken STAILQ_FOREACH(params, &softc->mode_queue, links) { 2921111206Sken if (params->mode_buf == ccb->csio.data_ptr) { 2922111206Sken found = 1; 2923111206Sken break; 2924111206Sken } 2925111206Sken } 2926111206Sken 2927111206Sken /* 2928111206Sken * This shouldn't happen. All mode sense and mode select 2929111206Sken * operations in the cd(4) driver MUST go through cdgetmode() and 2930111206Sken * cdsetmode()! 2931111206Sken */ 2932111206Sken if (found == 0) { 2933111206Sken xpt_print_path(periph->path); 2934111206Sken printf("mode buffer not found in mode queue!\n"); 2935111206Sken return (0); 2936111206Sken } 2937111206Sken 2938111206Sken params->cdb_size = 10; 2939111206Sken softc->minimum_command_size = 10; 2940111206Sken xpt_print_path(ccb->ccb_h.path); 2941111206Sken printf("%s(6) failed, increasing minimum CDB size to 10 bytes\n", 2942111206Sken (cdb[0] == MODE_SENSE_6) ? "MODE_SENSE" : "MODE_SELECT"); 2943111206Sken 2944111206Sken if (cdb[0] == MODE_SENSE_6) { 2945111206Sken struct scsi_mode_sense_10 ms10; 2946111206Sken struct scsi_mode_sense_6 *ms6; 2947111206Sken int len; 2948111206Sken 2949111206Sken ms6 = (struct scsi_mode_sense_6 *)cdb; 2950111206Sken 2951111206Sken bzero(&ms10, sizeof(ms10)); 2952111206Sken ms10.opcode = MODE_SENSE_10; 2953111206Sken ms10.byte2 = ms6->byte2; 2954111206Sken ms10.page = ms6->page; 2955111206Sken 2956111206Sken /* 2957111206Sken * 10 byte mode header, block descriptor, 2958111206Sken * sizeof(union cd_pages) 2959111206Sken */ 2960111206Sken len = sizeof(struct cd_mode_data_10); 2961111206Sken ccb->csio.dxfer_len = len; 2962111206Sken 2963111206Sken scsi_ulto2b(len, ms10.length); 2964111206Sken ms10.control = ms6->control; 2965111206Sken bcopy(&ms10, cdb, 10); 2966111206Sken ccb->csio.cdb_len = 10; 2967111206Sken } else { 2968111206Sken struct scsi_mode_select_10 ms10; 2969111206Sken struct scsi_mode_select_6 *ms6; 2970111206Sken struct scsi_mode_header_6 *header6; 2971111206Sken struct scsi_mode_header_10 *header10; 2972111206Sken struct scsi_mode_page_header *page_header; 2973111206Sken int blk_desc_len, page_num, page_size, len; 2974111206Sken 2975111206Sken ms6 = (struct scsi_mode_select_6 *)cdb; 2976111206Sken 2977111206Sken bzero(&ms10, sizeof(ms10)); 2978111206Sken ms10.opcode = MODE_SELECT_10; 2979111206Sken ms10.byte2 = ms6->byte2; 2980111206Sken 2981111206Sken header6 = (struct scsi_mode_header_6 *)params->mode_buf; 2982111206Sken header10 = (struct scsi_mode_header_10 *)params->mode_buf; 2983111206Sken 2984111206Sken page_header = find_mode_page_6(header6); 2985111206Sken page_num = page_header->page_code; 2986111206Sken 2987111206Sken blk_desc_len = header6->blk_desc_len; 2988111206Sken 2989111206Sken page_size = cdgetpagesize(page_num); 2990111206Sken 2991111206Sken if (page_size != (page_header->page_length + 2992111206Sken sizeof(*page_header))) 2993111206Sken page_size = page_header->page_length + 2994111206Sken sizeof(*page_header); 2995111206Sken 2996111206Sken len = sizeof(*header10) + blk_desc_len + page_size; 2997111206Sken 2998111206Sken len = min(params->alloc_len, len); 2999111206Sken 3000111206Sken /* 3001111206Sken * Since the 6 byte parameter header is shorter than the 10 3002111206Sken * byte parameter header, we need to copy the actual mode 3003111206Sken * page data, and the block descriptor, if any, so things wind 3004111206Sken * up in the right place. The regions will overlap, but 3005111206Sken * bcopy() does the right thing. 3006111206Sken */ 3007111206Sken bcopy(params->mode_buf + sizeof(*header6), 3008111206Sken params->mode_buf + sizeof(*header10), 3009111206Sken len - sizeof(*header10)); 3010111206Sken 3011111206Sken /* Make sure these fields are set correctly. */ 3012111206Sken scsi_ulto2b(0, header10->data_length); 3013111206Sken header10->medium_type = 0; 3014111206Sken scsi_ulto2b(blk_desc_len, header10->blk_desc_len); 3015111206Sken 3016111206Sken ccb->csio.dxfer_len = len; 3017111206Sken 3018111206Sken scsi_ulto2b(len, ms10.length); 3019111206Sken ms10.control = ms6->control; 3020111206Sken bcopy(&ms10, cdb, 10); 3021111206Sken ccb->csio.cdb_len = 10; 3022111206Sken } 3023111206Sken 3024111206Sken frozen = (ccb->ccb_h.status & CAM_DEV_QFRZN) != 0; 3025111206Sken ccb->ccb_h.status = CAM_REQUEUE_REQ; 3026111206Sken xpt_action(ccb); 3027111206Sken if (frozen) { 3028111206Sken cam_release_devq(ccb->ccb_h.path, 3029111206Sken /*relsim_flags*/0, 3030111206Sken /*openings*/0, 3031111206Sken /*timeout*/0, 3032111206Sken /*getcount_only*/0); 3033111206Sken } 3034111206Sken 3035111206Sken return (ERESTART); 3036111206Sken} 3037111206Sken 3038111206Skenstatic int 303939213Sgibbscderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 304039213Sgibbs{ 304139213Sgibbs struct cd_softc *softc; 304239213Sgibbs struct cam_periph *periph; 3043111206Sken int error; 304439213Sgibbs 304539213Sgibbs periph = xpt_path_periph(ccb->ccb_h.path); 304639213Sgibbs softc = (struct cd_softc *)periph->softc; 304739213Sgibbs 3048111206Sken error = 0; 3049111206Sken 305039514Sgibbs /* 3051111206Sken * We use a status of CAM_REQ_INVALID as shorthand -- if a 6 byte 3052111206Sken * CDB comes back with this particular error, try transforming it 3053111206Sken * into the 10 byte version. 3054111206Sken */ 3055111206Sken if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INVALID) { 3056111206Sken error = cd6byteworkaround(ccb); 3057111206Sken } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == 3058111206Sken CAM_SCSI_STATUS_ERROR) 3059111206Sken && (ccb->ccb_h.status & CAM_AUTOSNS_VALID) 3060111206Sken && (ccb->csio.scsi_status == SCSI_STATUS_CHECK_COND) 3061111206Sken && ((ccb->ccb_h.flags & CAM_SENSE_PHYS) == 0) 3062111206Sken && ((ccb->ccb_h.flags & CAM_SENSE_PTR) == 0)) { 3063111206Sken int sense_key, error_code, asc, ascq; 3064111206Sken 3065111206Sken scsi_extract_sense(&ccb->csio.sense_data, 3066111206Sken &error_code, &sense_key, &asc, &ascq); 3067111206Sken if (sense_key == SSD_KEY_ILLEGAL_REQUEST) 3068111206Sken error = cd6byteworkaround(ccb); 3069111206Sken } 3070111206Sken 3071111206Sken if (error == ERESTART) 3072111206Sken return (error); 3073111206Sken 3074111206Sken /* 307539514Sgibbs * XXX 307639514Sgibbs * Until we have a better way of doing pack validation, 307739514Sgibbs * don't treat UAs as errors. 307839514Sgibbs */ 307939514Sgibbs sense_flags |= SF_RETRY_UA; 308039213Sgibbs return (cam_periph_error(ccb, cam_flags, sense_flags, 308139213Sgibbs &softc->saved_ccb)); 308239213Sgibbs} 308339213Sgibbs 308439213Sgibbs/* 308539213Sgibbs * Read table of contents 308639213Sgibbs */ 308739213Sgibbsstatic int 308839213Sgibbscdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, 3089111206Sken u_int8_t *data, u_int32_t len, u_int32_t sense_flags) 309039213Sgibbs{ 309139213Sgibbs struct scsi_read_toc *scsi_cmd; 309239213Sgibbs u_int32_t ntoc; 309339213Sgibbs struct ccb_scsiio *csio; 309439213Sgibbs union ccb *ccb; 309539213Sgibbs int error; 309639213Sgibbs 309739213Sgibbs ntoc = len; 309839213Sgibbs error = 0; 309939213Sgibbs 310039213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 310139213Sgibbs 310239213Sgibbs csio = &ccb->csio; 310339213Sgibbs 310439213Sgibbs cam_fill_csio(csio, 310539213Sgibbs /* retries */ 1, 310639213Sgibbs /* cbfcnp */ cddone, 310739213Sgibbs /* flags */ CAM_DIR_IN, 310839213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 3109111206Sken /* data_ptr */ data, 311039213Sgibbs /* dxfer_len */ len, 311139213Sgibbs /* sense_len */ SSD_FULL_SIZE, 311239213Sgibbs sizeof(struct scsi_read_toc), 311339213Sgibbs /* timeout */ 50000); 311439213Sgibbs 311539213Sgibbs scsi_cmd = (struct scsi_read_toc *)&csio->cdb_io.cdb_bytes; 311639213Sgibbs bzero (scsi_cmd, sizeof(*scsi_cmd)); 311739213Sgibbs 311839213Sgibbs if (mode == CD_MSF_FORMAT) 311939213Sgibbs scsi_cmd->byte2 |= CD_MSF; 312039213Sgibbs scsi_cmd->from_track = start; 312139213Sgibbs /* scsi_ulto2b(ntoc, (u_int8_t *)scsi_cmd->data_len); */ 312239213Sgibbs scsi_cmd->data_len[0] = (ntoc) >> 8; 312339213Sgibbs scsi_cmd->data_len[1] = (ntoc) & 0xff; 312439213Sgibbs 312539213Sgibbs scsi_cmd->op_code = READ_TOC; 312639213Sgibbs 312774840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 3128111206Sken /*sense_flags*/SF_RETRY_UA | sense_flags); 312939213Sgibbs 313039213Sgibbs xpt_release_ccb(ccb); 313139213Sgibbs 313239213Sgibbs return(error); 313339213Sgibbs} 313439213Sgibbs 313539213Sgibbsstatic int 313639213Sgibbscdreadsubchannel(struct cam_periph *periph, u_int32_t mode, 313739213Sgibbs u_int32_t format, int track, 313839213Sgibbs struct cd_sub_channel_info *data, u_int32_t len) 313939213Sgibbs{ 314039213Sgibbs struct scsi_read_subchannel *scsi_cmd; 314139213Sgibbs struct ccb_scsiio *csio; 314239213Sgibbs union ccb *ccb; 314339213Sgibbs int error; 314439213Sgibbs 314539213Sgibbs error = 0; 314639213Sgibbs 314739213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 314839213Sgibbs 314939213Sgibbs csio = &ccb->csio; 315039213Sgibbs 315139213Sgibbs cam_fill_csio(csio, 315239213Sgibbs /* retries */ 1, 315339213Sgibbs /* cbfcnp */ cddone, 315439213Sgibbs /* flags */ CAM_DIR_IN, 315539213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 315639213Sgibbs /* data_ptr */ (u_int8_t *)data, 315739213Sgibbs /* dxfer_len */ len, 315839213Sgibbs /* sense_len */ SSD_FULL_SIZE, 315939213Sgibbs sizeof(struct scsi_read_subchannel), 316039213Sgibbs /* timeout */ 50000); 316139213Sgibbs 316239213Sgibbs scsi_cmd = (struct scsi_read_subchannel *)&csio->cdb_io.cdb_bytes; 316339213Sgibbs bzero (scsi_cmd, sizeof(*scsi_cmd)); 316439213Sgibbs 316539213Sgibbs scsi_cmd->op_code = READ_SUBCHANNEL; 316639213Sgibbs if (mode == CD_MSF_FORMAT) 316739213Sgibbs scsi_cmd->byte1 |= CD_MSF; 316839213Sgibbs scsi_cmd->byte2 = SRS_SUBQ; 316939213Sgibbs scsi_cmd->subchan_format = format; 317039213Sgibbs scsi_cmd->track = track; 317139213Sgibbs scsi_ulto2b(len, (u_int8_t *)scsi_cmd->data_len); 317239213Sgibbs scsi_cmd->control = 0; 317339213Sgibbs 317474840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 317574840Sken /*sense_flags*/SF_RETRY_UA); 317639213Sgibbs 317739213Sgibbs xpt_release_ccb(ccb); 317839213Sgibbs 317939213Sgibbs return(error); 318039213Sgibbs} 318139213Sgibbs 318239213Sgibbs 3183111206Sken/* 3184111206Sken * All MODE_SENSE requests in the cd(4) driver MUST go through this 3185111206Sken * routine. See comments in cd6byteworkaround() for details. 3186111206Sken */ 318739213Sgibbsstatic int 3188111206Skencdgetmode(struct cam_periph *periph, struct cd_mode_params *data, 3189111206Sken u_int32_t page) 319039213Sgibbs{ 3191111206Sken struct ccb_scsiio *csio; 3192111206Sken struct cd_softc *softc; 319339213Sgibbs union ccb *ccb; 3194111206Sken int param_len; 319539213Sgibbs int error; 319639213Sgibbs 3197111206Sken softc = (struct cd_softc *)periph->softc; 3198111206Sken 319939213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 320039213Sgibbs 320139213Sgibbs csio = &ccb->csio; 320239213Sgibbs 3203111206Sken data->cdb_size = softc->minimum_command_size; 3204111206Sken if (data->cdb_size < 10) 3205111206Sken param_len = sizeof(struct cd_mode_data); 3206111206Sken else 3207111206Sken param_len = sizeof(struct cd_mode_data_10); 320839213Sgibbs 3209111206Sken /* Don't say we've got more room than we actually allocated */ 3210111206Sken param_len = min(param_len, data->alloc_len); 321139213Sgibbs 3212111206Sken scsi_mode_sense_len(csio, 3213111206Sken /* retries */ 1, 3214111206Sken /* cbfcnp */ cddone, 3215111206Sken /* tag_action */ MSG_SIMPLE_Q_TAG, 3216111206Sken /* dbd */ 0, 3217111206Sken /* page_code */ SMS_PAGE_CTRL_CURRENT, 3218111206Sken /* page */ page, 3219111206Sken /* param_buf */ data->mode_buf, 3220111206Sken /* param_len */ param_len, 3221111206Sken /* minimum_cmd_size */ softc->minimum_command_size, 3222111206Sken /* sense_len */ SSD_FULL_SIZE, 3223111206Sken /* timeout */ 50000); 322439213Sgibbs 3225111206Sken /* 3226111206Sken * It would be nice not to have to do this, but there's no 3227111206Sken * available pointer in the CCB that would allow us to stuff the 3228111206Sken * mode params structure in there and retrieve it in 3229111206Sken * cd6byteworkaround(), so we can set the cdb size. The cdb size 3230111206Sken * lets the caller know what CDB size we ended up using, so they 3231111206Sken * can find the actual mode page offset. 3232111206Sken */ 3233111206Sken STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); 3234111206Sken 323574840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 323674840Sken /*sense_flags*/SF_RETRY_UA); 323739213Sgibbs 323839213Sgibbs xpt_release_ccb(ccb); 323939213Sgibbs 3240111206Sken STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); 3241111206Sken 3242111206Sken /* 3243111206Sken * This is a bit of belt-and-suspenders checking, but if we run 3244111206Sken * into a situation where the target sends back multiple block 3245111206Sken * descriptors, we might not have enough space in the buffer to 3246111206Sken * see the whole mode page. Better to return an error than 3247111206Sken * potentially access memory beyond our malloced region. 3248111206Sken */ 3249111206Sken if (error == 0) { 3250111206Sken u_int32_t data_len; 3251111206Sken 3252111206Sken if (data->cdb_size == 10) { 3253111206Sken struct scsi_mode_header_10 *hdr10; 3254111206Sken 3255111206Sken hdr10 = (struct scsi_mode_header_10 *)data->mode_buf; 3256111206Sken data_len = scsi_2btoul(hdr10->data_length); 3257111206Sken data_len += sizeof(hdr10->data_length); 3258111206Sken } else { 3259111206Sken struct scsi_mode_header_6 *hdr6; 3260111206Sken 3261111206Sken hdr6 = (struct scsi_mode_header_6 *)data->mode_buf; 3262111206Sken data_len = hdr6->data_length; 3263111206Sken data_len += sizeof(hdr6->data_length); 3264111206Sken } 3265111206Sken 3266111206Sken /* 3267111206Sken * Complain if there is more mode data available than we 3268111206Sken * allocated space for. This could potentially happen if 3269111206Sken * we miscalculated the page length for some reason, if the 3270111206Sken * drive returns multiple block descriptors, or if it sets 3271111206Sken * the data length incorrectly. 3272111206Sken */ 3273111206Sken if (data_len > data->alloc_len) { 3274111206Sken xpt_print_path(periph->path); 3275111206Sken printf("allocated modepage %d length %d < returned " 3276111206Sken "length %d\n", page, data->alloc_len, data_len); 3277111206Sken 3278111206Sken error = ENOSPC; 3279111206Sken } 3280111206Sken } 3281111206Sken return (error); 328239213Sgibbs} 328339213Sgibbs 3284111206Sken/* 3285111206Sken * All MODE_SELECT requests in the cd(4) driver MUST go through this 3286111206Sken * routine. See comments in cd6byteworkaround() for details. 3287111206Sken */ 328839213Sgibbsstatic int 3289111206Skencdsetmode(struct cam_periph *periph, struct cd_mode_params *data) 329039213Sgibbs{ 3291111206Sken struct ccb_scsiio *csio; 3292111206Sken struct cd_softc *softc; 329339213Sgibbs union ccb *ccb; 3294111206Sken int cdb_size, param_len; 329539213Sgibbs int error; 329639213Sgibbs 3297111206Sken softc = (struct cd_softc *)periph->softc; 3298111206Sken 329939213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 330039213Sgibbs 330139213Sgibbs csio = &ccb->csio; 330239213Sgibbs 330339213Sgibbs error = 0; 330439213Sgibbs 330539213Sgibbs /* 3306111206Sken * If the data is formatted for the 10 byte version of the mode 3307111206Sken * select parameter list, we need to use the 10 byte CDB. 3308111206Sken * Otherwise, we use whatever the stored minimum command size. 330939213Sgibbs */ 3310111206Sken if (data->cdb_size == 10) 3311111206Sken cdb_size = data->cdb_size; 3312111206Sken else 3313111206Sken cdb_size = softc->minimum_command_size; 331439213Sgibbs 3315111206Sken if (cdb_size >= 10) { 3316111206Sken struct scsi_mode_header_10 *mode_header; 3317111206Sken u_int32_t data_len; 3318111206Sken 3319111206Sken mode_header = (struct scsi_mode_header_10 *)data->mode_buf; 3320111206Sken 3321111206Sken data_len = scsi_2btoul(mode_header->data_length); 3322111206Sken 3323111206Sken scsi_ulto2b(0, mode_header->data_length); 3324111206Sken /* 3325111206Sken * SONY drives do not allow a mode select with a medium_type 3326111206Sken * value that has just been returned by a mode sense; use a 3327111206Sken * medium_type of 0 (Default) instead. 3328111206Sken */ 3329111206Sken mode_header->medium_type = 0; 3330111206Sken 3331111206Sken /* 3332111206Sken * Pass back whatever the drive passed to us, plus the size 3333111206Sken * of the data length field. 3334111206Sken */ 3335111206Sken param_len = data_len + sizeof(mode_header->data_length); 3336111206Sken 3337111206Sken } else { 3338111206Sken struct scsi_mode_header_6 *mode_header; 3339111206Sken 3340111206Sken mode_header = (struct scsi_mode_header_6 *)data->mode_buf; 3341111206Sken 3342111206Sken param_len = mode_header->data_length + 1; 3343111206Sken 3344111206Sken mode_header->data_length = 0; 3345111206Sken /* 3346111206Sken * SONY drives do not allow a mode select with a medium_type 3347111206Sken * value that has just been returned by a mode sense; use a 3348111206Sken * medium_type of 0 (Default) instead. 3349111206Sken */ 3350111206Sken mode_header->medium_type = 0; 3351111206Sken } 3352111206Sken 3353111206Sken /* Don't say we've got more room than we actually allocated */ 3354111206Sken param_len = min(param_len, data->alloc_len); 3355111206Sken 3356111206Sken scsi_mode_select_len(csio, 3357111206Sken /* retries */ 1, 3358111206Sken /* cbfcnp */ cddone, 3359111206Sken /* tag_action */ MSG_SIMPLE_Q_TAG, 3360111206Sken /* scsi_page_fmt */ 1, 3361111206Sken /* save_pages */ 0, 3362111206Sken /* param_buf */ data->mode_buf, 3363111206Sken /* param_len */ param_len, 3364111206Sken /* minimum_cmd_size */ cdb_size, 3365111206Sken /* sense_len */ SSD_FULL_SIZE, 3366111206Sken /* timeout */ 50000); 3367111206Sken 3368111206Sken /* See comments in cdgetmode() and cd6byteworkaround(). */ 3369111206Sken STAILQ_INSERT_TAIL(&softc->mode_queue, data, links); 3370111206Sken 337174840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 337274840Sken /*sense_flags*/SF_RETRY_UA); 337339213Sgibbs 337439213Sgibbs xpt_release_ccb(ccb); 337539213Sgibbs 3376111206Sken STAILQ_REMOVE(&softc->mode_queue, data, cd_mode_params, links); 3377111206Sken 3378111206Sken return (error); 337939213Sgibbs} 338039213Sgibbs 338139213Sgibbs 338239213Sgibbsstatic int 338339213Sgibbscdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len) 338439213Sgibbs{ 338539531Sken struct ccb_scsiio *csio; 338639213Sgibbs union ccb *ccb; 338739213Sgibbs int error; 338839531Sken u_int8_t cdb_len; 338939213Sgibbs 339039213Sgibbs error = 0; 339139213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 339239213Sgibbs csio = &ccb->csio; 339339531Sken /* 339439531Sken * Use the smallest possible command to perform the operation. 339539531Sken */ 339639531Sken if ((len & 0xffff0000) == 0) { 339739531Sken /* 339839531Sken * We can fit in a 10 byte cdb. 339939531Sken */ 340039531Sken struct scsi_play_10 *scsi_cmd; 340139213Sgibbs 340239531Sken scsi_cmd = (struct scsi_play_10 *)&csio->cdb_io.cdb_bytes; 340339531Sken bzero (scsi_cmd, sizeof(*scsi_cmd)); 340439531Sken scsi_cmd->op_code = PLAY_10; 340539531Sken scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); 340639531Sken scsi_ulto2b(len, (u_int8_t *)scsi_cmd->xfer_len); 340739531Sken cdb_len = sizeof(*scsi_cmd); 340839531Sken } else { 340939531Sken struct scsi_play_12 *scsi_cmd; 341039213Sgibbs 341139531Sken scsi_cmd = (struct scsi_play_12 *)&csio->cdb_io.cdb_bytes; 341239531Sken bzero (scsi_cmd, sizeof(*scsi_cmd)); 341339531Sken scsi_cmd->op_code = PLAY_12; 341439531Sken scsi_ulto4b(blk, (u_int8_t *)scsi_cmd->blk_addr); 341539531Sken scsi_ulto4b(len, (u_int8_t *)scsi_cmd->xfer_len); 341639531Sken cdb_len = sizeof(*scsi_cmd); 341739531Sken } 341839531Sken cam_fill_csio(csio, 341939531Sken /*retries*/2, 342039531Sken cddone, 342139531Sken /*flags*/CAM_DIR_NONE, 342239531Sken MSG_SIMPLE_Q_TAG, 342339531Sken /*dataptr*/NULL, 342439531Sken /*datalen*/0, 342539531Sken /*sense_len*/SSD_FULL_SIZE, 342639531Sken cdb_len, 342739531Sken /*timeout*/50 * 1000); 342839213Sgibbs 342974840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 343074840Sken /*sense_flags*/SF_RETRY_UA); 343139213Sgibbs 343239213Sgibbs xpt_release_ccb(ccb); 343339213Sgibbs 343439213Sgibbs return(error); 343539213Sgibbs} 343639213Sgibbs 343739213Sgibbsstatic int 343839213Sgibbscdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, 343939213Sgibbs u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf) 344039213Sgibbs{ 344139213Sgibbs struct scsi_play_msf *scsi_cmd; 344239213Sgibbs struct ccb_scsiio *csio; 344339213Sgibbs union ccb *ccb; 344439213Sgibbs int error; 344539213Sgibbs 344639213Sgibbs error = 0; 344739213Sgibbs 344839213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 344939213Sgibbs 345039213Sgibbs csio = &ccb->csio; 345139213Sgibbs 345239213Sgibbs cam_fill_csio(csio, 345339213Sgibbs /* retries */ 1, 345439213Sgibbs /* cbfcnp */ cddone, 345539531Sken /* flags */ CAM_DIR_NONE, 345639213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 345739213Sgibbs /* data_ptr */ NULL, 345839213Sgibbs /* dxfer_len */ 0, 345939213Sgibbs /* sense_len */ SSD_FULL_SIZE, 346039213Sgibbs sizeof(struct scsi_play_msf), 346139213Sgibbs /* timeout */ 50000); 346239213Sgibbs 346339213Sgibbs scsi_cmd = (struct scsi_play_msf *)&csio->cdb_io.cdb_bytes; 346439213Sgibbs bzero (scsi_cmd, sizeof(*scsi_cmd)); 346539213Sgibbs 346639213Sgibbs scsi_cmd->op_code = PLAY_MSF; 346739213Sgibbs scsi_cmd->start_m = startm; 346839213Sgibbs scsi_cmd->start_s = starts; 346939213Sgibbs scsi_cmd->start_f = startf; 347039213Sgibbs scsi_cmd->end_m = endm; 347139213Sgibbs scsi_cmd->end_s = ends; 347239213Sgibbs scsi_cmd->end_f = endf; 347339213Sgibbs 347474840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 347574840Sken /*sense_flags*/SF_RETRY_UA); 347639213Sgibbs 347739213Sgibbs xpt_release_ccb(ccb); 347839213Sgibbs 347939213Sgibbs return(error); 348039213Sgibbs} 348139213Sgibbs 348239213Sgibbs 348339213Sgibbsstatic int 348439213Sgibbscdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, 348539213Sgibbs u_int32_t etrack, u_int32_t eindex) 348639213Sgibbs{ 348739213Sgibbs struct scsi_play_track *scsi_cmd; 348839213Sgibbs struct ccb_scsiio *csio; 348939213Sgibbs union ccb *ccb; 349039213Sgibbs int error; 349139213Sgibbs 349239213Sgibbs error = 0; 349339213Sgibbs 349439213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 349539213Sgibbs 349639213Sgibbs csio = &ccb->csio; 349739213Sgibbs 349839213Sgibbs cam_fill_csio(csio, 349939213Sgibbs /* retries */ 1, 350039213Sgibbs /* cbfcnp */ cddone, 350139531Sken /* flags */ CAM_DIR_NONE, 350239213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 350339213Sgibbs /* data_ptr */ NULL, 350439213Sgibbs /* dxfer_len */ 0, 350539213Sgibbs /* sense_len */ SSD_FULL_SIZE, 350639213Sgibbs sizeof(struct scsi_play_track), 350739213Sgibbs /* timeout */ 50000); 350839213Sgibbs 350939213Sgibbs scsi_cmd = (struct scsi_play_track *)&csio->cdb_io.cdb_bytes; 351039213Sgibbs bzero (scsi_cmd, sizeof(*scsi_cmd)); 351139213Sgibbs 351239213Sgibbs scsi_cmd->op_code = PLAY_TRACK; 351339213Sgibbs scsi_cmd->start_track = strack; 351439213Sgibbs scsi_cmd->start_index = sindex; 351539213Sgibbs scsi_cmd->end_track = etrack; 351639213Sgibbs scsi_cmd->end_index = eindex; 351739213Sgibbs 351874840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 351974840Sken /*sense_flags*/SF_RETRY_UA); 352039213Sgibbs 352139213Sgibbs xpt_release_ccb(ccb); 352239213Sgibbs 352339213Sgibbs return(error); 352439213Sgibbs} 352539213Sgibbs 352639213Sgibbsstatic int 352739213Sgibbscdpause(struct cam_periph *periph, u_int32_t go) 352839213Sgibbs{ 352939213Sgibbs struct scsi_pause *scsi_cmd; 353039213Sgibbs struct ccb_scsiio *csio; 353139213Sgibbs union ccb *ccb; 353239213Sgibbs int error; 353339213Sgibbs 353439213Sgibbs error = 0; 353539213Sgibbs 353639213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 353739213Sgibbs 353839213Sgibbs csio = &ccb->csio; 353939213Sgibbs 354039213Sgibbs cam_fill_csio(csio, 354139213Sgibbs /* retries */ 1, 354239213Sgibbs /* cbfcnp */ cddone, 354339531Sken /* flags */ CAM_DIR_NONE, 354439213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 354539213Sgibbs /* data_ptr */ NULL, 354639213Sgibbs /* dxfer_len */ 0, 354739213Sgibbs /* sense_len */ SSD_FULL_SIZE, 354839213Sgibbs sizeof(struct scsi_pause), 354939213Sgibbs /* timeout */ 50000); 355039213Sgibbs 355139213Sgibbs scsi_cmd = (struct scsi_pause *)&csio->cdb_io.cdb_bytes; 355239213Sgibbs bzero (scsi_cmd, sizeof(*scsi_cmd)); 355339213Sgibbs 355439213Sgibbs scsi_cmd->op_code = PAUSE; 355539213Sgibbs scsi_cmd->resume = go; 355639213Sgibbs 355774840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 355874840Sken /*sense_flags*/SF_RETRY_UA); 355939213Sgibbs 356039213Sgibbs xpt_release_ccb(ccb); 356139213Sgibbs 356239213Sgibbs return(error); 356339213Sgibbs} 356439213Sgibbs 356539213Sgibbsstatic int 3566111206Skencdstartunit(struct cam_periph *periph, int load) 356739213Sgibbs{ 356839213Sgibbs union ccb *ccb; 356939213Sgibbs int error; 357039213Sgibbs 357139213Sgibbs error = 0; 357239213Sgibbs 357339213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 357439213Sgibbs 357539213Sgibbs scsi_start_stop(&ccb->csio, 357639213Sgibbs /* retries */ 1, 357739213Sgibbs /* cbfcnp */ cddone, 357839213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 357939213Sgibbs /* start */ TRUE, 3580111206Sken /* load_eject */ load, 358139213Sgibbs /* immediate */ FALSE, 358239213Sgibbs /* sense_len */ SSD_FULL_SIZE, 358339213Sgibbs /* timeout */ 50000); 358439213Sgibbs 358574840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 358674840Sken /*sense_flags*/SF_RETRY_UA); 358739213Sgibbs 358839213Sgibbs xpt_release_ccb(ccb); 358939213Sgibbs 359039213Sgibbs return(error); 359139213Sgibbs} 359239213Sgibbs 359339213Sgibbsstatic int 359439213Sgibbscdstopunit(struct cam_periph *periph, u_int32_t eject) 359539213Sgibbs{ 359639213Sgibbs union ccb *ccb; 359739213Sgibbs int error; 359839213Sgibbs 359939213Sgibbs error = 0; 360039213Sgibbs 360139213Sgibbs ccb = cdgetccb(periph, /* priority */ 1); 360239213Sgibbs 360339213Sgibbs scsi_start_stop(&ccb->csio, 360439213Sgibbs /* retries */ 1, 360539213Sgibbs /* cbfcnp */ cddone, 360639213Sgibbs /* tag_action */ MSG_SIMPLE_Q_TAG, 360739213Sgibbs /* start */ FALSE, 360839213Sgibbs /* load_eject */ eject, 360939213Sgibbs /* immediate */ FALSE, 361039213Sgibbs /* sense_len */ SSD_FULL_SIZE, 361139213Sgibbs /* timeout */ 50000); 361239213Sgibbs 361374840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 361474840Sken /*sense_flags*/SF_RETRY_UA); 361539213Sgibbs 361639213Sgibbs xpt_release_ccb(ccb); 361739213Sgibbs 361839213Sgibbs return(error); 361939213Sgibbs} 362060422Sken 362160422Skenstatic int 3622105421Snjlcdsetspeed(struct cam_periph *periph, u_int32_t rdspeed, u_int32_t wrspeed) 3623105421Snjl{ 3624105421Snjl struct scsi_set_speed *scsi_cmd; 3625105421Snjl struct ccb_scsiio *csio; 3626105421Snjl union ccb *ccb; 3627105421Snjl int error; 3628105421Snjl 3629105421Snjl error = 0; 3630105421Snjl ccb = cdgetccb(periph, /* priority */ 1); 3631105421Snjl csio = &ccb->csio; 3632105421Snjl 3633107193Snjl /* Preserve old behavior: units in multiples of CDROM speed */ 3634107193Snjl if (rdspeed < 177) 3635107193Snjl rdspeed *= 177; 3636107193Snjl if (wrspeed < 177) 3637107193Snjl wrspeed *= 177; 3638107193Snjl 3639105421Snjl cam_fill_csio(csio, 3640105421Snjl /* retries */ 1, 3641105421Snjl /* cbfcnp */ cddone, 3642105421Snjl /* flags */ CAM_DIR_NONE, 3643105421Snjl /* tag_action */ MSG_SIMPLE_Q_TAG, 3644105421Snjl /* data_ptr */ NULL, 3645105421Snjl /* dxfer_len */ 0, 3646105421Snjl /* sense_len */ SSD_FULL_SIZE, 3647105421Snjl sizeof(struct scsi_set_speed), 3648105421Snjl /* timeout */ 50000); 3649105421Snjl 3650105421Snjl scsi_cmd = (struct scsi_set_speed *)&csio->cdb_io.cdb_bytes; 3651105421Snjl bzero(scsi_cmd, sizeof(*scsi_cmd)); 3652105421Snjl 3653105421Snjl scsi_cmd->opcode = SET_CD_SPEED; 3654105421Snjl scsi_ulto2b(rdspeed, scsi_cmd->readspeed); 3655105421Snjl scsi_ulto2b(wrspeed, scsi_cmd->writespeed); 3656105421Snjl 3657105421Snjl error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 3658105421Snjl /*sense_flags*/SF_RETRY_UA); 3659105421Snjl 3660105421Snjl xpt_release_ccb(ccb); 3661105421Snjl 3662105421Snjl return(error); 3663105421Snjl} 3664105421Snjl 3665105421Snjlstatic int 366660422Skencdreportkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) 366760422Sken{ 366860422Sken union ccb *ccb; 366960422Sken u_int8_t *databuf; 367060422Sken u_int32_t lba; 367160422Sken int error; 367260422Sken int length; 367360422Sken 367460422Sken error = 0; 367560422Sken databuf = NULL; 367660422Sken lba = 0; 367760422Sken 367860422Sken ccb = cdgetccb(periph, /* priority */ 1); 367960422Sken 368060422Sken switch (authinfo->format) { 368160422Sken case DVD_REPORT_AGID: 368260422Sken length = sizeof(struct scsi_report_key_data_agid); 368360422Sken break; 368460422Sken case DVD_REPORT_CHALLENGE: 368560422Sken length = sizeof(struct scsi_report_key_data_challenge); 368660422Sken break; 368760422Sken case DVD_REPORT_KEY1: 368860422Sken length = sizeof(struct scsi_report_key_data_key1_key2); 368960422Sken break; 369060422Sken case DVD_REPORT_TITLE_KEY: 369160422Sken length = sizeof(struct scsi_report_key_data_title); 369260422Sken /* The lba field is only set for the title key */ 369360422Sken lba = authinfo->lba; 369460422Sken break; 369560422Sken case DVD_REPORT_ASF: 369660422Sken length = sizeof(struct scsi_report_key_data_asf); 369760422Sken break; 369860422Sken case DVD_REPORT_RPC: 369960422Sken length = sizeof(struct scsi_report_key_data_rpc); 370060422Sken break; 370160422Sken case DVD_INVALIDATE_AGID: 370260422Sken length = 0; 370360422Sken break; 370460422Sken default: 370560422Sken error = EINVAL; 370660422Sken goto bailout; 370760422Sken break; /* NOTREACHED */ 370860422Sken } 370960422Sken 371060422Sken if (length != 0) { 3711111119Simp databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); 371260422Sken } else 371360422Sken databuf = NULL; 371460422Sken 371560422Sken 371660422Sken scsi_report_key(&ccb->csio, 371760422Sken /* retries */ 1, 371860422Sken /* cbfcnp */ cddone, 371960422Sken /* tag_action */ MSG_SIMPLE_Q_TAG, 372060422Sken /* lba */ lba, 372160422Sken /* agid */ authinfo->agid, 372260422Sken /* key_format */ authinfo->format, 372360422Sken /* data_ptr */ databuf, 372460422Sken /* dxfer_len */ length, 372560422Sken /* sense_len */ SSD_FULL_SIZE, 372660422Sken /* timeout */ 50000); 372760422Sken 372874840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 372974840Sken /*sense_flags*/SF_RETRY_UA); 373060422Sken 373160422Sken if (error != 0) 373260422Sken goto bailout; 373360422Sken 373460422Sken if (ccb->csio.resid != 0) { 373560422Sken xpt_print_path(periph->path); 373660422Sken printf("warning, residual for report key command is %d\n", 373760422Sken ccb->csio.resid); 373860422Sken } 373960422Sken 374060422Sken switch(authinfo->format) { 374160422Sken case DVD_REPORT_AGID: { 374260422Sken struct scsi_report_key_data_agid *agid_data; 374360422Sken 374460422Sken agid_data = (struct scsi_report_key_data_agid *)databuf; 374560422Sken 374660422Sken authinfo->agid = (agid_data->agid & RKD_AGID_MASK) >> 374760422Sken RKD_AGID_SHIFT; 374860422Sken break; 374960422Sken } 375060422Sken case DVD_REPORT_CHALLENGE: { 375160422Sken struct scsi_report_key_data_challenge *chal_data; 375260422Sken 375360422Sken chal_data = (struct scsi_report_key_data_challenge *)databuf; 375460422Sken 375560422Sken bcopy(chal_data->challenge_key, authinfo->keychal, 375660422Sken min(sizeof(chal_data->challenge_key), 375760422Sken sizeof(authinfo->keychal))); 375860422Sken break; 375960422Sken } 376060422Sken case DVD_REPORT_KEY1: { 376160422Sken struct scsi_report_key_data_key1_key2 *key1_data; 376260422Sken 376360422Sken key1_data = (struct scsi_report_key_data_key1_key2 *)databuf; 376460422Sken 376560422Sken bcopy(key1_data->key1, authinfo->keychal, 376660422Sken min(sizeof(key1_data->key1), sizeof(authinfo->keychal))); 376760422Sken break; 376860422Sken } 376960422Sken case DVD_REPORT_TITLE_KEY: { 377060422Sken struct scsi_report_key_data_title *title_data; 377160422Sken 377260422Sken title_data = (struct scsi_report_key_data_title *)databuf; 377360422Sken 377460422Sken authinfo->cpm = (title_data->byte0 & RKD_TITLE_CPM) >> 377560422Sken RKD_TITLE_CPM_SHIFT; 377660422Sken authinfo->cp_sec = (title_data->byte0 & RKD_TITLE_CP_SEC) >> 377760422Sken RKD_TITLE_CP_SEC_SHIFT; 377860422Sken authinfo->cgms = (title_data->byte0 & RKD_TITLE_CMGS_MASK) >> 377960422Sken RKD_TITLE_CMGS_SHIFT; 378060422Sken bcopy(title_data->title_key, authinfo->keychal, 378160422Sken min(sizeof(title_data->title_key), 378260422Sken sizeof(authinfo->keychal))); 378360422Sken break; 378460422Sken } 378560422Sken case DVD_REPORT_ASF: { 378660422Sken struct scsi_report_key_data_asf *asf_data; 378760422Sken 378860422Sken asf_data = (struct scsi_report_key_data_asf *)databuf; 378960422Sken 379060422Sken authinfo->asf = asf_data->success & RKD_ASF_SUCCESS; 379160422Sken break; 379260422Sken } 379360422Sken case DVD_REPORT_RPC: { 379460422Sken struct scsi_report_key_data_rpc *rpc_data; 379560422Sken 379660422Sken rpc_data = (struct scsi_report_key_data_rpc *)databuf; 379760422Sken 379860422Sken authinfo->reg_type = (rpc_data->byte4 & RKD_RPC_TYPE_MASK) >> 379960422Sken RKD_RPC_TYPE_SHIFT; 380060422Sken authinfo->vend_rsts = 380160422Sken (rpc_data->byte4 & RKD_RPC_VENDOR_RESET_MASK) >> 380260422Sken RKD_RPC_VENDOR_RESET_SHIFT; 380360422Sken authinfo->user_rsts = rpc_data->byte4 & RKD_RPC_USER_RESET_MASK; 380471752Sken authinfo->region = rpc_data->region_mask; 380571752Sken authinfo->rpc_scheme = rpc_data->rpc_scheme1; 380660422Sken break; 380760422Sken } 380860422Sken case DVD_INVALIDATE_AGID: 380960422Sken break; 381060422Sken default: 381160422Sken /* This should be impossible, since we checked above */ 381260422Sken error = EINVAL; 381360422Sken goto bailout; 381460422Sken break; /* NOTREACHED */ 381560422Sken } 381660422Skenbailout: 381760422Sken if (databuf != NULL) 381860422Sken free(databuf, M_DEVBUF); 381960422Sken 382060422Sken xpt_release_ccb(ccb); 382160422Sken 382260422Sken return(error); 382360422Sken} 382460422Sken 382560422Skenstatic int 382660422Skencdsendkey(struct cam_periph *periph, struct dvd_authinfo *authinfo) 382760422Sken{ 382860422Sken union ccb *ccb; 382960422Sken u_int8_t *databuf; 383060422Sken int length; 383160422Sken int error; 383260422Sken 383360422Sken error = 0; 383460422Sken databuf = NULL; 383560422Sken 383660422Sken ccb = cdgetccb(periph, /* priority */ 1); 383760422Sken 383860422Sken switch(authinfo->format) { 383960422Sken case DVD_SEND_CHALLENGE: { 384060422Sken struct scsi_report_key_data_challenge *challenge_data; 384160422Sken 384260422Sken length = sizeof(*challenge_data); 384360422Sken 3844111119Simp challenge_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); 384560422Sken 384660422Sken databuf = (u_int8_t *)challenge_data; 384760422Sken 384860422Sken scsi_ulto2b(length - sizeof(challenge_data->data_len), 384960422Sken challenge_data->data_len); 385060422Sken 385160422Sken bcopy(authinfo->keychal, challenge_data->challenge_key, 385260422Sken min(sizeof(authinfo->keychal), 385360422Sken sizeof(challenge_data->challenge_key))); 385460422Sken break; 385560422Sken } 385660422Sken case DVD_SEND_KEY2: { 385760422Sken struct scsi_report_key_data_key1_key2 *key2_data; 385860422Sken 385960422Sken length = sizeof(*key2_data); 386060422Sken 3861111119Simp key2_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); 386260422Sken 386360422Sken databuf = (u_int8_t *)key2_data; 386460422Sken 386560422Sken scsi_ulto2b(length - sizeof(key2_data->data_len), 386660422Sken key2_data->data_len); 386760422Sken 386860422Sken bcopy(authinfo->keychal, key2_data->key1, 386960422Sken min(sizeof(authinfo->keychal), sizeof(key2_data->key1))); 387060422Sken 387160422Sken break; 387260422Sken } 387360422Sken case DVD_SEND_RPC: { 387460422Sken struct scsi_send_key_data_rpc *rpc_data; 387560422Sken 387660422Sken length = sizeof(*rpc_data); 387760422Sken 3878111119Simp rpc_data = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); 387960422Sken 388060422Sken databuf = (u_int8_t *)rpc_data; 388160422Sken 388260422Sken scsi_ulto2b(length - sizeof(rpc_data->data_len), 388360422Sken rpc_data->data_len); 388460422Sken 388560422Sken rpc_data->region_code = authinfo->region; 388660422Sken break; 388760422Sken } 388860422Sken default: 388960422Sken error = EINVAL; 389060422Sken goto bailout; 389160422Sken break; /* NOTREACHED */ 389260422Sken } 389360422Sken 389460422Sken scsi_send_key(&ccb->csio, 389560422Sken /* retries */ 1, 389660422Sken /* cbfcnp */ cddone, 389760422Sken /* tag_action */ MSG_SIMPLE_Q_TAG, 389860422Sken /* agid */ authinfo->agid, 389960422Sken /* key_format */ authinfo->format, 390060422Sken /* data_ptr */ databuf, 390160422Sken /* dxfer_len */ length, 390260422Sken /* sense_len */ SSD_FULL_SIZE, 390360422Sken /* timeout */ 50000); 390460422Sken 390574840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 390674840Sken /*sense_flags*/SF_RETRY_UA); 390760422Sken 390860422Skenbailout: 390960422Sken 391060422Sken if (databuf != NULL) 391160422Sken free(databuf, M_DEVBUF); 391260422Sken 391360422Sken xpt_release_ccb(ccb); 391460422Sken 391560422Sken return(error); 391660422Sken} 391760422Sken 391860422Skenstatic int 391960422Skencdreaddvdstructure(struct cam_periph *periph, struct dvd_struct *dvdstruct) 392060422Sken{ 392160422Sken union ccb *ccb; 392260422Sken u_int8_t *databuf; 392360422Sken u_int32_t address; 392460422Sken int error; 392560422Sken int length; 392660422Sken 392760422Sken error = 0; 392860422Sken databuf = NULL; 392960422Sken /* The address is reserved for many of the formats */ 393060422Sken address = 0; 393160422Sken 393260422Sken ccb = cdgetccb(periph, /* priority */ 1); 393360422Sken 393460422Sken switch(dvdstruct->format) { 393560422Sken case DVD_STRUCT_PHYSICAL: 393660422Sken length = sizeof(struct scsi_read_dvd_struct_data_physical); 393760422Sken break; 393860422Sken case DVD_STRUCT_COPYRIGHT: 393960422Sken length = sizeof(struct scsi_read_dvd_struct_data_copyright); 394060422Sken break; 394160422Sken case DVD_STRUCT_DISCKEY: 394260422Sken length = sizeof(struct scsi_read_dvd_struct_data_disc_key); 394360422Sken break; 394460422Sken case DVD_STRUCT_BCA: 394560422Sken length = sizeof(struct scsi_read_dvd_struct_data_bca); 394660422Sken break; 394760422Sken case DVD_STRUCT_MANUFACT: 394860422Sken length = sizeof(struct scsi_read_dvd_struct_data_manufacturer); 394960422Sken break; 395060422Sken case DVD_STRUCT_CMI: 395160422Sken error = ENODEV; 395260422Sken goto bailout; 395360422Sken#ifdef notyet 395460422Sken length = sizeof(struct scsi_read_dvd_struct_data_copy_manage); 395560422Sken address = dvdstruct->address; 395660422Sken#endif 395760422Sken break; /* NOTREACHED */ 395860422Sken case DVD_STRUCT_PROTDISCID: 395960422Sken length = sizeof(struct scsi_read_dvd_struct_data_prot_discid); 396060422Sken break; 396160422Sken case DVD_STRUCT_DISCKEYBLOCK: 396260422Sken length = sizeof(struct scsi_read_dvd_struct_data_disc_key_blk); 396360422Sken break; 396460422Sken case DVD_STRUCT_DDS: 396560422Sken length = sizeof(struct scsi_read_dvd_struct_data_dds); 396660422Sken break; 396760422Sken case DVD_STRUCT_MEDIUM_STAT: 396860422Sken length = sizeof(struct scsi_read_dvd_struct_data_medium_status); 396960422Sken break; 397060422Sken case DVD_STRUCT_SPARE_AREA: 397160422Sken length = sizeof(struct scsi_read_dvd_struct_data_spare_area); 397260422Sken break; 397360422Sken case DVD_STRUCT_RMD_LAST: 397460422Sken error = ENODEV; 397560422Sken goto bailout; 397660422Sken#ifdef notyet 397760422Sken length = sizeof(struct scsi_read_dvd_struct_data_rmd_borderout); 397860422Sken address = dvdstruct->address; 397960422Sken#endif 398060422Sken break; /* NOTREACHED */ 398160422Sken case DVD_STRUCT_RMD_RMA: 398260422Sken error = ENODEV; 398360422Sken goto bailout; 398460422Sken#ifdef notyet 398560422Sken length = sizeof(struct scsi_read_dvd_struct_data_rmd); 398660422Sken address = dvdstruct->address; 398760422Sken#endif 398860422Sken break; /* NOTREACHED */ 398960422Sken case DVD_STRUCT_PRERECORDED: 399060422Sken length = sizeof(struct scsi_read_dvd_struct_data_leadin); 399160422Sken break; 399260422Sken case DVD_STRUCT_UNIQUEID: 399360422Sken length = sizeof(struct scsi_read_dvd_struct_data_disc_id); 399460422Sken break; 399560422Sken case DVD_STRUCT_DCB: 399660422Sken error = ENODEV; 399760422Sken goto bailout; 399860422Sken#ifdef notyet 399960422Sken length = sizeof(struct scsi_read_dvd_struct_data_dcb); 400060422Sken address = dvdstruct->address; 400160422Sken#endif 400260422Sken break; /* NOTREACHED */ 400360422Sken case DVD_STRUCT_LIST: 400460422Sken /* 400560422Sken * This is the maximum allocation length for the READ DVD 400660422Sken * STRUCTURE command. There's nothing in the MMC3 spec 400760422Sken * that indicates a limit in the amount of data that can 400860422Sken * be returned from this call, other than the limits 400960422Sken * imposed by the 2-byte length variables. 401060422Sken */ 401160422Sken length = 65535; 401260422Sken break; 401360422Sken default: 401460422Sken error = EINVAL; 401560422Sken goto bailout; 401660422Sken break; /* NOTREACHED */ 401760422Sken } 401860422Sken 401960422Sken if (length != 0) { 4020111119Simp databuf = malloc(length, M_DEVBUF, M_WAITOK | M_ZERO); 402160422Sken } else 402260422Sken databuf = NULL; 402360422Sken 402460422Sken scsi_read_dvd_structure(&ccb->csio, 402560422Sken /* retries */ 1, 402660422Sken /* cbfcnp */ cddone, 402760422Sken /* tag_action */ MSG_SIMPLE_Q_TAG, 402860422Sken /* lba */ address, 402960422Sken /* layer_number */ dvdstruct->layer_num, 403060422Sken /* key_format */ dvdstruct->format, 403160422Sken /* agid */ dvdstruct->agid, 403260422Sken /* data_ptr */ databuf, 403360422Sken /* dxfer_len */ length, 403460422Sken /* sense_len */ SSD_FULL_SIZE, 403560422Sken /* timeout */ 50000); 403660422Sken 403774840Sken error = cdrunccb(ccb, cderror, /*cam_flags*/CAM_RETRY_SELTO, 403874840Sken /*sense_flags*/SF_RETRY_UA); 403960422Sken 404060422Sken if (error != 0) 404160422Sken goto bailout; 404260422Sken 404360422Sken switch(dvdstruct->format) { 404460422Sken case DVD_STRUCT_PHYSICAL: { 404560422Sken struct scsi_read_dvd_struct_data_layer_desc *inlayer; 404660422Sken struct dvd_layer *outlayer; 404760422Sken struct scsi_read_dvd_struct_data_physical *phys_data; 404860422Sken 404960422Sken phys_data = 405060422Sken (struct scsi_read_dvd_struct_data_physical *)databuf; 405160422Sken inlayer = &phys_data->layer_desc; 405260422Sken outlayer = (struct dvd_layer *)&dvdstruct->data; 405360422Sken 405460422Sken dvdstruct->length = sizeof(*inlayer); 405560422Sken 405660422Sken outlayer->book_type = (inlayer->book_type_version & 405760422Sken RDSD_BOOK_TYPE_MASK) >> RDSD_BOOK_TYPE_SHIFT; 405860422Sken outlayer->book_version = (inlayer->book_type_version & 405960422Sken RDSD_BOOK_VERSION_MASK); 406060422Sken outlayer->disc_size = (inlayer->disc_size_max_rate & 406160422Sken RDSD_DISC_SIZE_MASK) >> RDSD_DISC_SIZE_SHIFT; 406260422Sken outlayer->max_rate = (inlayer->disc_size_max_rate & 406360422Sken RDSD_MAX_RATE_MASK); 406460422Sken outlayer->nlayers = (inlayer->layer_info & 406560422Sken RDSD_NUM_LAYERS_MASK) >> RDSD_NUM_LAYERS_SHIFT; 406660422Sken outlayer->track_path = (inlayer->layer_info & 406760422Sken RDSD_TRACK_PATH_MASK) >> RDSD_TRACK_PATH_SHIFT; 406860422Sken outlayer->layer_type = (inlayer->layer_info & 406960422Sken RDSD_LAYER_TYPE_MASK); 407060422Sken outlayer->linear_density = (inlayer->density & 407160422Sken RDSD_LIN_DENSITY_MASK) >> RDSD_LIN_DENSITY_SHIFT; 407260422Sken outlayer->track_density = (inlayer->density & 407360422Sken RDSD_TRACK_DENSITY_MASK); 407460422Sken outlayer->bca = (inlayer->bca & RDSD_BCA_MASK) >> 407560422Sken RDSD_BCA_SHIFT; 407660422Sken outlayer->start_sector = scsi_3btoul(inlayer->main_data_start); 407760422Sken outlayer->end_sector = scsi_3btoul(inlayer->main_data_end); 407860422Sken outlayer->end_sector_l0 = 407960422Sken scsi_3btoul(inlayer->end_sector_layer0); 408060422Sken break; 408160422Sken } 408260422Sken case DVD_STRUCT_COPYRIGHT: { 408360422Sken struct scsi_read_dvd_struct_data_copyright *copy_data; 408460422Sken 408560422Sken copy_data = (struct scsi_read_dvd_struct_data_copyright *) 408660422Sken databuf; 408760422Sken 408860422Sken dvdstruct->cpst = copy_data->cps_type; 408960422Sken dvdstruct->rmi = copy_data->region_info; 409060422Sken dvdstruct->length = 0; 409160422Sken 409260422Sken break; 409360422Sken } 409460422Sken default: 409560422Sken /* 409660422Sken * Tell the user what the overall length is, no matter 409760422Sken * what we can actually fit in the data buffer. 409860422Sken */ 409960422Sken dvdstruct->length = length - ccb->csio.resid - 410060422Sken sizeof(struct scsi_read_dvd_struct_data_header); 410160422Sken 410260422Sken /* 410360422Sken * But only actually copy out the smaller of what we read 410460422Sken * in or what the structure can take. 410560422Sken */ 410660422Sken bcopy(databuf + sizeof(struct scsi_read_dvd_struct_data_header), 410760422Sken dvdstruct->data, 410860422Sken min(sizeof(dvdstruct->data), dvdstruct->length)); 410960422Sken break; 411060422Sken } 411160422Skenbailout: 411260422Sken 411360422Sken if (databuf != NULL) 411460422Sken free(databuf, M_DEVBUF); 411560422Sken 411660422Sken xpt_release_ccb(ccb); 411760422Sken 411860422Sken return(error); 411960422Sken} 412060422Sken 412160422Skenvoid 412260422Skenscsi_report_key(struct ccb_scsiio *csio, u_int32_t retries, 412360422Sken void (*cbfcnp)(struct cam_periph *, union ccb *), 412460422Sken u_int8_t tag_action, u_int32_t lba, u_int8_t agid, 412560422Sken u_int8_t key_format, u_int8_t *data_ptr, u_int32_t dxfer_len, 412660422Sken u_int8_t sense_len, u_int32_t timeout) 412760422Sken{ 412860422Sken struct scsi_report_key *scsi_cmd; 412960422Sken 413060422Sken scsi_cmd = (struct scsi_report_key *)&csio->cdb_io.cdb_bytes; 413160422Sken bzero(scsi_cmd, sizeof(*scsi_cmd)); 413260422Sken scsi_cmd->opcode = REPORT_KEY; 413360422Sken scsi_ulto4b(lba, scsi_cmd->lba); 413460422Sken scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); 413560422Sken scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | 413660422Sken (key_format & RK_KF_KEYFORMAT_MASK); 413760422Sken 413860422Sken cam_fill_csio(csio, 413960422Sken retries, 414060422Sken cbfcnp, 414160422Sken /*flags*/ (dxfer_len == 0) ? CAM_DIR_NONE : CAM_DIR_IN, 414260422Sken tag_action, 414360422Sken /*data_ptr*/ data_ptr, 414460422Sken /*dxfer_len*/ dxfer_len, 414560422Sken sense_len, 414660422Sken sizeof(*scsi_cmd), 414760422Sken timeout); 414860422Sken} 414960422Sken 415060422Skenvoid 415160422Skenscsi_send_key(struct ccb_scsiio *csio, u_int32_t retries, 415260422Sken void (*cbfcnp)(struct cam_periph *, union ccb *), 415360422Sken u_int8_t tag_action, u_int8_t agid, u_int8_t key_format, 415460422Sken u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, 415560422Sken u_int32_t timeout) 415660422Sken{ 415760422Sken struct scsi_send_key *scsi_cmd; 415860422Sken 415960422Sken scsi_cmd = (struct scsi_send_key *)&csio->cdb_io.cdb_bytes; 416060422Sken bzero(scsi_cmd, sizeof(*scsi_cmd)); 416160422Sken scsi_cmd->opcode = SEND_KEY; 416260422Sken 416360422Sken scsi_ulto2b(dxfer_len, scsi_cmd->param_len); 416460422Sken scsi_cmd->agid_keyformat = (agid << RK_KF_AGID_SHIFT) | 416560422Sken (key_format & RK_KF_KEYFORMAT_MASK); 416660422Sken 416760422Sken cam_fill_csio(csio, 416860422Sken retries, 416960422Sken cbfcnp, 417060422Sken /*flags*/ CAM_DIR_OUT, 417160422Sken tag_action, 417260422Sken /*data_ptr*/ data_ptr, 417360422Sken /*dxfer_len*/ dxfer_len, 417460422Sken sense_len, 417560422Sken sizeof(*scsi_cmd), 417660422Sken timeout); 417760422Sken} 417860422Sken 417960422Sken 418060422Skenvoid 418160422Skenscsi_read_dvd_structure(struct ccb_scsiio *csio, u_int32_t retries, 418260422Sken void (*cbfcnp)(struct cam_periph *, union ccb *), 418360422Sken u_int8_t tag_action, u_int32_t address, 418460422Sken u_int8_t layer_number, u_int8_t format, u_int8_t agid, 418560422Sken u_int8_t *data_ptr, u_int32_t dxfer_len, 418660422Sken u_int8_t sense_len, u_int32_t timeout) 418760422Sken{ 418860422Sken struct scsi_read_dvd_structure *scsi_cmd; 418960422Sken 419060422Sken scsi_cmd = (struct scsi_read_dvd_structure *)&csio->cdb_io.cdb_bytes; 419160422Sken bzero(scsi_cmd, sizeof(*scsi_cmd)); 419260422Sken scsi_cmd->opcode = READ_DVD_STRUCTURE; 419360422Sken 419460422Sken scsi_ulto4b(address, scsi_cmd->address); 419560422Sken scsi_cmd->layer_number = layer_number; 419660422Sken scsi_cmd->format = format; 419760422Sken scsi_ulto2b(dxfer_len, scsi_cmd->alloc_len); 419860422Sken /* The AGID is the top two bits of this byte */ 419960422Sken scsi_cmd->agid = agid << 6; 420060422Sken 420160422Sken cam_fill_csio(csio, 420260422Sken retries, 420360422Sken cbfcnp, 420460422Sken /*flags*/ CAM_DIR_IN, 420560422Sken tag_action, 420660422Sken /*data_ptr*/ data_ptr, 420760422Sken /*dxfer_len*/ dxfer_len, 420860422Sken sense_len, 420960422Sken sizeof(*scsi_cmd), 421060422Sken timeout); 421160422Sken} 4212