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 + -
显示快捷键?