scsi_cd.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,447 行 · 第 1/5 页
C
2,447 行
/* * Copyright (c) 1997 Justin T. Gibbs. * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: scsi_cd.c,v 1.14.2.5 1999/05/09 01:27:34 ken Exp $ *//* * Portions of this driver taken from the original FreeBSD cd driver. * Written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems for use under the MACH(2.5) operating system. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * * from: cd.c,v 1.83 1997/05/04 15:24:22 joerg Exp $ */#include "opt_cd.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/buf.h>#include <sys/dkbad.h>#include <sys/disklabel.h>#include <sys/diskslice.h>#include <sys/malloc.h>#include <sys/conf.h>#include <sys/cdio.h>#include <sys/devicestat.h>#include <sys/sysctl.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_extend.h>#include <cam/cam_periph.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_queue.h>#include <cam/scsi/scsi_message.h>#include <cam/scsi/scsi_da.h>#include <cam/scsi/scsi_cd.h>#define LEADOUT 0xaa /* leadout toc entry */struct cd_params { u_int32_t blksize; u_long disksize;};typedef enum { CD_Q_NONE = 0x00, CD_Q_NO_TOUCH = 0x01, CD_Q_BCD_TRACKS = 0x02, CD_Q_NO_CHANGER = 0x04, CD_Q_CHANGER = 0x08} cd_quirks;typedef enum { CD_FLAG_INVALID = 0x001, CD_FLAG_NEW_DISC = 0x002, CD_FLAG_DISC_LOCKED = 0x004, CD_FLAG_DISC_REMOVABLE = 0x008, CD_FLAG_TAGGED_QUEUING = 0x010, CD_FLAG_OPEN = 0x020, CD_FLAG_CHANGER = 0x040, CD_FLAG_ACTIVE = 0x080, CD_FLAG_SCHED_ON_COMP = 0x100, CD_FLAG_RETRY_UA = 0x200} cd_flags;typedef enum { CD_CCB_PROBE = 0x01, CD_CCB_BUFFER_IO = 0x02, CD_CCB_WAITING = 0x03, CD_CCB_TYPE_MASK = 0x0F, CD_CCB_RETRY_UA = 0x10} cd_ccb_state;typedef enum { CHANGER_TIMEOUT_SCHED = 0x01, CHANGER_SHORT_TMOUT_SCHED = 0x02, CHANGER_MANUAL_CALL = 0x04, CHANGER_NEED_TIMEOUT = 0x08} cd_changer_flags;#define ccb_state ppriv_field0#define ccb_bp ppriv_ptr1typedef enum { CD_STATE_PROBE, CD_STATE_NORMAL} cd_state;struct cd_softc { cam_pinfo pinfo; cd_state state; volatile cd_flags flags; struct buf_queue_head buf_queue; LIST_HEAD(, ccb_hdr) pending_ccbs; struct cd_params params; struct diskslices *cd_slices; union ccb saved_ccb; cd_quirks quirks; struct devstat device_stats; STAILQ_ENTRY(cd_softc) changer_links; struct cdchanger *changer; int bufs_left; struct cam_periph *periph;};struct cd_quirk_entry { struct scsi_inquiry_pattern inq_pat; cd_quirks quirks;};/* * These quirk entries aren't strictly necessary. Basically, what they do * is tell cdregister() up front that a device is a changer. Otherwise, it * will figure that fact out once it sees a LUN on the device that is * greater than 0. If it is known up front that a device is a changer, all * I/O to the device will go through the changer scheduling routines, as * opposed to the "normal" CD code. */static struct cd_quirk_entry cd_quirk_table[] ={ { { T_CDROM, SIP_MEDIA_REMOVABLE, "NRC", "MBR-7", "*"}, /*quirks*/ CD_Q_CHANGER }, { { T_CDROM, SIP_MEDIA_REMOVABLE, "PIONEER", "CD-ROM DRM-604X", "*"}, /* quirks */ CD_Q_CHANGER }, { { T_CDROM, SIP_MEDIA_REMOVABLE, "CHINON", "CD-ROM CDS-535","*"}, /* quirks */ CD_Q_BCD_TRACKS }};#ifndef MIN#define MIN(x,y) ((x<y) ? x : y)#endif#define CD_CDEV_MAJOR 15#define CD_BDEV_MAJOR 6static d_open_t cdopen;static d_read_t cdread;static d_close_t cdclose;static d_ioctl_t cdioctl;static d_strategy_t cdstrategy;static d_strategy_t cdstrategy1;static periph_init_t cdinit;static periph_ctor_t cdregister;static periph_dtor_t cdcleanup;static periph_start_t cdstart;static periph_oninv_t cdoninvalidate;static void cdasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);static void cdshorttimeout(void *arg);static void cdschedule(struct cam_periph *periph, int priority);static void cdrunchangerqueue(void *arg);static void cdchangerschedule(struct cd_softc *softc);static int cdrunccb(union ccb *ccb, int (*error_routine)(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags), u_int32_t cam_flags, u_int32_t sense_flags);static union ccb *cdgetccb(struct cam_periph *periph, u_int32_t priority);static void cddone(struct cam_periph *periph, union ccb *start_ccb);static int cderror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags);static void cdprevent(struct cam_periph *periph, int action);static int cdsize(dev_t dev, u_int32_t *size);static int cdreadtoc(struct cam_periph *periph, u_int32_t mode, u_int32_t start, struct cd_toc_entry *data, u_int32_t len);static int cdgetmode(struct cam_periph *periph, struct cd_mode_data *data, u_int32_t page);static int cdsetmode(struct cam_periph *periph, struct cd_mode_data *data);static int cdplay(struct cam_periph *periph, u_int32_t blk, u_int32_t len);static int cdreadsubchannel(struct cam_periph *periph, u_int32_t mode, u_int32_t format, int track, struct cd_sub_channel_info *data, u_int32_t len);static int cdplaymsf(struct cam_periph *periph, u_int32_t startm, u_int32_t starts, u_int32_t startf, u_int32_t endm, u_int32_t ends, u_int32_t endf);static int cdplaytracks(struct cam_periph *periph, u_int32_t strack, u_int32_t sindex, u_int32_t etrack, u_int32_t eindex);static int cdpause(struct cam_periph *periph, u_int32_t go);static int cdstopunit(struct cam_periph *periph, u_int32_t eject);static int cdstartunit(struct cam_periph *periph);static struct periph_driver cddriver ={ cdinit, "cd", TAILQ_HEAD_INITIALIZER(cddriver.units), /* generation */ 0};DATA_SET(periphdriver_set, cddriver);/* For 2.2-stable support */#ifndef D_DISK#define D_DISK 0#endifstatic struct cdevsw cd_cdevsw = { /*d_open*/ cdopen, /*d_close*/ cdclose, /*d_read*/ cdread, /*d_write*/ nowrite, /*d_ioctl*/ cdioctl, /*d_stop*/ nostop, /*d_reset*/ noreset, /*d_devtotty*/ nodevtotty, /*d_poll*/ seltrue, /*d_mmap*/ nommap, /*d_strategy*/ cdstrategy, /*d_name*/ "cd", /*d_spare*/ NULL, /*d_maj*/ -1, /*d_dump*/ nodump, /*d_psize*/ nopsize, /*d_flags*/ D_DISK, /*d_maxio*/ 0, /*b_maj*/ -1};static struct extend_array *cdperiphs;static int num_changers;#ifndef CHANGER_MIN_BUSY_SECONDS#define CHANGER_MIN_BUSY_SECONDS 5#endif#ifndef CHANGER_MAX_BUSY_SECONDS#define CHANGER_MAX_BUSY_SECONDS 15#endifstatic int changer_min_busy_seconds = CHANGER_MIN_BUSY_SECONDS;static int changer_max_busy_seconds = CHANGER_MAX_BUSY_SECONDS;/* * XXX KDM this CAM node should be moved if we ever get more CAM sysctl * variables. */SYSCTL_NODE(_kern, OID_AUTO, cam, CTLFLAG_RD, 0, "CAM Subsystem");SYSCTL_NODE(_kern_cam, OID_AUTO, cd, CTLFLAG_RD, 0, "CAM CDROM driver");SYSCTL_NODE(_kern_cam_cd, OID_AUTO, changer, CTLFLAG_RD, 0, "CD Changer");SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, min_busy_seconds, CTLFLAG_RW, &changer_min_busy_seconds, 0, "Minimum changer scheduling quantum");SYSCTL_INT(_kern_cam_cd_changer, OID_AUTO, max_busy_seconds, CTLFLAG_RW, &changer_max_busy_seconds, 0, "Maximum changer scheduling quantum");struct cdchanger { path_id_t path_id; target_id_t target_id; int num_devices; struct camq devq; struct timeval start_time; struct cd_softc *cur_device; struct callout_handle short_handle; struct callout_handle long_handle; volatile cd_changer_flags flags; STAILQ_ENTRY(cdchanger) changer_links; STAILQ_HEAD(chdevlist, cd_softc) chluns;};static STAILQ_HEAD(changerlist, cdchanger) changerq;voidcdinit(void){ cam_status status; struct cam_path *path; /* * Create our extend array for storing the devices we attach to. */ cdperiphs = cam_extend_new(); if (cdperiphs == NULL) { printf("cd: Failed to alloc extend array!\n"); return; } /* * Install a global async callback. This callback will * receive async callbacks like "new device found". */ status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); if (status == CAM_REQ_CMP) { struct ccb_setasync csa; xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_FOUND_DEVICE; csa.callback = cdasync; csa.callback_arg = NULL; xpt_action((union ccb *)&csa); status = csa.ccb_h.status; xpt_free_path(path); } if (status != CAM_REQ_CMP) { printf("cd: Failed to attach master async callback " "due to status 0x%x!\n", status); } else { /* If we were successfull, register our devsw */ cdevsw_add_generic(CD_BDEV_MAJOR, CD_CDEV_MAJOR, &cd_cdevsw); }}static voidcdoninvalidate(struct cam_periph *periph){ int s; struct cd_softc *softc; struct buf *q_bp; struct ccb_setasync csa; softc = (struct cd_softc *)periph->softc; /* * De-register any async callbacks. */ xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = 0; csa.callback = cdasync; csa.callback_arg = periph; xpt_action((union ccb *)&csa); softc->flags |= CD_FLAG_INVALID; /* * Although the oninvalidate() routines are always called at * splsoftcam, we need to be at splbio() here to keep the buffer * queue from being modified while we traverse it. */ s = splbio(); /* * Return all queued I/O with ENXIO. * XXX Handle any transactions queued to the card * with XPT_ABORT_CCB. */ while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){ bufq_remove(&softc->buf_queue, q_bp); q_bp->b_resid = q_bp->b_bcount; q_bp->b_error = ENXIO; q_bp->b_flags |= B_ERROR; biodone(q_bp); } splx(s); /* * If this device is part of a changer, and it was scheduled * to run, remove it from the run queue since we just nuked * all of its scheduled I/O. */ if ((softc->flags & CD_FLAG_CHANGER) && (softc->pinfo.index != CAM_UNQUEUED_INDEX)) camq_remove(&softc->changer->devq, softc->pinfo.index); xpt_print_path(periph->path); printf("lost device\n");}static voidcdcleanup(struct cam_periph *periph){ struct cd_softc *softc; int s; softc = (struct cd_softc *)periph->softc; xpt_print_path(periph->path); printf("removing device entry\n"); s = splsoftcam(); /* * In the queued, non-active case, the device in question * has already been removed from the changer run queue. Since this * device is active, we need to de-activate it, and schedule * another device to run. (if there is another one to run) */ if ((softc->flags & CD_FLAG_CHANGER) && (softc->flags & CD_FLAG_ACTIVE)) { /* * The purpose of the short timeout is soley to determine * whether the current device has finished or not. Well, * since we're removing the active device, we know that it * is finished. So, get rid of the short timeout. * Otherwise, if we're in the time period before the short * timeout fires, and there are no other devices in the * queue to run, there won't be any other device put in the * active slot. i.e., when we call cdrunchangerqueue() * below, it won't do anything. Then, when the short * timeout fires, it'll look at the "current device", which * we are free below, and possibly panic the kernel on a * bogus pointer reference. * * The long timeout doesn't really matter, since we * decrement the qfrozen_cnt to indicate that there is * nothing in the active slot now. Therefore, there won't * be any bogus pointer references there. */ if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { untimeout(cdshorttimeout, softc->changer, softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } softc->changer->devq.qfrozen_cnt--; softc->changer->flags |= CHANGER_MANUAL_CALL; cdrunchangerqueue(softc->changer); } /* * If we're removing the last device on the changer, go ahead and * remove the changer device structure. */ if ((softc->flags & CD_FLAG_CHANGER) && (--softc->changer->num_devices == 0)) { /* * Theoretically, there shouldn't be any timeouts left, but * I'm not completely sure that that will be the case. So, * it won't hurt to check and see if there are any left. */ if (softc->changer->flags & CHANGER_TIMEOUT_SCHED) { untimeout(cdrunchangerqueue, softc->changer, softc->changer->long_handle); softc->changer->flags &= ~CHANGER_TIMEOUT_SCHED; } if (softc->changer->flags & CHANGER_SHORT_TMOUT_SCHED) { untimeout(cdshorttimeout, softc->changer, softc->changer->short_handle); softc->changer->flags &= ~CHANGER_SHORT_TMOUT_SCHED; } STAILQ_REMOVE(&changerq, softc->changer, cdchanger, changer_links); xpt_print_path(periph->path); printf("removing changer entry\n"); free(softc->changer, M_DEVBUF);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?