⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi_subr.c

📁 早期freebsd实现
💻 C
字号:
/* * Copyright (c) 1992, 1993 *	The Regents of the University of California.  All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: *	This product includes software developed by the University of *	California, Lawrence Berkeley Laboratories. * * 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. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. * *	@(#)scsi_subr.c	8.1 (Berkeley) 6/16/93 * * from: $Header: scsi_subr.c,v 1.10 93/02/01 19:21:58 torek Exp $ (LBL) *//* * Generic SCSI host adapter driver. * Does almost nothing (most work is relegated to per-hba drivers). */#include <sys/param.h>#include <sys/buf.h>#include <sys/device.h>#include <dev/scsi/scsi.h>#include <dev/scsi/scsivar.h>/* * General subroutines, and scsi data. *//* table of lengths of scsi commands */const char scsicmdlen[8] = { 6, 10, 0, 0, 0, 12, 0, 0 };/* table of lengths of scsi messages */const signed char scsimsglen[0x24] = {	SMLEN_DONE,		/* MSG_CMD_COMPLETE */	SMLEN_EXTENDED,		/* MSG_EXT_MESSAGE */	1,			/* MSG_SAVE_DATA_PTR */	1,			/* MSG_RESTORE_PTR */	1,			/* MSG_DISCONNECT */	1,			/* MSG_INIT_DETECT_ERROR */	1,			/* MSG_ABORT */	1,			/* MSG_REJECT */	1,			/* MSG_NOOP */	1,			/* MSG_PARITY_ERROR */	1,			/* MSG_LCC */	1,			/* MSG_LCCF */	1,			/* MSG_BUS_DEVICE_RESET */	1,			/* MSG_ABORT_TAG */	1,			/* MSG_CLEAR_QUEUE */	1,			/* MSG_INITIATE_RECOVERY */	1,			/* MSG_RELEASE_RECOVERY */	1,			/* MSG_TERMINATE_PROCESS */	SMLEN_UNDEF,		/* 0x12 */	SMLEN_UNDEF,		/* 0x13 */	SMLEN_UNDEF,		/* 0x14 */	SMLEN_UNDEF,		/* 0x15 */	SMLEN_UNDEF,		/* 0x16 */	SMLEN_UNDEF,		/* 0x17 */	SMLEN_UNDEF,		/* 0x18 */	SMLEN_UNDEF,		/* 0x19 */	SMLEN_UNDEF,		/* 0x1a */	SMLEN_UNDEF,		/* 0x1b */	SMLEN_UNDEF,		/* 0x1c */	SMLEN_UNDEF,		/* 0x1d */	SMLEN_UNDEF,		/* 0x1e */	SMLEN_UNDEF,		/* 0x1f */	2,			/* MSG_SIMPLE_QTAG */	2,			/* MSG_HEAD_QTAG */	2,			/* MSG_ORDERED_QTAG */	2,			/* MSG_IGNORE_WIDE_RESID */};/* definition of `tg' target driver for autoconfig */static int scsi_targmatch __P((struct device *, struct cfdata *, void *));static void scsi_targattach __P((struct device *, struct device *, void *));struct cfdriver tgcd =    { NULL, "tg", scsi_targmatch, scsi_targattach,      DV_DULL, sizeof(struct targ) };void	scsi_targstart __P((struct device *, struct sq *, struct buf *,			scdgo_fn, struct device *));int	scsi_targgo __P((struct device *, int targ,			scintr_fn, struct device *, struct buf *, int));void	scsi_targintr __P((struct device *, int, int));void	scsi_targrel __P((struct device *));#define	NOBUF	((caddr_t)0)/* * Perform a TEST UNIT READY immediate (polled) command * on the given <target,unit> pair.  Return the status byte * returned, or -1 for none. */intscsi_test_unit_ready(hba, targ, unit)	struct hba_softc *hba;	int targ, unit;{	struct scsi_cdb cdb;	CDB6(&cdb)->cdb_cmd = CMD_TEST_UNIT_READY;	CDB6(&cdb)->cdb_lun_lbah = unit << 5;	*(short *)&CDB6(&cdb)->cdb_lbam = 0;	*(short *)&CDB6(&cdb)->cdb_len = 0;	return (hba->hba_driver->hd_icmd(hba, targ, &cdb, NOBUF, 0, 0));}/* * Request sense.  The sense is to be written into the given buffer. * The given length must be < 256. */intscsi_request_sense(hba, targ, unit, buf, len)	struct hba_softc *hba;	int targ, unit;	caddr_t buf;	int len;{	struct scsi_cdb cdb;	CDB6(&cdb)->cdb_cmd = CMD_REQUEST_SENSE;	CDB6(&cdb)->cdb_lun_lbah = unit << 5;	*(short *)&CDB6(&cdb)->cdb_lbam = 0;	CDB6(&cdb)->cdb_len = len;	CDB6(&cdb)->cdb_ctrl = 0;	return (hba->hba_driver->hd_icmd(hba, targ, &cdb, buf, len, B_READ));}/* * Called (indirectly, via config_found) from scsi_hbaattach. * Print target number, and if no device was configured there, * the hba as well. */intscsi_targprint(aux, hba)	void *aux;	char *hba;{	if (hba) {		printf("target %d on %s", *(int *)aux, hba);		return (UNCONF);	}	printf(" target %d", *(int *)aux);	return (QUIET);}/* * Print information about a unit found on some target. * If the unit was not configured, `targ' is the name of the target * on which the unit was found.  If it was, targ is NULL and we * let the unit's attach routine print the INQUIRE result if * appropriate. */static intscsi_unitprint(aux, targ)	void *aux;	char *targ;{	register struct scsi_attach_args *sa = aux;	if (targ) {		printf("unit %d at %s", sa->sa_unit, targ);		if ((sa->sa_inq_status & STS_MASK) == STS_GOOD) {			printf(" (");			scsi_printinq(&sa->sa_si);			printf(")");		}		return (UNCONF);	}	printf(" unit %d", sa->sa_unit);	return (QUIET);}/* * Generic target-match. */static intscsi_targmatch(parent, cf, aux)	struct device *parent;	register struct cfdata *cf;	void *aux;{	int targ = *(int *)aux;	return (cf->cf_loc[0] == targ || cf->cf_loc[0] == -1);}/* * And now, a generic `target attach' routine. * We assume that INQUIRY works. */static voidscsi_targattach(parent, self, aux)	struct device *parent, *self;	void *aux;{	register struct targ *t = (struct targ *)self;	register struct hba_softc *hba;	register struct hbadriver *hd;	register int targ, unit;	struct scsi_attach_args sa;	struct scsi_cdb si;	printf("\n");	t->t_targ = targ = *(int *)aux;	hba = (struct hba_softc *)parent;	hba->hba_targets[targ] = t;	/*	 * Probe each of the 8 units using the sequence	 *	TEST UNIT READY	 *	REQUEST SENSE	 *	INQUIRY	 * The first should not be necessary, but some SCSI devices	 * refuse to speak until it is done.  The second is only necessary	 * if the first returns a CHECK CONDITION status, but we do it	 * anyway.	 */	hd = hba->hba_driver;	sa.sa_targ = targ;	CDB6(&si)->cdb_cmd = CMD_INQUIRY;	*(short *)&CDB6(&si)->cdb_lbam = 0;	CDB6(&si)->cdb_len = sizeof sa.sa_si;	CDB6(&si)->cdb_ctrl = 0;	for (unit = 0; unit < 8; unit++) {		if (scsi_test_unit_ready(hba, targ, unit) == -1)			continue;		sa.sa_unit = unit;		sa.sa_req_status = scsi_request_sense(hba, targ, unit,		    (caddr_t)&sa.sa_sn, sizeof sa.sa_sn);		CDB6(&si)->cdb_lun_lbah = unit << 5;		sa.sa_inq_status = (*hd->hd_icmd)(hba, targ, &si,		    (caddr_t)&sa.sa_si, sizeof sa.sa_si, B_READ);		if ((sa.sa_inq_status & STS_MASK) == STS_GOOD &&#ifdef notdef /* XXX don't know if this is a reasonable test */		    (sa.sa_si.si_type & TYPE_QUAL_MASK) == TYPE_QUAL_NOTCONN &&#endif		    (sa.sa_si.si_type & TYPE_TYPE_MASK) == TYPE_NP) {			continue;		}		config_found(&t->t_dev, (void *)&sa, scsi_unitprint);	}}/* * Each unit calls scsi_establish to tell the hba and target of * its existence. */voidscsi_establish(u, dev, unit)	register struct unit *u;	struct device *dev;	register int unit;{	register struct targ *t;	register struct hba_softc *hba;	register struct hbadriver *hbd;	u->u_dev = dev;	t = (struct targ *)dev->dv_parent;	hba = (struct hba_softc *)t->t_dev.dv_parent;	hbd = hba->hba_driver;	t->t_units[unit] = u;	if (t->t_nunits == 0) {		/*		 * This is the first unit on the target.  We can		 * probably just call the hba start code, avoiding		 * one level of calls and queueing.  If we attach		 * another target we will fix this in the code below.		 */		u->u_start = hbd->hd_start;		u->u_go = hbd->hd_go;		u->u_rel = hbd->hd_rel;		u->u_updev = &hba->hba_dev;		t->t_firstunit = unit;	} else {		/*		 * This is not the only unit on the target, so we		 * must call the target start code rather than the		 * hba start code.  Fix the linkage on the first		 * target too (possibly for the 2nd, 3rd, ..., time).		 */		t->t_units[t->t_firstunit]->u_start = scsi_targstart;		t->t_units[t->t_firstunit]->u_go = scsi_targgo;		t->t_units[t->t_firstunit]->u_rel = scsi_targrel;		t->t_units[t->t_firstunit]->u_updev = &t->t_dev;		u->u_start = scsi_targstart;		u->u_go = scsi_targgo;		u->u_rel = scsi_targrel;		u->u_updev = &t->t_dev;	}	t->t_nunits++;			/* another unit is alive */	u->u_unit = unit;	u->u_targ = t->t_targ;		/* record target number, */	u->u_hba = hba;			/* hba ... */	u->u_hbd = hbd;			/* and driver */}/* NO DOUBT SOME OF THE STUFF PRINTED HERE IS USELESS */voidscsi_printinq(inq)	register struct scsi_inquiry *inq;{	register int iso, ecma, ansi, t;	static char *types[] = { "disk", "tape", "printer", "processor",	    "WORM", "ROM disk", "scanner", "magneto-optical",	    "jukebox", "lan" };	if ((t = (inq->si_type & TYPE_QUAL_MASK)) != 0)		printf("type-qual=0x%x ", t);	t = inq->si_type & TYPE_TYPE_MASK;	if (t < sizeof types / sizeof *types)		printf("%s", types[t]);	else		printf("<type %d>", t);	if (inq->si_qual & QUAL_RMB)		printf(" (removable)");	printf(" qual=0x%x", inq->si_qual & QUAL_MASK);	iso = (inq->si_qual >> VER_ISO_SHIFT) & VER_ISO_MASK;	ecma = (inq->si_qual >> VER_ECMA_SHIFT) & VER_ECMA_MASK;	ansi = (inq->si_qual >> VER_ANSI_SHIFT) & VER_ANSI_MASK;	printf(" version=<iso %d, ecma %d, ansi %d>", iso, ecma, ansi);	if (ansi == 1 || ansi == 2) {		char v[9], p[17], r[5];		scsi_inq_ansi((struct scsi_inq_ansi *)inq, v, p, r);		printf(" vendor %s, product %s, rev %s", v, p, r);	}}/* copy a counted string but trim trailing blanks; make the dest a C string */static voidscsi_str(src, dst, len)	register char *src, *dst;	register int len;{	while (src[len - 1] == ' ') {		if (--len == 0) {			*dst = 0;			return;		}	}	bcopy(src, dst, len);	dst[len] = 0;}voidscsi_inq_ansi(si, vendor, product, rev)	register struct scsi_inq_ansi *si;	char *vendor, *product, *rev;{	register int i, len;	/* if too short, extend with blanks */	len = si->si_len + 5;	/* 5 fixed; len is `additional' */	if (len < sizeof(*si))		for (i = len; i < sizeof *si; i++)			((char *)si)[i] = ' ';	scsi_str(si->si_vendor, vendor, sizeof si->si_vendor);	scsi_str(si->si_product, product, sizeof si->si_product);	scsi_str(si->si_rev, rev, sizeof si->si_rev);}/* * Tell all the devices on the given hba that it has been reset. * SHOULD PROBABLY DO MORE HERE */voidscsi_reset_units(hba)	register struct hba_softc *hba;{	register int targ, unit;	register struct targ *t;	register struct unit *u;	for (targ = 0; targ < 8; targ++) {		if ((t = hba->hba_targets[targ]) == NULL)			continue;		for (unit = 0; unit < 8; unit++)			if ((u = t->t_units[unit]) != NULL)				(*u->u_driver->ud_reset)(u);	}}/* * Start a unit on a target. * If the target is busy, just enqueue the unit; * once the target becomes free, we will call the hba start routine. * Otherwise, call the hba start routine now, and then when the hba * becomes free it will call the unit's dgo routine. */voidscsi_targstart(self, sq, bp, dgo, dev)	struct device *self;	register struct sq *sq;	struct buf *bp;	scdgo_fn dgo;	struct device *dev;{	register struct targ *t = (struct targ *)self;	register struct hba_softc *hba;	sq->sq_forw = NULL;	if (t->t_head == NULL)		t->t_head = sq;	else		t->t_tail->sq_forw = sq;	t->t_tail = sq;	if (t->t_busy == 0) {		t->t_busy = 1;		hba = (struct hba_softc *)t->t_dev.dv_parent;		(*hba->hba_driver->hd_start)(&hba->hba_dev, &t->t_forw, bp,		    dgo, dev);	} else {		sq->sq_bp = bp;		sq->sq_dgo = dgo;		sq->sq_dev = dev;	}}/* * The unit got the bus, and wants the hba to go. * Remember its interrupt handler; substitute ours instead. */intscsi_targgo(self, targ, intr, dev, bp, pad)	struct device *self;	int targ;	scintr_fn intr;	struct device *dev;	struct buf *bp;	int pad;{	register struct targ *t = (struct targ *)self;	register struct hba_softc *hba;	t->t_intr = intr;	t->t_intrdev = dev;	hba = (struct hba_softc *)t->t_dev.dv_parent;	return ((*hba->hba_driver->hd_go)(&hba->hba_dev, targ,	    scsi_targintr, &t->t_dev, bp, pad));}/* * The hba got an interrupt.  Dequeue the unit from the target * (the target is already off the hba queue) and then call the * underlying interrupt handler. */voidscsi_targintr(self, stat, resid)	struct device *self;	int stat, resid;{	register struct targ *t = (struct targ *)self;	register struct hba_softc *hba;	register struct sq *sq;	sq = t->t_head;if (sq == NULL) panic("scsi_targintr");	t->t_head = sq = sq->sq_forw;	(*t->t_intr)(t->t_intrdev, stat, resid);	if (sq != NULL) {		hba = (struct hba_softc *)t->t_dev.dv_parent;		(*hba->hba_driver->hd_start)(&hba->hba_dev, &t->t_forw,		    sq->sq_bp, sq->sq_dgo, sq->sq_dev);	} else		t->t_busy = 0;}/* * The unit decided that it needed to `give up' its hold on the bus early. */voidscsi_targrel(self)	struct device *self;{	register struct targ *t = (struct targ *)self;	register struct hba_softc *hba;	register struct sq *sq;	hba = (struct hba_softc *)t->t_dev.dv_parent;	sq = t->t_head;if (sq == NULL) panic("scsi_targrel");	/*	 * This target is at the head of the hba queue.	 * Remove it by calling hba bus release.  Then, if the	 * target queue is not empty, put it back on the hba queue.	 * (This produces round robin service.)	 */	(*hba->hba_driver->hd_rel)(&hba->hba_dev);	sq = sq->sq_forw;	if ((t->t_head = sq) != NULL)		(*hba->hba_driver->hd_start)(&hba->hba_dev, &t->t_forw,		    sq->sq_bp, sq->sq_dgo, sq->sq_dev);	else		t->t_busy = 0;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -