Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 28 Apr 2005 15:09:15 +0200
From:      "Bernard Peultier" <bernard.peultier@atempo.com>
To:        <freebsd-bugs@freebsd.org>
Subject:   Using Tape Device on SAN
Message-ID:  <KOEBLIEDJOKGEMJPOEJOGEOHCPAA.bernard.peultier@atempo.com>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.

------=_NextPart_000_001A_01C54C04.3DD952C0
Content-Type: text/plain;
	charset="iso-8859-1"
Content-Transfer-Encoding: 7bit

When we open the first time a tape device the driver (sa) set the scsi
reserve on the device. The only possibility to release the reserve is to
offline the cartridge.

Some others Operating system like Sun, set the scsi reserve and when we
close the device the driver release this scsi reserve.

I have made the modification on my FreeBSD5.3 sa driver.

Is it possible to have a status of this modification (include on the sources
.....)

On attach the patch file and a readme for all costumers who buy Time
Navigator Storage Node on FreeBSD.

Best Regards,
B.PEULTIER
Special Product Manager
Atempo - Time Navigator



------=_NextPart_000_001A_01C54C04.3DD952C0
Content-Type: application/octet-stream;
	name="scsi_sa.c.patch"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="scsi_sa.c.patch"

--- scsi_sa.c.old	Wed Apr 27 19:54:17 2005=0A=
+++ scsi_sa.c	Thu Apr 28 13:47:46 2005=0A=
@@ -633,7 +633,9 @@=0A=
 	}=0A=
 	=0A=
 	/* release the device if it is no longer mounted */=0A=
-	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0)=0A=
+/* Start release every time bpr Atempo */=0A=
+/*	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0)*/=0A=
+/* End release every time bpr Atempo */=0A=
 		sareservereleaseunit(periph, FALSE);=0A=
 =0A=
 	cam_periph_unlock(periph);=0A=
@@ -1803,6 +1805,12 @@=0A=
 	 */=0A=
 	=0A=
 	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {=0A=
+/* Start : reserve every time bpr Atempo */=0A=
+		error =3D sareservereleaseunit(periph, TRUE);=0A=
+		if (error) {=0A=
+			return (error);=0A=
+		}=0A=
+/* End : reserve every time bpr Atempo */=0A=
 		ccb =3D cam_periph_getccb(periph, 1);=0A=
 		scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
 		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=

------=_NextPart_000_001A_01C54C04.3DD952C0
Content-Type: application/octet-stream;
	name="scsi_sa.c"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="scsi_sa.c"

/*=0A=
 * Implementation of SCSI Sequential Access Peripheral driver for CAM.=0A=
 *=0A=
 * Copyright (c) 1999, 2000 Matthew Jacob=0A=
 * All rights reserved.=0A=
 *=0A=
 * Redistribution and use in source and binary forms, with or without=0A=
 * modification, are permitted provided that the following conditions=0A=
 * are met:=0A=
 * 1. Redistributions of source code must retain the above copyright=0A=
 *    notice, this list of conditions, and the following disclaimer,=0A=
 *    without modification, immediately at the beginning of the file.=0A=
 * 2. The name of the author may not be used to endorse or promote =
products=0A=
 *    derived from this software without specific prior written =
permission.=0A=
 *=0A=
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND=0A=
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0A=
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE=0A=
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE FOR=0A=
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL=0A=
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS=0A=
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)=0A=
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT=0A=
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY =
WAY=0A=
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0A=
 * SUCH DAMAGE.=0A=
 */=0A=
=0A=
#include <sys/cdefs.h>=0A=
__FBSDID("$FreeBSD: src/sys/cam/scsi/scsi_sa.c,v 1.101 2004/06/16 =
09:46:31 phk Exp $");=0A=
=0A=
#include <sys/param.h>=0A=
#include <sys/queue.h>=0A=
#ifdef _KERNEL=0A=
#include <sys/systm.h>=0A=
#include <sys/kernel.h>=0A=
#endif=0A=
#include <sys/types.h>=0A=
#include <sys/time.h>=0A=
#include <sys/bio.h>=0A=
#include <sys/limits.h>=0A=
#include <sys/malloc.h>=0A=
#include <sys/mtio.h>=0A=
#ifdef _KERNEL=0A=
#include <sys/conf.h>=0A=
#endif=0A=
#include <sys/devicestat.h>=0A=
=0A=
#ifndef _KERNEL=0A=
#include <stdio.h>=0A=
#include <string.h>=0A=
#endif=0A=
=0A=
#include <cam/cam.h>=0A=
#include <cam/cam_ccb.h>=0A=
#include <cam/cam_periph.h>=0A=
#include <cam/cam_xpt_periph.h>=0A=
#include <cam/cam_debug.h>=0A=
=0A=
#include <cam/scsi/scsi_all.h>=0A=
#include <cam/scsi/scsi_message.h>=0A=
#include <cam/scsi/scsi_sa.h>=0A=
=0A=
#ifdef _KERNEL=0A=
=0A=
#include <opt_sa.h>=0A=
=0A=
#ifndef SA_IO_TIMEOUT=0A=
#define SA_IO_TIMEOUT		4=0A=
#endif=0A=
#ifndef SA_SPACE_TIMEOUT=0A=
#define SA_SPACE_TIMEOUT	1 * 60=0A=
#endif=0A=
#ifndef SA_REWIND_TIMEOUT=0A=
#define SA_REWIND_TIMEOUT	2 * 60=0A=
#endif=0A=
#ifndef SA_ERASE_TIMEOUT=0A=
#define SA_ERASE_TIMEOUT	4 * 60=0A=
#endif=0A=
=0A=
#define	SCSIOP_TIMEOUT		(60 * 1000)	/* not an option */=0A=
=0A=
#define	IO_TIMEOUT		(SA_IO_TIMEOUT * 60 * 1000)=0A=
#define	REWIND_TIMEOUT		(SA_REWIND_TIMEOUT * 60 * 1000)=0A=
#define	ERASE_TIMEOUT		(SA_ERASE_TIMEOUT * 60 * 1000)=0A=
#define	SPACE_TIMEOUT		(SA_SPACE_TIMEOUT * 60 * 1000)=0A=
=0A=
/*=0A=
 * Additional options that can be set for config: SA_1FM_AT_EOT=0A=
 */=0A=
=0A=
#ifndef	UNUSED_PARAMETER=0A=
#define	UNUSED_PARAMETER(x)	x =3D x=0A=
#endif=0A=
=0A=
#define	QFRLS(ccb)	\=0A=
	if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)	\=0A=
		cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE)=0A=
=0A=
/*=0A=
 * Driver states=0A=
 */=0A=
=0A=
=0A=
typedef enum {=0A=
	SA_STATE_NORMAL, SA_STATE_ABNORMAL=0A=
} sa_state;=0A=
=0A=
#define ccb_pflags	ppriv_field0=0A=
#define ccb_bp	 	ppriv_ptr1=0A=
=0A=
#define	SA_CCB_BUFFER_IO	0x0=0A=
#define	SA_CCB_WAITING		0x1=0A=
#define	SA_CCB_TYPEMASK		0x1=0A=
#define	SA_POSITION_UPDATED	0x2=0A=
=0A=
#define	Set_CCB_Type(x, type)				\=0A=
	x->ccb_h.ccb_pflags &=3D ~SA_CCB_TYPEMASK;	\=0A=
	x->ccb_h.ccb_pflags |=3D type=0A=
=0A=
#define	CCB_Type(x)	(x->ccb_h.ccb_pflags & SA_CCB_TYPEMASK)=0A=
=0A=
=0A=
=0A=
typedef enum {=0A=
	SA_FLAG_OPEN		=3D 0x0001,=0A=
	SA_FLAG_FIXED		=3D 0x0002,=0A=
	SA_FLAG_TAPE_LOCKED	=3D 0x0004,=0A=
	SA_FLAG_TAPE_MOUNTED	=3D 0x0008,=0A=
	SA_FLAG_TAPE_WP		=3D 0x0010,=0A=
	SA_FLAG_TAPE_WRITTEN	=3D 0x0020,=0A=
	SA_FLAG_EOM_PENDING	=3D 0x0040,=0A=
	SA_FLAG_EIO_PENDING	=3D 0x0080,=0A=
	SA_FLAG_EOF_PENDING	=3D 0x0100,=0A=
	SA_FLAG_ERR_PENDING	=3D (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING|=0A=
				   SA_FLAG_EOF_PENDING),=0A=
	SA_FLAG_INVALID		=3D 0x0200,=0A=
	SA_FLAG_COMP_ENABLED	=3D 0x0400,=0A=
	SA_FLAG_COMP_SUPP	=3D 0x0800,=0A=
	SA_FLAG_COMP_UNSUPP	=3D 0x1000,=0A=
	SA_FLAG_TAPE_FROZEN	=3D 0x2000=0A=
} sa_flags;=0A=
=0A=
typedef enum {=0A=
	SA_MODE_REWIND		=3D 0x00,=0A=
	SA_MODE_NOREWIND	=3D 0x01,=0A=
	SA_MODE_OFFLINE		=3D 0x02=0A=
} sa_mode;=0A=
=0A=
typedef enum {=0A=
	SA_PARAM_NONE		=3D 0x00,=0A=
	SA_PARAM_BLOCKSIZE	=3D 0x01,=0A=
	SA_PARAM_DENSITY	=3D 0x02,=0A=
	SA_PARAM_COMPRESSION	=3D 0x04,=0A=
	SA_PARAM_BUFF_MODE	=3D 0x08,=0A=
	SA_PARAM_NUMBLOCKS	=3D 0x10,=0A=
	SA_PARAM_WP		=3D 0x20,=0A=
	SA_PARAM_SPEED		=3D 0x40,=0A=
	SA_PARAM_ALL		=3D 0x7f=0A=
} sa_params;=0A=
=0A=
typedef enum {=0A=
	SA_QUIRK_NONE		=3D 0x00,=0A=
	SA_QUIRK_NOCOMP		=3D 0x01,	/* Can't deal with compression at all */=0A=
	SA_QUIRK_FIXED		=3D 0x02,	/* Force fixed mode */=0A=
	SA_QUIRK_VARIABLE	=3D 0x04,	/* Force variable mode */=0A=
	SA_QUIRK_2FM		=3D 0x08,	/* Needs Two File Marks at EOD */=0A=
	SA_QUIRK_1FM		=3D 0x10,	/* No more than 1 File Mark at EOD */=0A=
	SA_QUIRK_NODREAD	=3D 0x20,	/* Don't try and dummy read density */=0A=
	SA_QUIRK_NO_MODESEL	=3D 0x40,	/* Don't do mode select at all */=0A=
	SA_QUIRK_NO_CPAGE	=3D 0x80	/* Don't use DEVICE COMPRESSION page */=0A=
} sa_quirks;=0A=
=0A=
/* units are bits 4-7, 16-21 (1024 units) */=0A=
#define SAUNIT(DEV) \=0A=
	(((minor(DEV) & 0xF0) >> 4) |  ((minor(DEV) & 0x3f0000) >> 16))=0A=
=0A=
#define SAMODE(z) ((minor(z) & 0x3))=0A=
#define SADENSITY(z) (((minor(z) >> 2) & 0x3))=0A=
#define	SA_IS_CTRL(z) (minor(z) & (1 << 29))=0A=
=0A=
#define SA_NOT_CTLDEV	0=0A=
#define SA_CTLDEV	1=0A=
=0A=
#define SA_ATYPE_R	0=0A=
#define SA_ATYPE_NR	1=0A=
#define SA_ATYPE_ER	2=0A=
=0A=
#define SAMINOR(ctl, unit, mode, access) \=0A=
	((ctl << 29) | ((unit & 0x3f0) << 16) | ((unit & 0xf) << 4) | \=0A=
	(mode << 0x2) | (access & 0x3))=0A=
=0A=
#define SA_NUM_MODES	4=0A=
struct sa_devs {=0A=
	struct cdev *ctl_dev;=0A=
	struct sa_mode_devs {=0A=
		struct cdev *r_dev;=0A=
		struct cdev *nr_dev;=0A=
		struct cdev *er_dev;=0A=
	} mode_devs[SA_NUM_MODES];=0A=
};=0A=
=0A=
struct sa_softc {=0A=
	sa_state	state;=0A=
	sa_flags	flags;=0A=
	sa_quirks	quirks;=0A=
	struct		bio_queue_head bio_queue;=0A=
	int		queue_count;=0A=
	struct		devstat *device_stats;=0A=
	struct sa_devs	devs;=0A=
	int		blk_gran;=0A=
	int		blk_mask;=0A=
	int		blk_shift;=0A=
	u_int32_t	max_blk;=0A=
	u_int32_t	min_blk;=0A=
	u_int32_t	comp_algorithm;=0A=
	u_int32_t	saved_comp_algorithm;=0A=
	u_int32_t	media_blksize;=0A=
	u_int32_t	last_media_blksize;=0A=
	u_int32_t	media_numblks;=0A=
	u_int8_t	media_density;=0A=
	u_int8_t	speed;=0A=
	u_int8_t	scsi_rev;=0A=
	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */=0A=
	int		buffer_mode;=0A=
	int		filemarks;=0A=
	union		ccb saved_ccb;=0A=
	int		last_resid_was_io;=0A=
=0A=
	/*=0A=
	 * Relative to BOT Location.=0A=
	 */=0A=
	daddr_t		fileno;=0A=
	daddr_t		blkno;=0A=
=0A=
	/*=0A=
	 * Latched Error Info=0A=
	 */=0A=
	struct {=0A=
		struct scsi_sense_data _last_io_sense;=0A=
		u_int32_t _last_io_resid;=0A=
		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];=0A=
		struct scsi_sense_data _last_ctl_sense;=0A=
		u_int32_t _last_ctl_resid;=0A=
		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];=0A=
#define	last_io_sense	errinfo._last_io_sense=0A=
#define	last_io_resid	errinfo._last_io_resid=0A=
#define	last_io_cdb	errinfo._last_io_cdb=0A=
#define	last_ctl_sense	errinfo._last_ctl_sense=0A=
#define	last_ctl_resid	errinfo._last_ctl_resid=0A=
#define	last_ctl_cdb	errinfo._last_ctl_cdb=0A=
	} errinfo;=0A=
	/*=0A=
	 * Misc other flags/state=0A=
	 */=0A=
	u_int32_t=0A=
				: 31,=0A=
		ctrl_mode	: 1;	/* control device open */=0A=
};=0A=
=0A=
struct sa_quirk_entry {=0A=
	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */=0A=
	sa_quirks quirks;	/* specific quirk type */=0A=
	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */=0A=
};=0A=
=0A=
static struct sa_quirk_entry sa_quirk_table[] =3D=0A=
{=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",=0A=
		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |=0A=
		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python 06408*", "*"}, SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python*", "*"}, SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 2525 25462", "-011"},=0A=
		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024=0A=
	},=0A=
#if	0=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,=0A=
	},=0A=
#endif=0A=
 	{=0A=
 		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "C56*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",=0A=
		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",=0A=
		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{	/* jreynold@primenet.com */=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",=0A=
		"STT8000N*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{	/* mike@sentex.net */=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",=0A=
		"STT20000*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " SLR*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",=0A=
		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",=0A=
		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024=0A=
	}=0A=
};=0A=
=0A=
static	d_open_t	saopen;=0A=
static	d_close_t	saclose;=0A=
static	d_strategy_t	sastrategy;=0A=
static	d_ioctl_t	saioctl;=0A=
static	periph_init_t	sainit;=0A=
static	periph_ctor_t	saregister;=0A=
static	periph_oninv_t	saoninvalidate;=0A=
static	periph_dtor_t	sacleanup;=0A=
static	periph_start_t	sastart;=0A=
static	void		saasync(void *callback_arg, u_int32_t code,=0A=
				struct cam_path *path, void *arg);=0A=
static	void		sadone(struct cam_periph *periph,=0A=
			       union ccb *start_ccb);=0A=
static  int		saerror(union ccb *ccb, u_int32_t cam_flags,=0A=
				u_int32_t sense_flags);=0A=
static int		samarkswanted(struct cam_periph *);=0A=
static int		sacheckeod(struct cam_periph *periph);=0A=
static int		sagetparams(struct cam_periph *periph,=0A=
				    sa_params params_to_get,=0A=
				    u_int32_t *blocksize, u_int8_t *density,=0A=
				    u_int32_t *numblocks, int *buff_mode,=0A=
				    u_int8_t *write_protect, u_int8_t *speed,=0A=
				    int *comp_supported, int *comp_enabled,=0A=
				    u_int32_t *comp_algorithm,=0A=
				    sa_comp_t *comp_page);=0A=
static int		sasetparams(struct cam_periph *periph,=0A=
				    sa_params params_to_set,=0A=
				    u_int32_t blocksize, u_int8_t density,=0A=
				    u_int32_t comp_algorithm,=0A=
				    u_int32_t sense_flags);=0A=
static void		saprevent(struct cam_periph *periph, int action);=0A=
static int		sarewind(struct cam_periph *periph);=0A=
static int		saspace(struct cam_periph *periph, int count,=0A=
				scsi_space_code code);=0A=
static int		samount(struct cam_periph *, int, struct cdev *);=0A=
static int		saretension(struct cam_periph *periph);=0A=
static int		sareservereleaseunit(struct cam_periph *periph,=0A=
					     int reserve);=0A=
static int		saloadunload(struct cam_periph *periph, int load);=0A=
static int		saerase(struct cam_periph *periph, int longerase);=0A=
static int		sawritefilemarks(struct cam_periph *periph,=0A=
					 int nmarks, int setmarks);=0A=
static int		sardpos(struct cam_periph *periph, int, u_int32_t *);=0A=
static int		sasetpos(struct cam_periph *periph, int, u_int32_t *);=0A=
=0A=
=0A=
static struct periph_driver sadriver =3D=0A=
{=0A=
	sainit, "sa",=0A=
	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0=0A=
};=0A=
=0A=
PERIPHDRIVER_DECLARE(sa, sadriver);=0A=
=0A=
/* For 2.2-stable support */=0A=
#ifndef D_TAPE=0A=
#define D_TAPE 0=0A=
#endif=0A=
=0A=
=0A=
static struct cdevsw sa_cdevsw =3D {=0A=
	.d_version =3D	D_VERSION,=0A=
	.d_open =3D	saopen,=0A=
	.d_close =3D	saclose,=0A=
	.d_read =3D	physread,=0A=
	.d_write =3D	physwrite,=0A=
	.d_ioctl =3D	saioctl,=0A=
	.d_strategy =3D	sastrategy,=0A=
	.d_name =3D	"sa",=0A=
	.d_flags =3D	D_TAPE | D_NEEDGIANT,=0A=
};=0A=
=0A=
static int=0A=
saopen(struct cdev *dev, int flags, int fmt, struct thread *td)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	int unit;=0A=
	int error;=0A=
	int s;=0A=
=0A=
	unit =3D SAUNIT(dev);=0A=
=0A=
	s =3D splsoftcam();=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL) {=0A=
		(void) splx(s);=0A=
		return (ENXIO);	=0A=
	}=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	if ((error =3D cam_periph_lock(periph, PRIBIO|PCATCH)) !=3D 0) {=0A=
		splx(s);=0A=
		return (error);=0A=
	}=0A=
	splx(s);=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,=0A=
	    ("saopen(%d): dev=3D0x%x softc=3D0x%x\n", unit, unit, =
softc->flags));=0A=
=0A=
	if (cam_periph_acquire(periph) !=3D CAM_REQ_CMP) {=0A=
		cam_periph_unlock(periph);=0A=
		return (ENXIO);=0A=
	}=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		softc->ctrl_mode =3D 1;=0A=
		cam_periph_unlock(periph);=0A=
		return (0);=0A=
	}=0A=
=0A=
=0A=
	if (softc->flags & SA_FLAG_OPEN) {=0A=
		error =3D EBUSY;=0A=
	} else if (softc->flags & SA_FLAG_INVALID) {=0A=
		error =3D ENXIO;=0A=
	} else {=0A=
		/*=0A=
		 * The function samount ensures media is loaded and ready.=0A=
		 * It also does a device RESERVE if the tape isn't yet mounted.=0A=
		 */=0A=
		error =3D samount(periph, flags, dev);=0A=
	}=0A=
=0A=
	if (error) {=0A=
		cam_periph_release(periph);=0A=
	} else {=0A=
		saprevent(periph, PR_PREVENT);=0A=
		softc->flags |=3D SA_FLAG_OPEN;=0A=
	}=0A=
	cam_periph_unlock(periph);=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saclose(struct cdev *dev, int flag, int fmt, struct thread *td)=0A=
{=0A=
	struct	cam_periph *periph;=0A=
	struct	sa_softc *softc;=0A=
	int	unit, mode, error, writing, tmp;=0A=
	int	closedbits =3D SA_FLAG_OPEN;=0A=
=0A=
	unit =3D SAUNIT(dev);=0A=
	mode =3D SAMODE(dev);=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL)=0A=
		return (ENXIO);	=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,=0A=
	    ("saclose(%d): dev=3D0x%x softc=3D0x%x\n", unit, unit, =
softc->flags));=0A=
=0A=
=0A=
	if ((error =3D cam_periph_lock(periph, PRIBIO)) !=3D 0) {=0A=
		return (error);=0A=
	}=0A=
=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		softc->ctrl_mode =3D 0;=0A=
		cam_periph_release(periph);=0A=
		cam_periph_unlock(periph);=0A=
		return (0);=0A=
	}=0A=
=0A=
	/*=0A=
	 * Were we writing the tape?=0A=
	 */=0A=
	writing =3D (softc->flags & SA_FLAG_TAPE_WRITTEN) !=3D 0;=0A=
=0A=
	/*=0A=
	 * See whether or not we need to write filemarks. If this=0A=
	 * fails, we probably have to assume we've lost tape=0A=
	 * position.=0A=
	 */=0A=
	error =3D sacheckeod(periph);=0A=
	if (error) {=0A=
		xpt_print_path(periph->path);=0A=
		printf("failed to write terminating filemark(s)\n");=0A=
		softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
	}=0A=
=0A=
	/*=0A=
	 * Whatever we end up doing, allow users to eject tapes from here on.=0A=
	 */=0A=
	saprevent(periph, PR_ALLOW);=0A=
=0A=
	/*=0A=
	 * Decide how to end...=0A=
	 */=0A=
	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0) {=0A=
		closedbits |=3D SA_FLAG_TAPE_FROZEN;=0A=
	} else switch (mode) {=0A=
	case SA_MODE_OFFLINE:=0A=
		/*=0A=
		 * An 'offline' close is an unconditional release of=0A=
		 * frozen && mount conditions, irrespective of whether=0A=
		 * these operations succeeded. The reason for this is=0A=
		 * to allow at least some kind of programmatic way=0A=
		 * around our state getting all fouled up. If somebody=0A=
		 * issues an 'offline' command, that will be allowed=0A=
		 * to clear state.=0A=
		 */=0A=
		(void) sarewind(periph);=0A=
		(void) saloadunload(periph, FALSE);=0A=
		closedbits |=3D SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;=0A=
		break;=0A=
	case SA_MODE_REWIND:=0A=
		/*=0A=
		 * If the rewind fails, return an error- if anyone cares,=0A=
		 * but not overwriting any previous error.=0A=
		 *=0A=
		 * We don't clear the notion of mounted here, but we do=0A=
		 * clear the notion of frozen if we successfully rewound.=0A=
		 */=0A=
		tmp =3D sarewind(periph);=0A=
		if (tmp) {=0A=
			if (error !=3D 0)=0A=
				error =3D tmp;=0A=
		} else {=0A=
			closedbits |=3D SA_FLAG_TAPE_FROZEN;=0A=
		}=0A=
		break;=0A=
	case SA_MODE_NOREWIND:=0A=
		/*=0A=
		 * If we're not rewinding/unloading the tape, find out=0A=
		 * whether we need to back up over one of two filemarks=0A=
		 * we wrote (if we wrote two filemarks) so that appends=0A=
		 * from this point on will be sane.=0A=
		 */=0A=
		if (error =3D=3D 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {=0A=
			tmp =3D saspace(periph, -1, SS_FILEMARKS);=0A=
			if (tmp) {=0A=
				xpt_print_path(periph->path);=0A=
				printf("unable to backspace over one of double"=0A=
				   " filemarks at end of tape\n");=0A=
				xpt_print_path(periph->path);=0A=
				printf("it is possible that this device"=0A=
				   " needs a SA_QUIRK_1FM quirk set for it\n");=0A=
				softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
			}=0A=
		}=0A=
		break;=0A=
	default:=0A=
		xpt_print_path(periph->path);=0A=
		panic("unknown mode 0x%x in saclose", mode);=0A=
		/* NOTREACHED */=0A=
		break;=0A=
	}=0A=
=0A=
	/*=0A=
	 * We wish to note here that there are no more filemarks to be written.=0A=
	 */=0A=
	softc->filemarks =3D 0;=0A=
	softc->flags &=3D ~SA_FLAG_TAPE_WRITTEN;=0A=
=0A=
	/*=0A=
	 * And we are no longer open for business.=0A=
	 */=0A=
	softc->flags &=3D ~closedbits;=0A=
=0A=
	/*=0A=
	 * Inform users if tape state if frozen....=0A=
	 */=0A=
	if (softc->flags & SA_FLAG_TAPE_FROZEN) {=0A=
		xpt_print_path(periph->path);=0A=
		printf("tape is now frozen- use an OFFLINE, REWIND or MTEOM "=0A=
		    "command to clear this state.\n");=0A=
	}=0A=
	=0A=
	/* release the device if it is no longer mounted */=0A=
/* Start release every time bpr Atempo */=0A=
/*	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0)*/=0A=
/* End release every time bpr Atempo */=0A=
		sareservereleaseunit(periph, FALSE);=0A=
=0A=
	cam_periph_unlock(periph);=0A=
	cam_periph_release(periph);=0A=
=0A=
	return (error);	=0A=
}=0A=
=0A=
/*=0A=
 * Actually translate the requested transfer into one the physical driver=0A=
 * can understand.  The transfer is described by a buf and will include=0A=
 * only one physical transfer.=0A=
 */=0A=
static void=0A=
sastrategy(struct bio *bp)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	int    s;=0A=
	=0A=
	bp->bio_resid =3D bp->bio_bcount;=0A=
	if (SA_IS_CTRL(bp->bio_dev)) {=0A=
		biofinish(bp, NULL, EINVAL);=0A=
		return;=0A=
	}=0A=
	periph =3D (struct cam_periph *)bp->bio_dev->si_drv1;=0A=
	if (periph =3D=3D NULL) {=0A=
		biofinish(bp, NULL, ENXIO);=0A=
		return;=0A=
	}=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	s =3D splsoftcam();=0A=
=0A=
	if (softc->flags & SA_FLAG_INVALID) {=0A=
		splx(s);=0A=
		biofinish(bp, NULL, ENXIO);=0A=
		return;=0A=
	}=0A=
=0A=
	if (softc->flags & SA_FLAG_TAPE_FROZEN) {=0A=
		splx(s);=0A=
		biofinish(bp, NULL, EPERM);=0A=
		return;=0A=
	}=0A=
=0A=
	splx(s);=0A=
=0A=
	/*=0A=
	 * If it's a null transfer, return immediatly=0A=
	 */=0A=
	if (bp->bio_bcount =3D=3D 0) {=0A=
		biodone(bp);=0A=
		return;=0A=
	}=0A=
=0A=
	/* valid request?  */=0A=
	if (softc->flags & SA_FLAG_FIXED) {=0A=
		/*=0A=
		 * Fixed block device.  The byte count must=0A=
		 * be a multiple of our block size.=0A=
		 */=0A=
		if (((softc->blk_mask !=3D ~0) &&=0A=
		    ((bp->bio_bcount & softc->blk_mask) !=3D 0)) ||=0A=
		    ((softc->blk_mask =3D=3D ~0) &&=0A=
		    ((bp->bio_bcount % softc->min_blk) !=3D 0))) {=0A=
			xpt_print_path(periph->path);=0A=
			printf("Invalid request.  Fixed block device "=0A=
			       "requests must be a multiple "=0A=
			       "of %d bytes\n", softc->min_blk);=0A=
			biofinish(bp, NULL, EINVAL);=0A=
			return;=0A=
		}=0A=
	} else if ((bp->bio_bcount > softc->max_blk) ||=0A=
		   (bp->bio_bcount < softc->min_blk) ||=0A=
		   (bp->bio_bcount & softc->blk_mask) !=3D 0) {=0A=
=0A=
		xpt_print_path(periph->path);=0A=
		printf("Invalid request.  Variable block device "=0A=
		    "requests must be ");=0A=
		if (softc->blk_mask !=3D 0) {=0A=
			printf("a multiple of %d ", (0x1 << softc->blk_gran));=0A=
		}=0A=
		printf("between %d and %d bytes\n", softc->min_blk,=0A=
		    softc->max_blk);=0A=
		biofinish(bp, NULL, EINVAL);=0A=
		return;=0A=
        }=0A=
	=0A=
	/*=0A=
	 * Mask interrupts so that the device cannot be invalidated until=0A=
	 * after we are in the queue.  Otherwise, we might not properly=0A=
	 * clean up one of the buffers.=0A=
	 */=0A=
	s =3D splbio();=0A=
	=0A=
	/*=0A=
	 * Place it at the end of the queue.=0A=
	 */=0A=
	bioq_insert_tail(&softc->bio_queue, bp);=0A=
	softc->queue_count++;=0A=
#if	0=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
	    ("sastrategy: queuing a %ld %s byte %s\n", bp->bio_bcount,=0A=
 	    (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",=0A=
	    (bp->bio_cmd =3D=3D BIO_READ)? "read" : "write"));=0A=
#endif=0A=
	if (softc->queue_count > 1) {=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
		    ("sastrategy: queue count now %d\n", softc->queue_count));=0A=
	}=0A=
	splx(s);=0A=
	=0A=
	/*=0A=
	 * Schedule ourselves for performing the work.=0A=
	 */=0A=
	xpt_schedule(periph, 1);=0A=
=0A=
	return;=0A=
}=0A=
=0A=
static int=0A=
saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct =
thread *td)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	scsi_space_code spaceop;=0A=
	int didlockperiph =3D 0;=0A=
	int s;=0A=
	int mode;=0A=
	int error =3D 0;=0A=
=0A=
	mode =3D SAMODE(dev);=0A=
	error =3D 0;		/* shut up gcc */=0A=
	spaceop =3D 0;		/* shut up gcc */=0A=
=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL)=0A=
		return (ENXIO);	=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * Check for control mode accesses. We allow MTIOCGET and=0A=
	 * MTIOCERRSTAT (but need to be the only one open in order=0A=
	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY=0A=
	 * and MTCOMP (but need to be the only one accessing this=0A=
	 * device to run those).=0A=
	 */=0A=
=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		switch (cmd) {=0A=
		case MTIOCGETEOTMODEL:=0A=
		case MTIOCGET:=0A=
			break;=0A=
		case MTIOCERRSTAT:=0A=
			/*=0A=
			 * If the periph isn't already locked, lock it=0A=
			 * so our MTIOCERRSTAT can reset latched error stats.=0A=
			 *=0A=
			 * If the periph is already locked, skip it because=0A=
			 * we're just getting status and it'll be up to the=0A=
			 * other thread that has this device open to do=0A=
			 * an MTIOCERRSTAT that would clear latched status.=0A=
			 */=0A=
			s =3D splsoftcam();=0A=
			if ((periph->flags & CAM_PERIPH_LOCKED) =3D=3D 0) {=0A=
				error =3D cam_periph_lock(periph, PRIBIO|PCATCH);=0A=
				if (error !=3D 0) {=0A=
					splx(s);=0A=
					return (error);=0A=
				}=0A=
				didlockperiph =3D 1;=0A=
			}=0A=
			break;=0A=
=0A=
		case MTIOCSETEOTMODEL:=0A=
		case MTSETBSIZ:=0A=
		case MTSETDNSTY:=0A=
		case MTCOMP:=0A=
			/*=0A=
			 * We need to acquire the peripheral here rather=0A=
			 * than at open time because we are sharing writable=0A=
			 * access to data structures.=0A=
			 */=0A=
			s =3D splsoftcam();=0A=
			error =3D cam_periph_lock(periph, PRIBIO|PCATCH);=0A=
			if (error !=3D 0) {=0A=
				splx(s);=0A=
				return (error);=0A=
			}=0A=
			didlockperiph =3D 1;=0A=
			break;=0A=
=0A=
		default:=0A=
			return (EINVAL);=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Find the device that the user is talking about=0A=
	 */=0A=
	switch (cmd) {=0A=
	case MTIOCGET:=0A=
	{=0A=
		struct mtget *g =3D (struct mtget *)arg;=0A=
=0A=
		/*=0A=
		 * If this isn't the control mode device, actually go out=0A=
		 * and ask the drive again what it's set to.=0A=
		 */=0A=
		if (!SA_IS_CTRL(dev)) {=0A=
			u_int8_t write_protect;=0A=
			int comp_enabled, comp_supported;=0A=
			error =3D sagetparams(periph, SA_PARAM_ALL,=0A=
			    &softc->media_blksize, &softc->media_density,=0A=
			    &softc->media_numblks, &softc->buffer_mode,=0A=
			    &write_protect, &softc->speed, &comp_supported,=0A=
			    &comp_enabled, &softc->comp_algorithm, NULL);=0A=
			if (error)=0A=
				break;=0A=
			if (write_protect)=0A=
				softc->flags |=3D SA_FLAG_TAPE_WP;=0A=
			else=0A=
				softc->flags &=3D ~SA_FLAG_TAPE_WP;=0A=
			softc->flags &=3D ~(SA_FLAG_COMP_SUPP|=0A=
			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);=0A=
			if (comp_supported) {=0A=
				if (softc->saved_comp_algorithm =3D=3D 0)=0A=
					softc->saved_comp_algorithm =3D=0A=
					    softc->comp_algorithm;=0A=
				softc->flags |=3D SA_FLAG_COMP_SUPP;=0A=
				if (comp_enabled)=0A=
					softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
			} else  =0A=
				softc->flags |=3D SA_FLAG_COMP_UNSUPP;=0A=
		}=0A=
		bzero(g, sizeof(struct mtget));=0A=
		g->mt_type =3D MT_ISAR;=0A=
		if (softc->flags & SA_FLAG_COMP_UNSUPP) {=0A=
			g->mt_comp =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp0 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp1 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp2 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp3 =3D MT_COMP_UNSUPP;=0A=
		} else {=0A=
			if ((softc->flags & SA_FLAG_COMP_ENABLED) =3D=3D 0) {=0A=
				g->mt_comp =3D MT_COMP_DISABLED;=0A=
			} else {=0A=
				g->mt_comp =3D softc->comp_algorithm;=0A=
			}=0A=
			g->mt_comp0 =3D softc->comp_algorithm;=0A=
			g->mt_comp1 =3D softc->comp_algorithm;=0A=
			g->mt_comp2 =3D softc->comp_algorithm;=0A=
			g->mt_comp3 =3D softc->comp_algorithm;=0A=
		}=0A=
		g->mt_density =3D softc->media_density;=0A=
		g->mt_density0 =3D softc->media_density;=0A=
		g->mt_density1 =3D softc->media_density;=0A=
		g->mt_density2 =3D softc->media_density;=0A=
		g->mt_density3 =3D softc->media_density;=0A=
		g->mt_blksiz =3D softc->media_blksize;=0A=
		g->mt_blksiz0 =3D softc->media_blksize;=0A=
		g->mt_blksiz1 =3D softc->media_blksize;=0A=
		g->mt_blksiz2 =3D softc->media_blksize;=0A=
		g->mt_blksiz3 =3D softc->media_blksize;=0A=
		g->mt_fileno =3D softc->fileno;=0A=
		g->mt_blkno =3D softc->blkno;=0A=
		g->mt_dsreg =3D (short) softc->dsreg;=0A=
		/*=0A=
		 * Yes, we know that this is likely to overflow=0A=
		 */=0A=
		if (softc->last_resid_was_io) {=0A=
			if ((g->mt_resid =3D (short) softc->last_io_resid) !=3D 0) {=0A=
				if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph) {=0A=
					softc->last_io_resid =3D 0;=0A=
				}=0A=
			}=0A=
		} else {=0A=
			if ((g->mt_resid =3D (short)softc->last_ctl_resid) !=3D 0) {=0A=
				if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph) {=0A=
					softc->last_ctl_resid =3D 0;=0A=
				}=0A=
			}=0A=
		}=0A=
		error =3D 0;=0A=
		break;=0A=
	}=0A=
	case MTIOCERRSTAT:=0A=
	{=0A=
		struct scsi_tape_errors *sep =3D=0A=
		    &((union mterrstat *)arg)->scsi_errstat;=0A=
=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,=0A=
		    ("saioctl: MTIOCERRSTAT\n"));=0A=
=0A=
		bzero(sep, sizeof(*sep));=0A=
		sep->io_resid =3D softc->last_io_resid;=0A=
		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,=0A=
		    sizeof (sep->io_sense));=0A=
		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,=0A=
		    sizeof (sep->io_cdb));=0A=
		sep->ctl_resid =3D softc->last_ctl_resid;=0A=
		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,=0A=
		    sizeof (sep->ctl_sense));=0A=
		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,=0A=
		    sizeof (sep->ctl_cdb));=0A=
=0A=
		if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph)=0A=
			bzero((caddr_t) &softc->errinfo,=0A=
			    sizeof (softc->errinfo));=0A=
		error =3D 0;=0A=
		break;=0A=
	}=0A=
	case MTIOCTOP:=0A=
	{=0A=
		struct mtop *mt;=0A=
		int    count;=0A=
=0A=
		mt =3D (struct mtop *)arg;=0A=
=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,=0A=
			 ("saioctl: op=3D0x%x count=3D0x%x\n",=0A=
			  mt->mt_op, mt->mt_count));=0A=
=0A=
		count =3D mt->mt_count;=0A=
		switch (mt->mt_op) {=0A=
		case MTWEOF:	/* write an end-of-file marker */=0A=
			/*=0A=
			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN=0A=
			 * flag because by keeping track of filemarks=0A=
			 * we have last written we know ehether or not=0A=
			 * we need to write more when we close the device.=0A=
			 */=0A=
			error =3D sawritefilemarks(periph, count, FALSE);=0A=
			break;=0A=
		case MTWSS:	/* write a setmark */=0A=
			error =3D sawritefilemarks(periph, count, TRUE);=0A=
			break;=0A=
		case MTBSR:	/* backward space record */=0A=
		case MTFSR:	/* forward space record */=0A=
		case MTBSF:	/* backward space file */=0A=
		case MTFSF:	/* forward space file */=0A=
		case MTBSS:	/* backward space setmark */=0A=
		case MTFSS:	/* forward space setmark */=0A=
		case MTEOD:	/* space to end of recorded medium */=0A=
		{=0A=
			int nmarks;=0A=
=0A=
			spaceop =3D SS_FILEMARKS;=0A=
			nmarks =3D softc->filemarks;=0A=
			error =3D sacheckeod(periph);=0A=
			if (error) {=0A=
				xpt_print_path(periph->path);=0A=
				printf("EOD check prior to spacing failed\n");=0A=
				softc->flags |=3D SA_FLAG_EIO_PENDING;=0A=
				break;=0A=
			}=0A=
			nmarks -=3D softc->filemarks;=0A=
			switch(mt->mt_op) {=0A=
			case MTBSR:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSR:=0A=
				spaceop =3D SS_BLOCKS;=0A=
				break;=0A=
			case MTBSF:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSF:=0A=
				break;=0A=
			case MTBSS:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSS:=0A=
				spaceop =3D SS_SETMARKS;=0A=
				break;=0A=
			case MTEOD:=0A=
				spaceop =3D SS_EOD;=0A=
				count =3D 0;=0A=
				nmarks =3D 0;=0A=
				break;=0A=
			default:=0A=
				error =3D EINVAL;=0A=
				break;=0A=
			}=0A=
			if (error)=0A=
				break;=0A=
=0A=
			nmarks =3D softc->filemarks;=0A=
			/*=0A=
			 * XXX: Why are we checking again?=0A=
			 */=0A=
			error =3D sacheckeod(periph);=0A=
			if (error)=0A=
				break;=0A=
			nmarks -=3D softc->filemarks;=0A=
			error =3D saspace(periph, count - nmarks, spaceop);=0A=
			/*=0A=
			 * At this point, clear that we've written the tape=0A=
			 * and that we've written any filemarks. We really=0A=
			 * don't know what the applications wishes to do next-=0A=
			 * the sacheckeod's will make sure we terminated the=0A=
			 * tape correctly if we'd been writing, but the next=0A=
			 * action the user application takes will set again=0A=
			 * whether we need to write filemarks.=0A=
			 */=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->filemarks =3D 0;=0A=
			break;=0A=
		}=0A=
		case MTREW:	/* rewind */=0A=
			(void) sacheckeod(periph);=0A=
			error =3D sarewind(periph);=0A=
			/* see above */=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			softc->filemarks =3D 0;=0A=
			break;=0A=
		case MTERASE:	/* erase */=0A=
			error =3D saerase(periph, count);=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			break;=0A=
		case MTRETENS:	/* re-tension tape */=0A=
			error =3D saretension(periph);		=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			break;=0A=
		case MTOFFL:	/* rewind and put the drive offline */=0A=
=0A=
			(void) sacheckeod(periph);=0A=
			/* see above */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_WRITTEN;=0A=
			softc->filemarks =3D 0;=0A=
=0A=
			error =3D sarewind(periph);=0A=
			/* clear the frozen flag anyway */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_FROZEN;=0A=
=0A=
			/*=0A=
			 * Be sure to allow media removal before ejecting.=0A=
			 */=0A=
=0A=
			saprevent(periph, PR_ALLOW);=0A=
			if (error =3D=3D 0) {=0A=
				error =3D saloadunload(periph, FALSE);=0A=
				if (error =3D=3D 0) {=0A=
					softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
				}=0A=
			}=0A=
			break;=0A=
=0A=
		case MTNOP:	/* no operation, sets status only */=0A=
		case MTCACHE:	/* enable controller cache */=0A=
		case MTNOCACHE:	/* disable controller cache */=0A=
			error =3D 0;=0A=
			break;=0A=
=0A=
		case MTSETBSIZ:	/* Set block size for device */=0A=
=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE, count,=0A=
					    0, 0, 0);=0A=
			if (error =3D=3D 0) {=0A=
				softc->last_media_blksize =3D=0A=
				    softc->media_blksize;=0A=
				softc->media_blksize =3D count;=0A=
				if (count) {=0A=
					softc->flags |=3D SA_FLAG_FIXED;=0A=
					if (powerof2(count)) {=0A=
						softc->blk_shift =3D=0A=
						    ffs(count) - 1;=0A=
						softc->blk_mask =3D count - 1;=0A=
					} else {=0A=
						softc->blk_mask =3D ~0;=0A=
						softc->blk_shift =3D 0;=0A=
					}=0A=
					/*=0A=
					 * Make the user's desire 'persistent'.=0A=
					 */=0A=
					softc->quirks &=3D ~SA_QUIRK_VARIABLE;=0A=
					softc->quirks |=3D SA_QUIRK_FIXED;=0A=
				} else {=0A=
					softc->flags &=3D ~SA_FLAG_FIXED;=0A=
					if (softc->max_blk =3D=3D 0) {=0A=
						softc->max_blk =3D ~0;=0A=
					}=0A=
					softc->blk_shift =3D 0;=0A=
					if (softc->blk_gran !=3D 0) {=0A=
						softc->blk_mask =3D=0A=
						    softc->blk_gran - 1;=0A=
					} else {=0A=
						softc->blk_mask =3D 0;=0A=
					}=0A=
					/*=0A=
					 * Make the user's desire 'persistent'.=0A=
					 */=0A=
					softc->quirks |=3D SA_QUIRK_VARIABLE;=0A=
					softc->quirks &=3D ~SA_QUIRK_FIXED;=0A=
				}=0A=
			}=0A=
			break;=0A=
		case MTSETDNSTY:	/* Set density for device and mode */=0A=
			if (count > UCHAR_MAX) {=0A=
				error =3D EINVAL;	=0A=
				break;=0A=
			} else {=0A=
				error =3D sasetparams(periph, SA_PARAM_DENSITY,=0A=
						    0, count, 0, 0);=0A=
			}=0A=
			break;=0A=
		case MTCOMP:	/* enable compression */=0A=
			/*=0A=
			 * Some devices don't support compression, and=0A=
			 * don't like it if you ask them for the=0A=
			 * compression page.=0A=
			 */=0A=
			if ((softc->quirks & SA_QUIRK_NOCOMP) ||=0A=
			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {=0A=
				error =3D ENODEV;=0A=
				break;=0A=
			}=0A=
			error =3D sasetparams(periph, SA_PARAM_COMPRESSION,=0A=
			    0, 0, count, SF_NO_PRINT);=0A=
			break;=0A=
		default:=0A=
			error =3D EINVAL;=0A=
		}=0A=
		break;=0A=
	}=0A=
	case MTIOCIEOT:=0A=
	case MTIOCEEOT:=0A=
		error =3D 0;=0A=
		break;=0A=
	case MTIOCRDSPOS:=0A=
		error =3D sardpos(periph, 0, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCRDHPOS:=0A=
		error =3D sardpos(periph, 1, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCSLOCATE:=0A=
		error =3D sasetpos(periph, 0, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCHLOCATE:=0A=
		error =3D sasetpos(periph, 1, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCGETEOTMODEL:=0A=
		error =3D 0;=0A=
		if (softc->quirks & SA_QUIRK_1FM)=0A=
			mode =3D 1;=0A=
		else=0A=
			mode =3D 2;=0A=
		*((u_int32_t *) arg) =3D mode;=0A=
		break;=0A=
	case MTIOCSETEOTMODEL:=0A=
		error =3D 0;=0A=
		switch (*((u_int32_t *) arg)) {=0A=
		case 1:=0A=
			softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
			softc->quirks |=3D SA_QUIRK_1FM;=0A=
			break;=0A=
		case 2:=0A=
			softc->quirks &=3D ~SA_QUIRK_1FM;=0A=
			softc->quirks |=3D SA_QUIRK_2FM;=0A=
			break;=0A=
		default:=0A=
			error =3D EINVAL;=0A=
			break;=0A=
		}=0A=
		break;=0A=
	default:=0A=
		error =3D cam_periph_ioctl(periph, cmd, arg, saerror);=0A=
		break;=0A=
	}=0A=
=0A=
	/*=0A=
	 * Check to see if we cleared a frozen state=0A=
	 */=0A=
	if (error =3D=3D 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {=0A=
		switch(cmd) {=0A=
		case MTIOCRDSPOS:=0A=
		case MTIOCRDHPOS:=0A=
		case MTIOCSLOCATE:=0A=
		case MTIOCHLOCATE:=0A=
			softc->fileno =3D (daddr_t) -1;=0A=
			softc->blkno =3D (daddr_t) -1;=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_FROZEN;=0A=
			xpt_print_path(periph->path);=0A=
			printf("tape state now unfrozen.\n");=0A=
			break;=0A=
		default:=0A=
			break;=0A=
		}=0A=
	}=0A=
	if (didlockperiph) {=0A=
		cam_periph_unlock(periph);=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static void=0A=
sainit(void)=0A=
{=0A=
	cam_status status;=0A=
	struct cam_path *path;=0A=
=0A=
	/*=0A=
	 * Install a global async callback.=0A=
	 */=0A=
	status =3D xpt_create_path(&path, NULL, CAM_XPT_PATH_ID,=0A=
				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);=0A=
=0A=
	if (status =3D=3D CAM_REQ_CMP) {=0A=
		/* Register the async callbacks of interrest */=0A=
		struct ccb_setasync csa; /*=0A=
					  * This is an immediate CCB,=0A=
					  * so using the stack is OK=0A=
					  */=0A=
		xpt_setup_ccb(&csa.ccb_h, path, 5);=0A=
		csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
		csa.event_enable =3D AC_FOUND_DEVICE;=0A=
		csa.callback =3D saasync;=0A=
		csa.callback_arg =3D NULL;=0A=
		xpt_action((union ccb *)&csa);=0A=
		status =3D csa.ccb_h.status;=0A=
		xpt_free_path(path);=0A=
	}=0A=
=0A=
	if (status !=3D CAM_REQ_CMP) {=0A=
		printf("sa: Failed to attach master async callback "=0A=
		       "due to status 0x%x!\n", status);=0A=
	}=0A=
}=0A=
=0A=
static void=0A=
saoninvalidate(struct cam_periph *periph)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_setasync csa;=0A=
	int s;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * De-register any async callbacks.=0A=
	 */=0A=
	xpt_setup_ccb(&csa.ccb_h, periph->path,=0A=
		      /* priority */ 5);=0A=
	csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
	csa.event_enable =3D 0;=0A=
	csa.callback =3D saasync;=0A=
	csa.callback_arg =3D periph;=0A=
	xpt_action((union ccb *)&csa);=0A=
=0A=
	softc->flags |=3D SA_FLAG_INVALID;=0A=
=0A=
	/*=0A=
	 * Although the oninvalidate() routines are always called at=0A=
	 * splsoftcam, we need to be at splbio() here to keep the buffer=0A=
	 * queue from being modified while we traverse it.=0A=
	 */=0A=
	s =3D splbio();=0A=
=0A=
	/*=0A=
	 * Return all queued I/O with ENXIO.=0A=
	 * XXX Handle any transactions queued to the card=0A=
	 *     with XPT_ABORT_CCB.=0A=
	 */=0A=
	bioq_flush(&softc->bio_queue, NULL, ENXIO);=0A=
	softc->queue_count =3D 0;=0A=
	splx(s);=0A=
=0A=
	xpt_print_path(periph->path);=0A=
	printf("lost device\n");=0A=
=0A=
}=0A=
=0A=
static void=0A=
sacleanup(struct cam_periph *periph)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	int i;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	devstat_remove_entry(softc->device_stats);=0A=
=0A=
	destroy_dev(softc->devs.ctl_dev);=0A=
=0A=
	for (i =3D 0; i < SA_NUM_MODES; i++) {=0A=
		destroy_dev(softc->devs.mode_devs[i].r_dev);=0A=
		destroy_dev(softc->devs.mode_devs[i].nr_dev);=0A=
		destroy_dev(softc->devs.mode_devs[i].er_dev);=0A=
	}=0A=
=0A=
	xpt_print_path(periph->path);=0A=
	printf("removing device entry\n");=0A=
	free(softc, M_DEVBUF);=0A=
}=0A=
=0A=
static void=0A=
saasync(void *callback_arg, u_int32_t code,=0A=
	struct cam_path *path, void *arg)=0A=
{=0A=
	struct cam_periph *periph;=0A=
=0A=
	periph =3D (struct cam_periph *)callback_arg;=0A=
	switch (code) {=0A=
	case AC_FOUND_DEVICE:=0A=
	{=0A=
		struct ccb_getdev *cgd;=0A=
		cam_status status;=0A=
=0A=
		cgd =3D (struct ccb_getdev *)arg;=0A=
		if (cgd =3D=3D NULL)=0A=
			break;=0A=
=0A=
		if (SID_TYPE(&cgd->inq_data) !=3D T_SEQUENTIAL)=0A=
			break;=0A=
=0A=
		/*=0A=
		 * Allocate a peripheral instance for=0A=
		 * this device and start the probe=0A=
		 * process.=0A=
		 */=0A=
		status =3D cam_periph_alloc(saregister, saoninvalidate,=0A=
					  sacleanup, sastart,=0A=
					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,=0A=
					  saasync, AC_FOUND_DEVICE, cgd);=0A=
=0A=
		if (status !=3D CAM_REQ_CMP=0A=
		 && status !=3D CAM_REQ_INPROG)=0A=
			printf("saasync: Unable to probe new device "=0A=
				"due to status 0x%x\n", status);=0A=
		break;=0A=
	}=0A=
	default:=0A=
		cam_periph_async(periph, code, path, arg);=0A=
		break;=0A=
	}=0A=
}=0A=
=0A=
static cam_status=0A=
saregister(struct cam_periph *periph, void *arg)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_setasync csa;=0A=
	struct ccb_getdev *cgd;=0A=
	caddr_t match;=0A=
	int i;=0A=
	=0A=
	cgd =3D (struct ccb_getdev *)arg;=0A=
	if (periph =3D=3D NULL) {=0A=
		printf("saregister: periph was NULL!!\n");=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
=0A=
	if (cgd =3D=3D NULL) {=0A=
		printf("saregister: no getdev CCB, can't register device\n");=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
=0A=
	softc =3D (struct sa_softc *)=0A=
	    malloc(sizeof (*softc), M_DEVBUF, M_NOWAIT | M_ZERO);=0A=
	if (softc =3D=3D NULL) {=0A=
		printf("saregister: Unable to probe new device. "=0A=
		       "Unable to allocate softc\n");				=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
	softc->scsi_rev =3D SID_ANSI_REV(&cgd->inq_data);=0A=
	softc->state =3D SA_STATE_NORMAL;=0A=
	softc->fileno =3D (daddr_t) -1;=0A=
	softc->blkno =3D (daddr_t) -1;=0A=
=0A=
	bioq_init(&softc->bio_queue);=0A=
	periph->softc =3D softc;=0A=
=0A=
	/*=0A=
	 * See if this device has any quirks.=0A=
	 */=0A=
	match =3D cam_quirkmatch((caddr_t)&cgd->inq_data,=0A=
			       (caddr_t)sa_quirk_table,=0A=
			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),=0A=
			       sizeof(*sa_quirk_table), scsi_inquiry_match);=0A=
=0A=
	if (match !=3D NULL) {=0A=
		softc->quirks =3D ((struct sa_quirk_entry *)match)->quirks;=0A=
		softc->last_media_blksize =3D=0A=
		    ((struct sa_quirk_entry *)match)->prefblk;=0A=
#ifdef	CAMDEBUG=0A=
		xpt_print_path(periph->path);=0A=
		printf("found quirk entry %d\n", (int)=0A=
		    (((struct sa_quirk_entry *) match) - sa_quirk_table));=0A=
#endif=0A=
	} else=0A=
		softc->quirks =3D SA_QUIRK_NONE;=0A=
=0A=
	/*=0A=
 	 * The SA driver supports a blocksize, but we don't know the=0A=
	 * blocksize until we media is inserted.  So, set a flag to=0A=
	 * indicate that the blocksize is unavailable right now.=0A=
	 */=0A=
	softc->device_stats =3D devstat_new_entry("sa", periph->unit_number, 0,=0A=
	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |=0A=
	    DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_TAPE);=0A=
=0A=
	softc->devs.ctl_dev =3D make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,=0A=
	    periph->unit_number, 0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR,=0A=
	    0660, "%s%d.ctl", periph->periph_name, periph->unit_number);=0A=
	softc->devs.ctl_dev->si_drv1 =3D periph;=0A=
=0A=
	for (i =3D 0; i < SA_NUM_MODES; i++) {=0A=
=0A=
		softc->devs.mode_devs[i].r_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_R),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].r_dev->si_drv1 =3D periph;=0A=
=0A=
		softc->devs.mode_devs[i].nr_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_NR),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].nr_dev->si_drv1 =3D periph;=0A=
=0A=
		softc->devs.mode_devs[i].er_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_ER),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].er_dev->si_drv1 =3D periph;=0A=
=0A=
		/*=0A=
		 * Make the (well known) aliases for the first mode.=0A=
		 */=0A=
		if (i =3D=3D 0) {=0A=
			struct cdev *alias;=0A=
=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].r_dev,=0A=
			   "%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].nr_dev,=0A=
			    "n%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].er_dev,=0A=
			    "e%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Add an async callback so that we get=0A=
	 * notified if this device goes away.=0A=
	 */=0A=
	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);=0A=
	csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
	csa.event_enable =3D AC_LOST_DEVICE;=0A=
	csa.callback =3D saasync;=0A=
	csa.callback_arg =3D periph;=0A=
	xpt_action((union ccb *)&csa);=0A=
=0A=
	xpt_announce_periph(periph, NULL);=0A=
=0A=
	return (CAM_REQ_CMP);=0A=
}=0A=
=0A=
static void=0A=
sastart(struct cam_periph *periph, union ccb *start_ccb)=0A=
{=0A=
	struct sa_softc *softc;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sastart\n"));=0A=
=0A=
	=0A=
	switch (softc->state) {=0A=
	case SA_STATE_NORMAL:=0A=
	{=0A=
		/* Pull a buffer from the queue and get going on it */		=0A=
		struct bio *bp;=0A=
		int s;=0A=
=0A=
		/*=0A=
		 * See if there is a buf with work for us to do..=0A=
		 */=0A=
		s =3D splbio();=0A=
		bp =3D bioq_first(&softc->bio_queue);=0A=
		if (periph->immediate_priority <=3D periph->pinfo.priority) {=0A=
			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,=0A=
					("queuing for immediate ccb\n"));=0A=
			Set_CCB_Type(start_ccb, SA_CCB_WAITING);=0A=
			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,=0A=
					  periph_links.sle);=0A=
			periph->immediate_priority =3D CAM_PRIORITY_NONE;=0A=
			splx(s);=0A=
			wakeup(&periph->ccb_list);=0A=
		} else if (bp =3D=3D NULL) {=0A=
			splx(s);=0A=
			xpt_release_ccb(start_ccb);=0A=
		} else if ((softc->flags & SA_FLAG_ERR_PENDING) !=3D 0) {=0A=
			struct bio *done_bp;=0A=
again:=0A=
			softc->queue_count--;=0A=
			bioq_remove(&softc->bio_queue, bp);=0A=
			bp->bio_resid =3D bp->bio_bcount;=0A=
			done_bp =3D bp;=0A=
			if ((softc->flags & SA_FLAG_EOM_PENDING) !=3D 0) {=0A=
				/*=0A=
				 * We now just clear errors in this case=0A=
				 * and let the residual be the notifier.=0A=
				 */=0A=
				bp->bio_error =3D 0;=0A=
			} else if ((softc->flags & SA_FLAG_EOF_PENDING) !=3D 0) {=0A=
				/*=0A=
				 * This can only happen if we're reading=0A=
				 * in fixed length mode. In this case,=0A=
				 * we dump the rest of the list the=0A=
				 * same way.=0A=
				 */=0A=
				bp->bio_error =3D 0;=0A=
				if (bioq_first(&softc->bio_queue) !=3D NULL) {=0A=
					biodone(done_bp);=0A=
					goto again;=0A=
				}=0A=
			} else if ((softc->flags & SA_FLAG_EIO_PENDING) !=3D 0) {=0A=
				bp->bio_error =3D EIO;=0A=
				bp->bio_flags |=3D BIO_ERROR;=0A=
			}=0A=
			bp =3D bioq_first(&softc->bio_queue);=0A=
			/*=0A=
			 * Only if we have no other buffers queued up=0A=
			 * do we clear the pending error flag.=0A=
			 */=0A=
			if (bp =3D=3D NULL)=0A=
				softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "=0A=
			    "%d more buffers queued up\n",=0A=
			    (softc->flags & SA_FLAG_ERR_PENDING),=0A=
			    (bp !=3D NULL)? "not " : " ", softc->queue_count));=0A=
			splx(s);=0A=
			xpt_release_ccb(start_ccb);=0A=
			biodone(done_bp);=0A=
		} else {=0A=
			u_int32_t length;=0A=
=0A=
			bioq_remove(&softc->bio_queue, bp);=0A=
			softc->queue_count--;=0A=
=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
				if (softc->blk_shift !=3D 0) {=0A=
					length =3D=0A=
					    bp->bio_bcount >> softc->blk_shift;=0A=
				} else if (softc->media_blksize !=3D 0) {=0A=
					length =3D bp->bio_bcount /=0A=
					    softc->media_blksize;=0A=
				} else {=0A=
					bp->bio_error =3D EIO;=0A=
					xpt_print_path(periph->path);=0A=
					printf("zero blocksize for "=0A=
					    "FIXED length writes?\n");=0A=
					splx(s);=0A=
					biodone(bp);=0A=
					break;=0A=
				}=0A=
#if	0=0A=
				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,=0A=
				    ("issuing a %d fixed record %s\n",=0A=
				    length,  (bp->bio_cmd =3D=3D BIO_READ)? "read" :=0A=
				    "write"));=0A=
#endif=0A=
			} else {=0A=
				length =3D bp->bio_bcount;=0A=
#if	0=0A=
				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,=0A=
				    ("issuing a %d variable byte %s\n",=0A=
				    length,  (bp->bio_cmd =3D=3D BIO_READ)? "read" :=0A=
				    "write"));=0A=
#endif=0A=
			}=0A=
			devstat_start_transaction_bio(softc->device_stats, bp);=0A=
			/*=0A=
			 * Some people have theorized that we should=0A=
			 * suppress illegal length indication if we are=0A=
			 * running in variable block mode so that we don't=0A=
			 * have to request sense every time our requested=0A=
			 * block size is larger than the written block.=0A=
			 * The residual information from the ccb allows=0A=
			 * us to identify this situation anyway.  The only=0A=
			 * problem with this is that we will not get=0A=
			 * information about blocks that are larger than=0A=
			 * our read buffer unless we set the block size=0A=
			 * in the mode page to something other than 0.=0A=
			 *=0A=
			 * I believe that this is a non-issue. If user apps=0A=
			 * don't adjust their read size to match our record=0A=
			 * size, that's just life. Anyway, the typical usage=0A=
			 * would be to issue, e.g., 64KB reads and occasionally=0A=
			 * have to do deal with 512 byte or 1KB intermediate=0A=
			 * records.=0A=
			 */=0A=
			softc->dsreg =3D (bp->bio_cmd =3D=3D BIO_READ)?=0A=
			    MTIO_DSREG_RD : MTIO_DSREG_WR;=0A=
			scsi_sa_read_write(&start_ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd =3D=3D BIO_READ),=0A=
			    FALSE, (softc->flags & SA_FLAG_FIXED) !=3D 0,=0A=
			    length, bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,=0A=
			    IO_TIMEOUT);=0A=
			start_ccb->ccb_h.ccb_pflags &=3D ~SA_POSITION_UPDATED;=0A=
			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);=0A=
			start_ccb->ccb_h.ccb_bp =3D bp;=0A=
			bp =3D bioq_first(&softc->bio_queue);=0A=
			splx(s);=0A=
			xpt_action(start_ccb);=0A=
		}=0A=
		=0A=
		if (bp !=3D NULL) {=0A=
			/* Have more work to do, so ensure we stay scheduled */=0A=
			xpt_schedule(periph, 1);=0A=
		}=0A=
		break;=0A=
	}=0A=
	case SA_STATE_ABNORMAL:=0A=
	default:=0A=
		panic("state 0x%x in sastart", softc->state);=0A=
		break;=0A=
	}=0A=
}=0A=
=0A=
=0A=
static void=0A=
sadone(struct cam_periph *periph, union ccb *done_ccb)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_scsiio *csio;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	csio =3D &done_ccb->csio;=0A=
	switch (CCB_Type(csio)) {=0A=
	case SA_CCB_BUFFER_IO:=0A=
	{=0A=
		struct bio *bp;=0A=
		int error;=0A=
=0A=
		softc->dsreg =3D MTIO_DSREG_REST;=0A=
		bp =3D (struct bio *)done_ccb->ccb_h.ccb_bp;=0A=
		error =3D 0;=0A=
		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) !=3D CAM_REQ_CMP) {=0A=
			if ((error =3D saerror(done_ccb, 0, 0)) =3D=3D ERESTART) {=0A=
				/*=0A=
				 * A retry was scheduled, so just return.=0A=
				 */=0A=
				return;=0A=
			}=0A=
		}=0A=
=0A=
		if (error =3D=3D EIO) {=0A=
			int s;			=0A=
=0A=
			/*=0A=
			 * Catastrophic error. Mark the tape as frozen=0A=
			 * (we no longer know tape position).=0A=
			 *=0A=
			 * Return all queued I/O with EIO, and unfreeze=0A=
			 * our queue so that future transactions that=0A=
			 * attempt to fix this problem can get to the=0A=
			 * device.=0A=
			 *=0A=
			 */=0A=
=0A=
			s =3D splbio();=0A=
			softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
			bioq_flush(&softc->bio_queue, NULL, EIO);=0A=
			splx(s);=0A=
		}=0A=
		if (error !=3D 0) {=0A=
			bp->bio_resid =3D bp->bio_bcount;=0A=
			bp->bio_error =3D error;=0A=
			bp->bio_flags |=3D BIO_ERROR;=0A=
			/*=0A=
			 * In the error case, position is updated in saerror.=0A=
			 */=0A=
		} else {=0A=
			bp->bio_resid =3D csio->resid;=0A=
			bp->bio_error =3D 0;=0A=
			if (csio->resid !=3D 0) {=0A=
				bp->bio_flags |=3D BIO_ERROR;=0A=
			}=0A=
			if (bp->bio_cmd =3D=3D BIO_WRITE) {=0A=
				softc->flags |=3D SA_FLAG_TAPE_WRITTEN;=0A=
				softc->filemarks =3D 0;=0A=
			}=0A=
			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&=0A=
			    (softc->blkno !=3D (daddr_t) -1)) {=0A=
				if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
					u_int32_t l;=0A=
					if (softc->blk_shift !=3D 0) {=0A=
						l =3D bp->bio_bcount >>=0A=
							softc->blk_shift;=0A=
					} else {=0A=
						l =3D bp->bio_bcount /=0A=
							softc->media_blksize;=0A=
					}=0A=
					softc->blkno +=3D (daddr_t) l;=0A=
				} else {=0A=
					softc->blkno++;=0A=
				}=0A=
			}=0A=
		}=0A=
		/*=0A=
		 * If we had an error (immediate or pending),=0A=
		 * release the device queue now.=0A=
		 */=0A=
		if (error || (softc->flags & SA_FLAG_ERR_PENDING))=0A=
			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);=0A=
#ifdef	CAMDEBUG=0A=
		if (error || bp->bio_resid) {=0A=
			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
			    	  ("error %d resid %ld count %ld\n", error,=0A=
				  bp->bio_resid, bp->bio_bcount));=0A=
		}=0A=
#endif=0A=
		biofinish(bp, softc->device_stats, 0);=0A=
		break;=0A=
	}=0A=
	case SA_CCB_WAITING:=0A=
	{=0A=
		/* Caller will release the CCB */=0A=
		wakeup(&done_ccb->ccb_h.cbfcnp);=0A=
		return;=0A=
	}=0A=
	}=0A=
	xpt_release_ccb(done_ccb);=0A=
}=0A=
=0A=
/*=0A=
 * Mount the tape (make sure it's ready for I/O).=0A=
 */=0A=
static int=0A=
samount(struct cam_periph *periph, int oflags, struct cdev *dev)=0A=
{=0A=
	struct	sa_softc *softc;=0A=
	union	ccb *ccb;=0A=
	int	error;=0A=
=0A=
	/*=0A=
	 * oflags can be checked for 'kind' of open (read-only check) - later=0A=
	 * dev can be checked for a control-mode or compression open - later=0A=
	 */=0A=
	UNUSED_PARAMETER(oflags);=0A=
	UNUSED_PARAMETER(dev);=0A=
=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * This should determine if something has happend since the last=0A=
	 * open/mount that would invalidate the mount. We do *not* want=0A=
	 * to retry this command- we just want the status. But we only=0A=
	 * do this if we're mounted already- if we're not mounted,=0A=
	 * we don't care about the unit read state and can instead use=0A=
	 * this opportunity to attempt to reserve the tape unit.=0A=
	 */=0A=
	=0A=
	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {=0A=
/* Start : reserve every time bpr Atempo */=0A=
		error =3D sareservereleaseunit(periph, TRUE);=0A=
		if (error) {=0A=
			return (error);=0A=
		}=0A=
/* End : reserve every time bpr Atempo */=0A=
		ccb =3D cam_periph_getccb(periph, 1);=0A=
		scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
		if (error =3D=3D ENXIO) {=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
			scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
		} else if (error) {=0A=
			/*=0A=
			 * We don't need to freeze the tape because we=0A=
			 * will now attempt to rewind/load it.=0A=
			 */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
			if (CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("error %d on TUR in samount\n", error);=0A=
			}=0A=
		}=0A=
	} else {=0A=
		error =3D sareservereleaseunit(periph, TRUE);=0A=
		if (error) {=0A=
			return (error);=0A=
		}=0A=
		ccb =3D cam_periph_getccb(periph, 1);=0A=
		scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
	}=0A=
=0A=
	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0) {=0A=
		struct scsi_read_block_limits_data *rblim =3D NULL;=0A=
		int comp_enabled, comp_supported;=0A=
		u_int8_t write_protect, guessing =3D 0;=0A=
=0A=
		/*=0A=
		 * Clear out old state.=0A=
		 */=0A=
		softc->flags &=3D ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|=0A=
				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|=0A=
				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);=0A=
		softc->filemarks =3D 0;=0A=
=0A=
		/*=0A=
		 * *Very* first off, make sure we're loaded to BOT.=0A=
		 */=0A=
		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
=0A=
		/*=0A=
		 * In case this doesn't work, do a REWIND instead=0A=
		 */=0A=
		if (error) {=0A=
			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,=0A=
			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
				softc->device_stats);=0A=
			QFRLS(ccb);=0A=
		}=0A=
		if (error) {=0A=
			xpt_release_ccb(ccb);=0A=
			goto exit;=0A=
		}=0A=
=0A=
		/*=0A=
		 * Do a dummy test read to force access to the=0A=
		 * media so that the drive will really know what's=0A=
		 * there. We actually don't really care what the=0A=
		 * blocksize on tape is and don't expect to really=0A=
		 * read a full record.=0A=
		 */=0A=
		rblim =3D (struct  scsi_read_block_limits_data *)=0A=
		    malloc(8192, M_TEMP, M_WAITOK);=0A=
		if (rblim =3D=3D NULL) {=0A=
			xpt_print_path(ccb->ccb_h.path);=0A=
			printf("no memory for test read\n");=0A=
			xpt_release_ccb(ccb);=0A=
			error =3D ENOMEM;=0A=
			goto exit;=0A=
		}=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_NODREAD) =3D=3D 0) {=0A=
			scsi_sa_read_write(&ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,=0A=
			    (void *) rblim, 8192, SSD_FULL_SIZE,=0A=
			    IO_TIMEOUT);=0A=
			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,=0A=
			    SF_NO_PRINT | SF_RETRY_UA,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
			if (error) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to rewind after test read\n");=0A=
				xpt_release_ccb(ccb);=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Next off, determine block limits.=0A=
		 */=0A=
		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,=0A=
		    rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
		error =3D cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,=0A=
		    SF_NO_PRINT | SF_RETRY_UA, softc->device_stats);=0A=
=0A=
		QFRLS(ccb);=0A=
		xpt_release_ccb(ccb);=0A=
=0A=
		if (error !=3D 0) {=0A=
			/*=0A=
			 * If it's less than SCSI-2, READ BLOCK LIMITS is not=0A=
			 * a MANDATORY command. Anyway- it doesn't matter-=0A=
			 * we can proceed anyway.=0A=
			 */=0A=
			softc->blk_gran =3D 0;=0A=
			softc->max_blk =3D ~0;=0A=
			softc->min_blk =3D 0;=0A=
		} else {=0A=
			if (softc->scsi_rev >=3D SCSI_REV_SPC) {=0A=
				softc->blk_gran =3D RBL_GRAN(rblim);=0A=
			} else {=0A=
				softc->blk_gran =3D 0;=0A=
			}=0A=
			/*=0A=
			 * We take max_blk =3D=3D min_blk to mean a default to=0A=
			 * fixed mode- but note that whatever we get out of=0A=
			 * sagetparams below will actually determine whether=0A=
			 * we are actually *in* fixed mode.=0A=
			 */=0A=
			softc->max_blk =3D scsi_3btoul(rblim->maximum);=0A=
			softc->min_blk =3D scsi_2btoul(rblim->minimum);=0A=
=0A=
=0A=
		}=0A=
		/*=0A=
		 * Next, perform a mode sense to determine=0A=
		 * current density, blocksize, compression etc.=0A=
		 */=0A=
		error =3D sagetparams(periph, SA_PARAM_ALL,=0A=
				    &softc->media_blksize,=0A=
				    &softc->media_density,=0A=
				    &softc->media_numblks,=0A=
				    &softc->buffer_mode, &write_protect,=0A=
				    &softc->speed, &comp_supported,=0A=
				    &comp_enabled, &softc->comp_algorithm,=0A=
				    NULL);=0A=
=0A=
		if (error !=3D 0) {=0A=
			/*=0A=
			 * We could work a little harder here. We could=0A=
			 * adjust our attempts to get information. It=0A=
			 * might be an ancient tape drive. If someone=0A=
			 * nudges us, we'll do that.=0A=
			 */=0A=
			goto exit;=0A=
		}=0A=
=0A=
		/*=0A=
		 * If no quirk has determined that this is a device that is=0A=
		 * preferred to be in fixed or variable mode, now is the time=0A=
		 * to find out.=0A=
	 	 */=0A=
		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) =3D=3D 0) {=0A=
			guessing =3D 1;=0A=
			/*=0A=
			 * This could be expensive to find out. Luckily we=0A=
			 * only need to do this once. If we start out in=0A=
			 * 'default' mode, try and set ourselves to one=0A=
			 * of the densities that would determine a wad=0A=
			 * of other stuff. Go from highest to lowest.=0A=
			 */=0A=
			if (softc->media_density =3D=3D SCSI_DEFAULT_DENSITY) {=0A=
				int i;=0A=
				static u_int8_t ctry[] =3D {=0A=
					SCSI_DENSITY_HALFINCH_PE,=0A=
					SCSI_DENSITY_HALFINCH_6250C,=0A=
					SCSI_DENSITY_HALFINCH_6250,=0A=
					SCSI_DENSITY_HALFINCH_1600,=0A=
					SCSI_DENSITY_HALFINCH_800,=0A=
					SCSI_DENSITY_QIC_4GB,=0A=
					SCSI_DENSITY_QIC_2GB,=0A=
					SCSI_DENSITY_QIC_525_320,=0A=
					SCSI_DENSITY_QIC_150,=0A=
					SCSI_DENSITY_QIC_120,=0A=
					SCSI_DENSITY_QIC_24,=0A=
					SCSI_DENSITY_QIC_11_9TRK,=0A=
					SCSI_DENSITY_QIC_11_4TRK,=0A=
					SCSI_DENSITY_QIC_1320,=0A=
					SCSI_DENSITY_QIC_3080,=0A=
					0=0A=
				};=0A=
				for (i =3D 0; ctry[i]; i++) {=0A=
					error =3D sasetparams(periph,=0A=
					    SA_PARAM_DENSITY, 0, ctry[i],=0A=
					    0, SF_NO_PRINT);=0A=
					if (error =3D=3D 0) {=0A=
						softc->media_density =3D ctry[i];=0A=
						break;=0A=
					}=0A=
				}=0A=
			}=0A=
			switch (softc->media_density) {=0A=
			case SCSI_DENSITY_QIC_11_4TRK:=0A=
			case SCSI_DENSITY_QIC_11_9TRK:=0A=
			case SCSI_DENSITY_QIC_24:=0A=
			case SCSI_DENSITY_QIC_120:=0A=
			case SCSI_DENSITY_QIC_150:=0A=
			case SCSI_DENSITY_QIC_525_320:=0A=
			case SCSI_DENSITY_QIC_1320:=0A=
			case SCSI_DENSITY_QIC_3080:=0A=
				softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
				softc->quirks |=3D SA_QUIRK_FIXED|SA_QUIRK_1FM;=0A=
				softc->last_media_blksize =3D 512;=0A=
				break;=0A=
			case SCSI_DENSITY_QIC_4GB:=0A=
			case SCSI_DENSITY_QIC_2GB:=0A=
				softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
				softc->quirks |=3D SA_QUIRK_FIXED|SA_QUIRK_1FM;=0A=
				softc->last_media_blksize =3D 1024;=0A=
				break;=0A=
			default:=0A=
				softc->last_media_blksize =3D=0A=
				    softc->media_blksize;=0A=
				softc->quirks |=3D SA_QUIRK_VARIABLE;=0A=
				break;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * If no quirk has determined that this is a device that needs=0A=
		 * to have 2 Filemarks at EOD, now is the time to find out.=0A=
		 */=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_2FM) =3D=3D 0) {=0A=
			switch (softc->media_density) {=0A=
			case SCSI_DENSITY_HALFINCH_800:=0A=
			case SCSI_DENSITY_HALFINCH_1600:=0A=
			case SCSI_DENSITY_HALFINCH_6250:=0A=
			case SCSI_DENSITY_HALFINCH_6250C:=0A=
			case SCSI_DENSITY_HALFINCH_PE:=0A=
				softc->quirks &=3D ~SA_QUIRK_1FM;=0A=
				softc->quirks |=3D SA_QUIRK_2FM;=0A=
				break;=0A=
			default:=0A=
				break;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now validate that some info we got makes sense.=0A=
		 */=0A=
		if ((softc->max_blk < softc->media_blksize) ||=0A=
		    (softc->min_blk > softc->media_blksize &&=0A=
		    softc->media_blksize)) {=0A=
			xpt_print_path(ccb->ccb_h.path);=0A=
			printf("BLOCK LIMITS (%d..%d) could not match current "=0A=
			    "block settings (%d)- adjusting\n", softc->min_blk,=0A=
			    softc->max_blk, softc->media_blksize);=0A=
			softc->max_blk =3D softc->min_blk =3D=0A=
			    softc->media_blksize;=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now put ourselves into the right frame of mind based=0A=
		 * upon quirks...=0A=
		 */=0A=
tryagain:=0A=
		/*=0A=
		 * If we want to be in FIXED mode and our current blocksize=0A=
		 * is not equal to our last blocksize (if nonzero), try and=0A=
		 * set ourselves to this last blocksize (as the 'preferred'=0A=
		 * block size).  The initial quirkmatch at registry sets the=0A=
		 * initial 'last' blocksize. If, for whatever reason, this=0A=
		 * 'last' blocksize is zero, set the blocksize to 512,=0A=
		 * or min_blk if that's larger.=0A=
		 */=0A=
		if ((softc->quirks & SA_QUIRK_FIXED) &&=0A=
		    (softc->quirks & SA_QUIRK_NO_MODESEL) =3D=3D 0 &&=0A=
		    (softc->media_blksize !=3D softc->last_media_blksize)) {=0A=
			softc->media_blksize =3D softc->last_media_blksize;=0A=
			if (softc->media_blksize =3D=3D 0) {=0A=
				softc->media_blksize =3D 512;=0A=
				if (softc->media_blksize < softc->min_blk) {=0A=
					softc->media_blksize =3D softc->min_blk;=0A=
				}=0A=
			}=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE,=0A=
			    softc->media_blksize, 0, 0, SF_NO_PRINT);=0A=
			if (error) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set fixed blocksize to %d\n",=0A=
				     softc->media_blksize);=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_VARIABLE) && =0A=
		    (softc->media_blksize !=3D 0)) {=0A=
			softc->last_media_blksize =3D softc->media_blksize;=0A=
			softc->media_blksize =3D 0;=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE,=0A=
			    0, 0, 0, SF_NO_PRINT);=0A=
			if (error) {=0A=
				/*=0A=
				 * If this fails and we were guessing, just=0A=
				 * assume that we got it wrong and go try=0A=
				 * fixed block mode. Don't even check against=0A=
				 * density code at this point.=0A=
				 */=0A=
				if (guessing) {=0A=
					softc->quirks &=3D ~SA_QUIRK_VARIABLE;=0A=
					softc->quirks |=3D SA_QUIRK_FIXED;=0A=
					if (softc->last_media_blksize =3D=3D 0)=0A=
						softc->last_media_blksize =3D 512;=0A=
					goto tryagain;=0A=
				}=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set variable blocksize\n");=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now that we have the current block size,=0A=
		 * set up some parameters for sastart's usage.=0A=
		 */=0A=
		if (softc->media_blksize) {=0A=
			softc->flags |=3D SA_FLAG_FIXED;=0A=
			if (powerof2(softc->media_blksize)) {=0A=
				softc->blk_shift =3D=0A=
				    ffs(softc->media_blksize) - 1;=0A=
				softc->blk_mask =3D softc->media_blksize - 1;=0A=
			} else {=0A=
				softc->blk_mask =3D ~0;=0A=
				softc->blk_shift =3D 0;=0A=
			}=0A=
		} else {=0A=
			/*=0A=
			 * The SCSI-3 spec allows 0 to mean "unspecified".=0A=
			 * The SCSI-1 spec allows 0 to mean 'infinite'.=0A=
			 *=0A=
			 * Either works here.=0A=
			 */=0A=
			if (softc->max_blk =3D=3D 0) {=0A=
				softc->max_blk =3D ~0;=0A=
			}=0A=
			softc->blk_shift =3D 0;=0A=
			if (softc->blk_gran !=3D 0) {=0A=
				softc->blk_mask =3D softc->blk_gran - 1;=0A=
			} else {=0A=
				softc->blk_mask =3D 0;=0A=
			}=0A=
		}=0A=
=0A=
		if (write_protect) =0A=
			softc->flags |=3D SA_FLAG_TAPE_WP;=0A=
=0A=
		if (comp_supported) {=0A=
			if (softc->saved_comp_algorithm =3D=3D 0)=0A=
				softc->saved_comp_algorithm =3D=0A=
				    softc->comp_algorithm;=0A=
			softc->flags |=3D SA_FLAG_COMP_SUPP;=0A=
			if (comp_enabled)=0A=
				softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
		} else=0A=
			softc->flags |=3D SA_FLAG_COMP_UNSUPP;=0A=
=0A=
		if ((softc->buffer_mode =3D=3D SMH_SA_BUF_MODE_NOBUF) &&=0A=
		    (softc->quirks & SA_QUIRK_NO_MODESEL) =3D=3D 0) {=0A=
			error =3D sasetparams(periph, SA_PARAM_BUFF_MODE, 0,=0A=
			    0, 0, SF_NO_PRINT);=0A=
			if (error =3D=3D 0) {=0A=
				softc->buffer_mode =3D SMH_SA_BUF_MODE_SIBUF;=0A=
			} else {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set buffered mode\n");=0A=
			}=0A=
			error =3D 0;	/* not an error */=0A=
		}=0A=
=0A=
=0A=
		if (error =3D=3D 0) {=0A=
			softc->flags |=3D SA_FLAG_TAPE_MOUNTED;=0A=
		}=0A=
exit:=0A=
		if (rblim !=3D NULL)=0A=
			free(rblim, M_TEMP);=0A=
=0A=
		if (error !=3D 0) {=0A=
			softc->dsreg =3D MTIO_DSREG_NIL;=0A=
		} else {=0A=
			softc->fileno =3D softc->blkno =3D 0;=0A=
			softc->dsreg =3D MTIO_DSREG_REST;=0A=
		}=0A=
#ifdef	SA_1FM_AT_EOD=0A=
		if ((softc->quirks & SA_QUIRK_2FM) =3D=3D 0)=0A=
			softc->quirks |=3D SA_QUIRK_1FM;=0A=
#else=0A=
		if ((softc->quirks & SA_QUIRK_1FM) =3D=3D 0)=0A=
			softc->quirks |=3D SA_QUIRK_2FM;=0A=
#endif=0A=
	} else=0A=
		xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If we return an error, we're not mounted any more,=0A=
	 * so release any device reservation.=0A=
	 */=0A=
	if (error !=3D 0) {=0A=
		(void) sareservereleaseunit(periph, FALSE);=0A=
	} else {=0A=
		/*=0A=
		 * Clear I/O residual.=0A=
		 */=0A=
		softc->last_io_resid =3D 0;=0A=
		softc->last_ctl_resid =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
/*=0A=
 * How many filemarks do we need to write if we were to terminate the=0A=
 * tape session right now? Note that this can be a negative number=0A=
 */=0A=
=0A=
static int=0A=
samarkswanted(struct cam_periph *periph)=0A=
{=0A=
	int	markswanted;=0A=
	struct	sa_softc *softc;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	markswanted =3D 0;=0A=
	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) !=3D 0) {=0A=
		markswanted++;=0A=
		if (softc->quirks & SA_QUIRK_2FM)=0A=
			markswanted++;=0A=
	}=0A=
	markswanted -=3D softc->filemarks;=0A=
	return (markswanted);=0A=
}=0A=
=0A=
static int=0A=
sacheckeod(struct cam_periph *periph)=0A=
{=0A=
	int	error;=0A=
	int	markswanted;=0A=
=0A=
	markswanted =3D samarkswanted(periph);=0A=
=0A=
	if (markswanted > 0) {=0A=
		error =3D sawritefilemarks(periph, markswanted, FALSE);=0A=
	} else {=0A=
		error =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)=0A=
{=0A=
	static const char *toobig =3D=0A=
	    "%d-byte tape record bigger than supplied buffer\n";=0A=
	struct	cam_periph *periph;=0A=
	struct	sa_softc *softc;=0A=
	struct	ccb_scsiio *csio;=0A=
	struct	scsi_sense_data *sense;=0A=
	u_int32_t resid =3D 0;=0A=
	int32_t	info =3D 0;=0A=
	cam_status status;=0A=
	int error_code, sense_key, asc, ascq, error, aqvalid;=0A=
=0A=
	periph =3D xpt_path_periph(ccb->ccb_h.path);=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	csio =3D &ccb->csio;=0A=
	sense =3D &csio->sense_data;=0A=
	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);=0A=
	aqvalid =3D sense->extra_len >=3D 6;=0A=
	error =3D 0;=0A=
=0A=
	status =3D csio->ccb_h.status & CAM_STATUS_MASK;=0A=
=0A=
	/*=0A=
	 * Calculate/latch up, any residuals... We do this in a funny 2-step=0A=
	 * so we can print stuff here if we have CAM_DEBUG enabled for this=0A=
	 * unit.=0A=
	 */=0A=
	if (status =3D=3D CAM_SCSI_STATUS_ERROR) {=0A=
		if ((sense->error_code & SSD_ERRCODE_VALID) !=3D 0) {=0A=
			info =3D (int32_t) scsi_4btoul(sense->info);=0A=
			resid =3D info;=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0)=0A=
				resid *=3D softc->media_blksize;=0A=
		} else {=0A=
			resid =3D csio->dxfer_len;=0A=
			info =3D resid;=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
				if (softc->media_blksize)=0A=
					info /=3D softc->media_blksize;=0A=
			}=0A=
		}=0A=
		if (CCB_Type(csio) =3D=3D SA_CCB_BUFFER_IO) {=0A=
			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,=0A=
			    sizeof (struct scsi_sense_data));=0A=
			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,=0A=
			    (int) csio->cdb_len);=0A=
			softc->last_io_resid =3D resid;=0A=
			softc->last_resid_was_io =3D 1;=0A=
		} else {=0A=
			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,=0A=
			    sizeof (struct scsi_sense_data));=0A=
			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,=0A=
			    (int) csio->cdb_len);=0A=
			softc->last_ctl_resid =3D resid;=0A=
			softc->last_resid_was_io =3D 0;=0A=
		}=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=3D0x%x Key 0x%x "=0A=
		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d "=0A=
		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,=0A=
		    sense_key, asc, ascq, status,=0A=
		    sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len));=0A=
	} else {=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
		    ("Cam Status 0x%x\n", status));=0A=
	}=0A=
=0A=
	switch (status) {=0A=
	case CAM_REQ_CMP:=0A=
		return (0);=0A=
	case CAM_SCSI_STATUS_ERROR:=0A=
		/*=0A=
		 * If a read/write command, we handle it here.=0A=
		 */=0A=
		if (CCB_Type(csio) !=3D SA_CCB_WAITING) {=0A=
			break;=0A=
		}=0A=
		/*=0A=
		 * If this was just EOM/EOP, Filemark, Setmark or ILI detected=0A=
		 * on a non read/write command, we assume it's not an error=0A=
		 * and propagate the residule and return.=0A=
		 */=0A=
		if ((aqvalid && asc =3D=3D 0 && ascq > 0 && ascq <=3D 5) ||=0A=
		    (aqvalid =3D=3D 0 && sense_key =3D=3D SSD_KEY_NO_SENSE)) {=0A=
			csio->resid =3D resid;=0A=
			QFRLS(ccb);=0A=
			return (0);=0A=
		}=0A=
		/*=0A=
		 * Otherwise, we let the common code handle this.=0A=
		 */=0A=
		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));=0A=
=0A=
	/*=0A=
	 * XXX: To Be Fixed=0A=
	 * We cannot depend upon CAM honoring retry counts for these.=0A=
	 */=0A=
	case CAM_SCSI_BUS_RESET:=0A=
	case CAM_BDR_SENT:=0A=
		if (ccb->ccb_h.retry_count <=3D 0) {=0A=
			return (EIO);=0A=
		}=0A=
		/* FALLTHROUGH */=0A=
	default:=0A=
		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));=0A=
	}=0A=
=0A=
	/*=0A=
	 * Handle filemark, end of tape, mismatched record sizes....=0A=
	 * From this point out, we're only handling read/write cases.=0A=
	 * Handle writes && reads differently.=0A=
	 */=0A=
=0A=
	if (csio->cdb_io.cdb_bytes[0] =3D=3D SA_WRITE) {=0A=
		if (sense_key =3D=3D SSD_KEY_VOLUME_OVERFLOW) {=0A=
			csio->resid =3D resid;=0A=
			error =3D ENOSPC;=0A=
		} else if (sense->flags & SSD_EOM) {=0A=
			softc->flags |=3D SA_FLAG_EOM_PENDING;=0A=
			/*=0A=
			 * Grotesque as it seems, the few times=0A=
			 * I've actually seen a non-zero resid,=0A=
			 * the tape drive actually lied and had=0A=
			 * written all the data!.=0A=
			 */=0A=
			csio->resid =3D 0;=0A=
		}=0A=
	} else {=0A=
		csio->resid =3D resid;=0A=
		if (sense_key =3D=3D SSD_KEY_BLANK_CHECK) {=0A=
			if (softc->quirks & SA_QUIRK_1FM) {=0A=
				error =3D 0;=0A=
				softc->flags |=3D SA_FLAG_EOM_PENDING;=0A=
			} else {=0A=
				error =3D EIO;=0A=
			}=0A=
		} else if (sense->flags & SSD_FILEMARK) {=0A=
			if (softc->flags & SA_FLAG_FIXED) {=0A=
				error =3D -1;=0A=
				softc->flags |=3D SA_FLAG_EOF_PENDING;=0A=
			}=0A=
			/*=0A=
			 * Unconditionally, if we detected a filemark on a read,=0A=
			 * mark that we've run moved a file ahead.=0A=
			 */=0A=
			if (softc->fileno !=3D (daddr_t) -1) {=0A=
				softc->fileno++;=0A=
				softc->blkno =3D 0;=0A=
				csio->ccb_h.ccb_pflags |=3D SA_POSITION_UPDATED;=0A=
			}=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Incorrect Length usually applies to read, but can apply to writes.=0A=
	 */=0A=
	if (error =3D=3D 0 && (sense->flags & SSD_ILI)) {=0A=
		if (info < 0) {=0A=
			xpt_print_path(csio->ccb_h.path);=0A=
			printf(toobig, csio->dxfer_len - info);=0A=
			csio->resid =3D csio->dxfer_len;=0A=
			error =3D EIO;=0A=
		} else {=0A=
			csio->resid =3D resid;=0A=
			if (softc->flags & SA_FLAG_FIXED) {=0A=
				softc->flags |=3D SA_FLAG_EIO_PENDING;=0A=
			}=0A=
			/*=0A=
			 * Bump the block number if we hadn't seen a filemark.=0A=
			 * Do this independent of errors (we've moved anyway).=0A=
			 */=0A=
			if ((sense->flags & SSD_FILEMARK) =3D=3D 0) {=0A=
				if (softc->blkno !=3D (daddr_t) -1) {=0A=
					softc->blkno++;=0A=
					csio->ccb_h.ccb_pflags |=3D=0A=
					   SA_POSITION_UPDATED;=0A=
				}=0A=
			}=0A=
		}=0A=
	}=0A=
=0A=
	if (error <=3D 0) {=0A=
		/*=0A=
		 * Unfreeze the queue if frozen as we're not returning anything=0A=
		 * to our waiters that would indicate an I/O error has occurred=0A=
		 * (yet).=0A=
		 */=0A=
		QFRLS(ccb);=0A=
		error =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sagetparams(struct cam_periph *periph, sa_params params_to_get,=0A=
	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,=0A=
	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,=0A=
	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,=0A=
	    sa_comp_t *tcs)=0A=
{=0A=
	union ccb *ccb;=0A=
	void *mode_buffer;=0A=
	struct scsi_mode_header_6 *mode_hdr;=0A=
	struct scsi_mode_blk_desc *mode_blk;=0A=
	int mode_buffer_len;=0A=
	struct sa_softc *softc;=0A=
	u_int8_t cpage;=0A=
	int error;=0A=
	cam_status status;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	if (softc->quirks & SA_QUIRK_NO_CPAGE)=0A=
		cpage =3D SA_DEVICE_CONFIGURATION_PAGE;=0A=
	else=0A=
		cpage =3D SA_DATA_COMPRESSION_PAGE;=0A=
=0A=
retry:=0A=
	mode_buffer_len =3D sizeof(*mode_hdr) + sizeof(*mode_blk);=0A=
=0A=
	if (params_to_get & SA_PARAM_COMPRESSION) {=0A=
		if (softc->quirks & SA_QUIRK_NOCOMP) {=0A=
			*comp_supported =3D FALSE;=0A=
			params_to_get &=3D ~SA_PARAM_COMPRESSION;=0A=
		} else=0A=
			mode_buffer_len +=3D sizeof (sa_comp_t);=0A=
	}=0A=
=0A=
	mode_buffer =3D malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);=0A=
	mode_hdr =3D (struct scsi_mode_header_6 *)mode_buffer;=0A=
	mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
=0A=
	/* it is safe to retry this */=0A=
	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?=0A=
	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,=0A=
	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
	    softc->device_stats);=0A=
	QFRLS(ccb);=0A=
=0A=
	status =3D ccb->ccb_h.status & CAM_STATUS_MASK;=0A=
=0A=
	if (error =3D=3D EINVAL && (params_to_get & SA_PARAM_COMPRESSION) !=3D =
0) {=0A=
		/*=0A=
		 * Hmm. Let's see if we can try another page...=0A=
		 * If we've already done that, give up on compression=0A=
		 * for this device and remember this for the future=0A=
		 * and attempt the request without asking for compression=0A=
		 * info.=0A=
		 */=0A=
		if (cpage =3D=3D SA_DATA_COMPRESSION_PAGE) {=0A=
			cpage =3D SA_DEVICE_CONFIGURATION_PAGE;=0A=
			goto retry;=0A=
		}=0A=
		softc->quirks |=3D SA_QUIRK_NOCOMP;=0A=
		free(mode_buffer, M_TEMP);=0A=
		goto retry;=0A=
	} else if (status =3D=3D CAM_SCSI_STATUS_ERROR) {=0A=
		/* Tell the user about the fatal error. */=0A=
		scsi_sense_print(&ccb->csio);=0A=
		goto sagetparamsexit;=0A=
	}=0A=
=0A=
	/*=0A=
	 * If the user only wants the compression information, and=0A=
	 * the device doesn't send back the block descriptor, it's=0A=
	 * no big deal.  If the user wants more than just=0A=
	 * compression, though, and the device doesn't pass back the=0A=
	 * block descriptor, we need to send another mode sense to=0A=
	 * get the block descriptor.=0A=
	 */=0A=
	if ((mode_hdr->blk_desc_len =3D=3D 0) &&=0A=
	    (params_to_get & SA_PARAM_COMPRESSION) &&=0A=
	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {=0A=
=0A=
		/*=0A=
		 * Decrease the mode buffer length by the size of=0A=
		 * the compression page, to make sure the data=0A=
		 * there doesn't get overwritten.=0A=
		 */=0A=
		mode_buffer_len -=3D sizeof (sa_comp_t);=0A=
=0A=
		/*=0A=
		 * Now move the compression page that we presumably=0A=
		 * got back down the memory chunk a little bit so=0A=
		 * it doesn't get spammed.=0A=
		 */=0A=
		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));=0A=
		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));=0A=
=0A=
		/*=0A=
		 * Now, we issue another mode sense and just ask=0A=
		 * for the block descriptor, etc.=0A=
		 */=0A=
=0A=
		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,=0A=
		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE,=0A=
		    SCSIOP_TIMEOUT);=0A=
=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
=0A=
		if (error !=3D 0)=0A=
			goto sagetparamsexit;=0A=
	}=0A=
=0A=
	if (params_to_get & SA_PARAM_BLOCKSIZE)=0A=
		*blocksize =3D scsi_3btoul(mode_blk->blklen);=0A=
=0A=
	if (params_to_get & SA_PARAM_NUMBLOCKS)=0A=
		*numblocks =3D scsi_3btoul(mode_blk->nblocks);=0A=
=0A=
	if (params_to_get & SA_PARAM_BUFF_MODE)=0A=
		*buff_mode =3D mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;=0A=
=0A=
	if (params_to_get & SA_PARAM_DENSITY)=0A=
		*density =3D mode_blk->density;=0A=
=0A=
	if (params_to_get & SA_PARAM_WP)=0A=
		*write_protect =3D (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;=0A=
=0A=
	if (params_to_get & SA_PARAM_SPEED)=0A=
		*speed =3D mode_hdr->dev_spec & SMH_SA_SPEED_MASK;=0A=
=0A=
	if (params_to_get & SA_PARAM_COMPRESSION) {=0A=
		sa_comp_t *ntcs =3D (sa_comp_t *) &mode_blk[1];=0A=
		if (cpage =3D=3D SA_DATA_COMPRESSION_PAGE) {=0A=
			struct scsi_data_compression_page *cp =3D &ntcs->dcomp;=0A=
			*comp_supported =3D=0A=
			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;=0A=
			*comp_enabled =3D=0A=
			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;=0A=
			*comp_algorithm =3D scsi_4btoul(cp->comp_algorithm);=0A=
		} else {=0A=
			struct scsi_dev_conf_page *cp =3D &ntcs->dconf;=0A=
			/*=0A=
			 * We don't really know whether this device supports=0A=
			 * Data Compression if the the algorithm field is=0A=
			 * zero. Just say we do.=0A=
			 */=0A=
			*comp_supported =3D TRUE;=0A=
			*comp_enabled =3D=0A=
			    (cp->sel_comp_alg !=3D SA_COMP_NONE)? TRUE : FALSE;=0A=
			*comp_algorithm =3D cp->sel_comp_alg;=0A=
		}=0A=
		if (tcs !=3D NULL)=0A=
			bcopy(ntcs, tcs, sizeof (sa_comp_t));=0A=
	}=0A=
=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		int idx;=0A=
		char *xyz =3D mode_buffer;=0A=
		xpt_print_path(periph->path);=0A=
		printf("Mode Sense Data=3D");=0A=
		for (idx =3D 0; idx < mode_buffer_len; idx++)=0A=
			printf(" 0x%02x", xyz[idx] & 0xff);=0A=
		printf("\n");=0A=
	}=0A=
=0A=
sagetparamsexit:=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	free(mode_buffer, M_TEMP);=0A=
	return (error);=0A=
}=0A=
=0A=
/*=0A=
 * The purpose of this function is to set one of four different =
parameters=0A=
 * for a tape drive:=0A=
 *	- blocksize=0A=
 *	- density=0A=
 *	- compression / compression algorithm=0A=
 *	- buffering mode=0A=
 *=0A=
 * The assumption is that this will be called from saioctl(), and =
therefore=0A=
 * from a process context.  Thus the waiting malloc calls below.  If that=0A=
 * assumption ever changes, the malloc calls should be changed to be=0A=
 * NOWAIT mallocs.=0A=
 *=0A=
 * Any or all of the four parameters may be set when this function is=0A=
 * called.  It should handle setting more than one parameter at once.=0A=
 */=0A=
static int=0A=
sasetparams(struct cam_periph *periph, sa_params params_to_set,=0A=
	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,=0A=
	    u_int32_t sense_flags)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	u_int32_t current_blocksize;=0A=
	u_int32_t current_calg;=0A=
	u_int8_t current_density;=0A=
	u_int8_t current_speed;=0A=
	int comp_enabled, comp_supported;=0A=
	void *mode_buffer;=0A=
	int mode_buffer_len;=0A=
	struct scsi_mode_header_6 *mode_hdr;=0A=
	struct scsi_mode_blk_desc *mode_blk;=0A=
	sa_comp_t *ccomp, *cpage;=0A=
	int buff_mode;=0A=
	union ccb *ccb =3D NULL;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccomp =3D malloc(sizeof (sa_comp_t), M_TEMP, M_WAITOK);=0A=
=0A=
	/*=0A=
	 * Since it doesn't make sense to set the number of blocks, or=0A=
	 * write protection, we won't try to get the current value.  We=0A=
	 * always want to get the blocksize, so we can set it back to the=0A=
	 * proper value.=0A=
	 */=0A=
	error =3D sagetparams(periph,=0A=
	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,=0A=
	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,=0A=
	    &current_speed, &comp_supported, &comp_enabled,=0A=
	    &current_calg, ccomp);=0A=
=0A=
	if (error !=3D 0) {=0A=
		free(ccomp, M_TEMP);=0A=
		return (error);=0A=
	}=0A=
=0A=
	mode_buffer_len =3D sizeof(*mode_hdr) + sizeof(*mode_blk);=0A=
	if (params_to_set & SA_PARAM_COMPRESSION)=0A=
		mode_buffer_len +=3D sizeof (sa_comp_t);=0A=
=0A=
	mode_buffer =3D malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);=0A=
=0A=
	mode_hdr =3D (struct scsi_mode_header_6 *)mode_buffer;=0A=
	mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
retry:=0A=
=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		if (mode_blk) {=0A=
			cpage =3D (sa_comp_t *)&mode_blk[1];=0A=
		} else {=0A=
			cpage =3D (sa_comp_t *)&mode_hdr[1];=0A=
		}=0A=
		bcopy(ccomp, cpage, sizeof (sa_comp_t));=0A=
		cpage->hdr.pagecode &=3D ~0x80;=0A=
	} else=0A=
		cpage =3D NULL;=0A=
=0A=
	/*=0A=
	 * If the caller wants us to set the blocksize, use the one they=0A=
	 * pass in.  Otherwise, use the blocksize we got back from the=0A=
	 * mode select above.=0A=
	 */=0A=
	if (mode_blk) {=0A=
		if (params_to_set & SA_PARAM_BLOCKSIZE)=0A=
			scsi_ulto3b(blocksize, mode_blk->blklen);=0A=
		else=0A=
			scsi_ulto3b(current_blocksize, mode_blk->blklen);=0A=
=0A=
		/*=0A=
		 * Set density if requested, else preserve old density.=0A=
		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better=0A=
		 * devices, else density we've latched up in our softc.=0A=
		 */=0A=
		if (params_to_set & SA_PARAM_DENSITY) {=0A=
			mode_blk->density =3D density;=0A=
		} else if (softc->scsi_rev > SCSI_REV_CCS) {=0A=
			mode_blk->density =3D SCSI_SAME_DENSITY;=0A=
		} else {=0A=
			mode_blk->density =3D softc->media_density;=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * For mode selects, these two fields must be zero.=0A=
	 */=0A=
	mode_hdr->data_length =3D 0;=0A=
	mode_hdr->medium_type =3D 0;=0A=
=0A=
	/* set the speed to the current value */=0A=
	mode_hdr->dev_spec =3D current_speed;=0A=
=0A=
	/* if set, set single-initiator buffering mode */=0A=
	if (softc->buffer_mode =3D=3D SMH_SA_BUF_MODE_SIBUF) {=0A=
		mode_hdr->dev_spec |=3D SMH_SA_BUF_MODE_SIBUF;=0A=
	}=0A=
=0A=
	if (mode_blk)=0A=
		mode_hdr->blk_desc_len =3D sizeof(struct scsi_mode_blk_desc);=0A=
	else=0A=
		mode_hdr->blk_desc_len =3D 0;=0A=
=0A=
	/*=0A=
	 * First, if the user wants us to set the compression algorithm or=0A=
	 * just turn compression on, check to make sure that this drive=0A=
	 * supports compression.=0A=
	 */=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		/*=0A=
		 * If the compression algorithm is 0, disable compression.=0A=
		 * If the compression algorithm is non-zero, enable=0A=
		 * compression and set the compression type to the=0A=
		 * specified compression algorithm, unless the algorithm is=0A=
		 * MT_COMP_ENABLE.  In that case, we look at the=0A=
		 * compression algorithm that is currently set and if it is=0A=
		 * non-zero, we leave it as-is.  If it is zero, and we have=0A=
		 * saved a compression algorithm from a time when=0A=
		 * compression was enabled before, set the compression to=0A=
		 * the saved value.=0A=
		 */=0A=
		switch (ccomp->hdr.pagecode & ~0x80) {=0A=
		case SA_DEVICE_CONFIGURATION_PAGE:=0A=
		{=0A=
			struct scsi_dev_conf_page *dcp =3D &cpage->dconf;=0A=
			if (calg =3D=3D 0) {=0A=
				dcp->sel_comp_alg =3D SA_COMP_NONE;=0A=
				break;=0A=
			}=0A=
			if (calg !=3D MT_COMP_ENABLE) {=0A=
				dcp->sel_comp_alg =3D calg;=0A=
			} else if (dcp->sel_comp_alg =3D=3D SA_COMP_NONE &&=0A=
			    softc->saved_comp_algorithm !=3D 0) {=0A=
				dcp->sel_comp_alg =3D softc->saved_comp_algorithm;=0A=
			}=0A=
			break;=0A=
		}=0A=
		case SA_DATA_COMPRESSION_PAGE:=0A=
		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {=0A=
			struct scsi_data_compression_page *dcp =3D &cpage->dcomp;=0A=
			if (calg =3D=3D 0) {=0A=
				/*=0A=
				 * Disable compression, but leave the=0A=
				 * decompression and the capability bit=0A=
				 * alone.=0A=
				 */=0A=
				dcp->dce_and_dcc =3D SA_DCP_DCC;=0A=
				dcp->dde_and_red |=3D SA_DCP_DDE;=0A=
				break;=0A=
			}=0A=
			/* enable compression && decompression */=0A=
			dcp->dce_and_dcc =3D SA_DCP_DCE | SA_DCP_DCC;=0A=
			dcp->dde_and_red |=3D SA_DCP_DDE;=0A=
			/*=0A=
			 * If there, use compression algorithm from caller.=0A=
			 * Otherwise, if there's a saved compression algorithm=0A=
			 * and there is no current algorithm, use the saved=0A=
			 * algorithm. Else parrot back what we got and hope=0A=
			 * for the best.=0A=
			 */=0A=
			if (calg !=3D MT_COMP_ENABLE) {=0A=
				scsi_ulto4b(calg, dcp->comp_algorithm);=0A=
				scsi_ulto4b(calg, dcp->decomp_algorithm);=0A=
			} else if (scsi_4btoul(dcp->comp_algorithm) =3D=3D 0 &&=0A=
			    softc->saved_comp_algorithm !=3D 0) {=0A=
				scsi_ulto4b(softc->saved_comp_algorithm,=0A=
				    dcp->comp_algorithm);=0A=
				scsi_ulto4b(softc->saved_comp_algorithm,=0A=
				    dcp->decomp_algorithm);=0A=
			}=0A=
			break;=0A=
		}=0A=
		/*=0A=
		 * Compression does not appear to be supported-=0A=
		 * at least via the DATA COMPRESSION page. It=0A=
		 * would be too much to ask us to believe that=0A=
		 * the page itself is supported, but incorrectly=0A=
		 * reports an ability to manipulate data compression,=0A=
		 * so we'll assume that this device doesn't support=0A=
		 * compression. We can just fall through for that.=0A=
		 */=0A=
		/* FALLTHROUGH */=0A=
		default:=0A=
			/*=0A=
			 * The drive doesn't seem to support compression,=0A=
			 * so turn off the set compression bit.=0A=
			 */=0A=
			params_to_set &=3D ~SA_PARAM_COMPRESSION;=0A=
			xpt_print_path(periph->path);=0A=
			printf("device does not seem to support compression\n");=0A=
=0A=
			/*=0A=
			 * If that was the only thing the user wanted us to set,=0A=
			 * clean up allocated resources and return with=0A=
			 * 'operation not supported'.=0A=
			 */=0A=
			if (params_to_set =3D=3D SA_PARAM_NONE) {=0A=
				free(mode_buffer, M_TEMP);=0A=
				xpt_release_ccb(ccb);=0A=
				return (ENODEV);=0A=
			}=0A=
		=0A=
			/*=0A=
			 * That wasn't the only thing the user wanted us to set.=0A=
			 * So, decrease the stated mode buffer length by the=0A=
			 * size of the compression mode page.=0A=
			 */=0A=
			mode_buffer_len -=3D sizeof(sa_comp_t);=0A=
		}=0A=
	}=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,=0A=
	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0,=0A=
	    sense_flags, softc->device_stats);=0A=
	QFRLS(ccb);=0A=
=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		int idx;=0A=
		char *xyz =3D mode_buffer;=0A=
		xpt_print_path(periph->path);=0A=
		printf("Err%d, Mode Select Data=3D", error);=0A=
		for (idx =3D 0; idx < mode_buffer_len; idx++)=0A=
			printf(" 0x%02x", xyz[idx] & 0xff);=0A=
		printf("\n");=0A=
	}=0A=
=0A=
=0A=
	if (error) {=0A=
		/*=0A=
		 * If we can, try without setting density/blocksize.=0A=
		 */=0A=
		if (mode_blk) {=0A=
			if ((params_to_set &=0A=
			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) =3D=3D 0) {=0A=
				mode_blk =3D NULL;=0A=
				goto retry;=0A=
			}=0A=
		} else {=0A=
			mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
			cpage =3D (sa_comp_t *)&mode_blk[1];=0A=
		}=0A=
=0A=
		/*=0A=
		 * If we were setting the blocksize, and that failed, we=0A=
		 * want to set it to its original value.  If we weren't=0A=
		 * setting the blocksize, we don't want to change it.=0A=
		 */=0A=
		scsi_ulto3b(current_blocksize, mode_blk->blklen);=0A=
=0A=
		/*=0A=
		 * Set density if requested, else preserve old density.=0A=
		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better=0A=
		 * devices, else density we've latched up in our softc.=0A=
		 */=0A=
		if (params_to_set & SA_PARAM_DENSITY) {=0A=
			mode_blk->density =3D current_density;=0A=
		} else if (softc->scsi_rev > SCSI_REV_CCS) {=0A=
			mode_blk->density =3D SCSI_SAME_DENSITY;=0A=
		} else {=0A=
			mode_blk->density =3D softc->media_density;=0A=
		}=0A=
=0A=
		if (params_to_set & SA_PARAM_COMPRESSION)=0A=
			bcopy(ccomp, cpage, sizeof (sa_comp_t));=0A=
=0A=
		/*=0A=
		 * The retry count is the only CCB field that might have been=0A=
		 * changed that we care about, so reset it back to 1.=0A=
		 */=0A=
		ccb->ccb_h.retry_count =3D 1;=0A=
		cam_periph_runccb(ccb, saerror, 0, sense_flags,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	if (ccomp !=3D NULL)=0A=
		free(ccomp, M_TEMP);=0A=
=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		if (error) {=0A=
			softc->flags &=3D ~SA_FLAG_COMP_ENABLED;=0A=
			/*=0A=
			 * Even if we get an error setting compression,=0A=
			 * do not say that we don't support it. We could=0A=
			 * have been wrong, or it may be media specific.=0A=
			 *	softc->flags &=3D ~SA_FLAG_COMP_SUPP;=0A=
			 */=0A=
			softc->saved_comp_algorithm =3D softc->comp_algorithm;=0A=
			softc->comp_algorithm =3D 0;=0A=
		} else {=0A=
			softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
			softc->comp_algorithm =3D calg;=0A=
		}=0A=
	}=0A=
=0A=
	free(mode_buffer, M_TEMP);=0A=
	return (error);=0A=
}=0A=
=0A=
static void=0A=
saprevent(struct cam_periph *periph, int action)=0A=
{=0A=
	struct	sa_softc *softc;=0A=
	union	ccb *ccb;		=0A=
	int	error, sf;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	if ((action =3D=3D PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) =
=3D=3D 0)=0A=
		return;=0A=
	if ((action =3D=3D PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) =
!=3D 0)=0A=
		return;=0A=
=0A=
	/*=0A=
	 * We can be quiet about illegal requests.=0A=
	 */=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		sf =3D 0;=0A=
	} else=0A=
		sf =3D SF_QUIET_IR;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,=0A=
	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats);=0A=
	QFRLS(ccb);=0A=
	if (error =3D=3D 0) {=0A=
		if (action =3D=3D PR_ALLOW)=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_LOCKED;=0A=
		else=0A=
			softc->flags |=3D SA_FLAG_TAPE_LOCKED;=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
}=0A=
=0A=
static int=0A=
sarewind(struct cam_periph *periph)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_REW;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	else=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saspace(struct cam_periph *periph, int count, scsi_space_code code)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* This cannot be retried */=0A=
=0A=
	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,=0A=
	    SSD_FULL_SIZE, SPACE_TIMEOUT);=0A=
=0A=
	/*=0A=
	 * Clear residual because we will be using it.=0A=
	 */=0A=
	softc->last_ctl_resid =3D 0;=0A=
=0A=
	softc->dsreg =3D (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If a spacing operation has failed, we need to invalidate=0A=
	 * this mount.=0A=
	 *=0A=
	 * If the spacing operation was setmarks or to end of recorded data,=0A=
	 * we no longer know our relative position.=0A=
	 *=0A=
	 * If the spacing operations was spacing files in reverse, we=0A=
	 * take account of the residual, but still check against less=0A=
	 * than zero- if we've gone negative, we must have hit BOT.=0A=
	 *=0A=
	 * If the spacing operations was spacing records in reverse and=0A=
	 * we have a residual, we've either hit BOT or hit a filemark.=0A=
	 * In the former case, we know our new record number (0). In=0A=
	 * the latter case, we have absolutely no idea what the real=0A=
	 * record number is- we've stopped between the end of the last=0A=
	 * record in the previous file and the filemark that stopped=0A=
	 * our spacing backwards.=0A=
	 */=0A=
	if (error) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (code =3D=3D SS_SETMARKS || code =3D=3D SS_EOD) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (code =3D=3D SS_FILEMARKS && softc->fileno !=3D (daddr_t) -1) =
{=0A=
		softc->fileno +=3D (count - softc->last_ctl_resid);=0A=
		if (softc->fileno < 0)	/* we must of hit BOT */=0A=
			softc->fileno =3D 0;=0A=
		softc->blkno =3D 0;=0A=
	} else if (code =3D=3D SS_BLOCKS && softc->blkno !=3D (daddr_t) -1) {=0A=
		softc->blkno +=3D (count - softc->last_ctl_resid);=0A=
		if (count < 0) {=0A=
			if (softc->last_ctl_resid || softc->blkno < 0) {=0A=
				if (softc->fileno =3D=3D 0) {=0A=
					softc->blkno =3D 0;=0A=
				} else {=0A=
					softc->blkno =3D (daddr_t) -1;=0A=
				}=0A=
			}=0A=
		}=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error, nwm =3D 0;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	/*=0A=
	 * Clear residual because we will be using it.=0A=
	 */=0A=
	softc->last_ctl_resid =3D 0;=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_FMK;=0A=
	/* this *must* not be retried */=0A=
	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	if (error =3D=3D 0 && nmarks) {=0A=
		struct sa_softc *softc =3D (struct sa_softc *)periph->softc;=0A=
		nwm =3D nmarks - softc->last_ctl_resid;=0A=
		softc->filemarks +=3D nwm;=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * Update relative positions (if we're doing that).=0A=
	 */=0A=
	if (error) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (softc->fileno !=3D (daddr_t) -1) {=0A=
		softc->fileno +=3D nwm;=0A=
		softc->blkno =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)=0A=
{=0A=
	struct scsi_tape_position_data loc;=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc =3D (struct sa_softc *)periph->softc;=0A=
	int error;=0A=
=0A=
	/*=0A=
	 * We try and flush any buffered writes here if we were writing=0A=
	 * and we're trying to get hardware block position. It eats=0A=
	 * up performance substantially, but I'm wary of drive firmware.=0A=
	 *=0A=
	 * I think that *logical* block position is probably okay-=0A=
	 * but hardware block position might have to wait for data=0A=
	 * to hit media to be valid. Caveat Emptor.=0A=
	 */=0A=
=0A=
	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {=0A=
		error =3D sawritefilemarks(periph, 0, 0);=0A=
		if (error && error !=3D EACCES)=0A=
			return (error);=0A=
	}=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
	softc->dsreg =3D MTIO_DSREG_RBSY;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);=0A=
=0A=
	if (error =3D=3D 0) {=0A=
		if (loc.flags & SA_RPOS_UNCERTAIN) {=0A=
			error =3D EINVAL;		/* nothing is certain */=0A=
		} else {=0A=
			*blkptr =3D scsi_4btoul(loc.firstblk);=0A=
		}=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	/*=0A=
	 * We used to try and flush any buffered writes here.=0A=
	 * Now we push this onto user applications to either=0A=
	 * flush the pending writes themselves (via a zero count=0A=
	 * WRITE FILEMARKS command) or they can trust their tape=0A=
	 * drive to do this correctly for them.=0A=
 	 */=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	=0A=
	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT);=0A=
=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_POS;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);=0A=
	xpt_release_ccb(ccb);=0A=
	/*=0A=
	 * Note relative file && block number position as now unknown.=0A=
	 */=0A=
	softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saretension(struct cam_periph *periph)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_TEN;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
	xpt_release_ccb(ccb);=0A=
	if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	else=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sareservereleaseunit(struct cam_periph *periph, int reserve)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph,  1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    FALSE,  0, SSD_FULL_SIZE,  SCSIOP_TIMEOUT, reserve);=0A=
	softc->dsreg =3D MTIO_DSREG_RBSY;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0,=0A=
	    SF_RETRY_UA | SF_NO_PRINT, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	QFRLS(ccb);=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If the error was Illegal Request, then the device doesn't support=0A=
	 * RESERVE/RELEASE. This is not an error.=0A=
	 */=0A=
	if (error =3D=3D EINVAL) {=0A=
		error =3D 0;=0A=
	}=0A=
=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saloadunload(struct cam_periph *periph, int load)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	QFRLS(ccb);=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	if (error || load =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	else if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saerase(struct cam_periph *periph, int longerase)=0A=
{=0A=
=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,=0A=
	    SSD_FULL_SIZE, ERASE_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_ZER;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
	xpt_release_ccb(ccb);=0A=
	return (error);=0A=
}=0A=
=0A=
#endif /* _KERNEL */=0A=
=0A=
/*=0A=
 * Read tape block limits command.=0A=
 */=0A=
void=0A=
scsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action,=0A=
		   struct scsi_read_block_limits_data *rlimit_buf,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_read_block_limits *scsi_cmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,=0A=
	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,=0A=
	     sizeof(*scsi_cmd), timeout);=0A=
=0A=
	scsi_cmd =3D (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D READ_BLOCK_LIMITS;=0A=
}=0A=
=0A=
void=0A=
scsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int readop, int sli,=0A=
		   int fixed, u_int32_t length, u_int8_t *data_ptr,=0A=
		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_sa_rw *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;=0A=
	scsi_cmd->opcode =3D readop ? SA_READ : SA_WRITE;=0A=
	scsi_cmd->sli_fixed =3D 0;=0A=
	if (sli && readop)=0A=
		scsi_cmd->sli_fixed |=3D SAR_SLI;=0A=
	if (fixed)=0A=
		scsi_cmd->sli_fixed |=3D SARW_FIXED;=0A=
	scsi_ulto3b(length, scsi_cmd->length);=0A=
	scsi_cmd->control =3D 0;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, readop ? CAM_DIR_IN : CAM_DIR_OUT,=0A=
	    tag_action, data_ptr, dxfer_len, sense_len,=0A=
	    sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,         =0A=
		 void (*cbfcnp)(struct cam_periph *, union ccb *),   =0A=
		 u_int8_t tag_action, int immediate, int eot,=0A=
		 int reten, int load, u_int8_t sense_len,=0A=
		 u_int32_t timeout)=0A=
{=0A=
	struct scsi_load_unload *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D LOAD_UNLOAD;=0A=
	if (immediate)=0A=
		scsi_cmd->immediate =3D SLU_IMMED;=0A=
	if (eot)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_EOT;=0A=
	if (reten)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_RETEN;=0A=
	if (load)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_LOAD;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,=0A=
	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);	=0A=
}=0A=
=0A=
void=0A=
scsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,         =0A=
	    void (*cbfcnp)(struct cam_periph *, union ccb *),   =0A=
	    u_int8_t tag_action, int immediate, u_int8_t sense_len,     =0A=
	    u_int32_t timeout)=0A=
{=0A=
	struct scsi_rewind *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D REWIND;=0A=
	if (immediate)=0A=
		scsi_cmd->immediate =3D SREW_IMMED;=0A=
	=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_space(struct ccb_scsiio *csio, u_int32_t retries,=0A=
	   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
	   u_int8_t tag_action, scsi_space_code code,=0A=
	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_space *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_space *)&csio->cdb_io.cdb_bytes;=0A=
	scsi_cmd->opcode =3D SPACE;=0A=
	scsi_cmd->code =3D code;=0A=
	scsi_ulto3b(count, scsi_cmd->count);=0A=
	scsi_cmd->control =3D 0;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		     void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		     u_int8_t tag_action, int immediate, int setmark,=0A=
		     u_int32_t num_marks, u_int8_t sense_len,=0A=
		     u_int32_t timeout)=0A=
{=0A=
	struct scsi_write_filemarks *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D WRITE_FILEMARKS;=0A=
	if (immediate)=0A=
		scsi_cmd->byte2 |=3D SWFMRK_IMMED;=0A=
	if (setmark)=0A=
		scsi_cmd->byte2 |=3D SWFMRK_WSMK;=0A=
	=0A=
	scsi_ulto3b(num_marks, scsi_cmd->num_marks);=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
/*=0A=
 * The reserve and release unit commands differ only by their opcodes.=0A=
 */=0A=
void=0A=
scsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,=0A=
			  void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
			  u_int8_t tag_action, int third_party,=0A=
			  int third_party_id, u_int8_t sense_len,=0A=
			  u_int32_t timeout, int reserve)=0A=
{=0A=
	struct scsi_reserve_release_unit *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_reserve_release_unit =
*)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
=0A=
	if (reserve)=0A=
		scsi_cmd->opcode =3D RESERVE_UNIT;=0A=
	else=0A=
		scsi_cmd->opcode =3D RELEASE_UNIT;=0A=
=0A=
	if (third_party) {=0A=
		scsi_cmd->lun_thirdparty |=3D SRRU_3RD_PARTY;=0A=
		scsi_cmd->lun_thirdparty |=3D=0A=
			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);=0A=
	}=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_erase(struct ccb_scsiio *csio, u_int32_t retries,=0A=
	   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
	   u_int8_t tag_action, int immediate, int long_erase,=0A=
	   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_erase *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_erase *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
=0A=
	scsi_cmd->opcode =3D ERASE;=0A=
=0A=
	if (immediate)=0A=
		scsi_cmd->lun_imm_long |=3D SE_IMMED;=0A=
=0A=
	if (long_erase)=0A=
		scsi_cmd->lun_imm_long |=3D SE_LONG;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
/*=0A=
 * Read Tape Position command.=0A=
 */=0A=
void=0A=
scsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int hardsoft,=0A=
		   struct scsi_tape_position_data *sbp,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_tape_read_position *scmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,=0A=
	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);=0A=
	scmd =3D (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scmd, sizeof(*scmd));=0A=
	scmd->opcode =3D READ_POSITION;=0A=
	scmd->byte1 =3D hardsoft;=0A=
}=0A=
=0A=
/*=0A=
 * Set Tape Position command.=0A=
 */=0A=
void=0A=
scsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_tape_locate *scmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,=0A=
	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);=0A=
	scmd =3D (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scmd, sizeof(*scmd));=0A=
	scmd->opcode =3D LOCATE;=0A=
	if (hardsoft)=0A=
		scmd->byte1 |=3D SA_SPOS_BT;=0A=
	scsi_ulto4b(blkno, scmd->blkaddr);=0A=
}=0A=

------=_NextPart_000_001A_01C54C04.3DD952C0
Content-Type: application/octet-stream;
	name="scsi_sa.c.orig"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="scsi_sa.c.orig"

/*=0A=
 * Implementation of SCSI Sequential Access Peripheral driver for CAM.=0A=
 *=0A=
 * Copyright (c) 1999, 2000 Matthew Jacob=0A=
 * All rights reserved.=0A=
 *=0A=
 * Redistribution and use in source and binary forms, with or without=0A=
 * modification, are permitted provided that the following conditions=0A=
 * are met:=0A=
 * 1. Redistributions of source code must retain the above copyright=0A=
 *    notice, this list of conditions, and the following disclaimer,=0A=
 *    without modification, immediately at the beginning of the file.=0A=
 * 2. The name of the author may not be used to endorse or promote =
products=0A=
 *    derived from this software without specific prior written =
permission.=0A=
 *=0A=
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND=0A=
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0A=
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR =
PURPOSE=0A=
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE =
LIABLE FOR=0A=
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL=0A=
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE =
GOODS=0A=
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)=0A=
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, =
STRICT=0A=
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY =
WAY=0A=
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0A=
 * SUCH DAMAGE.=0A=
 */=0A=
=0A=
#include <sys/cdefs.h>=0A=
__FBSDID("$FreeBSD: src/sys/cam/scsi/scsi_sa.c,v 1.101 2004/06/16 =
09:46:31 phk Exp $");=0A=
=0A=
#include <sys/param.h>=0A=
#include <sys/queue.h>=0A=
#ifdef _KERNEL=0A=
#include <sys/systm.h>=0A=
#include <sys/kernel.h>=0A=
#endif=0A=
#include <sys/types.h>=0A=
#include <sys/time.h>=0A=
#include <sys/bio.h>=0A=
#include <sys/limits.h>=0A=
#include <sys/malloc.h>=0A=
#include <sys/mtio.h>=0A=
#ifdef _KERNEL=0A=
#include <sys/conf.h>=0A=
#endif=0A=
#include <sys/devicestat.h>=0A=
=0A=
#ifndef _KERNEL=0A=
#include <stdio.h>=0A=
#include <string.h>=0A=
#endif=0A=
=0A=
#include <cam/cam.h>=0A=
#include <cam/cam_ccb.h>=0A=
#include <cam/cam_periph.h>=0A=
#include <cam/cam_xpt_periph.h>=0A=
#include <cam/cam_debug.h>=0A=
=0A=
#include <cam/scsi/scsi_all.h>=0A=
#include <cam/scsi/scsi_message.h>=0A=
#include <cam/scsi/scsi_sa.h>=0A=
=0A=
#ifdef _KERNEL=0A=
=0A=
#include <opt_sa.h>=0A=
=0A=
#ifndef SA_IO_TIMEOUT=0A=
#define SA_IO_TIMEOUT		4=0A=
#endif=0A=
#ifndef SA_SPACE_TIMEOUT=0A=
#define SA_SPACE_TIMEOUT	1 * 60=0A=
#endif=0A=
#ifndef SA_REWIND_TIMEOUT=0A=
#define SA_REWIND_TIMEOUT	2 * 60=0A=
#endif=0A=
#ifndef SA_ERASE_TIMEOUT=0A=
#define SA_ERASE_TIMEOUT	4 * 60=0A=
#endif=0A=
=0A=
#define	SCSIOP_TIMEOUT		(60 * 1000)	/* not an option */=0A=
=0A=
#define	IO_TIMEOUT		(SA_IO_TIMEOUT * 60 * 1000)=0A=
#define	REWIND_TIMEOUT		(SA_REWIND_TIMEOUT * 60 * 1000)=0A=
#define	ERASE_TIMEOUT		(SA_ERASE_TIMEOUT * 60 * 1000)=0A=
#define	SPACE_TIMEOUT		(SA_SPACE_TIMEOUT * 60 * 1000)=0A=
=0A=
/*=0A=
 * Additional options that can be set for config: SA_1FM_AT_EOT=0A=
 */=0A=
=0A=
#ifndef	UNUSED_PARAMETER=0A=
#define	UNUSED_PARAMETER(x)	x =3D x=0A=
#endif=0A=
=0A=
#define	QFRLS(ccb)	\=0A=
	if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)	\=0A=
		cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE)=0A=
=0A=
/*=0A=
 * Driver states=0A=
 */=0A=
=0A=
=0A=
typedef enum {=0A=
	SA_STATE_NORMAL, SA_STATE_ABNORMAL=0A=
} sa_state;=0A=
=0A=
#define ccb_pflags	ppriv_field0=0A=
#define ccb_bp	 	ppriv_ptr1=0A=
=0A=
#define	SA_CCB_BUFFER_IO	0x0=0A=
#define	SA_CCB_WAITING		0x1=0A=
#define	SA_CCB_TYPEMASK		0x1=0A=
#define	SA_POSITION_UPDATED	0x2=0A=
=0A=
#define	Set_CCB_Type(x, type)				\=0A=
	x->ccb_h.ccb_pflags &=3D ~SA_CCB_TYPEMASK;	\=0A=
	x->ccb_h.ccb_pflags |=3D type=0A=
=0A=
#define	CCB_Type(x)	(x->ccb_h.ccb_pflags & SA_CCB_TYPEMASK)=0A=
=0A=
=0A=
=0A=
typedef enum {=0A=
	SA_FLAG_OPEN		=3D 0x0001,=0A=
	SA_FLAG_FIXED		=3D 0x0002,=0A=
	SA_FLAG_TAPE_LOCKED	=3D 0x0004,=0A=
	SA_FLAG_TAPE_MOUNTED	=3D 0x0008,=0A=
	SA_FLAG_TAPE_WP		=3D 0x0010,=0A=
	SA_FLAG_TAPE_WRITTEN	=3D 0x0020,=0A=
	SA_FLAG_EOM_PENDING	=3D 0x0040,=0A=
	SA_FLAG_EIO_PENDING	=3D 0x0080,=0A=
	SA_FLAG_EOF_PENDING	=3D 0x0100,=0A=
	SA_FLAG_ERR_PENDING	=3D (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING|=0A=
				   SA_FLAG_EOF_PENDING),=0A=
	SA_FLAG_INVALID		=3D 0x0200,=0A=
	SA_FLAG_COMP_ENABLED	=3D 0x0400,=0A=
	SA_FLAG_COMP_SUPP	=3D 0x0800,=0A=
	SA_FLAG_COMP_UNSUPP	=3D 0x1000,=0A=
	SA_FLAG_TAPE_FROZEN	=3D 0x2000=0A=
} sa_flags;=0A=
=0A=
typedef enum {=0A=
	SA_MODE_REWIND		=3D 0x00,=0A=
	SA_MODE_NOREWIND	=3D 0x01,=0A=
	SA_MODE_OFFLINE		=3D 0x02=0A=
} sa_mode;=0A=
=0A=
typedef enum {=0A=
	SA_PARAM_NONE		=3D 0x00,=0A=
	SA_PARAM_BLOCKSIZE	=3D 0x01,=0A=
	SA_PARAM_DENSITY	=3D 0x02,=0A=
	SA_PARAM_COMPRESSION	=3D 0x04,=0A=
	SA_PARAM_BUFF_MODE	=3D 0x08,=0A=
	SA_PARAM_NUMBLOCKS	=3D 0x10,=0A=
	SA_PARAM_WP		=3D 0x20,=0A=
	SA_PARAM_SPEED		=3D 0x40,=0A=
	SA_PARAM_ALL		=3D 0x7f=0A=
} sa_params;=0A=
=0A=
typedef enum {=0A=
	SA_QUIRK_NONE		=3D 0x00,=0A=
	SA_QUIRK_NOCOMP		=3D 0x01,	/* Can't deal with compression at all */=0A=
	SA_QUIRK_FIXED		=3D 0x02,	/* Force fixed mode */=0A=
	SA_QUIRK_VARIABLE	=3D 0x04,	/* Force variable mode */=0A=
	SA_QUIRK_2FM		=3D 0x08,	/* Needs Two File Marks at EOD */=0A=
	SA_QUIRK_1FM		=3D 0x10,	/* No more than 1 File Mark at EOD */=0A=
	SA_QUIRK_NODREAD	=3D 0x20,	/* Don't try and dummy read density */=0A=
	SA_QUIRK_NO_MODESEL	=3D 0x40,	/* Don't do mode select at all */=0A=
	SA_QUIRK_NO_CPAGE	=3D 0x80	/* Don't use DEVICE COMPRESSION page */=0A=
} sa_quirks;=0A=
=0A=
/* units are bits 4-7, 16-21 (1024 units) */=0A=
#define SAUNIT(DEV) \=0A=
	(((minor(DEV) & 0xF0) >> 4) |  ((minor(DEV) & 0x3f0000) >> 16))=0A=
=0A=
#define SAMODE(z) ((minor(z) & 0x3))=0A=
#define SADENSITY(z) (((minor(z) >> 2) & 0x3))=0A=
#define	SA_IS_CTRL(z) (minor(z) & (1 << 29))=0A=
=0A=
#define SA_NOT_CTLDEV	0=0A=
#define SA_CTLDEV	1=0A=
=0A=
#define SA_ATYPE_R	0=0A=
#define SA_ATYPE_NR	1=0A=
#define SA_ATYPE_ER	2=0A=
=0A=
#define SAMINOR(ctl, unit, mode, access) \=0A=
	((ctl << 29) | ((unit & 0x3f0) << 16) | ((unit & 0xf) << 4) | \=0A=
	(mode << 0x2) | (access & 0x3))=0A=
=0A=
#define SA_NUM_MODES	4=0A=
struct sa_devs {=0A=
	struct cdev *ctl_dev;=0A=
	struct sa_mode_devs {=0A=
		struct cdev *r_dev;=0A=
		struct cdev *nr_dev;=0A=
		struct cdev *er_dev;=0A=
	} mode_devs[SA_NUM_MODES];=0A=
};=0A=
=0A=
struct sa_softc {=0A=
	sa_state	state;=0A=
	sa_flags	flags;=0A=
	sa_quirks	quirks;=0A=
	struct		bio_queue_head bio_queue;=0A=
	int		queue_count;=0A=
	struct		devstat *device_stats;=0A=
	struct sa_devs	devs;=0A=
	int		blk_gran;=0A=
	int		blk_mask;=0A=
	int		blk_shift;=0A=
	u_int32_t	max_blk;=0A=
	u_int32_t	min_blk;=0A=
	u_int32_t	comp_algorithm;=0A=
	u_int32_t	saved_comp_algorithm;=0A=
	u_int32_t	media_blksize;=0A=
	u_int32_t	last_media_blksize;=0A=
	u_int32_t	media_numblks;=0A=
	u_int8_t	media_density;=0A=
	u_int8_t	speed;=0A=
	u_int8_t	scsi_rev;=0A=
	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */=0A=
	int		buffer_mode;=0A=
	int		filemarks;=0A=
	union		ccb saved_ccb;=0A=
	int		last_resid_was_io;=0A=
=0A=
	/*=0A=
	 * Relative to BOT Location.=0A=
	 */=0A=
	daddr_t		fileno;=0A=
	daddr_t		blkno;=0A=
=0A=
	/*=0A=
	 * Latched Error Info=0A=
	 */=0A=
	struct {=0A=
		struct scsi_sense_data _last_io_sense;=0A=
		u_int32_t _last_io_resid;=0A=
		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];=0A=
		struct scsi_sense_data _last_ctl_sense;=0A=
		u_int32_t _last_ctl_resid;=0A=
		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];=0A=
#define	last_io_sense	errinfo._last_io_sense=0A=
#define	last_io_resid	errinfo._last_io_resid=0A=
#define	last_io_cdb	errinfo._last_io_cdb=0A=
#define	last_ctl_sense	errinfo._last_ctl_sense=0A=
#define	last_ctl_resid	errinfo._last_ctl_resid=0A=
#define	last_ctl_cdb	errinfo._last_ctl_cdb=0A=
	} errinfo;=0A=
	/*=0A=
	 * Misc other flags/state=0A=
	 */=0A=
	u_int32_t=0A=
				: 31,=0A=
		ctrl_mode	: 1;	/* control device open */=0A=
};=0A=
=0A=
struct sa_quirk_entry {=0A=
	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */=0A=
	sa_quirks quirks;	/* specific quirk type */=0A=
	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */=0A=
};=0A=
=0A=
static struct sa_quirk_entry sa_quirk_table[] =3D=0A=
{=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",=0A=
		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |=0A=
		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python 06408*", "*"}, SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "Python*", "*"}, SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 2525 25462", "-011"},=0A=
		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",=0A=
		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024=0A=
	},=0A=
#if	0=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,=0A=
	},=0A=
#endif=0A=
 	{=0A=
 		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "C56*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",=0A=
		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",=0A=
		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",=0A=
		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0=0A=
	},=0A=
	{	/* jreynold@primenet.com */=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",=0A=
		"STT8000N*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{	/* mike@sentex.net */=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",=0A=
		"STT20000*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",=0A=
		  " SLR*", "*"}, SA_QUIRK_1FM, 0=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",=0A=
		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512=0A=
	},=0A=
	{=0A=
		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",=0A=
		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024=0A=
	}=0A=
};=0A=
=0A=
static	d_open_t	saopen;=0A=
static	d_close_t	saclose;=0A=
static	d_strategy_t	sastrategy;=0A=
static	d_ioctl_t	saioctl;=0A=
static	periph_init_t	sainit;=0A=
static	periph_ctor_t	saregister;=0A=
static	periph_oninv_t	saoninvalidate;=0A=
static	periph_dtor_t	sacleanup;=0A=
static	periph_start_t	sastart;=0A=
static	void		saasync(void *callback_arg, u_int32_t code,=0A=
				struct cam_path *path, void *arg);=0A=
static	void		sadone(struct cam_periph *periph,=0A=
			       union ccb *start_ccb);=0A=
static  int		saerror(union ccb *ccb, u_int32_t cam_flags,=0A=
				u_int32_t sense_flags);=0A=
static int		samarkswanted(struct cam_periph *);=0A=
static int		sacheckeod(struct cam_periph *periph);=0A=
static int		sagetparams(struct cam_periph *periph,=0A=
				    sa_params params_to_get,=0A=
				    u_int32_t *blocksize, u_int8_t *density,=0A=
				    u_int32_t *numblocks, int *buff_mode,=0A=
				    u_int8_t *write_protect, u_int8_t *speed,=0A=
				    int *comp_supported, int *comp_enabled,=0A=
				    u_int32_t *comp_algorithm,=0A=
				    sa_comp_t *comp_page);=0A=
static int		sasetparams(struct cam_periph *periph,=0A=
				    sa_params params_to_set,=0A=
				    u_int32_t blocksize, u_int8_t density,=0A=
				    u_int32_t comp_algorithm,=0A=
				    u_int32_t sense_flags);=0A=
static void		saprevent(struct cam_periph *periph, int action);=0A=
static int		sarewind(struct cam_periph *periph);=0A=
static int		saspace(struct cam_periph *periph, int count,=0A=
				scsi_space_code code);=0A=
static int		samount(struct cam_periph *, int, struct cdev *);=0A=
static int		saretension(struct cam_periph *periph);=0A=
static int		sareservereleaseunit(struct cam_periph *periph,=0A=
					     int reserve);=0A=
static int		saloadunload(struct cam_periph *periph, int load);=0A=
static int		saerase(struct cam_periph *periph, int longerase);=0A=
static int		sawritefilemarks(struct cam_periph *periph,=0A=
					 int nmarks, int setmarks);=0A=
static int		sardpos(struct cam_periph *periph, int, u_int32_t *);=0A=
static int		sasetpos(struct cam_periph *periph, int, u_int32_t *);=0A=
=0A=
=0A=
static struct periph_driver sadriver =3D=0A=
{=0A=
	sainit, "sa",=0A=
	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0=0A=
};=0A=
=0A=
PERIPHDRIVER_DECLARE(sa, sadriver);=0A=
=0A=
/* For 2.2-stable support */=0A=
#ifndef D_TAPE=0A=
#define D_TAPE 0=0A=
#endif=0A=
=0A=
=0A=
static struct cdevsw sa_cdevsw =3D {=0A=
	.d_version =3D	D_VERSION,=0A=
	.d_open =3D	saopen,=0A=
	.d_close =3D	saclose,=0A=
	.d_read =3D	physread,=0A=
	.d_write =3D	physwrite,=0A=
	.d_ioctl =3D	saioctl,=0A=
	.d_strategy =3D	sastrategy,=0A=
	.d_name =3D	"sa",=0A=
	.d_flags =3D	D_TAPE | D_NEEDGIANT,=0A=
};=0A=
=0A=
static int=0A=
saopen(struct cdev *dev, int flags, int fmt, struct thread *td)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	int unit;=0A=
	int error;=0A=
	int s;=0A=
=0A=
	unit =3D SAUNIT(dev);=0A=
=0A=
	s =3D splsoftcam();=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL) {=0A=
		(void) splx(s);=0A=
		return (ENXIO);	=0A=
	}=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	if ((error =3D cam_periph_lock(periph, PRIBIO|PCATCH)) !=3D 0) {=0A=
		splx(s);=0A=
		return (error);=0A=
	}=0A=
	splx(s);=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,=0A=
	    ("saopen(%d): dev=3D0x%x softc=3D0x%x\n", unit, unit, =
softc->flags));=0A=
=0A=
	if (cam_periph_acquire(periph) !=3D CAM_REQ_CMP) {=0A=
		cam_periph_unlock(periph);=0A=
		return (ENXIO);=0A=
	}=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		softc->ctrl_mode =3D 1;=0A=
		cam_periph_unlock(periph);=0A=
		return (0);=0A=
	}=0A=
=0A=
=0A=
	if (softc->flags & SA_FLAG_OPEN) {=0A=
		error =3D EBUSY;=0A=
	} else if (softc->flags & SA_FLAG_INVALID) {=0A=
		error =3D ENXIO;=0A=
	} else {=0A=
		/*=0A=
		 * The function samount ensures media is loaded and ready.=0A=
		 * It also does a device RESERVE if the tape isn't yet mounted.=0A=
		 */=0A=
		error =3D samount(periph, flags, dev);=0A=
	}=0A=
=0A=
	if (error) {=0A=
		cam_periph_release(periph);=0A=
	} else {=0A=
		saprevent(periph, PR_PREVENT);=0A=
		softc->flags |=3D SA_FLAG_OPEN;=0A=
	}=0A=
	cam_periph_unlock(periph);=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saclose(struct cdev *dev, int flag, int fmt, struct thread *td)=0A=
{=0A=
	struct	cam_periph *periph;=0A=
	struct	sa_softc *softc;=0A=
	int	unit, mode, error, writing, tmp;=0A=
	int	closedbits =3D SA_FLAG_OPEN;=0A=
=0A=
	unit =3D SAUNIT(dev);=0A=
	mode =3D SAMODE(dev);=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL)=0A=
		return (ENXIO);	=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,=0A=
	    ("saclose(%d): dev=3D0x%x softc=3D0x%x\n", unit, unit, =
softc->flags));=0A=
=0A=
=0A=
	if ((error =3D cam_periph_lock(periph, PRIBIO)) !=3D 0) {=0A=
		return (error);=0A=
	}=0A=
=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		softc->ctrl_mode =3D 0;=0A=
		cam_periph_release(periph);=0A=
		cam_periph_unlock(periph);=0A=
		return (0);=0A=
	}=0A=
=0A=
	/*=0A=
	 * Were we writing the tape?=0A=
	 */=0A=
	writing =3D (softc->flags & SA_FLAG_TAPE_WRITTEN) !=3D 0;=0A=
=0A=
	/*=0A=
	 * See whether or not we need to write filemarks. If this=0A=
	 * fails, we probably have to assume we've lost tape=0A=
	 * position.=0A=
	 */=0A=
	error =3D sacheckeod(periph);=0A=
	if (error) {=0A=
		xpt_print_path(periph->path);=0A=
		printf("failed to write terminating filemark(s)\n");=0A=
		softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
	}=0A=
=0A=
	/*=0A=
	 * Whatever we end up doing, allow users to eject tapes from here on.=0A=
	 */=0A=
	saprevent(periph, PR_ALLOW);=0A=
=0A=
	/*=0A=
	 * Decide how to end...=0A=
	 */=0A=
	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0) {=0A=
		closedbits |=3D SA_FLAG_TAPE_FROZEN;=0A=
	} else switch (mode) {=0A=
	case SA_MODE_OFFLINE:=0A=
		/*=0A=
		 * An 'offline' close is an unconditional release of=0A=
		 * frozen && mount conditions, irrespective of whether=0A=
		 * these operations succeeded. The reason for this is=0A=
		 * to allow at least some kind of programmatic way=0A=
		 * around our state getting all fouled up. If somebody=0A=
		 * issues an 'offline' command, that will be allowed=0A=
		 * to clear state.=0A=
		 */=0A=
		(void) sarewind(periph);=0A=
		(void) saloadunload(periph, FALSE);=0A=
		closedbits |=3D SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;=0A=
		break;=0A=
	case SA_MODE_REWIND:=0A=
		/*=0A=
		 * If the rewind fails, return an error- if anyone cares,=0A=
		 * but not overwriting any previous error.=0A=
		 *=0A=
		 * We don't clear the notion of mounted here, but we do=0A=
		 * clear the notion of frozen if we successfully rewound.=0A=
		 */=0A=
		tmp =3D sarewind(periph);=0A=
		if (tmp) {=0A=
			if (error !=3D 0)=0A=
				error =3D tmp;=0A=
		} else {=0A=
			closedbits |=3D SA_FLAG_TAPE_FROZEN;=0A=
		}=0A=
		break;=0A=
	case SA_MODE_NOREWIND:=0A=
		/*=0A=
		 * If we're not rewinding/unloading the tape, find out=0A=
		 * whether we need to back up over one of two filemarks=0A=
		 * we wrote (if we wrote two filemarks) so that appends=0A=
		 * from this point on will be sane.=0A=
		 */=0A=
		if (error =3D=3D 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {=0A=
			tmp =3D saspace(periph, -1, SS_FILEMARKS);=0A=
			if (tmp) {=0A=
				xpt_print_path(periph->path);=0A=
				printf("unable to backspace over one of double"=0A=
				   " filemarks at end of tape\n");=0A=
				xpt_print_path(periph->path);=0A=
				printf("it is possible that this device"=0A=
				   " needs a SA_QUIRK_1FM quirk set for it\n");=0A=
				softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
			}=0A=
		}=0A=
		break;=0A=
	default:=0A=
		xpt_print_path(periph->path);=0A=
		panic("unknown mode 0x%x in saclose", mode);=0A=
		/* NOTREACHED */=0A=
		break;=0A=
	}=0A=
=0A=
	/*=0A=
	 * We wish to note here that there are no more filemarks to be written.=0A=
	 */=0A=
	softc->filemarks =3D 0;=0A=
	softc->flags &=3D ~SA_FLAG_TAPE_WRITTEN;=0A=
=0A=
	/*=0A=
	 * And we are no longer open for business.=0A=
	 */=0A=
	softc->flags &=3D ~closedbits;=0A=
=0A=
	/*=0A=
	 * Inform users if tape state if frozen....=0A=
	 */=0A=
	if (softc->flags & SA_FLAG_TAPE_FROZEN) {=0A=
		xpt_print_path(periph->path);=0A=
		printf("tape is now frozen- use an OFFLINE, REWIND or MTEOM "=0A=
		    "command to clear this state.\n");=0A=
	}=0A=
	=0A=
	/* release the device if it is no longer mounted */=0A=
	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0)=0A=
		sareservereleaseunit(periph, FALSE);=0A=
=0A=
	cam_periph_unlock(periph);=0A=
	cam_periph_release(periph);=0A=
=0A=
	return (error);	=0A=
}=0A=
=0A=
/*=0A=
 * Actually translate the requested transfer into one the physical driver=0A=
 * can understand.  The transfer is described by a buf and will include=0A=
 * only one physical transfer.=0A=
 */=0A=
static void=0A=
sastrategy(struct bio *bp)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	int    s;=0A=
	=0A=
	bp->bio_resid =3D bp->bio_bcount;=0A=
	if (SA_IS_CTRL(bp->bio_dev)) {=0A=
		biofinish(bp, NULL, EINVAL);=0A=
		return;=0A=
	}=0A=
	periph =3D (struct cam_periph *)bp->bio_dev->si_drv1;=0A=
	if (periph =3D=3D NULL) {=0A=
		biofinish(bp, NULL, ENXIO);=0A=
		return;=0A=
	}=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	s =3D splsoftcam();=0A=
=0A=
	if (softc->flags & SA_FLAG_INVALID) {=0A=
		splx(s);=0A=
		biofinish(bp, NULL, ENXIO);=0A=
		return;=0A=
	}=0A=
=0A=
	if (softc->flags & SA_FLAG_TAPE_FROZEN) {=0A=
		splx(s);=0A=
		biofinish(bp, NULL, EPERM);=0A=
		return;=0A=
	}=0A=
=0A=
	splx(s);=0A=
=0A=
	/*=0A=
	 * If it's a null transfer, return immediatly=0A=
	 */=0A=
	if (bp->bio_bcount =3D=3D 0) {=0A=
		biodone(bp);=0A=
		return;=0A=
	}=0A=
=0A=
	/* valid request?  */=0A=
	if (softc->flags & SA_FLAG_FIXED) {=0A=
		/*=0A=
		 * Fixed block device.  The byte count must=0A=
		 * be a multiple of our block size.=0A=
		 */=0A=
		if (((softc->blk_mask !=3D ~0) &&=0A=
		    ((bp->bio_bcount & softc->blk_mask) !=3D 0)) ||=0A=
		    ((softc->blk_mask =3D=3D ~0) &&=0A=
		    ((bp->bio_bcount % softc->min_blk) !=3D 0))) {=0A=
			xpt_print_path(periph->path);=0A=
			printf("Invalid request.  Fixed block device "=0A=
			       "requests must be a multiple "=0A=
			       "of %d bytes\n", softc->min_blk);=0A=
			biofinish(bp, NULL, EINVAL);=0A=
			return;=0A=
		}=0A=
	} else if ((bp->bio_bcount > softc->max_blk) ||=0A=
		   (bp->bio_bcount < softc->min_blk) ||=0A=
		   (bp->bio_bcount & softc->blk_mask) !=3D 0) {=0A=
=0A=
		xpt_print_path(periph->path);=0A=
		printf("Invalid request.  Variable block device "=0A=
		    "requests must be ");=0A=
		if (softc->blk_mask !=3D 0) {=0A=
			printf("a multiple of %d ", (0x1 << softc->blk_gran));=0A=
		}=0A=
		printf("between %d and %d bytes\n", softc->min_blk,=0A=
		    softc->max_blk);=0A=
		biofinish(bp, NULL, EINVAL);=0A=
		return;=0A=
        }=0A=
	=0A=
	/*=0A=
	 * Mask interrupts so that the device cannot be invalidated until=0A=
	 * after we are in the queue.  Otherwise, we might not properly=0A=
	 * clean up one of the buffers.=0A=
	 */=0A=
	s =3D splbio();=0A=
	=0A=
	/*=0A=
	 * Place it at the end of the queue.=0A=
	 */=0A=
	bioq_insert_tail(&softc->bio_queue, bp);=0A=
	softc->queue_count++;=0A=
#if	0=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
	    ("sastrategy: queuing a %ld %s byte %s\n", bp->bio_bcount,=0A=
 	    (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",=0A=
	    (bp->bio_cmd =3D=3D BIO_READ)? "read" : "write"));=0A=
#endif=0A=
	if (softc->queue_count > 1) {=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
		    ("sastrategy: queue count now %d\n", softc->queue_count));=0A=
	}=0A=
	splx(s);=0A=
	=0A=
	/*=0A=
	 * Schedule ourselves for performing the work.=0A=
	 */=0A=
	xpt_schedule(periph, 1);=0A=
=0A=
	return;=0A=
}=0A=
=0A=
static int=0A=
saioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct =
thread *td)=0A=
{=0A=
	struct cam_periph *periph;=0A=
	struct sa_softc *softc;=0A=
	scsi_space_code spaceop;=0A=
	int didlockperiph =3D 0;=0A=
	int s;=0A=
	int mode;=0A=
	int error =3D 0;=0A=
=0A=
	mode =3D SAMODE(dev);=0A=
	error =3D 0;		/* shut up gcc */=0A=
	spaceop =3D 0;		/* shut up gcc */=0A=
=0A=
	periph =3D (struct cam_periph *)dev->si_drv1;=0A=
	if (periph =3D=3D NULL)=0A=
		return (ENXIO);	=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * Check for control mode accesses. We allow MTIOCGET and=0A=
	 * MTIOCERRSTAT (but need to be the only one open in order=0A=
	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY=0A=
	 * and MTCOMP (but need to be the only one accessing this=0A=
	 * device to run those).=0A=
	 */=0A=
=0A=
	if (SA_IS_CTRL(dev)) {=0A=
		switch (cmd) {=0A=
		case MTIOCGETEOTMODEL:=0A=
		case MTIOCGET:=0A=
			break;=0A=
		case MTIOCERRSTAT:=0A=
			/*=0A=
			 * If the periph isn't already locked, lock it=0A=
			 * so our MTIOCERRSTAT can reset latched error stats.=0A=
			 *=0A=
			 * If the periph is already locked, skip it because=0A=
			 * we're just getting status and it'll be up to the=0A=
			 * other thread that has this device open to do=0A=
			 * an MTIOCERRSTAT that would clear latched status.=0A=
			 */=0A=
			s =3D splsoftcam();=0A=
			if ((periph->flags & CAM_PERIPH_LOCKED) =3D=3D 0) {=0A=
				error =3D cam_periph_lock(periph, PRIBIO|PCATCH);=0A=
				if (error !=3D 0) {=0A=
					splx(s);=0A=
					return (error);=0A=
				}=0A=
				didlockperiph =3D 1;=0A=
			}=0A=
			break;=0A=
=0A=
		case MTIOCSETEOTMODEL:=0A=
		case MTSETBSIZ:=0A=
		case MTSETDNSTY:=0A=
		case MTCOMP:=0A=
			/*=0A=
			 * We need to acquire the peripheral here rather=0A=
			 * than at open time because we are sharing writable=0A=
			 * access to data structures.=0A=
			 */=0A=
			s =3D splsoftcam();=0A=
			error =3D cam_periph_lock(periph, PRIBIO|PCATCH);=0A=
			if (error !=3D 0) {=0A=
				splx(s);=0A=
				return (error);=0A=
			}=0A=
			didlockperiph =3D 1;=0A=
			break;=0A=
=0A=
		default:=0A=
			return (EINVAL);=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Find the device that the user is talking about=0A=
	 */=0A=
	switch (cmd) {=0A=
	case MTIOCGET:=0A=
	{=0A=
		struct mtget *g =3D (struct mtget *)arg;=0A=
=0A=
		/*=0A=
		 * If this isn't the control mode device, actually go out=0A=
		 * and ask the drive again what it's set to.=0A=
		 */=0A=
		if (!SA_IS_CTRL(dev)) {=0A=
			u_int8_t write_protect;=0A=
			int comp_enabled, comp_supported;=0A=
			error =3D sagetparams(periph, SA_PARAM_ALL,=0A=
			    &softc->media_blksize, &softc->media_density,=0A=
			    &softc->media_numblks, &softc->buffer_mode,=0A=
			    &write_protect, &softc->speed, &comp_supported,=0A=
			    &comp_enabled, &softc->comp_algorithm, NULL);=0A=
			if (error)=0A=
				break;=0A=
			if (write_protect)=0A=
				softc->flags |=3D SA_FLAG_TAPE_WP;=0A=
			else=0A=
				softc->flags &=3D ~SA_FLAG_TAPE_WP;=0A=
			softc->flags &=3D ~(SA_FLAG_COMP_SUPP|=0A=
			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);=0A=
			if (comp_supported) {=0A=
				if (softc->saved_comp_algorithm =3D=3D 0)=0A=
					softc->saved_comp_algorithm =3D=0A=
					    softc->comp_algorithm;=0A=
				softc->flags |=3D SA_FLAG_COMP_SUPP;=0A=
				if (comp_enabled)=0A=
					softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
			} else  =0A=
				softc->flags |=3D SA_FLAG_COMP_UNSUPP;=0A=
		}=0A=
		bzero(g, sizeof(struct mtget));=0A=
		g->mt_type =3D MT_ISAR;=0A=
		if (softc->flags & SA_FLAG_COMP_UNSUPP) {=0A=
			g->mt_comp =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp0 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp1 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp2 =3D MT_COMP_UNSUPP;=0A=
			g->mt_comp3 =3D MT_COMP_UNSUPP;=0A=
		} else {=0A=
			if ((softc->flags & SA_FLAG_COMP_ENABLED) =3D=3D 0) {=0A=
				g->mt_comp =3D MT_COMP_DISABLED;=0A=
			} else {=0A=
				g->mt_comp =3D softc->comp_algorithm;=0A=
			}=0A=
			g->mt_comp0 =3D softc->comp_algorithm;=0A=
			g->mt_comp1 =3D softc->comp_algorithm;=0A=
			g->mt_comp2 =3D softc->comp_algorithm;=0A=
			g->mt_comp3 =3D softc->comp_algorithm;=0A=
		}=0A=
		g->mt_density =3D softc->media_density;=0A=
		g->mt_density0 =3D softc->media_density;=0A=
		g->mt_density1 =3D softc->media_density;=0A=
		g->mt_density2 =3D softc->media_density;=0A=
		g->mt_density3 =3D softc->media_density;=0A=
		g->mt_blksiz =3D softc->media_blksize;=0A=
		g->mt_blksiz0 =3D softc->media_blksize;=0A=
		g->mt_blksiz1 =3D softc->media_blksize;=0A=
		g->mt_blksiz2 =3D softc->media_blksize;=0A=
		g->mt_blksiz3 =3D softc->media_blksize;=0A=
		g->mt_fileno =3D softc->fileno;=0A=
		g->mt_blkno =3D softc->blkno;=0A=
		g->mt_dsreg =3D (short) softc->dsreg;=0A=
		/*=0A=
		 * Yes, we know that this is likely to overflow=0A=
		 */=0A=
		if (softc->last_resid_was_io) {=0A=
			if ((g->mt_resid =3D (short) softc->last_io_resid) !=3D 0) {=0A=
				if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph) {=0A=
					softc->last_io_resid =3D 0;=0A=
				}=0A=
			}=0A=
		} else {=0A=
			if ((g->mt_resid =3D (short)softc->last_ctl_resid) !=3D 0) {=0A=
				if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph) {=0A=
					softc->last_ctl_resid =3D 0;=0A=
				}=0A=
			}=0A=
		}=0A=
		error =3D 0;=0A=
		break;=0A=
	}=0A=
	case MTIOCERRSTAT:=0A=
	{=0A=
		struct scsi_tape_errors *sep =3D=0A=
		    &((union mterrstat *)arg)->scsi_errstat;=0A=
=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,=0A=
		    ("saioctl: MTIOCERRSTAT\n"));=0A=
=0A=
		bzero(sep, sizeof(*sep));=0A=
		sep->io_resid =3D softc->last_io_resid;=0A=
		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,=0A=
		    sizeof (sep->io_sense));=0A=
		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,=0A=
		    sizeof (sep->io_cdb));=0A=
		sep->ctl_resid =3D softc->last_ctl_resid;=0A=
		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,=0A=
		    sizeof (sep->ctl_sense));=0A=
		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,=0A=
		    sizeof (sep->ctl_cdb));=0A=
=0A=
		if (SA_IS_CTRL(dev) =3D=3D 0 || didlockperiph)=0A=
			bzero((caddr_t) &softc->errinfo,=0A=
			    sizeof (softc->errinfo));=0A=
		error =3D 0;=0A=
		break;=0A=
	}=0A=
	case MTIOCTOP:=0A=
	{=0A=
		struct mtop *mt;=0A=
		int    count;=0A=
=0A=
		mt =3D (struct mtop *)arg;=0A=
=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,=0A=
			 ("saioctl: op=3D0x%x count=3D0x%x\n",=0A=
			  mt->mt_op, mt->mt_count));=0A=
=0A=
		count =3D mt->mt_count;=0A=
		switch (mt->mt_op) {=0A=
		case MTWEOF:	/* write an end-of-file marker */=0A=
			/*=0A=
			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN=0A=
			 * flag because by keeping track of filemarks=0A=
			 * we have last written we know ehether or not=0A=
			 * we need to write more when we close the device.=0A=
			 */=0A=
			error =3D sawritefilemarks(periph, count, FALSE);=0A=
			break;=0A=
		case MTWSS:	/* write a setmark */=0A=
			error =3D sawritefilemarks(periph, count, TRUE);=0A=
			break;=0A=
		case MTBSR:	/* backward space record */=0A=
		case MTFSR:	/* forward space record */=0A=
		case MTBSF:	/* backward space file */=0A=
		case MTFSF:	/* forward space file */=0A=
		case MTBSS:	/* backward space setmark */=0A=
		case MTFSS:	/* forward space setmark */=0A=
		case MTEOD:	/* space to end of recorded medium */=0A=
		{=0A=
			int nmarks;=0A=
=0A=
			spaceop =3D SS_FILEMARKS;=0A=
			nmarks =3D softc->filemarks;=0A=
			error =3D sacheckeod(periph);=0A=
			if (error) {=0A=
				xpt_print_path(periph->path);=0A=
				printf("EOD check prior to spacing failed\n");=0A=
				softc->flags |=3D SA_FLAG_EIO_PENDING;=0A=
				break;=0A=
			}=0A=
			nmarks -=3D softc->filemarks;=0A=
			switch(mt->mt_op) {=0A=
			case MTBSR:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSR:=0A=
				spaceop =3D SS_BLOCKS;=0A=
				break;=0A=
			case MTBSF:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSF:=0A=
				break;=0A=
			case MTBSS:=0A=
				count =3D -count;=0A=
				/* FALLTHROUGH */=0A=
			case MTFSS:=0A=
				spaceop =3D SS_SETMARKS;=0A=
				break;=0A=
			case MTEOD:=0A=
				spaceop =3D SS_EOD;=0A=
				count =3D 0;=0A=
				nmarks =3D 0;=0A=
				break;=0A=
			default:=0A=
				error =3D EINVAL;=0A=
				break;=0A=
			}=0A=
			if (error)=0A=
				break;=0A=
=0A=
			nmarks =3D softc->filemarks;=0A=
			/*=0A=
			 * XXX: Why are we checking again?=0A=
			 */=0A=
			error =3D sacheckeod(periph);=0A=
			if (error)=0A=
				break;=0A=
			nmarks -=3D softc->filemarks;=0A=
			error =3D saspace(periph, count - nmarks, spaceop);=0A=
			/*=0A=
			 * At this point, clear that we've written the tape=0A=
			 * and that we've written any filemarks. We really=0A=
			 * don't know what the applications wishes to do next-=0A=
			 * the sacheckeod's will make sure we terminated the=0A=
			 * tape correctly if we'd been writing, but the next=0A=
			 * action the user application takes will set again=0A=
			 * whether we need to write filemarks.=0A=
			 */=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->filemarks =3D 0;=0A=
			break;=0A=
		}=0A=
		case MTREW:	/* rewind */=0A=
			(void) sacheckeod(periph);=0A=
			error =3D sarewind(periph);=0A=
			/* see above */=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			softc->filemarks =3D 0;=0A=
			break;=0A=
		case MTERASE:	/* erase */=0A=
			error =3D saerase(periph, count);=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			break;=0A=
		case MTRETENS:	/* re-tension tape */=0A=
			error =3D saretension(periph);		=0A=
			softc->flags &=3D=0A=
			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);=0A=
			softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			break;=0A=
		case MTOFFL:	/* rewind and put the drive offline */=0A=
=0A=
			(void) sacheckeod(periph);=0A=
			/* see above */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_WRITTEN;=0A=
			softc->filemarks =3D 0;=0A=
=0A=
			error =3D sarewind(periph);=0A=
			/* clear the frozen flag anyway */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_FROZEN;=0A=
=0A=
			/*=0A=
			 * Be sure to allow media removal before ejecting.=0A=
			 */=0A=
=0A=
			saprevent(periph, PR_ALLOW);=0A=
			if (error =3D=3D 0) {=0A=
				error =3D saloadunload(periph, FALSE);=0A=
				if (error =3D=3D 0) {=0A=
					softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
				}=0A=
			}=0A=
			break;=0A=
=0A=
		case MTNOP:	/* no operation, sets status only */=0A=
		case MTCACHE:	/* enable controller cache */=0A=
		case MTNOCACHE:	/* disable controller cache */=0A=
			error =3D 0;=0A=
			break;=0A=
=0A=
		case MTSETBSIZ:	/* Set block size for device */=0A=
=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE, count,=0A=
					    0, 0, 0);=0A=
			if (error =3D=3D 0) {=0A=
				softc->last_media_blksize =3D=0A=
				    softc->media_blksize;=0A=
				softc->media_blksize =3D count;=0A=
				if (count) {=0A=
					softc->flags |=3D SA_FLAG_FIXED;=0A=
					if (powerof2(count)) {=0A=
						softc->blk_shift =3D=0A=
						    ffs(count) - 1;=0A=
						softc->blk_mask =3D count - 1;=0A=
					} else {=0A=
						softc->blk_mask =3D ~0;=0A=
						softc->blk_shift =3D 0;=0A=
					}=0A=
					/*=0A=
					 * Make the user's desire 'persistent'.=0A=
					 */=0A=
					softc->quirks &=3D ~SA_QUIRK_VARIABLE;=0A=
					softc->quirks |=3D SA_QUIRK_FIXED;=0A=
				} else {=0A=
					softc->flags &=3D ~SA_FLAG_FIXED;=0A=
					if (softc->max_blk =3D=3D 0) {=0A=
						softc->max_blk =3D ~0;=0A=
					}=0A=
					softc->blk_shift =3D 0;=0A=
					if (softc->blk_gran !=3D 0) {=0A=
						softc->blk_mask =3D=0A=
						    softc->blk_gran - 1;=0A=
					} else {=0A=
						softc->blk_mask =3D 0;=0A=
					}=0A=
					/*=0A=
					 * Make the user's desire 'persistent'.=0A=
					 */=0A=
					softc->quirks |=3D SA_QUIRK_VARIABLE;=0A=
					softc->quirks &=3D ~SA_QUIRK_FIXED;=0A=
				}=0A=
			}=0A=
			break;=0A=
		case MTSETDNSTY:	/* Set density for device and mode */=0A=
			if (count > UCHAR_MAX) {=0A=
				error =3D EINVAL;	=0A=
				break;=0A=
			} else {=0A=
				error =3D sasetparams(periph, SA_PARAM_DENSITY,=0A=
						    0, count, 0, 0);=0A=
			}=0A=
			break;=0A=
		case MTCOMP:	/* enable compression */=0A=
			/*=0A=
			 * Some devices don't support compression, and=0A=
			 * don't like it if you ask them for the=0A=
			 * compression page.=0A=
			 */=0A=
			if ((softc->quirks & SA_QUIRK_NOCOMP) ||=0A=
			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {=0A=
				error =3D ENODEV;=0A=
				break;=0A=
			}=0A=
			error =3D sasetparams(periph, SA_PARAM_COMPRESSION,=0A=
			    0, 0, count, SF_NO_PRINT);=0A=
			break;=0A=
		default:=0A=
			error =3D EINVAL;=0A=
		}=0A=
		break;=0A=
	}=0A=
	case MTIOCIEOT:=0A=
	case MTIOCEEOT:=0A=
		error =3D 0;=0A=
		break;=0A=
	case MTIOCRDSPOS:=0A=
		error =3D sardpos(periph, 0, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCRDHPOS:=0A=
		error =3D sardpos(periph, 1, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCSLOCATE:=0A=
		error =3D sasetpos(periph, 0, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCHLOCATE:=0A=
		error =3D sasetpos(periph, 1, (u_int32_t *) arg);=0A=
		break;=0A=
	case MTIOCGETEOTMODEL:=0A=
		error =3D 0;=0A=
		if (softc->quirks & SA_QUIRK_1FM)=0A=
			mode =3D 1;=0A=
		else=0A=
			mode =3D 2;=0A=
		*((u_int32_t *) arg) =3D mode;=0A=
		break;=0A=
	case MTIOCSETEOTMODEL:=0A=
		error =3D 0;=0A=
		switch (*((u_int32_t *) arg)) {=0A=
		case 1:=0A=
			softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
			softc->quirks |=3D SA_QUIRK_1FM;=0A=
			break;=0A=
		case 2:=0A=
			softc->quirks &=3D ~SA_QUIRK_1FM;=0A=
			softc->quirks |=3D SA_QUIRK_2FM;=0A=
			break;=0A=
		default:=0A=
			error =3D EINVAL;=0A=
			break;=0A=
		}=0A=
		break;=0A=
	default:=0A=
		error =3D cam_periph_ioctl(periph, cmd, arg, saerror);=0A=
		break;=0A=
	}=0A=
=0A=
	/*=0A=
	 * Check to see if we cleared a frozen state=0A=
	 */=0A=
	if (error =3D=3D 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {=0A=
		switch(cmd) {=0A=
		case MTIOCRDSPOS:=0A=
		case MTIOCRDHPOS:=0A=
		case MTIOCSLOCATE:=0A=
		case MTIOCHLOCATE:=0A=
			softc->fileno =3D (daddr_t) -1;=0A=
			softc->blkno =3D (daddr_t) -1;=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_FROZEN;=0A=
			xpt_print_path(periph->path);=0A=
			printf("tape state now unfrozen.\n");=0A=
			break;=0A=
		default:=0A=
			break;=0A=
		}=0A=
	}=0A=
	if (didlockperiph) {=0A=
		cam_periph_unlock(periph);=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static void=0A=
sainit(void)=0A=
{=0A=
	cam_status status;=0A=
	struct cam_path *path;=0A=
=0A=
	/*=0A=
	 * Install a global async callback.=0A=
	 */=0A=
	status =3D xpt_create_path(&path, NULL, CAM_XPT_PATH_ID,=0A=
				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);=0A=
=0A=
	if (status =3D=3D CAM_REQ_CMP) {=0A=
		/* Register the async callbacks of interrest */=0A=
		struct ccb_setasync csa; /*=0A=
					  * This is an immediate CCB,=0A=
					  * so using the stack is OK=0A=
					  */=0A=
		xpt_setup_ccb(&csa.ccb_h, path, 5);=0A=
		csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
		csa.event_enable =3D AC_FOUND_DEVICE;=0A=
		csa.callback =3D saasync;=0A=
		csa.callback_arg =3D NULL;=0A=
		xpt_action((union ccb *)&csa);=0A=
		status =3D csa.ccb_h.status;=0A=
		xpt_free_path(path);=0A=
	}=0A=
=0A=
	if (status !=3D CAM_REQ_CMP) {=0A=
		printf("sa: Failed to attach master async callback "=0A=
		       "due to status 0x%x!\n", status);=0A=
	}=0A=
}=0A=
=0A=
static void=0A=
saoninvalidate(struct cam_periph *periph)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_setasync csa;=0A=
	int s;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * De-register any async callbacks.=0A=
	 */=0A=
	xpt_setup_ccb(&csa.ccb_h, periph->path,=0A=
		      /* priority */ 5);=0A=
	csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
	csa.event_enable =3D 0;=0A=
	csa.callback =3D saasync;=0A=
	csa.callback_arg =3D periph;=0A=
	xpt_action((union ccb *)&csa);=0A=
=0A=
	softc->flags |=3D SA_FLAG_INVALID;=0A=
=0A=
	/*=0A=
	 * Although the oninvalidate() routines are always called at=0A=
	 * splsoftcam, we need to be at splbio() here to keep the buffer=0A=
	 * queue from being modified while we traverse it.=0A=
	 */=0A=
	s =3D splbio();=0A=
=0A=
	/*=0A=
	 * Return all queued I/O with ENXIO.=0A=
	 * XXX Handle any transactions queued to the card=0A=
	 *     with XPT_ABORT_CCB.=0A=
	 */=0A=
	bioq_flush(&softc->bio_queue, NULL, ENXIO);=0A=
	softc->queue_count =3D 0;=0A=
	splx(s);=0A=
=0A=
	xpt_print_path(periph->path);=0A=
	printf("lost device\n");=0A=
=0A=
}=0A=
=0A=
static void=0A=
sacleanup(struct cam_periph *periph)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	int i;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	devstat_remove_entry(softc->device_stats);=0A=
=0A=
	destroy_dev(softc->devs.ctl_dev);=0A=
=0A=
	for (i =3D 0; i < SA_NUM_MODES; i++) {=0A=
		destroy_dev(softc->devs.mode_devs[i].r_dev);=0A=
		destroy_dev(softc->devs.mode_devs[i].nr_dev);=0A=
		destroy_dev(softc->devs.mode_devs[i].er_dev);=0A=
	}=0A=
=0A=
	xpt_print_path(periph->path);=0A=
	printf("removing device entry\n");=0A=
	free(softc, M_DEVBUF);=0A=
}=0A=
=0A=
static void=0A=
saasync(void *callback_arg, u_int32_t code,=0A=
	struct cam_path *path, void *arg)=0A=
{=0A=
	struct cam_periph *periph;=0A=
=0A=
	periph =3D (struct cam_periph *)callback_arg;=0A=
	switch (code) {=0A=
	case AC_FOUND_DEVICE:=0A=
	{=0A=
		struct ccb_getdev *cgd;=0A=
		cam_status status;=0A=
=0A=
		cgd =3D (struct ccb_getdev *)arg;=0A=
		if (cgd =3D=3D NULL)=0A=
			break;=0A=
=0A=
		if (SID_TYPE(&cgd->inq_data) !=3D T_SEQUENTIAL)=0A=
			break;=0A=
=0A=
		/*=0A=
		 * Allocate a peripheral instance for=0A=
		 * this device and start the probe=0A=
		 * process.=0A=
		 */=0A=
		status =3D cam_periph_alloc(saregister, saoninvalidate,=0A=
					  sacleanup, sastart,=0A=
					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,=0A=
					  saasync, AC_FOUND_DEVICE, cgd);=0A=
=0A=
		if (status !=3D CAM_REQ_CMP=0A=
		 && status !=3D CAM_REQ_INPROG)=0A=
			printf("saasync: Unable to probe new device "=0A=
				"due to status 0x%x\n", status);=0A=
		break;=0A=
	}=0A=
	default:=0A=
		cam_periph_async(periph, code, path, arg);=0A=
		break;=0A=
	}=0A=
}=0A=
=0A=
static cam_status=0A=
saregister(struct cam_periph *periph, void *arg)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_setasync csa;=0A=
	struct ccb_getdev *cgd;=0A=
	caddr_t match;=0A=
	int i;=0A=
	=0A=
	cgd =3D (struct ccb_getdev *)arg;=0A=
	if (periph =3D=3D NULL) {=0A=
		printf("saregister: periph was NULL!!\n");=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
=0A=
	if (cgd =3D=3D NULL) {=0A=
		printf("saregister: no getdev CCB, can't register device\n");=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
=0A=
	softc =3D (struct sa_softc *)=0A=
	    malloc(sizeof (*softc), M_DEVBUF, M_NOWAIT | M_ZERO);=0A=
	if (softc =3D=3D NULL) {=0A=
		printf("saregister: Unable to probe new device. "=0A=
		       "Unable to allocate softc\n");				=0A=
		return (CAM_REQ_CMP_ERR);=0A=
	}=0A=
	softc->scsi_rev =3D SID_ANSI_REV(&cgd->inq_data);=0A=
	softc->state =3D SA_STATE_NORMAL;=0A=
	softc->fileno =3D (daddr_t) -1;=0A=
	softc->blkno =3D (daddr_t) -1;=0A=
=0A=
	bioq_init(&softc->bio_queue);=0A=
	periph->softc =3D softc;=0A=
=0A=
	/*=0A=
	 * See if this device has any quirks.=0A=
	 */=0A=
	match =3D cam_quirkmatch((caddr_t)&cgd->inq_data,=0A=
			       (caddr_t)sa_quirk_table,=0A=
			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),=0A=
			       sizeof(*sa_quirk_table), scsi_inquiry_match);=0A=
=0A=
	if (match !=3D NULL) {=0A=
		softc->quirks =3D ((struct sa_quirk_entry *)match)->quirks;=0A=
		softc->last_media_blksize =3D=0A=
		    ((struct sa_quirk_entry *)match)->prefblk;=0A=
#ifdef	CAMDEBUG=0A=
		xpt_print_path(periph->path);=0A=
		printf("found quirk entry %d\n", (int)=0A=
		    (((struct sa_quirk_entry *) match) - sa_quirk_table));=0A=
#endif=0A=
	} else=0A=
		softc->quirks =3D SA_QUIRK_NONE;=0A=
=0A=
	/*=0A=
 	 * The SA driver supports a blocksize, but we don't know the=0A=
	 * blocksize until we media is inserted.  So, set a flag to=0A=
	 * indicate that the blocksize is unavailable right now.=0A=
	 */=0A=
	softc->device_stats =3D devstat_new_entry("sa", periph->unit_number, 0,=0A=
	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |=0A=
	    DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_TAPE);=0A=
=0A=
	softc->devs.ctl_dev =3D make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,=0A=
	    periph->unit_number, 0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR,=0A=
	    0660, "%s%d.ctl", periph->periph_name, periph->unit_number);=0A=
	softc->devs.ctl_dev->si_drv1 =3D periph;=0A=
=0A=
	for (i =3D 0; i < SA_NUM_MODES; i++) {=0A=
=0A=
		softc->devs.mode_devs[i].r_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_R),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].r_dev->si_drv1 =3D periph;=0A=
=0A=
		softc->devs.mode_devs[i].nr_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_NR),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].nr_dev->si_drv1 =3D periph;=0A=
=0A=
		softc->devs.mode_devs[i].er_dev =3D make_dev(&sa_cdevsw,=0A=
		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_ER),=0A=
		    UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d",=0A=
		    periph->periph_name, periph->unit_number, i);=0A=
		softc->devs.mode_devs[i].er_dev->si_drv1 =3D periph;=0A=
=0A=
		/*=0A=
		 * Make the (well known) aliases for the first mode.=0A=
		 */=0A=
		if (i =3D=3D 0) {=0A=
			struct cdev *alias;=0A=
=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].r_dev,=0A=
			   "%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].nr_dev,=0A=
			    "n%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
			alias =3D make_dev_alias(softc->devs.mode_devs[i].er_dev,=0A=
			    "e%s%d", periph->periph_name, periph->unit_number);=0A=
			alias->si_drv1 =3D periph;=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Add an async callback so that we get=0A=
	 * notified if this device goes away.=0A=
	 */=0A=
	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);=0A=
	csa.ccb_h.func_code =3D XPT_SASYNC_CB;=0A=
	csa.event_enable =3D AC_LOST_DEVICE;=0A=
	csa.callback =3D saasync;=0A=
	csa.callback_arg =3D periph;=0A=
	xpt_action((union ccb *)&csa);=0A=
=0A=
	xpt_announce_periph(periph, NULL);=0A=
=0A=
	return (CAM_REQ_CMP);=0A=
}=0A=
=0A=
static void=0A=
sastart(struct cam_periph *periph, union ccb *start_ccb)=0A=
{=0A=
	struct sa_softc *softc;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sastart\n"));=0A=
=0A=
	=0A=
	switch (softc->state) {=0A=
	case SA_STATE_NORMAL:=0A=
	{=0A=
		/* Pull a buffer from the queue and get going on it */		=0A=
		struct bio *bp;=0A=
		int s;=0A=
=0A=
		/*=0A=
		 * See if there is a buf with work for us to do..=0A=
		 */=0A=
		s =3D splbio();=0A=
		bp =3D bioq_first(&softc->bio_queue);=0A=
		if (periph->immediate_priority <=3D periph->pinfo.priority) {=0A=
			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,=0A=
					("queuing for immediate ccb\n"));=0A=
			Set_CCB_Type(start_ccb, SA_CCB_WAITING);=0A=
			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,=0A=
					  periph_links.sle);=0A=
			periph->immediate_priority =3D CAM_PRIORITY_NONE;=0A=
			splx(s);=0A=
			wakeup(&periph->ccb_list);=0A=
		} else if (bp =3D=3D NULL) {=0A=
			splx(s);=0A=
			xpt_release_ccb(start_ccb);=0A=
		} else if ((softc->flags & SA_FLAG_ERR_PENDING) !=3D 0) {=0A=
			struct bio *done_bp;=0A=
again:=0A=
			softc->queue_count--;=0A=
			bioq_remove(&softc->bio_queue, bp);=0A=
			bp->bio_resid =3D bp->bio_bcount;=0A=
			done_bp =3D bp;=0A=
			if ((softc->flags & SA_FLAG_EOM_PENDING) !=3D 0) {=0A=
				/*=0A=
				 * We now just clear errors in this case=0A=
				 * and let the residual be the notifier.=0A=
				 */=0A=
				bp->bio_error =3D 0;=0A=
			} else if ((softc->flags & SA_FLAG_EOF_PENDING) !=3D 0) {=0A=
				/*=0A=
				 * This can only happen if we're reading=0A=
				 * in fixed length mode. In this case,=0A=
				 * we dump the rest of the list the=0A=
				 * same way.=0A=
				 */=0A=
				bp->bio_error =3D 0;=0A=
				if (bioq_first(&softc->bio_queue) !=3D NULL) {=0A=
					biodone(done_bp);=0A=
					goto again;=0A=
				}=0A=
			} else if ((softc->flags & SA_FLAG_EIO_PENDING) !=3D 0) {=0A=
				bp->bio_error =3D EIO;=0A=
				bp->bio_flags |=3D BIO_ERROR;=0A=
			}=0A=
			bp =3D bioq_first(&softc->bio_queue);=0A=
			/*=0A=
			 * Only if we have no other buffers queued up=0A=
			 * do we clear the pending error flag.=0A=
			 */=0A=
			if (bp =3D=3D NULL)=0A=
				softc->flags &=3D ~SA_FLAG_ERR_PENDING;=0A=
			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "=0A=
			    "%d more buffers queued up\n",=0A=
			    (softc->flags & SA_FLAG_ERR_PENDING),=0A=
			    (bp !=3D NULL)? "not " : " ", softc->queue_count));=0A=
			splx(s);=0A=
			xpt_release_ccb(start_ccb);=0A=
			biodone(done_bp);=0A=
		} else {=0A=
			u_int32_t length;=0A=
=0A=
			bioq_remove(&softc->bio_queue, bp);=0A=
			softc->queue_count--;=0A=
=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
				if (softc->blk_shift !=3D 0) {=0A=
					length =3D=0A=
					    bp->bio_bcount >> softc->blk_shift;=0A=
				} else if (softc->media_blksize !=3D 0) {=0A=
					length =3D bp->bio_bcount /=0A=
					    softc->media_blksize;=0A=
				} else {=0A=
					bp->bio_error =3D EIO;=0A=
					xpt_print_path(periph->path);=0A=
					printf("zero blocksize for "=0A=
					    "FIXED length writes?\n");=0A=
					splx(s);=0A=
					biodone(bp);=0A=
					break;=0A=
				}=0A=
#if	0=0A=
				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,=0A=
				    ("issuing a %d fixed record %s\n",=0A=
				    length,  (bp->bio_cmd =3D=3D BIO_READ)? "read" :=0A=
				    "write"));=0A=
#endif=0A=
			} else {=0A=
				length =3D bp->bio_bcount;=0A=
#if	0=0A=
				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,=0A=
				    ("issuing a %d variable byte %s\n",=0A=
				    length,  (bp->bio_cmd =3D=3D BIO_READ)? "read" :=0A=
				    "write"));=0A=
#endif=0A=
			}=0A=
			devstat_start_transaction_bio(softc->device_stats, bp);=0A=
			/*=0A=
			 * Some people have theorized that we should=0A=
			 * suppress illegal length indication if we are=0A=
			 * running in variable block mode so that we don't=0A=
			 * have to request sense every time our requested=0A=
			 * block size is larger than the written block.=0A=
			 * The residual information from the ccb allows=0A=
			 * us to identify this situation anyway.  The only=0A=
			 * problem with this is that we will not get=0A=
			 * information about blocks that are larger than=0A=
			 * our read buffer unless we set the block size=0A=
			 * in the mode page to something other than 0.=0A=
			 *=0A=
			 * I believe that this is a non-issue. If user apps=0A=
			 * don't adjust their read size to match our record=0A=
			 * size, that's just life. Anyway, the typical usage=0A=
			 * would be to issue, e.g., 64KB reads and occasionally=0A=
			 * have to do deal with 512 byte or 1KB intermediate=0A=
			 * records.=0A=
			 */=0A=
			softc->dsreg =3D (bp->bio_cmd =3D=3D BIO_READ)?=0A=
			    MTIO_DSREG_RD : MTIO_DSREG_WR;=0A=
			scsi_sa_read_write(&start_ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd =3D=3D BIO_READ),=0A=
			    FALSE, (softc->flags & SA_FLAG_FIXED) !=3D 0,=0A=
			    length, bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,=0A=
			    IO_TIMEOUT);=0A=
			start_ccb->ccb_h.ccb_pflags &=3D ~SA_POSITION_UPDATED;=0A=
			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);=0A=
			start_ccb->ccb_h.ccb_bp =3D bp;=0A=
			bp =3D bioq_first(&softc->bio_queue);=0A=
			splx(s);=0A=
			xpt_action(start_ccb);=0A=
		}=0A=
		=0A=
		if (bp !=3D NULL) {=0A=
			/* Have more work to do, so ensure we stay scheduled */=0A=
			xpt_schedule(periph, 1);=0A=
		}=0A=
		break;=0A=
	}=0A=
	case SA_STATE_ABNORMAL:=0A=
	default:=0A=
		panic("state 0x%x in sastart", softc->state);=0A=
		break;=0A=
	}=0A=
}=0A=
=0A=
=0A=
static void=0A=
sadone(struct cam_periph *periph, union ccb *done_ccb)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	struct ccb_scsiio *csio;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	csio =3D &done_ccb->csio;=0A=
	switch (CCB_Type(csio)) {=0A=
	case SA_CCB_BUFFER_IO:=0A=
	{=0A=
		struct bio *bp;=0A=
		int error;=0A=
=0A=
		softc->dsreg =3D MTIO_DSREG_REST;=0A=
		bp =3D (struct bio *)done_ccb->ccb_h.ccb_bp;=0A=
		error =3D 0;=0A=
		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) !=3D CAM_REQ_CMP) {=0A=
			if ((error =3D saerror(done_ccb, 0, 0)) =3D=3D ERESTART) {=0A=
				/*=0A=
				 * A retry was scheduled, so just return.=0A=
				 */=0A=
				return;=0A=
			}=0A=
		}=0A=
=0A=
		if (error =3D=3D EIO) {=0A=
			int s;			=0A=
=0A=
			/*=0A=
			 * Catastrophic error. Mark the tape as frozen=0A=
			 * (we no longer know tape position).=0A=
			 *=0A=
			 * Return all queued I/O with EIO, and unfreeze=0A=
			 * our queue so that future transactions that=0A=
			 * attempt to fix this problem can get to the=0A=
			 * device.=0A=
			 *=0A=
			 */=0A=
=0A=
			s =3D splbio();=0A=
			softc->flags |=3D SA_FLAG_TAPE_FROZEN;=0A=
			bioq_flush(&softc->bio_queue, NULL, EIO);=0A=
			splx(s);=0A=
		}=0A=
		if (error !=3D 0) {=0A=
			bp->bio_resid =3D bp->bio_bcount;=0A=
			bp->bio_error =3D error;=0A=
			bp->bio_flags |=3D BIO_ERROR;=0A=
			/*=0A=
			 * In the error case, position is updated in saerror.=0A=
			 */=0A=
		} else {=0A=
			bp->bio_resid =3D csio->resid;=0A=
			bp->bio_error =3D 0;=0A=
			if (csio->resid !=3D 0) {=0A=
				bp->bio_flags |=3D BIO_ERROR;=0A=
			}=0A=
			if (bp->bio_cmd =3D=3D BIO_WRITE) {=0A=
				softc->flags |=3D SA_FLAG_TAPE_WRITTEN;=0A=
				softc->filemarks =3D 0;=0A=
			}=0A=
			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&=0A=
			    (softc->blkno !=3D (daddr_t) -1)) {=0A=
				if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
					u_int32_t l;=0A=
					if (softc->blk_shift !=3D 0) {=0A=
						l =3D bp->bio_bcount >>=0A=
							softc->blk_shift;=0A=
					} else {=0A=
						l =3D bp->bio_bcount /=0A=
							softc->media_blksize;=0A=
					}=0A=
					softc->blkno +=3D (daddr_t) l;=0A=
				} else {=0A=
					softc->blkno++;=0A=
				}=0A=
			}=0A=
		}=0A=
		/*=0A=
		 * If we had an error (immediate or pending),=0A=
		 * release the device queue now.=0A=
		 */=0A=
		if (error || (softc->flags & SA_FLAG_ERR_PENDING))=0A=
			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);=0A=
#ifdef	CAMDEBUG=0A=
		if (error || bp->bio_resid) {=0A=
			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
			    	  ("error %d resid %ld count %ld\n", error,=0A=
				  bp->bio_resid, bp->bio_bcount));=0A=
		}=0A=
#endif=0A=
		biofinish(bp, softc->device_stats, 0);=0A=
		break;=0A=
	}=0A=
	case SA_CCB_WAITING:=0A=
	{=0A=
		/* Caller will release the CCB */=0A=
		wakeup(&done_ccb->ccb_h.cbfcnp);=0A=
		return;=0A=
	}=0A=
	}=0A=
	xpt_release_ccb(done_ccb);=0A=
}=0A=
=0A=
/*=0A=
 * Mount the tape (make sure it's ready for I/O).=0A=
 */=0A=
static int=0A=
samount(struct cam_periph *periph, int oflags, struct cdev *dev)=0A=
{=0A=
	struct	sa_softc *softc;=0A=
	union	ccb *ccb;=0A=
	int	error;=0A=
=0A=
	/*=0A=
	 * oflags can be checked for 'kind' of open (read-only check) - later=0A=
	 * dev can be checked for a control-mode or compression open - later=0A=
	 */=0A=
	UNUSED_PARAMETER(oflags);=0A=
	UNUSED_PARAMETER(dev);=0A=
=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	/*=0A=
	 * This should determine if something has happend since the last=0A=
	 * open/mount that would invalidate the mount. We do *not* want=0A=
	 * to retry this command- we just want the status. But we only=0A=
	 * do this if we're mounted already- if we're not mounted,=0A=
	 * we don't care about the unit read state and can instead use=0A=
	 * this opportunity to attempt to reserve the tape unit.=0A=
	 */=0A=
	=0A=
	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {=0A=
		ccb =3D cam_periph_getccb(periph, 1);=0A=
		scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
		if (error =3D=3D ENXIO) {=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
			scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
		} else if (error) {=0A=
			/*=0A=
			 * We don't need to freeze the tape because we=0A=
			 * will now attempt to rewind/load it.=0A=
			 */=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_MOUNTED;=0A=
			if (CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("error %d on TUR in samount\n", error);=0A=
			}=0A=
		}=0A=
	} else {=0A=
		error =3D sareservereleaseunit(periph, TRUE);=0A=
		if (error) {=0A=
			return (error);=0A=
		}=0A=
		ccb =3D cam_periph_getccb(periph, 1);=0A=
		scsi_test_unit_ready(&ccb->csio, 0, sadone,=0A=
		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
	}=0A=
=0A=
	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) =3D=3D 0) {=0A=
		struct scsi_read_block_limits_data *rblim =3D NULL;=0A=
		int comp_enabled, comp_supported;=0A=
		u_int8_t write_protect, guessing =3D 0;=0A=
=0A=
		/*=0A=
		 * Clear out old state.=0A=
		 */=0A=
		softc->flags &=3D ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|=0A=
				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|=0A=
				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);=0A=
		softc->filemarks =3D 0;=0A=
=0A=
		/*=0A=
		 * *Very* first off, make sure we're loaded to BOT.=0A=
		 */=0A=
		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
=0A=
		/*=0A=
		 * In case this doesn't work, do a REWIND instead=0A=
		 */=0A=
		if (error) {=0A=
			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,=0A=
			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
				softc->device_stats);=0A=
			QFRLS(ccb);=0A=
		}=0A=
		if (error) {=0A=
			xpt_release_ccb(ccb);=0A=
			goto exit;=0A=
		}=0A=
=0A=
		/*=0A=
		 * Do a dummy test read to force access to the=0A=
		 * media so that the drive will really know what's=0A=
		 * there. We actually don't really care what the=0A=
		 * blocksize on tape is and don't expect to really=0A=
		 * read a full record.=0A=
		 */=0A=
		rblim =3D (struct  scsi_read_block_limits_data *)=0A=
		    malloc(8192, M_TEMP, M_WAITOK);=0A=
		if (rblim =3D=3D NULL) {=0A=
			xpt_print_path(ccb->ccb_h.path);=0A=
			printf("no memory for test read\n");=0A=
			xpt_release_ccb(ccb);=0A=
			error =3D ENOMEM;=0A=
			goto exit;=0A=
		}=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_NODREAD) =3D=3D 0) {=0A=
			scsi_sa_read_write(&ccb->csio, 0, sadone,=0A=
			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,=0A=
			    (void *) rblim, 8192, SSD_FULL_SIZE,=0A=
			    IO_TIMEOUT);=0A=
			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
			error =3D cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,=0A=
			    SF_NO_PRINT | SF_RETRY_UA,=0A=
			    softc->device_stats);=0A=
			QFRLS(ccb);=0A=
			if (error) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to rewind after test read\n");=0A=
				xpt_release_ccb(ccb);=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Next off, determine block limits.=0A=
		 */=0A=
		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,=0A=
		    rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
		error =3D cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,=0A=
		    SF_NO_PRINT | SF_RETRY_UA, softc->device_stats);=0A=
=0A=
		QFRLS(ccb);=0A=
		xpt_release_ccb(ccb);=0A=
=0A=
		if (error !=3D 0) {=0A=
			/*=0A=
			 * If it's less than SCSI-2, READ BLOCK LIMITS is not=0A=
			 * a MANDATORY command. Anyway- it doesn't matter-=0A=
			 * we can proceed anyway.=0A=
			 */=0A=
			softc->blk_gran =3D 0;=0A=
			softc->max_blk =3D ~0;=0A=
			softc->min_blk =3D 0;=0A=
		} else {=0A=
			if (softc->scsi_rev >=3D SCSI_REV_SPC) {=0A=
				softc->blk_gran =3D RBL_GRAN(rblim);=0A=
			} else {=0A=
				softc->blk_gran =3D 0;=0A=
			}=0A=
			/*=0A=
			 * We take max_blk =3D=3D min_blk to mean a default to=0A=
			 * fixed mode- but note that whatever we get out of=0A=
			 * sagetparams below will actually determine whether=0A=
			 * we are actually *in* fixed mode.=0A=
			 */=0A=
			softc->max_blk =3D scsi_3btoul(rblim->maximum);=0A=
			softc->min_blk =3D scsi_2btoul(rblim->minimum);=0A=
=0A=
=0A=
		}=0A=
		/*=0A=
		 * Next, perform a mode sense to determine=0A=
		 * current density, blocksize, compression etc.=0A=
		 */=0A=
		error =3D sagetparams(periph, SA_PARAM_ALL,=0A=
				    &softc->media_blksize,=0A=
				    &softc->media_density,=0A=
				    &softc->media_numblks,=0A=
				    &softc->buffer_mode, &write_protect,=0A=
				    &softc->speed, &comp_supported,=0A=
				    &comp_enabled, &softc->comp_algorithm,=0A=
				    NULL);=0A=
=0A=
		if (error !=3D 0) {=0A=
			/*=0A=
			 * We could work a little harder here. We could=0A=
			 * adjust our attempts to get information. It=0A=
			 * might be an ancient tape drive. If someone=0A=
			 * nudges us, we'll do that.=0A=
			 */=0A=
			goto exit;=0A=
		}=0A=
=0A=
		/*=0A=
		 * If no quirk has determined that this is a device that is=0A=
		 * preferred to be in fixed or variable mode, now is the time=0A=
		 * to find out.=0A=
	 	 */=0A=
		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) =3D=3D 0) {=0A=
			guessing =3D 1;=0A=
			/*=0A=
			 * This could be expensive to find out. Luckily we=0A=
			 * only need to do this once. If we start out in=0A=
			 * 'default' mode, try and set ourselves to one=0A=
			 * of the densities that would determine a wad=0A=
			 * of other stuff. Go from highest to lowest.=0A=
			 */=0A=
			if (softc->media_density =3D=3D SCSI_DEFAULT_DENSITY) {=0A=
				int i;=0A=
				static u_int8_t ctry[] =3D {=0A=
					SCSI_DENSITY_HALFINCH_PE,=0A=
					SCSI_DENSITY_HALFINCH_6250C,=0A=
					SCSI_DENSITY_HALFINCH_6250,=0A=
					SCSI_DENSITY_HALFINCH_1600,=0A=
					SCSI_DENSITY_HALFINCH_800,=0A=
					SCSI_DENSITY_QIC_4GB,=0A=
					SCSI_DENSITY_QIC_2GB,=0A=
					SCSI_DENSITY_QIC_525_320,=0A=
					SCSI_DENSITY_QIC_150,=0A=
					SCSI_DENSITY_QIC_120,=0A=
					SCSI_DENSITY_QIC_24,=0A=
					SCSI_DENSITY_QIC_11_9TRK,=0A=
					SCSI_DENSITY_QIC_11_4TRK,=0A=
					SCSI_DENSITY_QIC_1320,=0A=
					SCSI_DENSITY_QIC_3080,=0A=
					0=0A=
				};=0A=
				for (i =3D 0; ctry[i]; i++) {=0A=
					error =3D sasetparams(periph,=0A=
					    SA_PARAM_DENSITY, 0, ctry[i],=0A=
					    0, SF_NO_PRINT);=0A=
					if (error =3D=3D 0) {=0A=
						softc->media_density =3D ctry[i];=0A=
						break;=0A=
					}=0A=
				}=0A=
			}=0A=
			switch (softc->media_density) {=0A=
			case SCSI_DENSITY_QIC_11_4TRK:=0A=
			case SCSI_DENSITY_QIC_11_9TRK:=0A=
			case SCSI_DENSITY_QIC_24:=0A=
			case SCSI_DENSITY_QIC_120:=0A=
			case SCSI_DENSITY_QIC_150:=0A=
			case SCSI_DENSITY_QIC_525_320:=0A=
			case SCSI_DENSITY_QIC_1320:=0A=
			case SCSI_DENSITY_QIC_3080:=0A=
				softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
				softc->quirks |=3D SA_QUIRK_FIXED|SA_QUIRK_1FM;=0A=
				softc->last_media_blksize =3D 512;=0A=
				break;=0A=
			case SCSI_DENSITY_QIC_4GB:=0A=
			case SCSI_DENSITY_QIC_2GB:=0A=
				softc->quirks &=3D ~SA_QUIRK_2FM;=0A=
				softc->quirks |=3D SA_QUIRK_FIXED|SA_QUIRK_1FM;=0A=
				softc->last_media_blksize =3D 1024;=0A=
				break;=0A=
			default:=0A=
				softc->last_media_blksize =3D=0A=
				    softc->media_blksize;=0A=
				softc->quirks |=3D SA_QUIRK_VARIABLE;=0A=
				break;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * If no quirk has determined that this is a device that needs=0A=
		 * to have 2 Filemarks at EOD, now is the time to find out.=0A=
		 */=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_2FM) =3D=3D 0) {=0A=
			switch (softc->media_density) {=0A=
			case SCSI_DENSITY_HALFINCH_800:=0A=
			case SCSI_DENSITY_HALFINCH_1600:=0A=
			case SCSI_DENSITY_HALFINCH_6250:=0A=
			case SCSI_DENSITY_HALFINCH_6250C:=0A=
			case SCSI_DENSITY_HALFINCH_PE:=0A=
				softc->quirks &=3D ~SA_QUIRK_1FM;=0A=
				softc->quirks |=3D SA_QUIRK_2FM;=0A=
				break;=0A=
			default:=0A=
				break;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now validate that some info we got makes sense.=0A=
		 */=0A=
		if ((softc->max_blk < softc->media_blksize) ||=0A=
		    (softc->min_blk > softc->media_blksize &&=0A=
		    softc->media_blksize)) {=0A=
			xpt_print_path(ccb->ccb_h.path);=0A=
			printf("BLOCK LIMITS (%d..%d) could not match current "=0A=
			    "block settings (%d)- adjusting\n", softc->min_blk,=0A=
			    softc->max_blk, softc->media_blksize);=0A=
			softc->max_blk =3D softc->min_blk =3D=0A=
			    softc->media_blksize;=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now put ourselves into the right frame of mind based=0A=
		 * upon quirks...=0A=
		 */=0A=
tryagain:=0A=
		/*=0A=
		 * If we want to be in FIXED mode and our current blocksize=0A=
		 * is not equal to our last blocksize (if nonzero), try and=0A=
		 * set ourselves to this last blocksize (as the 'preferred'=0A=
		 * block size).  The initial quirkmatch at registry sets the=0A=
		 * initial 'last' blocksize. If, for whatever reason, this=0A=
		 * 'last' blocksize is zero, set the blocksize to 512,=0A=
		 * or min_blk if that's larger.=0A=
		 */=0A=
		if ((softc->quirks & SA_QUIRK_FIXED) &&=0A=
		    (softc->quirks & SA_QUIRK_NO_MODESEL) =3D=3D 0 &&=0A=
		    (softc->media_blksize !=3D softc->last_media_blksize)) {=0A=
			softc->media_blksize =3D softc->last_media_blksize;=0A=
			if (softc->media_blksize =3D=3D 0) {=0A=
				softc->media_blksize =3D 512;=0A=
				if (softc->media_blksize < softc->min_blk) {=0A=
					softc->media_blksize =3D softc->min_blk;=0A=
				}=0A=
			}=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE,=0A=
			    softc->media_blksize, 0, 0, SF_NO_PRINT);=0A=
			if (error) {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set fixed blocksize to %d\n",=0A=
				     softc->media_blksize);=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		if ((softc->quirks & SA_QUIRK_VARIABLE) && =0A=
		    (softc->media_blksize !=3D 0)) {=0A=
			softc->last_media_blksize =3D softc->media_blksize;=0A=
			softc->media_blksize =3D 0;=0A=
			error =3D sasetparams(periph, SA_PARAM_BLOCKSIZE,=0A=
			    0, 0, 0, SF_NO_PRINT);=0A=
			if (error) {=0A=
				/*=0A=
				 * If this fails and we were guessing, just=0A=
				 * assume that we got it wrong and go try=0A=
				 * fixed block mode. Don't even check against=0A=
				 * density code at this point.=0A=
				 */=0A=
				if (guessing) {=0A=
					softc->quirks &=3D ~SA_QUIRK_VARIABLE;=0A=
					softc->quirks |=3D SA_QUIRK_FIXED;=0A=
					if (softc->last_media_blksize =3D=3D 0)=0A=
						softc->last_media_blksize =3D 512;=0A=
					goto tryagain;=0A=
				}=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set variable blocksize\n");=0A=
				goto exit;=0A=
			}=0A=
		}=0A=
=0A=
		/*=0A=
		 * Now that we have the current block size,=0A=
		 * set up some parameters for sastart's usage.=0A=
		 */=0A=
		if (softc->media_blksize) {=0A=
			softc->flags |=3D SA_FLAG_FIXED;=0A=
			if (powerof2(softc->media_blksize)) {=0A=
				softc->blk_shift =3D=0A=
				    ffs(softc->media_blksize) - 1;=0A=
				softc->blk_mask =3D softc->media_blksize - 1;=0A=
			} else {=0A=
				softc->blk_mask =3D ~0;=0A=
				softc->blk_shift =3D 0;=0A=
			}=0A=
		} else {=0A=
			/*=0A=
			 * The SCSI-3 spec allows 0 to mean "unspecified".=0A=
			 * The SCSI-1 spec allows 0 to mean 'infinite'.=0A=
			 *=0A=
			 * Either works here.=0A=
			 */=0A=
			if (softc->max_blk =3D=3D 0) {=0A=
				softc->max_blk =3D ~0;=0A=
			}=0A=
			softc->blk_shift =3D 0;=0A=
			if (softc->blk_gran !=3D 0) {=0A=
				softc->blk_mask =3D softc->blk_gran - 1;=0A=
			} else {=0A=
				softc->blk_mask =3D 0;=0A=
			}=0A=
		}=0A=
=0A=
		if (write_protect) =0A=
			softc->flags |=3D SA_FLAG_TAPE_WP;=0A=
=0A=
		if (comp_supported) {=0A=
			if (softc->saved_comp_algorithm =3D=3D 0)=0A=
				softc->saved_comp_algorithm =3D=0A=
				    softc->comp_algorithm;=0A=
			softc->flags |=3D SA_FLAG_COMP_SUPP;=0A=
			if (comp_enabled)=0A=
				softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
		} else=0A=
			softc->flags |=3D SA_FLAG_COMP_UNSUPP;=0A=
=0A=
		if ((softc->buffer_mode =3D=3D SMH_SA_BUF_MODE_NOBUF) &&=0A=
		    (softc->quirks & SA_QUIRK_NO_MODESEL) =3D=3D 0) {=0A=
			error =3D sasetparams(periph, SA_PARAM_BUFF_MODE, 0,=0A=
			    0, 0, SF_NO_PRINT);=0A=
			if (error =3D=3D 0) {=0A=
				softc->buffer_mode =3D SMH_SA_BUF_MODE_SIBUF;=0A=
			} else {=0A=
				xpt_print_path(ccb->ccb_h.path);=0A=
				printf("unable to set buffered mode\n");=0A=
			}=0A=
			error =3D 0;	/* not an error */=0A=
		}=0A=
=0A=
=0A=
		if (error =3D=3D 0) {=0A=
			softc->flags |=3D SA_FLAG_TAPE_MOUNTED;=0A=
		}=0A=
exit:=0A=
		if (rblim !=3D NULL)=0A=
			free(rblim, M_TEMP);=0A=
=0A=
		if (error !=3D 0) {=0A=
			softc->dsreg =3D MTIO_DSREG_NIL;=0A=
		} else {=0A=
			softc->fileno =3D softc->blkno =3D 0;=0A=
			softc->dsreg =3D MTIO_DSREG_REST;=0A=
		}=0A=
#ifdef	SA_1FM_AT_EOD=0A=
		if ((softc->quirks & SA_QUIRK_2FM) =3D=3D 0)=0A=
			softc->quirks |=3D SA_QUIRK_1FM;=0A=
#else=0A=
		if ((softc->quirks & SA_QUIRK_1FM) =3D=3D 0)=0A=
			softc->quirks |=3D SA_QUIRK_2FM;=0A=
#endif=0A=
	} else=0A=
		xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If we return an error, we're not mounted any more,=0A=
	 * so release any device reservation.=0A=
	 */=0A=
	if (error !=3D 0) {=0A=
		(void) sareservereleaseunit(periph, FALSE);=0A=
	} else {=0A=
		/*=0A=
		 * Clear I/O residual.=0A=
		 */=0A=
		softc->last_io_resid =3D 0;=0A=
		softc->last_ctl_resid =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
/*=0A=
 * How many filemarks do we need to write if we were to terminate the=0A=
 * tape session right now? Note that this can be a negative number=0A=
 */=0A=
=0A=
static int=0A=
samarkswanted(struct cam_periph *periph)=0A=
{=0A=
	int	markswanted;=0A=
	struct	sa_softc *softc;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	markswanted =3D 0;=0A=
	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) !=3D 0) {=0A=
		markswanted++;=0A=
		if (softc->quirks & SA_QUIRK_2FM)=0A=
			markswanted++;=0A=
	}=0A=
	markswanted -=3D softc->filemarks;=0A=
	return (markswanted);=0A=
}=0A=
=0A=
static int=0A=
sacheckeod(struct cam_periph *periph)=0A=
{=0A=
	int	error;=0A=
	int	markswanted;=0A=
=0A=
	markswanted =3D samarkswanted(periph);=0A=
=0A=
	if (markswanted > 0) {=0A=
		error =3D sawritefilemarks(periph, markswanted, FALSE);=0A=
	} else {=0A=
		error =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)=0A=
{=0A=
	static const char *toobig =3D=0A=
	    "%d-byte tape record bigger than supplied buffer\n";=0A=
	struct	cam_periph *periph;=0A=
	struct	sa_softc *softc;=0A=
	struct	ccb_scsiio *csio;=0A=
	struct	scsi_sense_data *sense;=0A=
	u_int32_t resid =3D 0;=0A=
	int32_t	info =3D 0;=0A=
	cam_status status;=0A=
	int error_code, sense_key, asc, ascq, error, aqvalid;=0A=
=0A=
	periph =3D xpt_path_periph(ccb->ccb_h.path);=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	csio =3D &ccb->csio;=0A=
	sense =3D &csio->sense_data;=0A=
	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);=0A=
	aqvalid =3D sense->extra_len >=3D 6;=0A=
	error =3D 0;=0A=
=0A=
	status =3D csio->ccb_h.status & CAM_STATUS_MASK;=0A=
=0A=
	/*=0A=
	 * Calculate/latch up, any residuals... We do this in a funny 2-step=0A=
	 * so we can print stuff here if we have CAM_DEBUG enabled for this=0A=
	 * unit.=0A=
	 */=0A=
	if (status =3D=3D CAM_SCSI_STATUS_ERROR) {=0A=
		if ((sense->error_code & SSD_ERRCODE_VALID) !=3D 0) {=0A=
			info =3D (int32_t) scsi_4btoul(sense->info);=0A=
			resid =3D info;=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0)=0A=
				resid *=3D softc->media_blksize;=0A=
		} else {=0A=
			resid =3D csio->dxfer_len;=0A=
			info =3D resid;=0A=
			if ((softc->flags & SA_FLAG_FIXED) !=3D 0) {=0A=
				if (softc->media_blksize)=0A=
					info /=3D softc->media_blksize;=0A=
			}=0A=
		}=0A=
		if (CCB_Type(csio) =3D=3D SA_CCB_BUFFER_IO) {=0A=
			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,=0A=
			    sizeof (struct scsi_sense_data));=0A=
			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,=0A=
			    (int) csio->cdb_len);=0A=
			softc->last_io_resid =3D resid;=0A=
			softc->last_resid_was_io =3D 1;=0A=
		} else {=0A=
			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,=0A=
			    sizeof (struct scsi_sense_data));=0A=
			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,=0A=
			    (int) csio->cdb_len);=0A=
			softc->last_ctl_resid =3D resid;=0A=
			softc->last_resid_was_io =3D 0;=0A=
		}=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=3D0x%x Key 0x%x "=0A=
		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d "=0A=
		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,=0A=
		    sense_key, asc, ascq, status,=0A=
		    sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len));=0A=
	} else {=0A=
		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,=0A=
		    ("Cam Status 0x%x\n", status));=0A=
	}=0A=
=0A=
	switch (status) {=0A=
	case CAM_REQ_CMP:=0A=
		return (0);=0A=
	case CAM_SCSI_STATUS_ERROR:=0A=
		/*=0A=
		 * If a read/write command, we handle it here.=0A=
		 */=0A=
		if (CCB_Type(csio) !=3D SA_CCB_WAITING) {=0A=
			break;=0A=
		}=0A=
		/*=0A=
		 * If this was just EOM/EOP, Filemark, Setmark or ILI detected=0A=
		 * on a non read/write command, we assume it's not an error=0A=
		 * and propagate the residule and return.=0A=
		 */=0A=
		if ((aqvalid && asc =3D=3D 0 && ascq > 0 && ascq <=3D 5) ||=0A=
		    (aqvalid =3D=3D 0 && sense_key =3D=3D SSD_KEY_NO_SENSE)) {=0A=
			csio->resid =3D resid;=0A=
			QFRLS(ccb);=0A=
			return (0);=0A=
		}=0A=
		/*=0A=
		 * Otherwise, we let the common code handle this.=0A=
		 */=0A=
		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));=0A=
=0A=
	/*=0A=
	 * XXX: To Be Fixed=0A=
	 * We cannot depend upon CAM honoring retry counts for these.=0A=
	 */=0A=
	case CAM_SCSI_BUS_RESET:=0A=
	case CAM_BDR_SENT:=0A=
		if (ccb->ccb_h.retry_count <=3D 0) {=0A=
			return (EIO);=0A=
		}=0A=
		/* FALLTHROUGH */=0A=
	default:=0A=
		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));=0A=
	}=0A=
=0A=
	/*=0A=
	 * Handle filemark, end of tape, mismatched record sizes....=0A=
	 * From this point out, we're only handling read/write cases.=0A=
	 * Handle writes && reads differently.=0A=
	 */=0A=
=0A=
	if (csio->cdb_io.cdb_bytes[0] =3D=3D SA_WRITE) {=0A=
		if (sense_key =3D=3D SSD_KEY_VOLUME_OVERFLOW) {=0A=
			csio->resid =3D resid;=0A=
			error =3D ENOSPC;=0A=
		} else if (sense->flags & SSD_EOM) {=0A=
			softc->flags |=3D SA_FLAG_EOM_PENDING;=0A=
			/*=0A=
			 * Grotesque as it seems, the few times=0A=
			 * I've actually seen a non-zero resid,=0A=
			 * the tape drive actually lied and had=0A=
			 * written all the data!.=0A=
			 */=0A=
			csio->resid =3D 0;=0A=
		}=0A=
	} else {=0A=
		csio->resid =3D resid;=0A=
		if (sense_key =3D=3D SSD_KEY_BLANK_CHECK) {=0A=
			if (softc->quirks & SA_QUIRK_1FM) {=0A=
				error =3D 0;=0A=
				softc->flags |=3D SA_FLAG_EOM_PENDING;=0A=
			} else {=0A=
				error =3D EIO;=0A=
			}=0A=
		} else if (sense->flags & SSD_FILEMARK) {=0A=
			if (softc->flags & SA_FLAG_FIXED) {=0A=
				error =3D -1;=0A=
				softc->flags |=3D SA_FLAG_EOF_PENDING;=0A=
			}=0A=
			/*=0A=
			 * Unconditionally, if we detected a filemark on a read,=0A=
			 * mark that we've run moved a file ahead.=0A=
			 */=0A=
			if (softc->fileno !=3D (daddr_t) -1) {=0A=
				softc->fileno++;=0A=
				softc->blkno =3D 0;=0A=
				csio->ccb_h.ccb_pflags |=3D SA_POSITION_UPDATED;=0A=
			}=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * Incorrect Length usually applies to read, but can apply to writes.=0A=
	 */=0A=
	if (error =3D=3D 0 && (sense->flags & SSD_ILI)) {=0A=
		if (info < 0) {=0A=
			xpt_print_path(csio->ccb_h.path);=0A=
			printf(toobig, csio->dxfer_len - info);=0A=
			csio->resid =3D csio->dxfer_len;=0A=
			error =3D EIO;=0A=
		} else {=0A=
			csio->resid =3D resid;=0A=
			if (softc->flags & SA_FLAG_FIXED) {=0A=
				softc->flags |=3D SA_FLAG_EIO_PENDING;=0A=
			}=0A=
			/*=0A=
			 * Bump the block number if we hadn't seen a filemark.=0A=
			 * Do this independent of errors (we've moved anyway).=0A=
			 */=0A=
			if ((sense->flags & SSD_FILEMARK) =3D=3D 0) {=0A=
				if (softc->blkno !=3D (daddr_t) -1) {=0A=
					softc->blkno++;=0A=
					csio->ccb_h.ccb_pflags |=3D=0A=
					   SA_POSITION_UPDATED;=0A=
				}=0A=
			}=0A=
		}=0A=
	}=0A=
=0A=
	if (error <=3D 0) {=0A=
		/*=0A=
		 * Unfreeze the queue if frozen as we're not returning anything=0A=
		 * to our waiters that would indicate an I/O error has occurred=0A=
		 * (yet).=0A=
		 */=0A=
		QFRLS(ccb);=0A=
		error =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sagetparams(struct cam_periph *periph, sa_params params_to_get,=0A=
	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,=0A=
	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,=0A=
	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,=0A=
	    sa_comp_t *tcs)=0A=
{=0A=
	union ccb *ccb;=0A=
	void *mode_buffer;=0A=
	struct scsi_mode_header_6 *mode_hdr;=0A=
	struct scsi_mode_blk_desc *mode_blk;=0A=
	int mode_buffer_len;=0A=
	struct sa_softc *softc;=0A=
	u_int8_t cpage;=0A=
	int error;=0A=
	cam_status status;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	if (softc->quirks & SA_QUIRK_NO_CPAGE)=0A=
		cpage =3D SA_DEVICE_CONFIGURATION_PAGE;=0A=
	else=0A=
		cpage =3D SA_DATA_COMPRESSION_PAGE;=0A=
=0A=
retry:=0A=
	mode_buffer_len =3D sizeof(*mode_hdr) + sizeof(*mode_blk);=0A=
=0A=
	if (params_to_get & SA_PARAM_COMPRESSION) {=0A=
		if (softc->quirks & SA_QUIRK_NOCOMP) {=0A=
			*comp_supported =3D FALSE;=0A=
			params_to_get &=3D ~SA_PARAM_COMPRESSION;=0A=
		} else=0A=
			mode_buffer_len +=3D sizeof (sa_comp_t);=0A=
	}=0A=
=0A=
	mode_buffer =3D malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);=0A=
	mode_hdr =3D (struct scsi_mode_header_6 *)mode_buffer;=0A=
	mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
=0A=
	/* it is safe to retry this */=0A=
	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?=0A=
	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,=0A=
	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
	    softc->device_stats);=0A=
	QFRLS(ccb);=0A=
=0A=
	status =3D ccb->ccb_h.status & CAM_STATUS_MASK;=0A=
=0A=
	if (error =3D=3D EINVAL && (params_to_get & SA_PARAM_COMPRESSION) !=3D =
0) {=0A=
		/*=0A=
		 * Hmm. Let's see if we can try another page...=0A=
		 * If we've already done that, give up on compression=0A=
		 * for this device and remember this for the future=0A=
		 * and attempt the request without asking for compression=0A=
		 * info.=0A=
		 */=0A=
		if (cpage =3D=3D SA_DATA_COMPRESSION_PAGE) {=0A=
			cpage =3D SA_DEVICE_CONFIGURATION_PAGE;=0A=
			goto retry;=0A=
		}=0A=
		softc->quirks |=3D SA_QUIRK_NOCOMP;=0A=
		free(mode_buffer, M_TEMP);=0A=
		goto retry;=0A=
	} else if (status =3D=3D CAM_SCSI_STATUS_ERROR) {=0A=
		/* Tell the user about the fatal error. */=0A=
		scsi_sense_print(&ccb->csio);=0A=
		goto sagetparamsexit;=0A=
	}=0A=
=0A=
	/*=0A=
	 * If the user only wants the compression information, and=0A=
	 * the device doesn't send back the block descriptor, it's=0A=
	 * no big deal.  If the user wants more than just=0A=
	 * compression, though, and the device doesn't pass back the=0A=
	 * block descriptor, we need to send another mode sense to=0A=
	 * get the block descriptor.=0A=
	 */=0A=
	if ((mode_hdr->blk_desc_len =3D=3D 0) &&=0A=
	    (params_to_get & SA_PARAM_COMPRESSION) &&=0A=
	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {=0A=
=0A=
		/*=0A=
		 * Decrease the mode buffer length by the size of=0A=
		 * the compression page, to make sure the data=0A=
		 * there doesn't get overwritten.=0A=
		 */=0A=
		mode_buffer_len -=3D sizeof (sa_comp_t);=0A=
=0A=
		/*=0A=
		 * Now move the compression page that we presumably=0A=
		 * got back down the memory chunk a little bit so=0A=
		 * it doesn't get spammed.=0A=
		 */=0A=
		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));=0A=
		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));=0A=
=0A=
		/*=0A=
		 * Now, we issue another mode sense and just ask=0A=
		 * for the block descriptor, etc.=0A=
		 */=0A=
=0A=
		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,=0A=
		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE,=0A=
		    SCSIOP_TIMEOUT);=0A=
=0A=
		error =3D cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
=0A=
		if (error !=3D 0)=0A=
			goto sagetparamsexit;=0A=
	}=0A=
=0A=
	if (params_to_get & SA_PARAM_BLOCKSIZE)=0A=
		*blocksize =3D scsi_3btoul(mode_blk->blklen);=0A=
=0A=
	if (params_to_get & SA_PARAM_NUMBLOCKS)=0A=
		*numblocks =3D scsi_3btoul(mode_blk->nblocks);=0A=
=0A=
	if (params_to_get & SA_PARAM_BUFF_MODE)=0A=
		*buff_mode =3D mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;=0A=
=0A=
	if (params_to_get & SA_PARAM_DENSITY)=0A=
		*density =3D mode_blk->density;=0A=
=0A=
	if (params_to_get & SA_PARAM_WP)=0A=
		*write_protect =3D (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;=0A=
=0A=
	if (params_to_get & SA_PARAM_SPEED)=0A=
		*speed =3D mode_hdr->dev_spec & SMH_SA_SPEED_MASK;=0A=
=0A=
	if (params_to_get & SA_PARAM_COMPRESSION) {=0A=
		sa_comp_t *ntcs =3D (sa_comp_t *) &mode_blk[1];=0A=
		if (cpage =3D=3D SA_DATA_COMPRESSION_PAGE) {=0A=
			struct scsi_data_compression_page *cp =3D &ntcs->dcomp;=0A=
			*comp_supported =3D=0A=
			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;=0A=
			*comp_enabled =3D=0A=
			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;=0A=
			*comp_algorithm =3D scsi_4btoul(cp->comp_algorithm);=0A=
		} else {=0A=
			struct scsi_dev_conf_page *cp =3D &ntcs->dconf;=0A=
			/*=0A=
			 * We don't really know whether this device supports=0A=
			 * Data Compression if the the algorithm field is=0A=
			 * zero. Just say we do.=0A=
			 */=0A=
			*comp_supported =3D TRUE;=0A=
			*comp_enabled =3D=0A=
			    (cp->sel_comp_alg !=3D SA_COMP_NONE)? TRUE : FALSE;=0A=
			*comp_algorithm =3D cp->sel_comp_alg;=0A=
		}=0A=
		if (tcs !=3D NULL)=0A=
			bcopy(ntcs, tcs, sizeof (sa_comp_t));=0A=
	}=0A=
=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		int idx;=0A=
		char *xyz =3D mode_buffer;=0A=
		xpt_print_path(periph->path);=0A=
		printf("Mode Sense Data=3D");=0A=
		for (idx =3D 0; idx < mode_buffer_len; idx++)=0A=
			printf(" 0x%02x", xyz[idx] & 0xff);=0A=
		printf("\n");=0A=
	}=0A=
=0A=
sagetparamsexit:=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	free(mode_buffer, M_TEMP);=0A=
	return (error);=0A=
}=0A=
=0A=
/*=0A=
 * The purpose of this function is to set one of four different =
parameters=0A=
 * for a tape drive:=0A=
 *	- blocksize=0A=
 *	- density=0A=
 *	- compression / compression algorithm=0A=
 *	- buffering mode=0A=
 *=0A=
 * The assumption is that this will be called from saioctl(), and =
therefore=0A=
 * from a process context.  Thus the waiting malloc calls below.  If that=0A=
 * assumption ever changes, the malloc calls should be changed to be=0A=
 * NOWAIT mallocs.=0A=
 *=0A=
 * Any or all of the four parameters may be set when this function is=0A=
 * called.  It should handle setting more than one parameter at once.=0A=
 */=0A=
static int=0A=
sasetparams(struct cam_periph *periph, sa_params params_to_set,=0A=
	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,=0A=
	    u_int32_t sense_flags)=0A=
{=0A=
	struct sa_softc *softc;=0A=
	u_int32_t current_blocksize;=0A=
	u_int32_t current_calg;=0A=
	u_int8_t current_density;=0A=
	u_int8_t current_speed;=0A=
	int comp_enabled, comp_supported;=0A=
	void *mode_buffer;=0A=
	int mode_buffer_len;=0A=
	struct scsi_mode_header_6 *mode_hdr;=0A=
	struct scsi_mode_blk_desc *mode_blk;=0A=
	sa_comp_t *ccomp, *cpage;=0A=
	int buff_mode;=0A=
	union ccb *ccb =3D NULL;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccomp =3D malloc(sizeof (sa_comp_t), M_TEMP, M_WAITOK);=0A=
=0A=
	/*=0A=
	 * Since it doesn't make sense to set the number of blocks, or=0A=
	 * write protection, we won't try to get the current value.  We=0A=
	 * always want to get the blocksize, so we can set it back to the=0A=
	 * proper value.=0A=
	 */=0A=
	error =3D sagetparams(periph,=0A=
	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,=0A=
	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,=0A=
	    &current_speed, &comp_supported, &comp_enabled,=0A=
	    &current_calg, ccomp);=0A=
=0A=
	if (error !=3D 0) {=0A=
		free(ccomp, M_TEMP);=0A=
		return (error);=0A=
	}=0A=
=0A=
	mode_buffer_len =3D sizeof(*mode_hdr) + sizeof(*mode_blk);=0A=
	if (params_to_set & SA_PARAM_COMPRESSION)=0A=
		mode_buffer_len +=3D sizeof (sa_comp_t);=0A=
=0A=
	mode_buffer =3D malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);=0A=
=0A=
	mode_hdr =3D (struct scsi_mode_header_6 *)mode_buffer;=0A=
	mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
retry:=0A=
=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		if (mode_blk) {=0A=
			cpage =3D (sa_comp_t *)&mode_blk[1];=0A=
		} else {=0A=
			cpage =3D (sa_comp_t *)&mode_hdr[1];=0A=
		}=0A=
		bcopy(ccomp, cpage, sizeof (sa_comp_t));=0A=
		cpage->hdr.pagecode &=3D ~0x80;=0A=
	} else=0A=
		cpage =3D NULL;=0A=
=0A=
	/*=0A=
	 * If the caller wants us to set the blocksize, use the one they=0A=
	 * pass in.  Otherwise, use the blocksize we got back from the=0A=
	 * mode select above.=0A=
	 */=0A=
	if (mode_blk) {=0A=
		if (params_to_set & SA_PARAM_BLOCKSIZE)=0A=
			scsi_ulto3b(blocksize, mode_blk->blklen);=0A=
		else=0A=
			scsi_ulto3b(current_blocksize, mode_blk->blklen);=0A=
=0A=
		/*=0A=
		 * Set density if requested, else preserve old density.=0A=
		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better=0A=
		 * devices, else density we've latched up in our softc.=0A=
		 */=0A=
		if (params_to_set & SA_PARAM_DENSITY) {=0A=
			mode_blk->density =3D density;=0A=
		} else if (softc->scsi_rev > SCSI_REV_CCS) {=0A=
			mode_blk->density =3D SCSI_SAME_DENSITY;=0A=
		} else {=0A=
			mode_blk->density =3D softc->media_density;=0A=
		}=0A=
	}=0A=
=0A=
	/*=0A=
	 * For mode selects, these two fields must be zero.=0A=
	 */=0A=
	mode_hdr->data_length =3D 0;=0A=
	mode_hdr->medium_type =3D 0;=0A=
=0A=
	/* set the speed to the current value */=0A=
	mode_hdr->dev_spec =3D current_speed;=0A=
=0A=
	/* if set, set single-initiator buffering mode */=0A=
	if (softc->buffer_mode =3D=3D SMH_SA_BUF_MODE_SIBUF) {=0A=
		mode_hdr->dev_spec |=3D SMH_SA_BUF_MODE_SIBUF;=0A=
	}=0A=
=0A=
	if (mode_blk)=0A=
		mode_hdr->blk_desc_len =3D sizeof(struct scsi_mode_blk_desc);=0A=
	else=0A=
		mode_hdr->blk_desc_len =3D 0;=0A=
=0A=
	/*=0A=
	 * First, if the user wants us to set the compression algorithm or=0A=
	 * just turn compression on, check to make sure that this drive=0A=
	 * supports compression.=0A=
	 */=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		/*=0A=
		 * If the compression algorithm is 0, disable compression.=0A=
		 * If the compression algorithm is non-zero, enable=0A=
		 * compression and set the compression type to the=0A=
		 * specified compression algorithm, unless the algorithm is=0A=
		 * MT_COMP_ENABLE.  In that case, we look at the=0A=
		 * compression algorithm that is currently set and if it is=0A=
		 * non-zero, we leave it as-is.  If it is zero, and we have=0A=
		 * saved a compression algorithm from a time when=0A=
		 * compression was enabled before, set the compression to=0A=
		 * the saved value.=0A=
		 */=0A=
		switch (ccomp->hdr.pagecode & ~0x80) {=0A=
		case SA_DEVICE_CONFIGURATION_PAGE:=0A=
		{=0A=
			struct scsi_dev_conf_page *dcp =3D &cpage->dconf;=0A=
			if (calg =3D=3D 0) {=0A=
				dcp->sel_comp_alg =3D SA_COMP_NONE;=0A=
				break;=0A=
			}=0A=
			if (calg !=3D MT_COMP_ENABLE) {=0A=
				dcp->sel_comp_alg =3D calg;=0A=
			} else if (dcp->sel_comp_alg =3D=3D SA_COMP_NONE &&=0A=
			    softc->saved_comp_algorithm !=3D 0) {=0A=
				dcp->sel_comp_alg =3D softc->saved_comp_algorithm;=0A=
			}=0A=
			break;=0A=
		}=0A=
		case SA_DATA_COMPRESSION_PAGE:=0A=
		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {=0A=
			struct scsi_data_compression_page *dcp =3D &cpage->dcomp;=0A=
			if (calg =3D=3D 0) {=0A=
				/*=0A=
				 * Disable compression, but leave the=0A=
				 * decompression and the capability bit=0A=
				 * alone.=0A=
				 */=0A=
				dcp->dce_and_dcc =3D SA_DCP_DCC;=0A=
				dcp->dde_and_red |=3D SA_DCP_DDE;=0A=
				break;=0A=
			}=0A=
			/* enable compression && decompression */=0A=
			dcp->dce_and_dcc =3D SA_DCP_DCE | SA_DCP_DCC;=0A=
			dcp->dde_and_red |=3D SA_DCP_DDE;=0A=
			/*=0A=
			 * If there, use compression algorithm from caller.=0A=
			 * Otherwise, if there's a saved compression algorithm=0A=
			 * and there is no current algorithm, use the saved=0A=
			 * algorithm. Else parrot back what we got and hope=0A=
			 * for the best.=0A=
			 */=0A=
			if (calg !=3D MT_COMP_ENABLE) {=0A=
				scsi_ulto4b(calg, dcp->comp_algorithm);=0A=
				scsi_ulto4b(calg, dcp->decomp_algorithm);=0A=
			} else if (scsi_4btoul(dcp->comp_algorithm) =3D=3D 0 &&=0A=
			    softc->saved_comp_algorithm !=3D 0) {=0A=
				scsi_ulto4b(softc->saved_comp_algorithm,=0A=
				    dcp->comp_algorithm);=0A=
				scsi_ulto4b(softc->saved_comp_algorithm,=0A=
				    dcp->decomp_algorithm);=0A=
			}=0A=
			break;=0A=
		}=0A=
		/*=0A=
		 * Compression does not appear to be supported-=0A=
		 * at least via the DATA COMPRESSION page. It=0A=
		 * would be too much to ask us to believe that=0A=
		 * the page itself is supported, but incorrectly=0A=
		 * reports an ability to manipulate data compression,=0A=
		 * so we'll assume that this device doesn't support=0A=
		 * compression. We can just fall through for that.=0A=
		 */=0A=
		/* FALLTHROUGH */=0A=
		default:=0A=
			/*=0A=
			 * The drive doesn't seem to support compression,=0A=
			 * so turn off the set compression bit.=0A=
			 */=0A=
			params_to_set &=3D ~SA_PARAM_COMPRESSION;=0A=
			xpt_print_path(periph->path);=0A=
			printf("device does not seem to support compression\n");=0A=
=0A=
			/*=0A=
			 * If that was the only thing the user wanted us to set,=0A=
			 * clean up allocated resources and return with=0A=
			 * 'operation not supported'.=0A=
			 */=0A=
			if (params_to_set =3D=3D SA_PARAM_NONE) {=0A=
				free(mode_buffer, M_TEMP);=0A=
				xpt_release_ccb(ccb);=0A=
				return (ENODEV);=0A=
			}=0A=
		=0A=
			/*=0A=
			 * That wasn't the only thing the user wanted us to set.=0A=
			 * So, decrease the stated mode buffer length by the=0A=
			 * size of the compression mode page.=0A=
			 */=0A=
			mode_buffer_len -=3D sizeof(sa_comp_t);=0A=
		}=0A=
	}=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,=0A=
	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0,=0A=
	    sense_flags, softc->device_stats);=0A=
	QFRLS(ccb);=0A=
=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		int idx;=0A=
		char *xyz =3D mode_buffer;=0A=
		xpt_print_path(periph->path);=0A=
		printf("Err%d, Mode Select Data=3D", error);=0A=
		for (idx =3D 0; idx < mode_buffer_len; idx++)=0A=
			printf(" 0x%02x", xyz[idx] & 0xff);=0A=
		printf("\n");=0A=
	}=0A=
=0A=
=0A=
	if (error) {=0A=
		/*=0A=
		 * If we can, try without setting density/blocksize.=0A=
		 */=0A=
		if (mode_blk) {=0A=
			if ((params_to_set &=0A=
			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) =3D=3D 0) {=0A=
				mode_blk =3D NULL;=0A=
				goto retry;=0A=
			}=0A=
		} else {=0A=
			mode_blk =3D (struct scsi_mode_blk_desc *)&mode_hdr[1];=0A=
			cpage =3D (sa_comp_t *)&mode_blk[1];=0A=
		}=0A=
=0A=
		/*=0A=
		 * If we were setting the blocksize, and that failed, we=0A=
		 * want to set it to its original value.  If we weren't=0A=
		 * setting the blocksize, we don't want to change it.=0A=
		 */=0A=
		scsi_ulto3b(current_blocksize, mode_blk->blklen);=0A=
=0A=
		/*=0A=
		 * Set density if requested, else preserve old density.=0A=
		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better=0A=
		 * devices, else density we've latched up in our softc.=0A=
		 */=0A=
		if (params_to_set & SA_PARAM_DENSITY) {=0A=
			mode_blk->density =3D current_density;=0A=
		} else if (softc->scsi_rev > SCSI_REV_CCS) {=0A=
			mode_blk->density =3D SCSI_SAME_DENSITY;=0A=
		} else {=0A=
			mode_blk->density =3D softc->media_density;=0A=
		}=0A=
=0A=
		if (params_to_set & SA_PARAM_COMPRESSION)=0A=
			bcopy(ccomp, cpage, sizeof (sa_comp_t));=0A=
=0A=
		/*=0A=
		 * The retry count is the only CCB field that might have been=0A=
		 * changed that we care about, so reset it back to 1.=0A=
		 */=0A=
		ccb->ccb_h.retry_count =3D 1;=0A=
		cam_periph_runccb(ccb, saerror, 0, sense_flags,=0A=
		    softc->device_stats);=0A=
		QFRLS(ccb);=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	if (ccomp !=3D NULL)=0A=
		free(ccomp, M_TEMP);=0A=
=0A=
	if (params_to_set & SA_PARAM_COMPRESSION) {=0A=
		if (error) {=0A=
			softc->flags &=3D ~SA_FLAG_COMP_ENABLED;=0A=
			/*=0A=
			 * Even if we get an error setting compression,=0A=
			 * do not say that we don't support it. We could=0A=
			 * have been wrong, or it may be media specific.=0A=
			 *	softc->flags &=3D ~SA_FLAG_COMP_SUPP;=0A=
			 */=0A=
			softc->saved_comp_algorithm =3D softc->comp_algorithm;=0A=
			softc->comp_algorithm =3D 0;=0A=
		} else {=0A=
			softc->flags |=3D SA_FLAG_COMP_ENABLED;=0A=
			softc->comp_algorithm =3D calg;=0A=
		}=0A=
	}=0A=
=0A=
	free(mode_buffer, M_TEMP);=0A=
	return (error);=0A=
}=0A=
=0A=
static void=0A=
saprevent(struct cam_periph *periph, int action)=0A=
{=0A=
	struct	sa_softc *softc;=0A=
	union	ccb *ccb;		=0A=
	int	error, sf;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	if ((action =3D=3D PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) =
=3D=3D 0)=0A=
		return;=0A=
	if ((action =3D=3D PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) =
!=3D 0)=0A=
		return;=0A=
=0A=
	/*=0A=
	 * We can be quiet about illegal requests.=0A=
	 */=0A=
	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {=0A=
		sf =3D 0;=0A=
	} else=0A=
		sf =3D SF_QUIET_IR;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,=0A=
	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats);=0A=
	QFRLS(ccb);=0A=
	if (error =3D=3D 0) {=0A=
		if (action =3D=3D PR_ALLOW)=0A=
			softc->flags &=3D ~SA_FLAG_TAPE_LOCKED;=0A=
		else=0A=
			softc->flags |=3D SA_FLAG_TAPE_LOCKED;=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
}=0A=
=0A=
static int=0A=
sarewind(struct cam_periph *periph)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_REW;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	else=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saspace(struct cam_periph *periph, int count, scsi_space_code code)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
		=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* This cannot be retried */=0A=
=0A=
	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,=0A=
	    SSD_FULL_SIZE, SPACE_TIMEOUT);=0A=
=0A=
	/*=0A=
	 * Clear residual because we will be using it.=0A=
	 */=0A=
	softc->last_ctl_resid =3D 0;=0A=
=0A=
	softc->dsreg =3D (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If a spacing operation has failed, we need to invalidate=0A=
	 * this mount.=0A=
	 *=0A=
	 * If the spacing operation was setmarks or to end of recorded data,=0A=
	 * we no longer know our relative position.=0A=
	 *=0A=
	 * If the spacing operations was spacing files in reverse, we=0A=
	 * take account of the residual, but still check against less=0A=
	 * than zero- if we've gone negative, we must have hit BOT.=0A=
	 *=0A=
	 * If the spacing operations was spacing records in reverse and=0A=
	 * we have a residual, we've either hit BOT or hit a filemark.=0A=
	 * In the former case, we know our new record number (0). In=0A=
	 * the latter case, we have absolutely no idea what the real=0A=
	 * record number is- we've stopped between the end of the last=0A=
	 * record in the previous file and the filemark that stopped=0A=
	 * our spacing backwards.=0A=
	 */=0A=
	if (error) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (code =3D=3D SS_SETMARKS || code =3D=3D SS_EOD) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (code =3D=3D SS_FILEMARKS && softc->fileno !=3D (daddr_t) -1) =
{=0A=
		softc->fileno +=3D (count - softc->last_ctl_resid);=0A=
		if (softc->fileno < 0)	/* we must of hit BOT */=0A=
			softc->fileno =3D 0;=0A=
		softc->blkno =3D 0;=0A=
	} else if (code =3D=3D SS_BLOCKS && softc->blkno !=3D (daddr_t) -1) {=0A=
		softc->blkno +=3D (count - softc->last_ctl_resid);=0A=
		if (count < 0) {=0A=
			if (softc->last_ctl_resid || softc->blkno < 0) {=0A=
				if (softc->fileno =3D=3D 0) {=0A=
					softc->blkno =3D 0;=0A=
				} else {=0A=
					softc->blkno =3D (daddr_t) -1;=0A=
				}=0A=
			}=0A=
		}=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error, nwm =3D 0;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	/*=0A=
	 * Clear residual because we will be using it.=0A=
	 */=0A=
	softc->last_ctl_resid =3D 0;=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_FMK;=0A=
	/* this *must* not be retried */=0A=
	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
=0A=
	if (error =3D=3D 0 && nmarks) {=0A=
		struct sa_softc *softc =3D (struct sa_softc *)periph->softc;=0A=
		nwm =3D nmarks - softc->last_ctl_resid;=0A=
		softc->filemarks +=3D nwm;=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * Update relative positions (if we're doing that).=0A=
	 */=0A=
	if (error) {=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	} else if (softc->fileno !=3D (daddr_t) -1) {=0A=
		softc->fileno +=3D nwm;=0A=
		softc->blkno =3D 0;=0A=
	}=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)=0A=
{=0A=
	struct scsi_tape_position_data loc;=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc =3D (struct sa_softc *)periph->softc;=0A=
	int error;=0A=
=0A=
	/*=0A=
	 * We try and flush any buffered writes here if we were writing=0A=
	 * and we're trying to get hardware block position. It eats=0A=
	 * up performance substantially, but I'm wary of drive firmware.=0A=
	 *=0A=
	 * I think that *logical* block position is probably okay-=0A=
	 * but hardware block position might have to wait for data=0A=
	 * to hit media to be valid. Caveat Emptor.=0A=
	 */=0A=
=0A=
	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {=0A=
		error =3D sawritefilemarks(periph, 0, 0);=0A=
		if (error && error !=3D EACCES)=0A=
			return (error);=0A=
	}=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT);=0A=
	softc->dsreg =3D MTIO_DSREG_RBSY;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);=0A=
=0A=
	if (error =3D=3D 0) {=0A=
		if (loc.flags & SA_RPOS_UNCERTAIN) {=0A=
			error =3D EINVAL;		/* nothing is certain */=0A=
		} else {=0A=
			*blkptr =3D scsi_4btoul(loc.firstblk);=0A=
		}=0A=
	}=0A=
=0A=
	xpt_release_ccb(ccb);=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	/*=0A=
	 * We used to try and flush any buffered writes here.=0A=
	 * Now we push this onto user applications to either=0A=
	 * flush the pending writes themselves (via a zero count=0A=
	 * WRITE FILEMARKS command) or they can trust their tape=0A=
	 * drive to do this correctly for them.=0A=
 	 */=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	=0A=
	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT);=0A=
=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_POS;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);=0A=
	xpt_release_ccb(ccb);=0A=
	/*=0A=
	 * Note relative file && block number position as now unknown.=0A=
	 */=0A=
	softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saretension(struct cam_periph *periph)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_TEN;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
	xpt_release_ccb(ccb);=0A=
	if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	else=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
sareservereleaseunit(struct cam_periph *periph, int reserve)=0A=
{=0A=
	union ccb *ccb;=0A=
	struct sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
	ccb =3D cam_periph_getccb(periph,  1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,=0A=
	    FALSE,  0, SSD_FULL_SIZE,  SCSIOP_TIMEOUT, reserve);=0A=
	softc->dsreg =3D MTIO_DSREG_RBSY;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0,=0A=
	    SF_RETRY_UA | SF_NO_PRINT, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	QFRLS(ccb);=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	/*=0A=
	 * If the error was Illegal Request, then the device doesn't support=0A=
	 * RESERVE/RELEASE. This is not an error.=0A=
	 */=0A=
	if (error =3D=3D EINVAL) {=0A=
		error =3D 0;=0A=
	}=0A=
=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saloadunload(struct cam_periph *periph, int load)=0A=
{=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int	error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	/* It is safe to retry this operation */=0A=
	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,=0A=
	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
	QFRLS(ccb);=0A=
	xpt_release_ccb(ccb);=0A=
=0A=
	if (error || load =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) -1;=0A=
	else if (error =3D=3D 0)=0A=
		softc->fileno =3D softc->blkno =3D (daddr_t) 0;=0A=
	return (error);=0A=
}=0A=
=0A=
static int=0A=
saerase(struct cam_periph *periph, int longerase)=0A=
{=0A=
=0A=
	union	ccb *ccb;=0A=
	struct	sa_softc *softc;=0A=
	int error;=0A=
=0A=
	softc =3D (struct sa_softc *)periph->softc;=0A=
=0A=
	ccb =3D cam_periph_getccb(periph, 1);=0A=
=0A=
	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,=0A=
	    SSD_FULL_SIZE, ERASE_TIMEOUT);=0A=
=0A=
	softc->dsreg =3D MTIO_DSREG_ZER;=0A=
	error =3D cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);=0A=
	softc->dsreg =3D MTIO_DSREG_REST;=0A=
=0A=
	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) !=3D 0)=0A=
		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);=0A=
	xpt_release_ccb(ccb);=0A=
	return (error);=0A=
}=0A=
=0A=
#endif /* _KERNEL */=0A=
=0A=
/*=0A=
 * Read tape block limits command.=0A=
 */=0A=
void=0A=
scsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action,=0A=
		   struct scsi_read_block_limits_data *rlimit_buf,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_read_block_limits *scsi_cmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,=0A=
	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,=0A=
	     sizeof(*scsi_cmd), timeout);=0A=
=0A=
	scsi_cmd =3D (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D READ_BLOCK_LIMITS;=0A=
}=0A=
=0A=
void=0A=
scsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int readop, int sli,=0A=
		   int fixed, u_int32_t length, u_int8_t *data_ptr,=0A=
		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_sa_rw *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;=0A=
	scsi_cmd->opcode =3D readop ? SA_READ : SA_WRITE;=0A=
	scsi_cmd->sli_fixed =3D 0;=0A=
	if (sli && readop)=0A=
		scsi_cmd->sli_fixed |=3D SAR_SLI;=0A=
	if (fixed)=0A=
		scsi_cmd->sli_fixed |=3D SARW_FIXED;=0A=
	scsi_ulto3b(length, scsi_cmd->length);=0A=
	scsi_cmd->control =3D 0;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, readop ? CAM_DIR_IN : CAM_DIR_OUT,=0A=
	    tag_action, data_ptr, dxfer_len, sense_len,=0A=
	    sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,         =0A=
		 void (*cbfcnp)(struct cam_periph *, union ccb *),   =0A=
		 u_int8_t tag_action, int immediate, int eot,=0A=
		 int reten, int load, u_int8_t sense_len,=0A=
		 u_int32_t timeout)=0A=
{=0A=
	struct scsi_load_unload *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D LOAD_UNLOAD;=0A=
	if (immediate)=0A=
		scsi_cmd->immediate =3D SLU_IMMED;=0A=
	if (eot)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_EOT;=0A=
	if (reten)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_RETEN;=0A=
	if (load)=0A=
		scsi_cmd->eot_reten_load |=3D SLU_LOAD;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,=0A=
	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);	=0A=
}=0A=
=0A=
void=0A=
scsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,         =0A=
	    void (*cbfcnp)(struct cam_periph *, union ccb *),   =0A=
	    u_int8_t tag_action, int immediate, u_int8_t sense_len,     =0A=
	    u_int32_t timeout)=0A=
{=0A=
	struct scsi_rewind *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D REWIND;=0A=
	if (immediate)=0A=
		scsi_cmd->immediate =3D SREW_IMMED;=0A=
	=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_space(struct ccb_scsiio *csio, u_int32_t retries,=0A=
	   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
	   u_int8_t tag_action, scsi_space_code code,=0A=
	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_space *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_space *)&csio->cdb_io.cdb_bytes;=0A=
	scsi_cmd->opcode =3D SPACE;=0A=
	scsi_cmd->code =3D code;=0A=
	scsi_ulto3b(count, scsi_cmd->count);=0A=
	scsi_cmd->control =3D 0;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		     void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		     u_int8_t tag_action, int immediate, int setmark,=0A=
		     u_int32_t num_marks, u_int8_t sense_len,=0A=
		     u_int32_t timeout)=0A=
{=0A=
	struct scsi_write_filemarks *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
	scsi_cmd->opcode =3D WRITE_FILEMARKS;=0A=
	if (immediate)=0A=
		scsi_cmd->byte2 |=3D SWFMRK_IMMED;=0A=
	if (setmark)=0A=
		scsi_cmd->byte2 |=3D SWFMRK_WSMK;=0A=
	=0A=
	scsi_ulto3b(num_marks, scsi_cmd->num_marks);=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
/*=0A=
 * The reserve and release unit commands differ only by their opcodes.=0A=
 */=0A=
void=0A=
scsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,=0A=
			  void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
			  u_int8_t tag_action, int third_party,=0A=
			  int third_party_id, u_int8_t sense_len,=0A=
			  u_int32_t timeout, int reserve)=0A=
{=0A=
	struct scsi_reserve_release_unit *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_reserve_release_unit =
*)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
=0A=
	if (reserve)=0A=
		scsi_cmd->opcode =3D RESERVE_UNIT;=0A=
	else=0A=
		scsi_cmd->opcode =3D RELEASE_UNIT;=0A=
=0A=
	if (third_party) {=0A=
		scsi_cmd->lun_thirdparty |=3D SRRU_3RD_PARTY;=0A=
		scsi_cmd->lun_thirdparty |=3D=0A=
			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);=0A=
	}=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
void=0A=
scsi_erase(struct ccb_scsiio *csio, u_int32_t retries,=0A=
	   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
	   u_int8_t tag_action, int immediate, int long_erase,=0A=
	   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_erase *scsi_cmd;=0A=
=0A=
	scsi_cmd =3D (struct scsi_erase *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scsi_cmd, sizeof(*scsi_cmd));=0A=
=0A=
	scsi_cmd->opcode =3D ERASE;=0A=
=0A=
	if (immediate)=0A=
		scsi_cmd->lun_imm_long |=3D SE_IMMED;=0A=
=0A=
	if (long_erase)=0A=
		scsi_cmd->lun_imm_long |=3D SE_LONG;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,=0A=
	    0, sense_len, sizeof(*scsi_cmd), timeout);=0A=
}=0A=
=0A=
/*=0A=
 * Read Tape Position command.=0A=
 */=0A=
void=0A=
scsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int hardsoft,=0A=
		   struct scsi_tape_position_data *sbp,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_tape_read_position *scmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,=0A=
	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);=0A=
	scmd =3D (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scmd, sizeof(*scmd));=0A=
	scmd->opcode =3D READ_POSITION;=0A=
	scmd->byte1 =3D hardsoft;=0A=
}=0A=
=0A=
/*=0A=
 * Set Tape Position command.=0A=
 */=0A=
void=0A=
scsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,=0A=
		   void (*cbfcnp)(struct cam_periph *, union ccb *),=0A=
		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,=0A=
		   u_int8_t sense_len, u_int32_t timeout)=0A=
{=0A=
	struct scsi_tape_locate *scmd;=0A=
=0A=
	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,=0A=
	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);=0A=
	scmd =3D (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;=0A=
	bzero(scmd, sizeof(*scmd));=0A=
	scmd->opcode =3D LOCATE;=0A=
	if (hardsoft)=0A=
		scmd->byte1 |=3D SA_SPOS_BT;=0A=
	scsi_ulto4b(blkno, scmd->blkaddr);=0A=
}=0A=

------=_NextPart_000_001A_01C54C04.3DD952C0
Content-Type: application/octet-stream;
	name="README"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
	filename="README"

# Atempo Bpr 28/04/2005=0A=
#=0A=
# On FreeBSD 5.3 the sa driver make a scsi reserve and it release only =
when we execute a offline=0A=
#=0A=
# To correct the sa drive we must patch it.=0A=
#=0A=
# =0A=
1/ Patch source driver=0A=
=0A=
=0A=
cd /usr/src/sys/cam/scsi=0A=
patch <sa_scsi.c.patch=0A=
=0A=
2/ Create Kernel Configuration File=0A=
=0A=
cd /usr/src/sys/i386/conf=0A=
cp GENERIC GENERIC_Atempo=0A=
=0A=
3/ Build & Install the Kernel=0A=
=0A=
cd /usr/src=0A=
=0A=
# Compile the kernel.=0A=
make buildkernel KERNCONF=3DGENERIC_Atempo=0A=
=0A=
# Install the new kernel.=0A=
make installkernel KERNCONF=3DGENERIC_Atempo=0A=
=0A=
4/ Reboot the System=0A=
=0A=
=0A=
For more informations about the kernel building=0A=
http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-bu=
ilding.html=0A=
=0A=
Explication : =0A=
=0A=
With this patch we reserve the drive on each open and release on each =
close=0A=
=0A=
=0A=
=0A=

------=_NextPart_000_001A_01C54C04.3DD952C0--




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?KOEBLIEDJOKGEMJPOEJOGEOHCPAA.bernard.peultier>