www.pudn.com > cdrecord.zip > scsi-bsd.c


/* @(#)scsi-bsd.c	1.14 98/10/09 Copyright 1997 J. Schilling */
#ifndef lint
static	char __sccsid[] =
	"@(#)scsi-bsd.c	1.14 98/10/09 Copyright 1997 J. Schilling";
#endif
/*
 *	Interface for the NetBSD/FreeBSD/OpenBSD generic SCSI implementation.
 *
 *	This is a hack, that tries to emulate the functionality
 *	of the scg driver.
 *	The SCSI tranport of the generic *BSD implementation is very
 *	similar to the SCSI command transport of the
 *	6 years older scg driver.
 *
 *	Copyright (c) 1997 J. Schilling
 */
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef HAVE_CAMLIB_H

#undef	sense
#include 

#define	MAX_SCG		4	/* Max # of SCSI controllers */
#define	MAX_TGT		8
#define	MAX_LUN		8

LOCAL	short	scgfiles[MAX_SCG][MAX_TGT][MAX_LUN];

/*#define	MAX_DMA_BSD	(32*1024)*/
#define	MAX_DMA_BSD	(60*1024)	/* More seems to make problems */

#if	defined(__NetBSD__) && defined(TYPE_ATAPI)
/*
 * NetBSD 1.3 has a merged SCSI/ATAPI system, so this structure
 * is slightly different.
 */
#define	MAYBE_ATAPI
#define	SADDR_ISSCSI(a)	((a).type == TYPE_SCSI)

#define	SADDR_BUS(a)	(SADDR_ISSCSI(a)?(a).addr.scsi.scbus:(MAX_SCG-1))
#define	SADDR_TARGET(a)	(SADDR_ISSCSI(a)?(a).addr.scsi.target:(a).addr.atapi.atbus*2+(a).addr.atapi.drive)
#define	SADDR_LUN(a)	(SADDR_ISSCSI(a)?(a).addr.scsi.lun:0)
#else
#define	SADDR_ISSCSI(a)	(1)

#define	SADDR_BUS(a)	(a).scbus
#define	SADDR_TARGET(a)	(a).target
#define	SADDR_LUN(a)	(a).lun
#endif	/* __NetBSD__ && TYPE_ATAPI */

LOCAL	int	scsi_send	__PR((int f, struct scg_cmd *sp));
LOCAL	BOOL	scsi_setup	__PR((int f, int busno, int tgt, int tlun));

EXPORT
int scsi_open(device, busno, tgt, tlun)
	char	*device;
	int	busno;
	int	tgt;
	int	tlun;
{
	register int	f;
	register int	b;
	register int	t;
	register int	l;
	register int	nopen = 0;
	char		devname[64];

	for (b=0; b < MAX_SCG; b++) {
		for (t=0; t < MAX_TGT; t++) {
			for (l=0; l < MAX_LUN ; l++)
				scgfiles[b][t][l] = (short)-1;
		}
	}

	if (*device != '\0' || (busno == -2 && tgt == -2))
		goto openbydev;
	if (busno >= 0 && tgt >= 0 && tlun >= 0) {
		if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN)
			return (-1);

		sprintf(devname, "/dev/su%d-%d-%d", busno, tgt, tlun);
		f = open(devname, O_RDWR);
		if (f < 0) {
			goto openbydev;
		}
		scgfiles[busno][tgt][tlun] = f;
		return(1);

	} else for (b=0; b < MAX_SCG; b++) {
		for (t=0; t < MAX_TGT; t++) {
			for (l=0; l < MAX_LUN ; l++) {
				sprintf(devname, "/dev/su%d-%d-%d", b, t, l);
				f = open(devname, O_RDWR);
/*				error("open (%s) = %d\n", devname, f);*/

				if (f < 0) {
					if (errno != ENOENT &&
					    errno != ENXIO &&
					    errno != ENODEV)
						comerr("Cannot open '%s'.\n", devname);
				} else {
					if (scsi_setup(f, b, t, l))
						nopen++;
				}
			}
		}
	}
	/*
	 * Could not open /dev/su-* or got dev=devname:b,l,l / dev=devname:@,l
	 * We do the apropriate tests and try our best.
	 */
openbydev:
	if (nopen == 0) {
		struct scsi_addr saddr;

		if (device[0] == '\0')
			return (0);
		f = open(device, O_RDWR);
		if (f < 0)
			return (0);
		if (tgt == -2) {
			if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) {
				close(f);
				errno = EINVAL;
				return (0);
			}
			scsibus = busno	= SADDR_BUS(saddr);
			target  = tgt	= SADDR_TARGET(saddr);
			if ((tlun >= 0) && (tlun != SADDR_LUN(saddr))) {
				close(f);
				errno = EINVAL;
				return (0);
			}
			lun     = tlun	= SADDR_LUN(saddr);
		}
		if (scsi_setup(f, busno, tgt, tlun))
			nopen++;
	}
	return (nopen);
}

LOCAL BOOL
scsi_setup(f, busno, tgt, tlun)
	int	f;
	int	busno;
	int	tgt;
	int	tlun;
{
	struct scsi_addr saddr;
	int	Bus;
	int	Target;
	int	Lun;
	BOOL	onetarget = FALSE;

	if (scsibus >= 0 && target >= 0 && lun >= 0)
		onetarget = TRUE;

	if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) {
		errmsg("Cannot get SCSI addr.\n");
		close(f);
		return (FALSE);
	}
	Bus	= SADDR_BUS(saddr);
	Target	= SADDR_TARGET(saddr);
	Lun	= SADDR_LUN(saddr);

	if (debug)
		printf("Bus: %d Target: %d Lun: %d\n", Bus, Target, Lun);

	if (Bus >= MAX_SCG || Target >= 8 || Lun >= 8) {
		close(f);
		return (FALSE);
	}

	if (scgfiles[Bus][Target][Lun] == (short)-1)
		scgfiles[Bus][Target][Lun] = (short)f;

	if (onetarget) {
		if (Bus == busno && Target == tgt && Lun == tlun) {
			return (TRUE);
		} else {
			scgfiles[Bus][Target][Lun] = (short)-1;
			close(f);
		}
	}
	return (FALSE);
}

LOCAL long
scsi_maxdma()
{
	long maxdma = MAX_DMA_BSD;

	return (maxdma);
}

EXPORT void *
scsi_getbuf(amt)
	long	amt;
{
	void	*ret;

	if (scg_maxdma == 0)
		scg_maxdma = scsi_maxdma();

	if (amt <= 0 || amt > scg_maxdma)
		return ((void *)0);
	if (debug)
		printf("scsi_getbuf: %ld bytes\n", amt);
	ret = valloc((size_t)(amt));
	return (ret);
}

EXPORT
BOOL scsi_havebus(busno)
	int	busno;
{
	register int	t;
	register int	l;

	if (busno < 0 || busno >= MAX_SCG)
		return (FALSE);

	for (t=0; t < MAX_TGT; t++) {
		for (l=0; l < MAX_LUN ; l++)
			if (scgfiles[busno][t][l] >= 0)
				return (TRUE);
	}
	return (FALSE);
}

EXPORT
int scsi_fileno(busno, tgt, tlun)
	int	busno;
	int	tgt;
	int	tlun;
{
	if (busno < 0 || busno >= MAX_SCG ||
	    tgt < 0 || tgt >= MAX_TGT ||
	    tlun < 0 || tlun >= MAX_LUN)
		return (-1);

	return ((int)scgfiles[busno][tgt][tlun]);
}

EXPORT
int scsi_isatapi()
{
#ifdef	MAYBE_ATAPI
	int	f = scsi_fileno(scsibus, target, lun);
	struct scsi_addr saddr;

	if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0)
		return (-1);

	if (!SADDR_ISSCSI(saddr))
		return (TRUE);
#endif
	return (FALSE);
}

EXPORT
int scsireset()
{
	int	f = scsi_fileno(scsibus, target, lun);

	return (ioctl(f, SCIOCRESET, 0));
}

LOCAL int
scsi_send(int f, struct scg_cmd *sp)
{
	scsireq_t	req;
	register long	*lp1;
	register long	*lp2;
	int		ret = 0;

/*	printf("f: %d\n", f);*/
	if (f < 0) {
		sp->error = SCG_FATAL;
		return (0);
	}
	req.flags = SCCMD_ESCAPE;	/* We set the SCSI cmd len */
	if (sp->flags & SCG_RECV_DATA)
		req.flags |= SCCMD_READ;
	else if (sp->size > 0)
		req.flags |= SCCMD_WRITE;

	req.timeout = sp->timeout * 1000;
	lp1 = (long *)sp->cdb.cmd_cdb;
	lp2 = (long *)req.cmd;
	*lp2++ = *lp1++;
	*lp2++ = *lp1++;
	*lp2++ = *lp1++;
	*lp2++ = *lp1++;
	req.cmdlen = sp->cdb_len;
	req.databuf = sp->addr;
	req.datalen = sp->size;
	req.datalen_used = 0;
	fillbytes(req.sense, sizeof(req.sense), '\0');
	if (sp->sense_len > sizeof(req.sense))
		req.senselen = sizeof(req.sense);
	else if (sp->sense_len < 0)
		req.senselen = 0;
	else
		req.senselen = sp->sense_len;
	req.senselen_used = 0;
	req.status = 0;
	req.retsts = 0;
	req.error = 0;

	if (ioctl(f, SCIOCCOMMAND, (void *)&req) < 0) {
		ret  = -1;
		sp->ux_errno = geterrno();
		if (sp->ux_errno != ENOTTY)
			ret = 0;
	} else {
		sp->ux_errno = 0;
		if (req.retsts != SCCMD_OK)
			sp->ux_errno = EIO;
	}
	fillbytes(&sp->scb, sizeof(sp->scb), '\0');
	fillbytes(&sp->u_sense.cmd_sense, sizeof(sp->u_sense.cmd_sense), '\0');
	sp->resid = req.datalen - req.datalen_used;
	sp->sense_count = req.senselen_used;
	if (sp->sense_count > SCG_MAX_SENSE)
		sp->sense_count = SCG_MAX_SENSE;
	movebytes(req.sense, sp->u_sense.cmd_sense, sp->sense_count);
	sp->u_scb.cmd_scb[0] = req.status;

	switch (req.retsts) {

	case SCCMD_OK:
#ifdef	BSD_SCSI_SENSE_BUG
				sp->u_scb.cmd_scb[0] = 0;
				sp->ux_errno = 0;
#endif
				sp->error = SCG_NO_ERROR;	break;
	case SCCMD_TIMEOUT:	sp->error = SCG_TIMEOUT;	break;
	default:
	case SCCMD_BUSY:	sp->error = SCG_RETRYABLE;	break;
	case SCCMD_SENSE:	sp->error = SCG_RETRYABLE;	break;
	case SCCMD_UNKNOWN:	sp->error = SCG_FATAL;		break;
	}

	return (ret);
}
#define	sense	u_sense.Sense

#else /* BSD_CAM */
/*
 *	Interface for the FreeBSD CAM passthrough device.
 *
 * Copyright (c) 1998 Michael Smith 
 * Copyright (c) 1998 Kenneth D. Merry 
 * Copyright (c) 1998 Joerg Schilling 
 * 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.
 * 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.
 *
 * 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.
 *
 */

#undef	sense
#define scsi_sense CAM_scsi_sense
#define scsi_inquiry CAM_scsi_inquiry
#include 
#include 
#include 
#include 
#include 
#include 

#define CAM_MAXDEVS	128
struct cam_device *cam_devices[CAM_MAXDEVS + 1];

/*
 * Build a list of everything we can find.
 */
EXPORT
int scsi_open(device, busno, tgt, tlun)
	char	*device;
	int	busno;
	int	tgt;
	int	tlun;
{
	struct cam_device		*dev;
	char				name[16];
	int				unit;
	int				nopen = 0;
	union ccb			ccb;
	int				bufsize;
	struct periph_match_pattern	*match_pat;
	int				fd;

	for (unit = 0; unit <= CAM_MAXDEVS; unit++) {
		cam_devices[unit] = (struct cam_device *)-1;
	}

	/*
	 * If we're not scanning the bus, just open one device.
	 */
	if (busno >= 0 && tgt >= 0 && tlun >= 0) {
		cam_devices[0] = cam_open_btl(busno, tgt, tlun, O_RDWR, NULL);
		if (cam_devices[0] == NULL)
			return(-1);
		nopen++;
		return(nopen);
	}

	/*
	 * First open the transport layer device.  There's no point in the
	 * rest of this if we can't open it.
	 */

	if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
		errmsg("Open of %s failed.\n", XPT_DEVICE);	/* XXX Should be removed */
		return(-1);
	}
	fillbytes(&ccb, sizeof(union ccb), '\0');

	/*
	 * Get a list of up to CAM_MAXDEVS passthrough devices in the
	 * system.
	 */
	ccb.ccb_h.func_code = XPT_DEV_MATCH;

	/*
	 * Setup the result buffer.
	 */
	bufsize = sizeof(struct dev_match_result) * CAM_MAXDEVS;
	ccb.cdm.match_buf_len = bufsize;
	ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
	if (ccb.cdm.matches == NULL) {
		errmsg("Couldn't malloc match buffer.\n");	/* XXX Should be removed */
		close(fd);
		return(-1);
	}
	ccb.cdm.num_matches = 0;

	/*
	 * Setup the pattern buffer.  We're matching against all
	 * peripherals named "pass".
	 */
	ccb.cdm.num_patterns = 1;
	ccb.cdm.pattern_buf_len = sizeof(struct dev_match_pattern);
	ccb.cdm.patterns = (struct dev_match_pattern *)malloc(
		sizeof(struct dev_match_pattern));
	if (ccb.cdm.patterns == NULL) {
		errmsg("Couldn't malloc pattern buffer.\n");	/* XXX Should be removed */
		close(fd);
		free(ccb.cdm.matches);
		return(-1);
	}
	ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
	match_pat = &ccb.cdm.patterns[0].pattern.periph_pattern;
	sprintf(match_pat->periph_name, "pass");
	match_pat->flags = PERIPH_MATCH_NAME;

	if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
		errmsg("CAMIOCOMMAND ioctl failed.\n");		/* XXX Should be removed */
		close(fd);
		free(ccb.cdm.matches);
		free(ccb.cdm.patterns);
		return(-1);
	}

	if ((ccb.ccb_h.status != CAM_REQ_CMP)
	 || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
	    && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
		errmsgno(EX_BAD, "Got CAM error 0x%X, CDM error %d.\n",
			ccb.ccb_h.status, ccb.cdm.status);
		close(fd);
		free(ccb.cdm.matches);
		free(ccb.cdm.patterns);
		return(-1);
	}

	free(ccb.cdm.patterns);
	close(fd);

	for (unit = 0; unit < MIN(CAM_MAXDEVS, ccb.cdm.num_matches); unit++) {
		struct periph_match_result *periph_result;

		/*
		 * We shouldn't have anything other than peripheral
		 * matches in here.  If we do, it means an error in the
		 * device matching code in the transport layer.
		 */
		if (ccb.cdm.matches[unit].type != DEV_MATCH_PERIPH) {
			errmsgno(EX_BAD,
				"Kernel error! got periph match type %d!!\n",
				ccb.cdm.matches[unit].type);
			free(ccb.cdm.matches);
			return(-1);
		}
		periph_result = &ccb.cdm.matches[unit].result.periph_result;

		sprintf(name, "/dev/%s%d", periph_result->periph_name,
			periph_result->unit_number);

		/*
		 * cam_open_pass() avoids all lookup and translation from
		 * "regular device name" to passthrough unit number and
		 * just opens the device in question as a passthrough device.
		 */
		cam_devices[unit] = cam_open_pass(name, O_RDWR, NULL);

		/*
		 * If we get back NULL from the open routine, it wasn't
		 * able to open the given passthrough device for one reason
		 * or another.
		 */
		if (cam_devices[unit] == NULL) {
			errmsgno(EX_BAD, "Error opening /dev/%s%d\n",
				periph_result->periph_name,
				periph_result->unit_number);
			errmsgno(EX_BAD, "%s\n", cam_errbuf);
			break;
		}
		nopen++;
	}

	free(ccb.cdm.matches);
	return (nopen);
}

LOCAL
long scsi_maxdma()
{
	return (MAXPHYS);
}

EXPORT void *
scsi_getbuf(amt)
	long	amt;
{
	void	*ret;

	if (scg_maxdma == 0)
		scg_maxdma = MAXPHYS;

	if (amt <= 0 || amt > scg_maxdma)
		return ((void *)0);
	if (debug)
		printf("scsi_getbuf: %ld bytes\n", amt);
	ret = valloc((size_t)(amt));
	return (ret);
}

EXPORT
BOOL scsi_havebus(int busno)
{
	int		unit;

	/*
	 * There's a "cleaner" way to do this, using the matching code, but
	 * it would involve more code than this solution...
	 */
	for (unit = 0; cam_devices[unit] != (struct cam_device *)-1; unit++) {
		if (cam_devices[unit] == NULL)
			continue;
		if (cam_devices[unit]->path_id == busno)
			return (TRUE);
	}
	return (FALSE);
}

EXPORT
int scsi_fileno(bus, unit, lun)
{
	int		i;

	for (i = 0; cam_devices[i] != (struct cam_device *)-1; i++) {
		if (cam_devices[i] == NULL)
			continue;
		if ((cam_devices[i]->path_id == bus) &&
		    (cam_devices[i]->target_id == unit) &&
		    (cam_devices[i]->target_lun == lun))
			return (i);
	}
	return (-1);
}

EXPORT
int scsi_isatapi()
{
	return (FALSE);
}

EXPORT
int scsireset()
{
	/* XXX synchronous reset command - is this wise? */
	return (-1);
}

LOCAL int
scsi_send(int unit, struct scg_cmd *sp)
{
	struct cam_device	*dev;
	union ccb		ccb_space;
	union ccb		*ccb = &ccb_space;
	int			rv, result;
	u_int32_t		ccb_flags;

	if (unit < 0) {
#if 0
		fprintf(stderr, "attempt to reference invalid unit %d\n", unit);
#endif
		sp->error = SCG_FATAL;
		return (0);
	}

	dev = cam_devices[unit];
	fillbytes(&ccb->ccb_h, sizeof(struct ccb_hdr), '\0');
	ccb->ccb_h.path_id = dev->path_id;
	ccb->ccb_h.target_id = dev->target_id;
	ccb->ccb_h.target_lun = dev->target_lun;

	/* Build the CCB */
	fillbytes(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio), '\0');
	movebytes(sp->cdb.cmd_cdb, &ccb->csio.cdb_io.cdb_bytes, sp->cdb_len);

	/*
	 * Set the data direction flags.
	 */
	if (sp->size != 0) {
		ccb_flags = (sp->flags & SCG_RECV_DATA) ? CAM_DIR_IN :
							   CAM_DIR_OUT;
	} else {
		ccb_flags = CAM_DIR_NONE;
	}

	ccb_flags |= CAM_DEV_QFRZDIS;

	/*
	 * If you don't want to bother with sending tagged commands under CAM,
	 * we don't need to do anything to cdrecord.  If you want to send
	 * tagged commands to those devices that support it, we'll need to set
	 * the tag action valid field like this in scsi_send():
 	 *         
	 *        ccb_flags |= CAM_DEV_QFRZDIS | CAM_TAG_ACTION_VALID;
	 */

	cam_fill_csio(&ccb->csio,
		      /* retries */ 1,
		      /* cbfncp */ NULL,
		      /* flags */ ccb_flags,
		      /* tag_action */ MSG_SIMPLE_Q_TAG,
		      /* data_ptr */ (u_int8_t *)sp->addr,
		      /* dxfer_len */ sp->size,
		      /* sense_len */ SSD_FULL_SIZE,
		      /* cdb_len */ sp->cdb_len,
		      /* timeout */ sp->timeout * 1000);

	/* Run the command */
	errno = 0;
	if ((rv = cam_send_ccb(dev, ccb)) == -1) {
		return (rv);
	} else {
		/*
		 * Check for command status.  Selection timeouts are fatal.
		 * For command timeouts, we pass back the appropriate
		 * error.  If we completed successfully, there's (obviously)
		 * no error.  We declare anything else "retryable".
		 */
		switch(ccb->ccb_h.status & CAM_STATUS_MASK) {
			case CAM_SEL_TIMEOUT:
				result = SCG_FATAL;
				break;
			case CAM_CMD_TIMEOUT:
				result = SCG_TIMEOUT;
				break;
			case CAM_REQ_CMP:
				result = SCG_NO_ERROR;
				break;
			default:
				result = SCG_RETRYABLE;
				break;
		}
	}

	sp->error = result;
	if (result != SCG_NO_ERROR)
		sp->ux_errno = EIO;

	/* Pass the result back up */
	fillbytes(&sp->scb, sizeof(sp->scb), '\0');
	fillbytes(&sp->u_sense.cmd_sense, sizeof(sp->u_sense.cmd_sense), '\0');
	sp->resid = ccb->csio.resid;
	sp->sense_count = SSD_FULL_SIZE - ccb->csio.sense_resid;

	/*
	 * Determine how much room we have for sense data.
	 */
	if (sp->sense_count > SCG_MAX_SENSE)
		sp->sense_count = SCG_MAX_SENSE;

	/* Copy the sense data out */
	movebytes(&ccb->csio.sense_data, &sp->u_sense.cmd_sense, sp->sense_count);

	sp->u_scb.cmd_scb[0] = ccb->csio.scsi_status;

	return (0);
}

#undef scsi_sense
#undef scsi_inquiry
#define sense u_sense.Sense

#endif /* BSD_CAM */