scsi_sa.c revision 195534
1139743Simp/*-
239213Sgibbs * Implementation of SCSI Sequential Access Peripheral driver for CAM.
339213Sgibbs *
458251Smjacob * Copyright (c) 1999, 2000 Matthew Jacob
539213Sgibbs * All rights reserved.
639213Sgibbs *
739213Sgibbs * Redistribution and use in source and binary forms, with or without
839213Sgibbs * modification, are permitted provided that the following conditions
939213Sgibbs * are met:
1039213Sgibbs * 1. Redistributions of source code must retain the above copyright
1139213Sgibbs *    notice, this list of conditions, and the following disclaimer,
1239213Sgibbs *    without modification, immediately at the beginning of the file.
1339213Sgibbs * 2. The name of the author may not be used to endorse or promote products
1439213Sgibbs *    derived from this software without specific prior written permission.
1539213Sgibbs *
1639213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1739213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1839213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1939213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2039213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2139213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2239213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2339213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2439213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2539213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2639213Sgibbs * SUCH DAMAGE.
2739213Sgibbs */
2839213Sgibbs
29116162Sobrien#include <sys/cdefs.h>
30116162Sobrien__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_sa.c 195534 2009-07-10 08:18:08Z scottl $");
31116162Sobrien
3239213Sgibbs#include <sys/param.h>
3339213Sgibbs#include <sys/queue.h>
3455205Speter#ifdef _KERNEL
3539213Sgibbs#include <sys/systm.h>
3639213Sgibbs#include <sys/kernel.h>
3739213Sgibbs#endif
3839213Sgibbs#include <sys/types.h>
39110517Sphk#include <sys/time.h>
4060041Sphk#include <sys/bio.h>
41114216Skan#include <sys/limits.h>
4239213Sgibbs#include <sys/malloc.h>
4339213Sgibbs#include <sys/mtio.h>
4465061Speter#ifdef _KERNEL
4539213Sgibbs#include <sys/conf.h>
4665061Speter#endif
47154360Smjacob#include <sys/fcntl.h>
4839213Sgibbs#include <sys/devicestat.h>
4939213Sgibbs
5055205Speter#ifndef _KERNEL
5139213Sgibbs#include <stdio.h>
5239213Sgibbs#include <string.h>
5339213Sgibbs#endif
5439213Sgibbs
5539213Sgibbs#include <cam/cam.h>
5639213Sgibbs#include <cam/cam_ccb.h>
5739213Sgibbs#include <cam/cam_periph.h>
5839213Sgibbs#include <cam/cam_xpt_periph.h>
5939213Sgibbs#include <cam/cam_debug.h>
6039213Sgibbs
6139213Sgibbs#include <cam/scsi/scsi_all.h>
6239213Sgibbs#include <cam/scsi/scsi_message.h>
6339213Sgibbs#include <cam/scsi/scsi_sa.h>
6439213Sgibbs
6555205Speter#ifdef _KERNEL
6639213Sgibbs
6739884Sken#include <opt_sa.h>
6839884Sken
6979100Smjacob#ifndef SA_IO_TIMEOUT
7079100Smjacob#define SA_IO_TIMEOUT		4
7179100Smjacob#endif
7239884Sken#ifndef SA_SPACE_TIMEOUT
7339884Sken#define SA_SPACE_TIMEOUT	1 * 60
7439884Sken#endif
7539884Sken#ifndef SA_REWIND_TIMEOUT
7639884Sken#define SA_REWIND_TIMEOUT	2 * 60
7739884Sken#endif
7839884Sken#ifndef SA_ERASE_TIMEOUT
7939884Sken#define SA_ERASE_TIMEOUT	4 * 60
8039884Sken#endif
8153259Smjacob
8279100Smjacob#define	SCSIOP_TIMEOUT		(60 * 1000)	/* not an option */
8379100Smjacob
8479100Smjacob#define	IO_TIMEOUT		(SA_IO_TIMEOUT * 60 * 1000)
8554099Smjacob#define	REWIND_TIMEOUT		(SA_REWIND_TIMEOUT * 60 * 1000)
8654099Smjacob#define	ERASE_TIMEOUT		(SA_ERASE_TIMEOUT * 60 * 1000)
8754099Smjacob#define	SPACE_TIMEOUT		(SA_SPACE_TIMEOUT * 60 * 1000)
8853259Smjacob
8943636Smjacob/*
9051875Smjacob * Additional options that can be set for config: SA_1FM_AT_EOT
9143636Smjacob */
9253259Smjacob
9341906Smjacob#ifndef	UNUSED_PARAMETER
9441906Smjacob#define	UNUSED_PARAMETER(x)	x = x
9541906Smjacob#endif
9641906Smjacob
9753259Smjacob#define	QFRLS(ccb)	\
9853259Smjacob	if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) != 0)	\
9953259Smjacob		cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE)
10053259Smjacob
10153259Smjacob/*
10253259Smjacob * Driver states
10353259Smjacob */
10453259Smjacob
105147723SavatarMALLOC_DEFINE(M_SCSISA, "SCSI sa", "SCSI sequential access buffers");
10653259Smjacob
10739213Sgibbstypedef enum {
10846962Smjacob	SA_STATE_NORMAL, SA_STATE_ABNORMAL
10939213Sgibbs} sa_state;
11039213Sgibbs
11171082Smjacob#define ccb_pflags	ppriv_field0
11271082Smjacob#define ccb_bp	 	ppriv_ptr1
11339213Sgibbs
11471082Smjacob#define	SA_CCB_BUFFER_IO	0x0
11571082Smjacob#define	SA_CCB_WAITING		0x1
11671082Smjacob#define	SA_CCB_TYPEMASK		0x1
11771082Smjacob#define	SA_POSITION_UPDATED	0x2
11839213Sgibbs
11971082Smjacob#define	Set_CCB_Type(x, type)				\
12071082Smjacob	x->ccb_h.ccb_pflags &= ~SA_CCB_TYPEMASK;	\
12171082Smjacob	x->ccb_h.ccb_pflags |= type
12271082Smjacob
12371082Smjacob#define	CCB_Type(x)	(x->ccb_h.ccb_pflags & SA_CCB_TYPEMASK)
12471082Smjacob
12571082Smjacob
12671082Smjacob
12739213Sgibbstypedef enum {
12839213Sgibbs	SA_FLAG_OPEN		= 0x0001,
12939213Sgibbs	SA_FLAG_FIXED		= 0x0002,
13039213Sgibbs	SA_FLAG_TAPE_LOCKED	= 0x0004,
13139213Sgibbs	SA_FLAG_TAPE_MOUNTED	= 0x0008,
13239213Sgibbs	SA_FLAG_TAPE_WP		= 0x0010,
13339213Sgibbs	SA_FLAG_TAPE_WRITTEN	= 0x0020,
13441906Smjacob	SA_FLAG_EOM_PENDING	= 0x0040,
13541906Smjacob	SA_FLAG_EIO_PENDING	= 0x0080,
13641906Smjacob	SA_FLAG_EOF_PENDING	= 0x0100,
13739213Sgibbs	SA_FLAG_ERR_PENDING	= (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING|
13839213Sgibbs				   SA_FLAG_EOF_PENDING),
13941906Smjacob	SA_FLAG_INVALID		= 0x0200,
14041906Smjacob	SA_FLAG_COMP_ENABLED	= 0x0400,
14146962Smjacob	SA_FLAG_COMP_SUPP	= 0x0800,
14246962Smjacob	SA_FLAG_COMP_UNSUPP	= 0x1000,
14346962Smjacob	SA_FLAG_TAPE_FROZEN	= 0x2000
14439213Sgibbs} sa_flags;
14539213Sgibbs
14639213Sgibbstypedef enum {
14739213Sgibbs	SA_MODE_REWIND		= 0x00,
14839213Sgibbs	SA_MODE_NOREWIND	= 0x01,
14939213Sgibbs	SA_MODE_OFFLINE		= 0x02
15039213Sgibbs} sa_mode;
15139213Sgibbs
15239213Sgibbstypedef enum {
15339213Sgibbs	SA_PARAM_NONE		= 0x00,
15439213Sgibbs	SA_PARAM_BLOCKSIZE	= 0x01,
15539213Sgibbs	SA_PARAM_DENSITY	= 0x02,
15639213Sgibbs	SA_PARAM_COMPRESSION	= 0x04,
15739213Sgibbs	SA_PARAM_BUFF_MODE	= 0x08,
15839213Sgibbs	SA_PARAM_NUMBLOCKS	= 0x10,
15939213Sgibbs	SA_PARAM_WP		= 0x20,
16039213Sgibbs	SA_PARAM_SPEED		= 0x40,
16139213Sgibbs	SA_PARAM_ALL		= 0x7f
16239213Sgibbs} sa_params;
16339213Sgibbs
16439213Sgibbstypedef enum {
16539213Sgibbs	SA_QUIRK_NONE		= 0x00,
16660235Smjacob	SA_QUIRK_NOCOMP		= 0x01,	/* Can't deal with compression at all */
16760235Smjacob	SA_QUIRK_FIXED		= 0x02,	/* Force fixed mode */
16860235Smjacob	SA_QUIRK_VARIABLE	= 0x04,	/* Force variable mode */
16943636Smjacob	SA_QUIRK_2FM		= 0x08,	/* Needs Two File Marks at EOD */
17056981Smjacob	SA_QUIRK_1FM		= 0x10,	/* No more than 1 File Mark at EOD */
17160235Smjacob	SA_QUIRK_NODREAD	= 0x20,	/* Don't try and dummy read density */
17271082Smjacob	SA_QUIRK_NO_MODESEL	= 0x40,	/* Don't do mode select at all */
17371082Smjacob	SA_QUIRK_NO_CPAGE	= 0x80	/* Don't use DEVICE COMPRESSION page */
17439213Sgibbs} sa_quirks;
17539213Sgibbs
176191304Sed#define	SAMODE(z)	(dev2unit(z) & 0x3)
177191304Sed#define	SADENSITY(z)	((dev2unit(z) >> 2) & 0x3)
178191304Sed#define	SA_IS_CTRL(z)	(dev2unit(z) & (1 << 4))
17953283Smjacob
18053259Smjacob#define SA_NOT_CTLDEV	0
18153259Smjacob#define SA_CTLDEV	1
18253259Smjacob
18353259Smjacob#define SA_ATYPE_R	0
18453259Smjacob#define SA_ATYPE_NR	1
18553259Smjacob#define SA_ATYPE_ER	2
18653259Smjacob
187191304Sed#define	SAMINOR(ctl, mode, access) \
188191304Sed	((ctl << 4) | (mode << 2) | (access & 0x3))
18953259Smjacob
19053259Smjacob#define SA_NUM_MODES	4
19153259Smjacobstruct sa_devs {
192130585Sphk	struct cdev *ctl_dev;
19353259Smjacob	struct sa_mode_devs {
194130585Sphk		struct cdev *r_dev;
195130585Sphk		struct cdev *nr_dev;
196130585Sphk		struct cdev *er_dev;
19753259Smjacob	} mode_devs[SA_NUM_MODES];
19853259Smjacob};
19953259Smjacob
20039213Sgibbsstruct sa_softc {
20139213Sgibbs	sa_state	state;
20239213Sgibbs	sa_flags	flags;
20339213Sgibbs	sa_quirks	quirks;
20459249Sphk	struct		bio_queue_head bio_queue;
20546962Smjacob	int		queue_count;
206112006Sphk	struct		devstat *device_stats;
20753259Smjacob	struct sa_devs	devs;
20839213Sgibbs	int		blk_gran;
20939213Sgibbs	int		blk_mask;
21039213Sgibbs	int		blk_shift;
21139213Sgibbs	u_int32_t	max_blk;
21239213Sgibbs	u_int32_t	min_blk;
21341674Smjacob	u_int32_t	comp_algorithm;
21441674Smjacob	u_int32_t	saved_comp_algorithm;
21539213Sgibbs	u_int32_t	media_blksize;
21641906Smjacob	u_int32_t	last_media_blksize;
21739213Sgibbs	u_int32_t	media_numblks;
21841674Smjacob	u_int8_t	media_density;
21939213Sgibbs	u_int8_t	speed;
22041674Smjacob	u_int8_t	scsi_rev;
22143636Smjacob	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */
22239213Sgibbs	int		buffer_mode;
22339213Sgibbs	int		filemarks;
22439213Sgibbs	union		ccb saved_ccb;
22571268Smjacob	int		last_resid_was_io;
22646962Smjacob
22741948Smjacob	/*
22843636Smjacob	 * Relative to BOT Location.
22943636Smjacob	 */
23043636Smjacob	daddr_t		fileno;
23143636Smjacob	daddr_t		blkno;
23243636Smjacob
23343636Smjacob	/*
23441948Smjacob	 * Latched Error Info
23541948Smjacob	 */
23642009Smjacob	struct {
23742009Smjacob		struct scsi_sense_data _last_io_sense;
23842009Smjacob		u_int32_t _last_io_resid;
23942009Smjacob		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];
24042009Smjacob		struct scsi_sense_data _last_ctl_sense;
24142009Smjacob		u_int32_t _last_ctl_resid;
24242009Smjacob		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];
24342009Smjacob#define	last_io_sense	errinfo._last_io_sense
24442009Smjacob#define	last_io_resid	errinfo._last_io_resid
24542009Smjacob#define	last_io_cdb	errinfo._last_io_cdb
24642009Smjacob#define	last_ctl_sense	errinfo._last_ctl_sense
24742009Smjacob#define	last_ctl_resid	errinfo._last_ctl_resid
24842009Smjacob#define	last_ctl_cdb	errinfo._last_ctl_cdb
24942009Smjacob	} errinfo;
25043636Smjacob	/*
25143636Smjacob	 * Misc other flags/state
25243636Smjacob	 */
25343636Smjacob	u_int32_t
254154360Smjacob					: 29,
255154360Smjacob		open_rdonly		: 1,	/* open read-only */
256154360Smjacob		open_pending_mount	: 1,	/* open pending mount */
257154360Smjacob		ctrl_mode		: 1;	/* control device open */
25839213Sgibbs};
25939213Sgibbs
26039213Sgibbsstruct sa_quirk_entry {
26142563Smjacob	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */
26242563Smjacob	sa_quirks quirks;	/* specific quirk type */
26342563Smjacob	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */
26439213Sgibbs};
26539213Sgibbs
26639213Sgibbsstatic struct sa_quirk_entry sa_quirk_table[] =
26739213Sgibbs{
26839213Sgibbs	{
26960235Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",
27060235Smjacob		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |
27160235Smjacob		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768
27260235Smjacob	},
27360235Smjacob	{
27439213Sgibbs		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
27577810Snon		  "Python 06408*", "*"}, SA_QUIRK_NODREAD, 0
27677581Snon	},
27777581Snon	{
27877581Snon		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
27956981Smjacob		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0
28041351Sjoerg	},
28141351Sjoerg	{
28242130Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28356981Smjacob		  "Python*", "*"}, SA_QUIRK_NODREAD, 0
28456981Smjacob	},
28556981Smjacob	{
28656981Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28743636Smjacob		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
28842130Smjacob	},
28942130Smjacob	{
29042563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29168500Smjacob		  "VIPER 2525 25462", "-011"},
29268500Smjacob		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0
29368500Smjacob	},
29468500Smjacob	{
29568500Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29646962Smjacob		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
29742563Smjacob	},
29871082Smjacob#if	0
29942563Smjacob	{
30042533Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
30171082Smjacob		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,
30271082Smjacob	},
30371082Smjacob#endif
304107943Strhodes 	{
305107943Strhodes 		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
306107943Strhodes		  "C56*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
307107943Strhodes	},
30871082Smjacob	{
30971082Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31046962Smjacob		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
31146962Smjacob	},
31246962Smjacob	{
31346962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31444354Smjacob		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
31542533Smjacob	},
31642533Smjacob	{
31742716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31842716Smjacob		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
31942716Smjacob	},
32042716Smjacob	{
32142716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",
32242716Smjacob		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
32342716Smjacob	},
32442716Smjacob	{
32542716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",
32642716Smjacob		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
32742716Smjacob	},
32851744Smjacob	{	/* jreynold@primenet.com */
32951744Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
33051744Smjacob		"STT8000N*", "*"}, SA_QUIRK_1FM, 0
33151744Smjacob	},
33251875Smjacob	{	/* mike@sentex.net */
33351875Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
33451875Smjacob		"STT20000*", "*"}, SA_QUIRK_1FM, 0
33551875Smjacob	},
33642716Smjacob	{
33741351Sjoerg		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
33843636Smjacob		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
33942563Smjacob	},
34042563Smjacob	{
34142563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
34247519Smjacob		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
34347519Smjacob	},
34447519Smjacob	{
34547519Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
34648192Smjacob		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
34748192Smjacob	},
34848192Smjacob	{
34948192Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35043636Smjacob		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
35142563Smjacob	},
35242563Smjacob	{
35346962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35446962Smjacob		  " SLR*", "*"}, SA_QUIRK_1FM, 0
35546962Smjacob	},
35646962Smjacob	{
35742563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
35843636Smjacob		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
35945752Smjacob	},
36045752Smjacob	{
36145752Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
36245752Smjacob		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
36339213Sgibbs	}
36439213Sgibbs};
36539213Sgibbs
36639213Sgibbsstatic	d_open_t	saopen;
36739213Sgibbsstatic	d_close_t	saclose;
36839213Sgibbsstatic	d_strategy_t	sastrategy;
36939213Sgibbsstatic	d_ioctl_t	saioctl;
37039213Sgibbsstatic	periph_init_t	sainit;
37139213Sgibbsstatic	periph_ctor_t	saregister;
37240603Skenstatic	periph_oninv_t	saoninvalidate;
37339213Sgibbsstatic	periph_dtor_t	sacleanup;
37439213Sgibbsstatic	periph_start_t	sastart;
37539213Sgibbsstatic	void		saasync(void *callback_arg, u_int32_t code,
37639213Sgibbs				struct cam_path *path, void *arg);
37739213Sgibbsstatic	void		sadone(struct cam_periph *periph,
37839213Sgibbs			       union ccb *start_ccb);
37939213Sgibbsstatic  int		saerror(union ccb *ccb, u_int32_t cam_flags,
38039213Sgibbs				u_int32_t sense_flags);
38168114Smjacobstatic int		samarkswanted(struct cam_periph *);
38239213Sgibbsstatic int		sacheckeod(struct cam_periph *periph);
38339213Sgibbsstatic int		sagetparams(struct cam_periph *periph,
38439213Sgibbs				    sa_params params_to_get,
38539213Sgibbs				    u_int32_t *blocksize, u_int8_t *density,
38639213Sgibbs				    u_int32_t *numblocks, int *buff_mode,
38739213Sgibbs				    u_int8_t *write_protect, u_int8_t *speed,
38839213Sgibbs				    int *comp_supported, int *comp_enabled,
38939213Sgibbs				    u_int32_t *comp_algorithm,
39046962Smjacob				    sa_comp_t *comp_page);
39139213Sgibbsstatic int		sasetparams(struct cam_periph *periph,
39239213Sgibbs				    sa_params params_to_set,
39339213Sgibbs				    u_int32_t blocksize, u_int8_t density,
39442563Smjacob				    u_int32_t comp_algorithm,
39542563Smjacob				    u_int32_t sense_flags);
39639213Sgibbsstatic void		saprevent(struct cam_periph *periph, int action);
39739213Sgibbsstatic int		sarewind(struct cam_periph *periph);
39839213Sgibbsstatic int		saspace(struct cam_periph *periph, int count,
39939213Sgibbs				scsi_space_code code);
400130585Sphkstatic int		samount(struct cam_periph *, int, struct cdev *);
40139213Sgibbsstatic int		saretension(struct cam_periph *periph);
40239213Sgibbsstatic int		sareservereleaseunit(struct cam_periph *periph,
40339213Sgibbs					     int reserve);
40439213Sgibbsstatic int		saloadunload(struct cam_periph *periph, int load);
40539213Sgibbsstatic int		saerase(struct cam_periph *periph, int longerase);
40639213Sgibbsstatic int		sawritefilemarks(struct cam_periph *periph,
40739213Sgibbs					 int nmarks, int setmarks);
40841918Smjacobstatic int		sardpos(struct cam_periph *periph, int, u_int32_t *);
40941918Smjacobstatic int		sasetpos(struct cam_periph *periph, int, u_int32_t *);
41039213Sgibbs
41141918Smjacob
41239213Sgibbsstatic struct periph_driver sadriver =
41339213Sgibbs{
41439213Sgibbs	sainit, "sa",
41539213Sgibbs	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0
41639213Sgibbs};
41739213Sgibbs
41872119SpeterPERIPHDRIVER_DECLARE(sa, sadriver);
41939213Sgibbs
42039213Sgibbs/* For 2.2-stable support */
42139213Sgibbs#ifndef D_TAPE
42239213Sgibbs#define D_TAPE 0
42339213Sgibbs#endif
42439213Sgibbs
42539213Sgibbs
42647625Sphkstatic struct cdevsw sa_cdevsw = {
427126080Sphk	.d_version =	D_VERSION,
428111815Sphk	.d_open =	saopen,
429111815Sphk	.d_close =	saclose,
430111815Sphk	.d_read =	physread,
431111815Sphk	.d_write =	physwrite,
432111815Sphk	.d_ioctl =	saioctl,
433111815Sphk	.d_strategy =	sastrategy,
434111815Sphk	.d_name =	"sa",
435126080Sphk	.d_flags =	D_TAPE | D_NEEDGIANT,
43639213Sgibbs};
43739213Sgibbs
43839213Sgibbsstatic int
439130585Sphksaopen(struct cdev *dev, int flags, int fmt, struct thread *td)
44039213Sgibbs{
44139213Sgibbs	struct cam_periph *periph;
44239213Sgibbs	struct sa_softc *softc;
44339213Sgibbs	int error;
44439213Sgibbs
445101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
446168752Sscottl	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
447168752Sscottl		return (ENXIO);
44853259Smjacob	}
449168752Sscottl
450168752Sscottl	cam_periph_lock(periph);
451168752Sscottl
45239213Sgibbs	softc = (struct sa_softc *)periph->softc;
45339213Sgibbs
45446962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
455191304Sed	    ("saopen(%s): softc=0x%x\n", devtoname(dev), softc->flags));
45639213Sgibbs
45743636Smjacob	if (SA_IS_CTRL(dev)) {
45843636Smjacob		softc->ctrl_mode = 1;
45953259Smjacob		cam_periph_unlock(periph);
46043636Smjacob		return (0);
46143636Smjacob	}
46243636Smjacob
463168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
464168752Sscottl		cam_periph_unlock(periph);
465168752Sscottl		cam_periph_release(periph);
466168752Sscottl		return (error);
467168752Sscottl	}
468168752Sscottl
46953259Smjacob	if (softc->flags & SA_FLAG_OPEN) {
47053259Smjacob		error = EBUSY;
47153259Smjacob	} else if (softc->flags & SA_FLAG_INVALID) {
47253259Smjacob		error = ENXIO;
47353259Smjacob	} else {
47453259Smjacob		/*
475154360Smjacob		 * Preserve whether this is a read_only open.
476154360Smjacob		 */
477154360Smjacob		softc->open_rdonly = (flags & O_RDWR) == O_RDONLY;
478154360Smjacob
479154360Smjacob		/*
48053259Smjacob		 * The function samount ensures media is loaded and ready.
48153259Smjacob		 * It also does a device RESERVE if the tape isn't yet mounted.
482154360Smjacob		 *
483154360Smjacob		 * If the mount fails and this was a non-blocking open,
484154360Smjacob		 * make this a 'open_pending_mount' action.
48553259Smjacob		 */
48653259Smjacob		error = samount(periph, flags, dev);
487154360Smjacob		if (error && (flags & O_NONBLOCK)) {
488154360Smjacob			softc->flags |= SA_FLAG_OPEN;
489154360Smjacob			softc->open_pending_mount = 1;
490168752Sscottl			cam_periph_unhold(periph);
491154360Smjacob			cam_periph_unlock(periph);
492154360Smjacob			return (0);
493154360Smjacob		}
49439213Sgibbs	}
49539213Sgibbs
49653259Smjacob	if (error) {
497168752Sscottl		cam_periph_unhold(periph);
498168752Sscottl		cam_periph_unlock(periph);
49953259Smjacob		cam_periph_release(periph);
500168752Sscottl		return (error);
50139213Sgibbs	}
502168752Sscottl
503168752Sscottl	saprevent(periph, PR_PREVENT);
504168752Sscottl	softc->flags |= SA_FLAG_OPEN;
505168752Sscottl
506168752Sscottl	cam_periph_unhold(periph);
50739213Sgibbs	cam_periph_unlock(periph);
50839213Sgibbs	return (error);
50939213Sgibbs}
51039213Sgibbs
51139213Sgibbsstatic int
512130585Sphksaclose(struct cdev *dev, int flag, int fmt, struct thread *td)
51339213Sgibbs{
51439213Sgibbs	struct	cam_periph *periph;
51539213Sgibbs	struct	sa_softc *softc;
516191304Sed	int	mode, error, writing, tmp;
51742009Smjacob	int	closedbits = SA_FLAG_OPEN;
51839213Sgibbs
51939213Sgibbs	mode = SAMODE(dev);
520101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
52139213Sgibbs	if (periph == NULL)
52239213Sgibbs		return (ENXIO);
52339213Sgibbs
524168752Sscottl	cam_periph_lock(periph);
525168752Sscottl
52639213Sgibbs	softc = (struct sa_softc *)periph->softc;
52739213Sgibbs
52846962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
529191304Sed	    ("saclose(%s): softc=0x%x\n", devtoname(dev), softc->flags));
53046962Smjacob
53146962Smjacob
532154360Smjacob	softc->open_rdonly = 0;
53343636Smjacob	if (SA_IS_CTRL(dev)) {
53443636Smjacob		softc->ctrl_mode = 0;
535168752Sscottl		cam_periph_unlock(periph);
53653259Smjacob		cam_periph_release(periph);
53743636Smjacob		return (0);
53843636Smjacob	}
53943636Smjacob
540154360Smjacob	if (softc->open_pending_mount) {
541154360Smjacob		softc->flags &= ~SA_FLAG_OPEN;
542154360Smjacob		softc->open_pending_mount = 0;
543168752Sscottl		cam_periph_unlock(periph);
544154360Smjacob		cam_periph_release(periph);
545154360Smjacob		return (0);
546154360Smjacob	}
547154360Smjacob
548168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO)) != 0) {
549168752Sscottl		cam_periph_unlock(periph);
550168752Sscottl		return (error);
551168752Sscottl	}
552168752Sscottl
55341906Smjacob	/*
55446962Smjacob	 * Were we writing the tape?
55541906Smjacob	 */
55646962Smjacob	writing = (softc->flags & SA_FLAG_TAPE_WRITTEN) != 0;
55746962Smjacob
55846962Smjacob	/*
55946962Smjacob	 * See whether or not we need to write filemarks. If this
56046962Smjacob	 * fails, we probably have to assume we've lost tape
56146962Smjacob	 * position.
56246962Smjacob	 */
56341906Smjacob	error = sacheckeod(periph);
56441906Smjacob	if (error) {
565164906Smjacob		xpt_print(periph->path,
566164906Smjacob		    "failed to write terminating filemark(s)\n");
56746962Smjacob		softc->flags |= SA_FLAG_TAPE_FROZEN;
56841906Smjacob	}
56939213Sgibbs
57041906Smjacob	/*
57141906Smjacob	 * Whatever we end up doing, allow users to eject tapes from here on.
57241906Smjacob	 */
57339213Sgibbs	saprevent(periph, PR_ALLOW);
57439213Sgibbs
57541906Smjacob	/*
57641906Smjacob	 * Decide how to end...
57741906Smjacob	 */
57853522Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
57953522Smjacob		closedbits |= SA_FLAG_TAPE_FROZEN;
58053522Smjacob	} else switch (mode) {
58139213Sgibbs	case SA_MODE_OFFLINE:
58246962Smjacob		/*
58346962Smjacob		 * An 'offline' close is an unconditional release of
58446962Smjacob		 * frozen && mount conditions, irrespective of whether
58546962Smjacob		 * these operations succeeded. The reason for this is
58646962Smjacob		 * to allow at least some kind of programmatic way
58746962Smjacob		 * around our state getting all fouled up. If somebody
58846962Smjacob		 * issues an 'offline' command, that will be allowed
58946962Smjacob		 * to clear state.
59046962Smjacob		 */
59146962Smjacob		(void) sarewind(periph);
59246962Smjacob		(void) saloadunload(periph, FALSE);
59346962Smjacob		closedbits |= SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;
59439213Sgibbs		break;
59541906Smjacob	case SA_MODE_REWIND:
59646962Smjacob		/*
59746962Smjacob		 * If the rewind fails, return an error- if anyone cares,
59846962Smjacob		 * but not overwriting any previous error.
59946962Smjacob		 *
60046962Smjacob		 * We don't clear the notion of mounted here, but we do
60146962Smjacob		 * clear the notion of frozen if we successfully rewound.
60246962Smjacob		 */
60346962Smjacob		tmp = sarewind(periph);
60446962Smjacob		if (tmp) {
60546962Smjacob			if (error != 0)
60646962Smjacob				error = tmp;
60746962Smjacob		} else {
60846962Smjacob			closedbits |= SA_FLAG_TAPE_FROZEN;
60946962Smjacob		}
61041906Smjacob		break;
61139213Sgibbs	case SA_MODE_NOREWIND:
61241906Smjacob		/*
61341906Smjacob		 * If we're not rewinding/unloading the tape, find out
61441906Smjacob		 * whether we need to back up over one of two filemarks
61541906Smjacob		 * we wrote (if we wrote two filemarks) so that appends
61641906Smjacob		 * from this point on will be sane.
61741906Smjacob		 */
61846962Smjacob		if (error == 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {
61946962Smjacob			tmp = saspace(periph, -1, SS_FILEMARKS);
62046962Smjacob			if (tmp) {
621164906Smjacob				xpt_print(periph->path, "unable to backspace "
622164906Smjacob				    "over one of double filemarks at end of "
623164906Smjacob				    "tape\n");
624164906Smjacob				xpt_print(periph->path, "it is possible that "
625164906Smjacob				    "this device needs a SA_QUIRK_1FM quirk set"
626164906Smjacob				    "for it\n");
62746962Smjacob				softc->flags |= SA_FLAG_TAPE_FROZEN;
62841906Smjacob			}
62941906Smjacob		}
63039213Sgibbs		break;
63146962Smjacob	default:
632164906Smjacob		xpt_print(periph->path, "unknown mode 0x%x in saclose\n", mode);
63346962Smjacob		/* NOTREACHED */
63446962Smjacob		break;
63539213Sgibbs	}
63639213Sgibbs
63741906Smjacob	/*
63841948Smjacob	 * We wish to note here that there are no more filemarks to be written.
63941906Smjacob	 */
64041906Smjacob	softc->filemarks = 0;
64141948Smjacob	softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
64241906Smjacob
64341906Smjacob	/*
64441906Smjacob	 * And we are no longer open for business.
64541906Smjacob	 */
64642009Smjacob	softc->flags &= ~closedbits;
64746962Smjacob
64846962Smjacob	/*
64946962Smjacob	 * Inform users if tape state if frozen....
65046962Smjacob	 */
65146962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
652164906Smjacob		xpt_print(periph->path, "tape is now frozen- use an OFFLINE, "
653164906Smjacob		    "REWIND or MTEOM command to clear this state.\n");
65446962Smjacob	}
65539213Sgibbs
65653259Smjacob	/* release the device if it is no longer mounted */
65753259Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0)
65853259Smjacob		sareservereleaseunit(periph, FALSE);
65939213Sgibbs
660168752Sscottl	cam_periph_unhold(periph);
66139213Sgibbs	cam_periph_unlock(periph);
66239213Sgibbs	cam_periph_release(periph);
66339213Sgibbs
66446962Smjacob	return (error);
66539213Sgibbs}
66639213Sgibbs
66739213Sgibbs/*
66839213Sgibbs * Actually translate the requested transfer into one the physical driver
66939213Sgibbs * can understand.  The transfer is described by a buf and will include
67039213Sgibbs * only one physical transfer.
67139213Sgibbs */
67239213Sgibbsstatic void
67359249Sphksastrategy(struct bio *bp)
67439213Sgibbs{
67539213Sgibbs	struct cam_periph *periph;
67639213Sgibbs	struct sa_softc *softc;
67739213Sgibbs
67876362Sphk	bp->bio_resid = bp->bio_bcount;
67959249Sphk	if (SA_IS_CTRL(bp->bio_dev)) {
68076362Sphk		biofinish(bp, NULL, EINVAL);
68176362Sphk		return;
68243636Smjacob	}
683101940Snjl	periph = (struct cam_periph *)bp->bio_dev->si_drv1;
68439213Sgibbs	if (periph == NULL) {
68576362Sphk		biofinish(bp, NULL, ENXIO);
68676362Sphk		return;
68739213Sgibbs	}
688168752Sscottl	cam_periph_lock(periph);
689168752Sscottl
69039213Sgibbs	softc = (struct sa_softc *)periph->softc;
69139213Sgibbs
69240603Sken	if (softc->flags & SA_FLAG_INVALID) {
693168752Sscottl		cam_periph_unlock(periph);
69476362Sphk		biofinish(bp, NULL, ENXIO);
69576362Sphk		return;
69640603Sken	}
69740603Sken
69846962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
699168752Sscottl		cam_periph_unlock(periph);
70076362Sphk		biofinish(bp, NULL, EPERM);
70176362Sphk		return;
70246962Smjacob	}
70346962Smjacob
704154360Smjacob	/*
705154360Smjacob	 * This should actually never occur as the write(2)
706154360Smjacob	 * system call traps attempts to write to a read-only
707154360Smjacob	 * file descriptor.
708154360Smjacob	 */
709154360Smjacob	if (bp->bio_cmd == BIO_WRITE && softc->open_rdonly) {
710168752Sscottl		cam_periph_unlock(periph);
711154360Smjacob		biofinish(bp, NULL, EBADF);
712154360Smjacob		return;
713154360Smjacob	}
714154360Smjacob
715154360Smjacob	if (softc->open_pending_mount) {
716154360Smjacob		int error = samount(periph, 0, bp->bio_dev);
717154360Smjacob		if (error) {
718168752Sscottl			cam_periph_unlock(periph);
719154360Smjacob			biofinish(bp, NULL, ENXIO);
720154360Smjacob			return;
721154360Smjacob		}
722154360Smjacob		saprevent(periph, PR_PREVENT);
723154360Smjacob		softc->open_pending_mount = 0;
724154360Smjacob	}
725154360Smjacob
726154360Smjacob
72739213Sgibbs	/*
728154360Smjacob	 * If it's a null transfer, return immediately
72939213Sgibbs	 */
73076362Sphk	if (bp->bio_bcount == 0) {
731168752Sscottl		cam_periph_unlock(periph);
73276362Sphk		biodone(bp);
73376362Sphk		return;
73476362Sphk	}
73539213Sgibbs
73639213Sgibbs	/* valid request?  */
73739213Sgibbs	if (softc->flags & SA_FLAG_FIXED) {
73839213Sgibbs		/*
73939213Sgibbs		 * Fixed block device.  The byte count must
74039213Sgibbs		 * be a multiple of our block size.
74139213Sgibbs		 */
74242716Smjacob		if (((softc->blk_mask != ~0) &&
74359249Sphk		    ((bp->bio_bcount & softc->blk_mask) != 0)) ||
74442716Smjacob		    ((softc->blk_mask == ~0) &&
74559249Sphk		    ((bp->bio_bcount % softc->min_blk) != 0))) {
746164906Smjacob			xpt_print(periph->path, "Invalid request.  Fixed block "
747164906Smjacob			    "device requests must be a multiple of %d bytes\n",
748164906Smjacob			    softc->min_blk);
749168752Sscottl			cam_periph_unlock(periph);
75076362Sphk			biofinish(bp, NULL, EINVAL);
75176362Sphk			return;
75239213Sgibbs		}
75359249Sphk	} else if ((bp->bio_bcount > softc->max_blk) ||
75459249Sphk		   (bp->bio_bcount < softc->min_blk) ||
75559249Sphk		   (bp->bio_bcount & softc->blk_mask) != 0) {
75639213Sgibbs
75739213Sgibbs		xpt_print_path(periph->path);
758164906Smjacob		printf("Invalid request.  Variable block "
759164906Smjacob		    "device requests must be ");
76039213Sgibbs		if (softc->blk_mask != 0) {
76142716Smjacob			printf("a multiple of %d ", (0x1 << softc->blk_gran));
76239213Sgibbs		}
76342716Smjacob		printf("between %d and %d bytes\n", softc->min_blk,
76442716Smjacob		    softc->max_blk);
765168752Sscottl		cam_periph_unlock(periph);
76676362Sphk		biofinish(bp, NULL, EINVAL);
76776362Sphk		return;
76839213Sgibbs        }
76939213Sgibbs
77039213Sgibbs	/*
77142716Smjacob	 * Place it at the end of the queue.
77239213Sgibbs	 */
77359249Sphk	bioq_insert_tail(&softc->bio_queue, bp);
77446962Smjacob	softc->queue_count++;
775115660Smjacob#if	0
776115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
777115660Smjacob	    ("sastrategy: queuing a %ld %s byte %s\n", bp->bio_bcount,
778115660Smjacob 	    (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",
779115660Smjacob	    (bp->bio_cmd == BIO_READ)? "read" : "write"));
780115660Smjacob#endif
781115660Smjacob	if (softc->queue_count > 1) {
782115660Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
783115660Smjacob		    ("sastrategy: queue count now %d\n", softc->queue_count));
784115660Smjacob	}
78539213Sgibbs
78639213Sgibbs	/*
78739213Sgibbs	 * Schedule ourselves for performing the work.
78839213Sgibbs	 */
78942716Smjacob	xpt_schedule(periph, 1);
790168752Sscottl	cam_periph_unlock(periph);
79139213Sgibbs
79239213Sgibbs	return;
79339213Sgibbs}
79439213Sgibbs
795154360Smjacob
796154360Smjacob#define	PENDING_MOUNT_CHECK(softc, periph, dev)		\
797154360Smjacob	if (softc->open_pending_mount) {		\
798154360Smjacob		error = samount(periph, 0, dev);	\
799154360Smjacob		if (error) {				\
800154360Smjacob			break;				\
801154360Smjacob		}					\
802154360Smjacob		saprevent(periph, PR_PREVENT);		\
803154360Smjacob		softc->open_pending_mount = 0;		\
804154360Smjacob	}
805154360Smjacob
80639213Sgibbsstatic int
807130585Sphksaioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
80839213Sgibbs{
80939213Sgibbs	struct cam_periph *periph;
81039213Sgibbs	struct sa_softc *softc;
81148520Speter	scsi_space_code spaceop;
81243636Smjacob	int didlockperiph = 0;
81339213Sgibbs	int mode;
81453259Smjacob	int error = 0;
81539213Sgibbs
81639213Sgibbs	mode = SAMODE(dev);
81748520Speter	error = 0;		/* shut up gcc */
81848520Speter	spaceop = 0;		/* shut up gcc */
81939213Sgibbs
820101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
82139213Sgibbs	if (periph == NULL)
82239213Sgibbs		return (ENXIO);
82339213Sgibbs
824168752Sscottl	cam_periph_lock(periph);
82539213Sgibbs	softc = (struct sa_softc *)periph->softc;
82639213Sgibbs
82739213Sgibbs	/*
82843636Smjacob	 * Check for control mode accesses. We allow MTIOCGET and
82943636Smjacob	 * MTIOCERRSTAT (but need to be the only one open in order
83043636Smjacob	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY
83143636Smjacob	 * and MTCOMP (but need to be the only one accessing this
83243636Smjacob	 * device to run those).
83343636Smjacob	 */
83443636Smjacob
83543636Smjacob	if (SA_IS_CTRL(dev)) {
83643636Smjacob		switch (cmd) {
83746962Smjacob		case MTIOCGETEOTMODEL:
83843636Smjacob		case MTIOCGET:
83943636Smjacob			break;
84043636Smjacob		case MTIOCERRSTAT:
84143636Smjacob			/*
84243636Smjacob			 * If the periph isn't already locked, lock it
84343636Smjacob			 * so our MTIOCERRSTAT can reset latched error stats.
84443636Smjacob			 *
84543636Smjacob			 * If the periph is already locked, skip it because
84643636Smjacob			 * we're just getting status and it'll be up to the
84743636Smjacob			 * other thread that has this device open to do
84843636Smjacob			 * an MTIOCERRSTAT that would clear latched status.
84943636Smjacob			 */
85043636Smjacob			if ((periph->flags & CAM_PERIPH_LOCKED) == 0) {
851168752Sscottl				error = cam_periph_hold(periph, PRIBIO|PCATCH);
852168752Sscottl				 if (error != 0)
85343636Smjacob					return (error);
85443636Smjacob				didlockperiph = 1;
85543636Smjacob			}
85643636Smjacob			break;
85743636Smjacob
858145050Smjacob		case MTIOCTOP:
859145050Smjacob		{
860145050Smjacob			struct mtop *mt = (struct mtop *) arg;
861145050Smjacob
862145050Smjacob			/*
863145050Smjacob			 * Check to make sure it's an OP we can perform
864145050Smjacob			 * with no media inserted.
865145050Smjacob			 */
866145050Smjacob			switch (mt->mt_op) {
867145050Smjacob			case MTSETBSIZ:
868145050Smjacob			case MTSETDNSTY:
869145050Smjacob			case MTCOMP:
870145050Smjacob				mt = NULL;
871145050Smjacob				/* FALLTHROUGH */
872145050Smjacob			default:
873145050Smjacob				break;
874145050Smjacob			}
875145050Smjacob			if (mt != NULL) {
876145050Smjacob				break;
877145050Smjacob			}
878145050Smjacob			/* FALLTHROUGH */
879145050Smjacob		}
88046962Smjacob		case MTIOCSETEOTMODEL:
88143636Smjacob			/*
88243636Smjacob			 * We need to acquire the peripheral here rather
88343636Smjacob			 * than at open time because we are sharing writable
88443636Smjacob			 * access to data structures.
88543636Smjacob			 */
886168752Sscottl			error = cam_periph_hold(periph, PRIBIO|PCATCH);
887168752Sscottl			if (error != 0)
88843636Smjacob				return (error);
88943636Smjacob			didlockperiph = 1;
89043636Smjacob			break;
89143636Smjacob
89243636Smjacob		default:
89343636Smjacob			return (EINVAL);
89443636Smjacob		}
89543636Smjacob	}
89643636Smjacob
89743636Smjacob	/*
89839213Sgibbs	 * Find the device that the user is talking about
89939213Sgibbs	 */
90039213Sgibbs	switch (cmd) {
90139213Sgibbs	case MTIOCGET:
90239213Sgibbs	{
90339213Sgibbs		struct mtget *g = (struct mtget *)arg;
90439213Sgibbs
90553259Smjacob		/*
90653259Smjacob		 * If this isn't the control mode device, actually go out
90753259Smjacob		 * and ask the drive again what it's set to.
90853259Smjacob		 */
909154360Smjacob		if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) {
91053259Smjacob			u_int8_t write_protect;
91153259Smjacob			int comp_enabled, comp_supported;
91253259Smjacob			error = sagetparams(periph, SA_PARAM_ALL,
91353259Smjacob			    &softc->media_blksize, &softc->media_density,
91453259Smjacob			    &softc->media_numblks, &softc->buffer_mode,
91553259Smjacob			    &write_protect, &softc->speed, &comp_supported,
91653259Smjacob			    &comp_enabled, &softc->comp_algorithm, NULL);
91753259Smjacob			if (error)
91853259Smjacob				break;
91953259Smjacob			if (write_protect)
92053259Smjacob				softc->flags |= SA_FLAG_TAPE_WP;
92153259Smjacob			else
92253259Smjacob				softc->flags &= ~SA_FLAG_TAPE_WP;
92353259Smjacob			softc->flags &= ~(SA_FLAG_COMP_SUPP|
92453259Smjacob			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);
92553259Smjacob			if (comp_supported) {
92653259Smjacob				if (softc->saved_comp_algorithm == 0)
92753259Smjacob					softc->saved_comp_algorithm =
92853259Smjacob					    softc->comp_algorithm;
92953259Smjacob				softc->flags |= SA_FLAG_COMP_SUPP;
93053259Smjacob				if (comp_enabled)
93153259Smjacob					softc->flags |= SA_FLAG_COMP_ENABLED;
93253259Smjacob			} else
93353259Smjacob				softc->flags |= SA_FLAG_COMP_UNSUPP;
93453259Smjacob		}
93539213Sgibbs		bzero(g, sizeof(struct mtget));
93641948Smjacob		g->mt_type = MT_ISAR;
93739213Sgibbs		if (softc->flags & SA_FLAG_COMP_UNSUPP) {
93839213Sgibbs			g->mt_comp = MT_COMP_UNSUPP;
93939213Sgibbs			g->mt_comp0 = MT_COMP_UNSUPP;
94039213Sgibbs			g->mt_comp1 = MT_COMP_UNSUPP;
94139213Sgibbs			g->mt_comp2 = MT_COMP_UNSUPP;
94239213Sgibbs			g->mt_comp3 = MT_COMP_UNSUPP;
94339213Sgibbs		} else {
94446962Smjacob			if ((softc->flags & SA_FLAG_COMP_ENABLED) == 0) {
94546962Smjacob				g->mt_comp = MT_COMP_DISABLED;
94646962Smjacob			} else {
94746962Smjacob				g->mt_comp = softc->comp_algorithm;
94846962Smjacob			}
94939213Sgibbs			g->mt_comp0 = softc->comp_algorithm;
95039213Sgibbs			g->mt_comp1 = softc->comp_algorithm;
95139213Sgibbs			g->mt_comp2 = softc->comp_algorithm;
95239213Sgibbs			g->mt_comp3 = softc->comp_algorithm;
95339213Sgibbs		}
95446962Smjacob		g->mt_density = softc->media_density;
95539213Sgibbs		g->mt_density0 = softc->media_density;
95639213Sgibbs		g->mt_density1 = softc->media_density;
95739213Sgibbs		g->mt_density2 = softc->media_density;
95839213Sgibbs		g->mt_density3 = softc->media_density;
95946962Smjacob		g->mt_blksiz = softc->media_blksize;
96039213Sgibbs		g->mt_blksiz0 = softc->media_blksize;
96139213Sgibbs		g->mt_blksiz1 = softc->media_blksize;
96239213Sgibbs		g->mt_blksiz2 = softc->media_blksize;
96339213Sgibbs		g->mt_blksiz3 = softc->media_blksize;
96443636Smjacob		g->mt_fileno = softc->fileno;
96543636Smjacob		g->mt_blkno = softc->blkno;
96643636Smjacob		g->mt_dsreg = (short) softc->dsreg;
96771268Smjacob		/*
96871268Smjacob		 * Yes, we know that this is likely to overflow
96971268Smjacob		 */
97071268Smjacob		if (softc->last_resid_was_io) {
97171268Smjacob			if ((g->mt_resid = (short) softc->last_io_resid) != 0) {
97271268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
97371268Smjacob					softc->last_io_resid = 0;
97471268Smjacob				}
97571268Smjacob			}
97671268Smjacob		} else {
97771268Smjacob			if ((g->mt_resid = (short)softc->last_ctl_resid) != 0) {
97871268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
97971268Smjacob					softc->last_ctl_resid = 0;
98071268Smjacob				}
98171268Smjacob			}
98271268Smjacob		}
98339213Sgibbs		error = 0;
98439213Sgibbs		break;
98539213Sgibbs	}
98641948Smjacob	case MTIOCERRSTAT:
98741948Smjacob	{
98841948Smjacob		struct scsi_tape_errors *sep =
98941948Smjacob		    &((union mterrstat *)arg)->scsi_errstat;
99041948Smjacob
99141948Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
99241948Smjacob		    ("saioctl: MTIOCERRSTAT\n"));
99341948Smjacob
99441948Smjacob		bzero(sep, sizeof(*sep));
99541948Smjacob		sep->io_resid = softc->last_io_resid;
99641948Smjacob		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,
99741948Smjacob		    sizeof (sep->io_sense));
99842009Smjacob		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,
99942009Smjacob		    sizeof (sep->io_cdb));
100042009Smjacob		sep->ctl_resid = softc->last_ctl_resid;
100141948Smjacob		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,
100241948Smjacob		    sizeof (sep->ctl_sense));
100342009Smjacob		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
100442009Smjacob		    sizeof (sep->ctl_cdb));
100543636Smjacob
1006154360Smjacob		if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) ||
1007154360Smjacob		    didlockperiph)
100843636Smjacob			bzero((caddr_t) &softc->errinfo,
100943636Smjacob			    sizeof (softc->errinfo));
101041948Smjacob		error = 0;
101141948Smjacob		break;
101241948Smjacob	}
101339213Sgibbs	case MTIOCTOP:
101439213Sgibbs	{
101539213Sgibbs		struct mtop *mt;
101639213Sgibbs		int    count;
101739213Sgibbs
1018154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
1019154360Smjacob
102039213Sgibbs		mt = (struct mtop *)arg;
102139213Sgibbs
1022154360Smjacob
102339213Sgibbs		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
102439213Sgibbs			 ("saioctl: op=0x%x count=0x%x\n",
102539213Sgibbs			  mt->mt_op, mt->mt_count));
102639213Sgibbs
102739213Sgibbs		count = mt->mt_count;
102839213Sgibbs		switch (mt->mt_op) {
102941906Smjacob		case MTWEOF:	/* write an end-of-file marker */
103068114Smjacob			/*
103168114Smjacob			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN
103268114Smjacob			 * flag because by keeping track of filemarks
103368114Smjacob			 * we have last written we know ehether or not
103468114Smjacob			 * we need to write more when we close the device.
103568114Smjacob			 */
103641906Smjacob			error = sawritefilemarks(periph, count, FALSE);
103739213Sgibbs			break;
103842009Smjacob		case MTWSS:	/* write a setmark */
103942009Smjacob			error = sawritefilemarks(periph, count, TRUE);
104042009Smjacob			break;
104139213Sgibbs		case MTBSR:	/* backward space record */
104239213Sgibbs		case MTFSR:	/* forward space record */
104339213Sgibbs		case MTBSF:	/* backward space file */
104439213Sgibbs		case MTFSF:	/* forward space file */
104542009Smjacob		case MTBSS:	/* backward space setmark */
104642009Smjacob		case MTFSS:	/* forward space setmark */
104739213Sgibbs		case MTEOD:	/* space to end of recorded medium */
104839213Sgibbs		{
104939213Sgibbs			int nmarks;
105039213Sgibbs
105148520Speter			spaceop = SS_FILEMARKS;
105239213Sgibbs			nmarks = softc->filemarks;
105339213Sgibbs			error = sacheckeod(periph);
105441906Smjacob			if (error) {
1055164906Smjacob				xpt_print(periph->path,
1056164906Smjacob				    "EOD check prior to spacing failed\n");
105741906Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
105841906Smjacob				break;
105941906Smjacob			}
106039213Sgibbs			nmarks -= softc->filemarks;
106142009Smjacob			switch(mt->mt_op) {
106242009Smjacob			case MTBSR:
106339213Sgibbs				count = -count;
106442009Smjacob				/* FALLTHROUGH */
106542009Smjacob			case MTFSR:
106639213Sgibbs				spaceop = SS_BLOCKS;
106742009Smjacob				break;
106842009Smjacob			case MTBSF:
106942009Smjacob				count = -count;
107042009Smjacob				/* FALLTHROUGH */
107142009Smjacob			case MTFSF:
107242009Smjacob				break;
107342009Smjacob			case MTBSS:
107442009Smjacob				count = -count;
107542009Smjacob				/* FALLTHROUGH */
107642009Smjacob			case MTFSS:
107742009Smjacob				spaceop = SS_SETMARKS;
107842009Smjacob				break;
107942009Smjacob			case MTEOD:
108039213Sgibbs				spaceop = SS_EOD;
108139213Sgibbs				count = 0;
108239213Sgibbs				nmarks = 0;
108342009Smjacob				break;
108442009Smjacob			default:
108542009Smjacob				error = EINVAL;
108642009Smjacob				break;
108739213Sgibbs			}
108842009Smjacob			if (error)
108942009Smjacob				break;
109039213Sgibbs
109139213Sgibbs			nmarks = softc->filemarks;
109242009Smjacob			/*
109342009Smjacob			 * XXX: Why are we checking again?
109442009Smjacob			 */
109539213Sgibbs			error = sacheckeod(periph);
109642009Smjacob			if (error)
109742009Smjacob				break;
109839213Sgibbs			nmarks -= softc->filemarks;
109942009Smjacob			error = saspace(periph, count - nmarks, spaceop);
110041906Smjacob			/*
110141906Smjacob			 * At this point, clear that we've written the tape
110241906Smjacob			 * and that we've written any filemarks. We really
110341906Smjacob			 * don't know what the applications wishes to do next-
110441906Smjacob			 * the sacheckeod's will make sure we terminated the
110541906Smjacob			 * tape correctly if we'd been writing, but the next
110641906Smjacob			 * action the user application takes will set again
110741906Smjacob			 * whether we need to write filemarks.
110841906Smjacob			 */
110946962Smjacob			softc->flags &=
111046962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
111141906Smjacob			softc->filemarks = 0;
111239213Sgibbs			break;
111339213Sgibbs		}
111439213Sgibbs		case MTREW:	/* rewind */
1115154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
111641906Smjacob			(void) sacheckeod(periph);
111739213Sgibbs			error = sarewind(periph);
111841906Smjacob			/* see above */
111942009Smjacob			softc->flags &=
112046962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
112182575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
112241906Smjacob			softc->filemarks = 0;
112339213Sgibbs			break;
112439213Sgibbs		case MTERASE:	/* erase */
1125154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
112639213Sgibbs			error = saerase(periph, count);
112746962Smjacob			softc->flags &=
112846962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
112982575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
113039213Sgibbs			break;
113139213Sgibbs		case MTRETENS:	/* re-tension tape */
1132154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
113339213Sgibbs			error = saretension(periph);
113446962Smjacob			softc->flags &=
113546962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
113682575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
113739213Sgibbs			break;
113839213Sgibbs		case MTOFFL:	/* rewind and put the drive offline */
113941906Smjacob
1140154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1141154360Smjacob
114241906Smjacob			(void) sacheckeod(periph);
114341906Smjacob			/* see above */
114441906Smjacob			softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
114541906Smjacob			softc->filemarks = 0;
114641906Smjacob
114746962Smjacob			error = sarewind(periph);
114853522Smjacob			/* clear the frozen flag anyway */
114953522Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
115046962Smjacob
115139213Sgibbs			/*
115253522Smjacob			 * Be sure to allow media removal before ejecting.
115339213Sgibbs			 */
115446962Smjacob
115539213Sgibbs			saprevent(periph, PR_ALLOW);
115653522Smjacob			if (error == 0) {
115743636Smjacob				error = saloadunload(periph, FALSE);
115853522Smjacob				if (error == 0) {
115953522Smjacob					softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
116053522Smjacob				}
116153522Smjacob			}
116246962Smjacob			break;
116339213Sgibbs
116439213Sgibbs		case MTNOP:	/* no operation, sets status only */
116539213Sgibbs		case MTCACHE:	/* enable controller cache */
116639213Sgibbs		case MTNOCACHE:	/* disable controller cache */
116739213Sgibbs			error = 0;
116839213Sgibbs			break;
116946962Smjacob
117039213Sgibbs		case MTSETBSIZ:	/* Set block size for device */
117139213Sgibbs
1172154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1173154360Smjacob
117439213Sgibbs			error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
117542563Smjacob					    0, 0, 0);
117641674Smjacob			if (error == 0) {
117741906Smjacob				softc->last_media_blksize =
117841906Smjacob				    softc->media_blksize;
117941674Smjacob				softc->media_blksize = count;
118041674Smjacob				if (count) {
118141674Smjacob					softc->flags |= SA_FLAG_FIXED;
118241674Smjacob					if (powerof2(count)) {
118341674Smjacob						softc->blk_shift =
118441674Smjacob						    ffs(count) - 1;
118541674Smjacob						softc->blk_mask = count - 1;
118641674Smjacob					} else {
118741674Smjacob						softc->blk_mask = ~0;
118841674Smjacob						softc->blk_shift = 0;
118941674Smjacob					}
119041906Smjacob					/*
119141906Smjacob					 * Make the user's desire 'persistent'.
119241906Smjacob					 */
119341906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
119441906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
119541674Smjacob				} else {
119641674Smjacob					softc->flags &= ~SA_FLAG_FIXED;
119741674Smjacob					if (softc->max_blk == 0) {
119841674Smjacob						softc->max_blk = ~0;
119941674Smjacob					}
120041674Smjacob					softc->blk_shift = 0;
120141674Smjacob					if (softc->blk_gran != 0) {
120241674Smjacob						softc->blk_mask =
120341674Smjacob						    softc->blk_gran - 1;
120441674Smjacob					} else {
120541674Smjacob						softc->blk_mask = 0;
120641674Smjacob					}
120741906Smjacob					/*
120841906Smjacob					 * Make the user's desire 'persistent'.
120941906Smjacob					 */
121041906Smjacob					softc->quirks |= SA_QUIRK_VARIABLE;
121141906Smjacob					softc->quirks &= ~SA_QUIRK_FIXED;
121241674Smjacob				}
121341674Smjacob			}
121439213Sgibbs			break;
121539213Sgibbs		case MTSETDNSTY:	/* Set density for device and mode */
1216154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1217154360Smjacob
121839213Sgibbs			if (count > UCHAR_MAX) {
121939213Sgibbs				error = EINVAL;
122039213Sgibbs				break;
122139213Sgibbs			} else {
122239213Sgibbs				error = sasetparams(periph, SA_PARAM_DENSITY,
122342563Smjacob						    0, count, 0, 0);
122439213Sgibbs			}
122539213Sgibbs			break;
122639213Sgibbs		case MTCOMP:	/* enable compression */
1227154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
122839213Sgibbs			/*
122939213Sgibbs			 * Some devices don't support compression, and
123039213Sgibbs			 * don't like it if you ask them for the
123139213Sgibbs			 * compression page.
123239213Sgibbs			 */
123343636Smjacob			if ((softc->quirks & SA_QUIRK_NOCOMP) ||
123443636Smjacob			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {
123539213Sgibbs				error = ENODEV;
123639213Sgibbs				break;
123739213Sgibbs			}
123839213Sgibbs			error = sasetparams(periph, SA_PARAM_COMPRESSION,
123954099Smjacob			    0, 0, count, SF_NO_PRINT);
124039213Sgibbs			break;
124139213Sgibbs		default:
124239213Sgibbs			error = EINVAL;
124339213Sgibbs		}
124439213Sgibbs		break;
124539213Sgibbs	}
124639213Sgibbs	case MTIOCIEOT:
124739213Sgibbs	case MTIOCEEOT:
124839213Sgibbs		error = 0;
124939213Sgibbs		break;
125041918Smjacob	case MTIOCRDSPOS:
1251154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
125241918Smjacob		error = sardpos(periph, 0, (u_int32_t *) arg);
125341918Smjacob		break;
125441918Smjacob	case MTIOCRDHPOS:
1255154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
125641918Smjacob		error = sardpos(periph, 1, (u_int32_t *) arg);
125741918Smjacob		break;
125841918Smjacob	case MTIOCSLOCATE:
1259154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
126041918Smjacob		error = sasetpos(periph, 0, (u_int32_t *) arg);
126141918Smjacob		break;
126241918Smjacob	case MTIOCHLOCATE:
1263154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
126441918Smjacob		error = sasetpos(periph, 1, (u_int32_t *) arg);
126541918Smjacob		break;
126646962Smjacob	case MTIOCGETEOTMODEL:
126746962Smjacob		error = 0;
126846962Smjacob		if (softc->quirks & SA_QUIRK_1FM)
126946962Smjacob			mode = 1;
127046962Smjacob		else
127146962Smjacob			mode = 2;
127246962Smjacob		*((u_int32_t *) arg) = mode;
127346962Smjacob		break;
127446962Smjacob	case MTIOCSETEOTMODEL:
127546962Smjacob		error = 0;
127646962Smjacob		switch (*((u_int32_t *) arg)) {
127746962Smjacob		case 1:
127846962Smjacob			softc->quirks &= ~SA_QUIRK_2FM;
127946962Smjacob			softc->quirks |= SA_QUIRK_1FM;
128046962Smjacob			break;
128146962Smjacob		case 2:
128246962Smjacob			softc->quirks &= ~SA_QUIRK_1FM;
128346962Smjacob			softc->quirks |= SA_QUIRK_2FM;
128446962Smjacob			break;
128546962Smjacob		default:
128646962Smjacob			error = EINVAL;
128746962Smjacob			break;
128846962Smjacob		}
128946962Smjacob		break;
129039213Sgibbs	default:
129139213Sgibbs		error = cam_periph_ioctl(periph, cmd, arg, saerror);
129239213Sgibbs		break;
129339213Sgibbs	}
129471268Smjacob
129571268Smjacob	/*
129671268Smjacob	 * Check to see if we cleared a frozen state
129771268Smjacob	 */
129871268Smjacob	if (error == 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {
129971268Smjacob		switch(cmd) {
130071268Smjacob		case MTIOCRDSPOS:
130171268Smjacob		case MTIOCRDHPOS:
130271268Smjacob		case MTIOCSLOCATE:
130371268Smjacob		case MTIOCHLOCATE:
130471268Smjacob			softc->fileno = (daddr_t) -1;
130571268Smjacob			softc->blkno = (daddr_t) -1;
130671268Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
1307164906Smjacob			xpt_print(periph->path,
1308164906Smjacob			    "tape state now unfrozen.\n");
130971268Smjacob			break;
131071268Smjacob		default:
131171268Smjacob			break;
131271268Smjacob		}
131371268Smjacob	}
131443636Smjacob	if (didlockperiph) {
1315168752Sscottl		cam_periph_unhold(periph);
131643636Smjacob	}
1317168752Sscottl	cam_periph_unlock(periph);
131839213Sgibbs	return (error);
131939213Sgibbs}
132039213Sgibbs
132139213Sgibbsstatic void
132239213Sgibbssainit(void)
132339213Sgibbs{
132439213Sgibbs	cam_status status;
132539213Sgibbs
132639213Sgibbs	/*
132739213Sgibbs	 * Install a global async callback.
132839213Sgibbs	 */
1329169605Sscottl	status = xpt_register_async(AC_FOUND_DEVICE, saasync, NULL, NULL);
133039213Sgibbs
133139213Sgibbs	if (status != CAM_REQ_CMP) {
133239213Sgibbs		printf("sa: Failed to attach master async callback "
133339213Sgibbs		       "due to status 0x%x!\n", status);
133439213Sgibbs	}
133539213Sgibbs}
133639213Sgibbs
133739213Sgibbsstatic void
133840603Skensaoninvalidate(struct cam_periph *periph)
133940603Sken{
134040603Sken	struct sa_softc *softc;
134140603Sken
134240603Sken	softc = (struct sa_softc *)periph->softc;
134340603Sken
134440603Sken	/*
134540603Sken	 * De-register any async callbacks.
134640603Sken	 */
1347169605Sscottl	xpt_register_async(0, saasync, periph, periph->path);
134840603Sken
134940603Sken	softc->flags |= SA_FLAG_INVALID;
135040603Sken
135140603Sken	/*
135240603Sken	 * Return all queued I/O with ENXIO.
135340603Sken	 * XXX Handle any transactions queued to the card
135440603Sken	 *     with XPT_ABORT_CCB.
135540603Sken	 */
1356112946Sphk	bioq_flush(&softc->bio_queue, NULL, ENXIO);
135746962Smjacob	softc->queue_count = 0;
135840603Sken
1359164906Smjacob	xpt_print(periph->path, "lost device\n");
136040603Sken
136140603Sken}
136240603Sken
136340603Skenstatic void
136439213Sgibbssacleanup(struct cam_periph *periph)
136539213Sgibbs{
136640603Sken	struct sa_softc *softc;
136753259Smjacob	int i;
136840603Sken
136940603Sken	softc = (struct sa_softc *)periph->softc;
137040603Sken
1371187028Strasz	xpt_print(periph->path, "removing device entry\n");
1372112006Sphk	devstat_remove_entry(softc->device_stats);
1373187028Strasz	cam_periph_unlock(periph);
137453259Smjacob	destroy_dev(softc->devs.ctl_dev);
137553259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
137653259Smjacob		destroy_dev(softc->devs.mode_devs[i].r_dev);
137753259Smjacob		destroy_dev(softc->devs.mode_devs[i].nr_dev);
137853259Smjacob		destroy_dev(softc->devs.mode_devs[i].er_dev);
137953259Smjacob	}
1380187028Strasz	cam_periph_lock(periph);
1381147723Savatar	free(softc, M_SCSISA);
138239213Sgibbs}
138339213Sgibbs
138439213Sgibbsstatic void
138539213Sgibbssaasync(void *callback_arg, u_int32_t code,
138639213Sgibbs	struct cam_path *path, void *arg)
138739213Sgibbs{
138839213Sgibbs	struct cam_periph *periph;
138939213Sgibbs
139039213Sgibbs	periph = (struct cam_periph *)callback_arg;
139139213Sgibbs	switch (code) {
139239213Sgibbs	case AC_FOUND_DEVICE:
139339213Sgibbs	{
139439213Sgibbs		struct ccb_getdev *cgd;
139539213Sgibbs		cam_status status;
139639213Sgibbs
139739213Sgibbs		cgd = (struct ccb_getdev *)arg;
139879177Smjacob		if (cgd == NULL)
139979177Smjacob			break;
140039213Sgibbs
1401195534Sscottl		if (cgd->protocol != PROTO_SCSI)
1402195534Sscottl			break;
1403195534Sscottl
140456148Smjacob		if (SID_TYPE(&cgd->inq_data) != T_SEQUENTIAL)
140539213Sgibbs			break;
140639213Sgibbs
140739213Sgibbs		/*
140839213Sgibbs		 * Allocate a peripheral instance for
140939213Sgibbs		 * this device and start the probe
141039213Sgibbs		 * process.
141139213Sgibbs		 */
141240603Sken		status = cam_periph_alloc(saregister, saoninvalidate,
141340603Sken					  sacleanup, sastart,
141439213Sgibbs					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,
141539213Sgibbs					  saasync, AC_FOUND_DEVICE, cgd);
141639213Sgibbs
141739213Sgibbs		if (status != CAM_REQ_CMP
141839213Sgibbs		 && status != CAM_REQ_INPROG)
141939213Sgibbs			printf("saasync: Unable to probe new device "
142039213Sgibbs				"due to status 0x%x\n", status);
142139213Sgibbs		break;
142239213Sgibbs	}
142339213Sgibbs	default:
142447413Sgibbs		cam_periph_async(periph, code, path, arg);
142539213Sgibbs		break;
142639213Sgibbs	}
142739213Sgibbs}
142839213Sgibbs
142939213Sgibbsstatic cam_status
143039213Sgibbssaregister(struct cam_periph *periph, void *arg)
143139213Sgibbs{
143239213Sgibbs	struct sa_softc *softc;
143339213Sgibbs	struct ccb_getdev *cgd;
143439213Sgibbs	caddr_t match;
143553259Smjacob	int i;
143639213Sgibbs
143739213Sgibbs	cgd = (struct ccb_getdev *)arg;
143839213Sgibbs	if (periph == NULL) {
143939213Sgibbs		printf("saregister: periph was NULL!!\n");
144054099Smjacob		return (CAM_REQ_CMP_ERR);
144139213Sgibbs	}
144239213Sgibbs
144339213Sgibbs	if (cgd == NULL) {
144439213Sgibbs		printf("saregister: no getdev CCB, can't register device\n");
144554099Smjacob		return (CAM_REQ_CMP_ERR);
144639213Sgibbs	}
144739213Sgibbs
144867723Smjacob	softc = (struct sa_softc *)
1449147723Savatar	    malloc(sizeof (*softc), M_SCSISA, M_NOWAIT | M_ZERO);
145039213Sgibbs	if (softc == NULL) {
145139213Sgibbs		printf("saregister: Unable to probe new device. "
145239213Sgibbs		       "Unable to allocate softc\n");
145354099Smjacob		return (CAM_REQ_CMP_ERR);
145439213Sgibbs	}
145541674Smjacob	softc->scsi_rev = SID_ANSI_REV(&cgd->inq_data);
145639213Sgibbs	softc->state = SA_STATE_NORMAL;
145743636Smjacob	softc->fileno = (daddr_t) -1;
145843636Smjacob	softc->blkno = (daddr_t) -1;
145943636Smjacob
146059249Sphk	bioq_init(&softc->bio_queue);
146139213Sgibbs	periph->softc = softc;
146239213Sgibbs
146339213Sgibbs	/*
146439213Sgibbs	 * See if this device has any quirks.
146539213Sgibbs	 */
146639213Sgibbs	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
146739213Sgibbs			       (caddr_t)sa_quirk_table,
146839213Sgibbs			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),
146939213Sgibbs			       sizeof(*sa_quirk_table), scsi_inquiry_match);
147039213Sgibbs
147142563Smjacob	if (match != NULL) {
147239213Sgibbs		softc->quirks = ((struct sa_quirk_entry *)match)->quirks;
147342563Smjacob		softc->last_media_blksize =
147442563Smjacob		    ((struct sa_quirk_entry *)match)->prefblk;
147542563Smjacob#ifdef	CAMDEBUG
1476164906Smjacob		xpt_print(periph->path, "found quirk entry %d\n",
1477164906Smjacob		    (int) (((struct sa_quirk_entry *) match) - sa_quirk_table));
147842563Smjacob#endif
147942563Smjacob	} else
148039213Sgibbs		softc->quirks = SA_QUIRK_NONE;
148139213Sgibbs
148239213Sgibbs	/*
148339213Sgibbs 	 * The SA driver supports a blocksize, but we don't know the
148446962Smjacob	 * blocksize until we media is inserted.  So, set a flag to
148539213Sgibbs	 * indicate that the blocksize is unavailable right now.
148639213Sgibbs	 */
1487169605Sscottl	cam_periph_unlock(periph);
1488112006Sphk	softc->device_stats = devstat_new_entry("sa", periph->unit_number, 0,
148956148Smjacob	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |
149056148Smjacob	    DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_TAPE);
149153259Smjacob
149253259Smjacob	softc->devs.ctl_dev = make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,
1493191304Sed	    0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR,
149472804Smjacob	    0660, "%s%d.ctl", periph->periph_name, periph->unit_number);
1495101940Snjl	softc->devs.ctl_dev->si_drv1 = periph;
149653259Smjacob
149753259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
149853283Smjacob
149953259Smjacob		softc->devs.mode_devs[i].r_dev = make_dev(&sa_cdevsw,
1500191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_R),
150172804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d",
150253259Smjacob		    periph->periph_name, periph->unit_number, i);
1503101940Snjl		softc->devs.mode_devs[i].r_dev->si_drv1 = periph;
150453283Smjacob
150553259Smjacob		softc->devs.mode_devs[i].nr_dev = make_dev(&sa_cdevsw,
1506191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_NR),
150772804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d",
150853259Smjacob		    periph->periph_name, periph->unit_number, i);
1509101940Snjl		softc->devs.mode_devs[i].nr_dev->si_drv1 = periph;
151053259Smjacob
151153283Smjacob		softc->devs.mode_devs[i].er_dev = make_dev(&sa_cdevsw,
1512191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_ER),
151372804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d",
151453259Smjacob		    periph->periph_name, periph->unit_number, i);
1515101940Snjl		softc->devs.mode_devs[i].er_dev->si_drv1 = periph;
151665838Smjacob
151765838Smjacob		/*
151865838Smjacob		 * Make the (well known) aliases for the first mode.
151965838Smjacob		 */
152065838Smjacob		if (i == 0) {
1521130585Sphk			struct cdev *alias;
1522101940Snjl
1523101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].r_dev,
152472804Smjacob			   "%s%d", periph->periph_name, periph->unit_number);
1525101940Snjl			alias->si_drv1 = periph;
1526101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].nr_dev,
152772804Smjacob			    "n%s%d", periph->periph_name, periph->unit_number);
1528101940Snjl			alias->si_drv1 = periph;
1529101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].er_dev,
153072804Smjacob			    "e%s%d", periph->periph_name, periph->unit_number);
1531101940Snjl			alias->si_drv1 = periph;
153265838Smjacob		}
153353259Smjacob	}
1534168872Sscottl	cam_periph_lock(periph);
153553259Smjacob
153639213Sgibbs	/*
153739213Sgibbs	 * Add an async callback so that we get
153839213Sgibbs	 * notified if this device goes away.
153939213Sgibbs	 */
1540169605Sscottl	xpt_register_async(AC_LOST_DEVICE, saasync, periph, periph->path);
154139213Sgibbs
154239213Sgibbs	xpt_announce_periph(periph, NULL);
154339213Sgibbs
154454099Smjacob	return (CAM_REQ_CMP);
154539213Sgibbs}
154639213Sgibbs
154739213Sgibbsstatic void
154839213Sgibbssastart(struct cam_periph *periph, union ccb *start_ccb)
154939213Sgibbs{
155039213Sgibbs	struct sa_softc *softc;
155139213Sgibbs
155239213Sgibbs	softc = (struct sa_softc *)periph->softc;
155339213Sgibbs
1554115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sastart\n"));
155571082Smjacob
155639213Sgibbs
155739213Sgibbs	switch (softc->state) {
155839213Sgibbs	case SA_STATE_NORMAL:
155939213Sgibbs	{
156039213Sgibbs		/* Pull a buffer from the queue and get going on it */
156159249Sphk		struct bio *bp;
156239213Sgibbs
156339213Sgibbs		/*
156439213Sgibbs		 * See if there is a buf with work for us to do..
156539213Sgibbs		 */
156659249Sphk		bp = bioq_first(&softc->bio_queue);
156739213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority) {
156839213Sgibbs			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
156939213Sgibbs					("queuing for immediate ccb\n"));
157071082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_WAITING);
157139213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
157239213Sgibbs					  periph_links.sle);
157339213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
157439213Sgibbs			wakeup(&periph->ccb_list);
157539213Sgibbs		} else if (bp == NULL) {
157639213Sgibbs			xpt_release_ccb(start_ccb);
157739213Sgibbs		} else if ((softc->flags & SA_FLAG_ERR_PENDING) != 0) {
157859249Sphk			struct bio *done_bp;
157982575Smjacobagain:
158046962Smjacob			softc->queue_count--;
158159249Sphk			bioq_remove(&softc->bio_queue, bp);
158259249Sphk			bp->bio_resid = bp->bio_bcount;
158382575Smjacob			done_bp = bp;
158439213Sgibbs			if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) {
158582575Smjacob				/*
158682575Smjacob				 * We now just clear errors in this case
158782575Smjacob				 * and let the residual be the notifier.
158882575Smjacob				 */
158982575Smjacob				bp->bio_error = 0;
159082575Smjacob			} else if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) {
159182575Smjacob				/*
159282575Smjacob				 * This can only happen if we're reading
159382575Smjacob				 * in fixed length mode. In this case,
159482575Smjacob				 * we dump the rest of the list the
159582575Smjacob				 * same way.
159682575Smjacob				 */
159782575Smjacob				bp->bio_error = 0;
159882575Smjacob				if (bioq_first(&softc->bio_queue) != NULL) {
159982575Smjacob					biodone(done_bp);
160082575Smjacob					goto again;
160182575Smjacob				}
160282575Smjacob			} else if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) {
160359249Sphk				bp->bio_error = EIO;
160482575Smjacob				bp->bio_flags |= BIO_ERROR;
160541948Smjacob			}
160659249Sphk			bp = bioq_first(&softc->bio_queue);
160744354Smjacob			/*
160844354Smjacob			 * Only if we have no other buffers queued up
160944354Smjacob			 * do we clear the pending error flag.
161044354Smjacob			 */
161144354Smjacob			if (bp == NULL)
161244354Smjacob				softc->flags &= ~SA_FLAG_ERR_PENDING;
161344354Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
161446962Smjacob			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "
161546962Smjacob			    "%d more buffers queued up\n",
161644354Smjacob			    (softc->flags & SA_FLAG_ERR_PENDING),
161746962Smjacob			    (bp != NULL)? "not " : " ", softc->queue_count));
161844354Smjacob			xpt_release_ccb(start_ccb);
161941948Smjacob			biodone(done_bp);
162039213Sgibbs		} else {
162139213Sgibbs			u_int32_t length;
162239213Sgibbs
162359249Sphk			bioq_remove(&softc->bio_queue, bp);
162446962Smjacob			softc->queue_count--;
162539213Sgibbs
162639213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0) {
162739213Sgibbs				if (softc->blk_shift != 0) {
162839213Sgibbs					length =
162959249Sphk					    bp->bio_bcount >> softc->blk_shift;
163043636Smjacob				} else if (softc->media_blksize != 0) {
163171082Smjacob					length = bp->bio_bcount /
163271082Smjacob					    softc->media_blksize;
163343636Smjacob				} else {
163459249Sphk					bp->bio_error = EIO;
1635164906Smjacob					xpt_print(periph->path, "zero blocksize"
1636164906Smjacob					    " for FIXED length writes?\n");
163743636Smjacob					biodone(bp);
163843636Smjacob					break;
163939213Sgibbs				}
1640115660Smjacob#if	0
1641115660Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1642115660Smjacob				    ("issuing a %d fixed record %s\n",
1643115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1644115660Smjacob				    "write"));
1645115660Smjacob#endif
164639213Sgibbs			} else {
164759249Sphk				length = bp->bio_bcount;
1648115660Smjacob#if	0
164941906Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1650115660Smjacob				    ("issuing a %d variable byte %s\n",
1651115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1652115660Smjacob				    "write"));
1653115660Smjacob#endif
165439213Sgibbs			}
1655112260Sphk			devstat_start_transaction_bio(softc->device_stats, bp);
165639213Sgibbs			/*
165741906Smjacob			 * Some people have theorized that we should
165839213Sgibbs			 * suppress illegal length indication if we are
165939213Sgibbs			 * running in variable block mode so that we don't
166039213Sgibbs			 * have to request sense every time our requested
166139213Sgibbs			 * block size is larger than the written block.
166239213Sgibbs			 * The residual information from the ccb allows
166339213Sgibbs			 * us to identify this situation anyway.  The only
166439213Sgibbs			 * problem with this is that we will not get
166539213Sgibbs			 * information about blocks that are larger than
166639213Sgibbs			 * our read buffer unless we set the block size
166739213Sgibbs			 * in the mode page to something other than 0.
166841906Smjacob			 *
166941906Smjacob			 * I believe that this is a non-issue. If user apps
167041906Smjacob			 * don't adjust their read size to match our record
167141906Smjacob			 * size, that's just life. Anyway, the typical usage
167241906Smjacob			 * would be to issue, e.g., 64KB reads and occasionally
167341906Smjacob			 * have to do deal with 512 byte or 1KB intermediate
167441906Smjacob			 * records.
167539213Sgibbs			 */
167659249Sphk			softc->dsreg = (bp->bio_cmd == BIO_READ)?
167743636Smjacob			    MTIO_DSREG_RD : MTIO_DSREG_WR;
167846962Smjacob			scsi_sa_read_write(&start_ccb->csio, 0, sadone,
167959249Sphk			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd == BIO_READ),
168046962Smjacob			    FALSE, (softc->flags & SA_FLAG_FIXED) != 0,
168159249Sphk			    length, bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,
168279100Smjacob			    IO_TIMEOUT);
168371082Smjacob			start_ccb->ccb_h.ccb_pflags &= ~SA_POSITION_UPDATED;
168471082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);
168539213Sgibbs			start_ccb->ccb_h.ccb_bp = bp;
168659249Sphk			bp = bioq_first(&softc->bio_queue);
168739213Sgibbs			xpt_action(start_ccb);
168839213Sgibbs		}
168939213Sgibbs
169039213Sgibbs		if (bp != NULL) {
169139213Sgibbs			/* Have more work to do, so ensure we stay scheduled */
169246962Smjacob			xpt_schedule(periph, 1);
169339213Sgibbs		}
169439213Sgibbs		break;
169539213Sgibbs	}
169646962Smjacob	case SA_STATE_ABNORMAL:
169746962Smjacob	default:
169846962Smjacob		panic("state 0x%x in sastart", softc->state);
169946962Smjacob		break;
170039213Sgibbs	}
170139213Sgibbs}
170239213Sgibbs
170339213Sgibbs
170439213Sgibbsstatic void
170539213Sgibbssadone(struct cam_periph *periph, union ccb *done_ccb)
170639213Sgibbs{
170739213Sgibbs	struct sa_softc *softc;
170839213Sgibbs	struct ccb_scsiio *csio;
170939213Sgibbs
171039213Sgibbs	softc = (struct sa_softc *)periph->softc;
171139213Sgibbs	csio = &done_ccb->csio;
171271082Smjacob	switch (CCB_Type(csio)) {
171339213Sgibbs	case SA_CCB_BUFFER_IO:
171439213Sgibbs	{
171559249Sphk		struct bio *bp;
171639213Sgibbs		int error;
171739213Sgibbs
171843636Smjacob		softc->dsreg = MTIO_DSREG_REST;
171959249Sphk		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
172039213Sgibbs		error = 0;
172139213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
172239213Sgibbs			if ((error = saerror(done_ccb, 0, 0)) == ERESTART) {
172339213Sgibbs				/*
172441948Smjacob				 * A retry was scheduled, so just return.
172539213Sgibbs				 */
172639213Sgibbs				return;
172739213Sgibbs			}
172839213Sgibbs		}
172939213Sgibbs
173039213Sgibbs		if (error == EIO) {
173139213Sgibbs
173239213Sgibbs			/*
173353522Smjacob			 * Catastrophic error. Mark the tape as frozen
173453522Smjacob			 * (we no longer know tape position).
173553522Smjacob			 *
173644354Smjacob			 * Return all queued I/O with EIO, and unfreeze
173739213Sgibbs			 * our queue so that future transactions that
173839213Sgibbs			 * attempt to fix this problem can get to the
173939213Sgibbs			 * device.
174039213Sgibbs			 *
174139213Sgibbs			 */
174239213Sgibbs
174353522Smjacob			softc->flags |= SA_FLAG_TAPE_FROZEN;
1744112946Sphk			bioq_flush(&softc->bio_queue, NULL, EIO);
174539213Sgibbs		}
174639213Sgibbs		if (error != 0) {
174759249Sphk			bp->bio_resid = bp->bio_bcount;
174859249Sphk			bp->bio_error = error;
174959249Sphk			bp->bio_flags |= BIO_ERROR;
175043636Smjacob			/*
175143636Smjacob			 * In the error case, position is updated in saerror.
175243636Smjacob			 */
175339213Sgibbs		} else {
175459249Sphk			bp->bio_resid = csio->resid;
175559249Sphk			bp->bio_error = 0;
175639213Sgibbs			if (csio->resid != 0) {
175759249Sphk				bp->bio_flags |= BIO_ERROR;
175839213Sgibbs			}
175959249Sphk			if (bp->bio_cmd == BIO_WRITE) {
176039213Sgibbs				softc->flags |= SA_FLAG_TAPE_WRITTEN;
176139213Sgibbs				softc->filemarks = 0;
176239213Sgibbs			}
176371082Smjacob			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&
176471082Smjacob			    (softc->blkno != (daddr_t) -1)) {
176543636Smjacob				if ((softc->flags & SA_FLAG_FIXED) != 0) {
176643636Smjacob					u_int32_t l;
176743636Smjacob					if (softc->blk_shift != 0) {
176859249Sphk						l = bp->bio_bcount >>
176943636Smjacob							softc->blk_shift;
177043636Smjacob					} else {
177159249Sphk						l = bp->bio_bcount /
177243636Smjacob							softc->media_blksize;
177343636Smjacob					}
177443636Smjacob					softc->blkno += (daddr_t) l;
177543636Smjacob				} else {
177643636Smjacob					softc->blkno++;
177743636Smjacob				}
177843636Smjacob			}
177939213Sgibbs		}
178046962Smjacob		/*
178146962Smjacob		 * If we had an error (immediate or pending),
178246962Smjacob		 * release the device queue now.
178346962Smjacob		 */
178446962Smjacob		if (error || (softc->flags & SA_FLAG_ERR_PENDING))
178546962Smjacob			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);
178641674Smjacob#ifdef	CAMDEBUG
178759249Sphk		if (error || bp->bio_resid) {
178841674Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
178941674Smjacob			    	  ("error %d resid %ld count %ld\n", error,
179059249Sphk				  bp->bio_resid, bp->bio_bcount));
179141674Smjacob		}
179241674Smjacob#endif
1793112006Sphk		biofinish(bp, softc->device_stats, 0);
179439213Sgibbs		break;
179539213Sgibbs	}
179639213Sgibbs	case SA_CCB_WAITING:
179739213Sgibbs	{
179839213Sgibbs		/* Caller will release the CCB */
179939213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
180039213Sgibbs		return;
180139213Sgibbs	}
180239213Sgibbs	}
180339213Sgibbs	xpt_release_ccb(done_ccb);
180439213Sgibbs}
180539213Sgibbs
180641906Smjacob/*
180741906Smjacob * Mount the tape (make sure it's ready for I/O).
180841906Smjacob */
180939213Sgibbsstatic int
1810130585Sphksamount(struct cam_periph *periph, int oflags, struct cdev *dev)
181139213Sgibbs{
181239213Sgibbs	struct	sa_softc *softc;
181339213Sgibbs	union	ccb *ccb;
181439213Sgibbs	int	error;
181539213Sgibbs
181641906Smjacob	/*
181741906Smjacob	 * oflags can be checked for 'kind' of open (read-only check) - later
181841906Smjacob	 * dev can be checked for a control-mode or compression open - later
181941906Smjacob	 */
182041906Smjacob	UNUSED_PARAMETER(oflags);
182141906Smjacob	UNUSED_PARAMETER(dev);
182241906Smjacob
182341906Smjacob
182439213Sgibbs	softc = (struct sa_softc *)periph->softc;
182539213Sgibbs
182639213Sgibbs	/*
182753259Smjacob	 * This should determine if something has happend since the last
182853259Smjacob	 * open/mount that would invalidate the mount. We do *not* want
182953259Smjacob	 * to retry this command- we just want the status. But we only
183053259Smjacob	 * do this if we're mounted already- if we're not mounted,
183153259Smjacob	 * we don't care about the unit read state and can instead use
183253259Smjacob	 * this opportunity to attempt to reserve the tape unit.
183339213Sgibbs	 */
183453259Smjacob
183553259Smjacob	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {
183653259Smjacob		ccb = cam_periph_getccb(periph, 1);
183753259Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
183879100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
183954099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1840112006Sphk		    softc->device_stats);
184153259Smjacob		QFRLS(ccb);
184253259Smjacob		if (error == ENXIO) {
184353259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
184453259Smjacob			scsi_test_unit_ready(&ccb->csio, 0, sadone,
184579100Smjacob			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
184654099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1847112006Sphk			    softc->device_stats);
184853259Smjacob			QFRLS(ccb);
184953259Smjacob		} else if (error) {
185054099Smjacob			/*
185154099Smjacob			 * We don't need to freeze the tape because we
185254099Smjacob			 * will now attempt to rewind/load it.
185354099Smjacob			 */
185453259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
1855144430Ssam			if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
1856164906Smjacob				xpt_print(periph->path,
1857164906Smjacob				    "error %d on TUR in samount\n", error);
185854099Smjacob			}
185953259Smjacob		}
186053259Smjacob	} else {
186153259Smjacob		error = sareservereleaseunit(periph, TRUE);
186253259Smjacob		if (error) {
186353259Smjacob			return (error);
186453259Smjacob		}
186553259Smjacob		ccb = cam_periph_getccb(periph, 1);
186654105Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
186779100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
186854105Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1869112006Sphk		    softc->device_stats);
187054105Smjacob		QFRLS(ccb);
187153259Smjacob	}
187239213Sgibbs
187339213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
187453259Smjacob		struct scsi_read_block_limits_data *rblim = NULL;
187553259Smjacob		int comp_enabled, comp_supported;
187641906Smjacob		u_int8_t write_protect, guessing = 0;
187739213Sgibbs
187839213Sgibbs		/*
187939213Sgibbs		 * Clear out old state.
188039213Sgibbs		 */
188139213Sgibbs		softc->flags &= ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|
188239213Sgibbs				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|
188346962Smjacob				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);
188439213Sgibbs		softc->filemarks = 0;
188539213Sgibbs
188639213Sgibbs		/*
188743636Smjacob		 * *Very* first off, make sure we're loaded to BOT.
188839213Sgibbs		 */
188943636Smjacob		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
189054099Smjacob		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);
189154099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1892112006Sphk		    softc->device_stats);
189353259Smjacob		QFRLS(ccb);
189453259Smjacob
189543636Smjacob		/*
189644354Smjacob		 * In case this doesn't work, do a REWIND instead
189743636Smjacob		 */
189844354Smjacob		if (error) {
189953259Smjacob			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
190053259Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
190154099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1902112006Sphk				softc->device_stats);
190353259Smjacob			QFRLS(ccb);
190443636Smjacob		}
190543636Smjacob		if (error) {
190643636Smjacob			xpt_release_ccb(ccb);
190743636Smjacob			goto exit;
190843636Smjacob		}
190943636Smjacob
191043636Smjacob		/*
191153259Smjacob		 * Do a dummy test read to force access to the
191253259Smjacob		 * media so that the drive will really know what's
191354099Smjacob		 * there. We actually don't really care what the
191454099Smjacob		 * blocksize on tape is and don't expect to really
191554099Smjacob		 * read a full record.
191643636Smjacob		 */
191739213Sgibbs		rblim = (struct  scsi_read_block_limits_data *)
1918170830Sscottl		    malloc(8192, M_SCSISA, M_NOWAIT);
191953259Smjacob		if (rblim == NULL) {
1920164906Smjacob			xpt_print(periph->path, "no memory for test read\n");
192153259Smjacob			xpt_release_ccb(ccb);
192253259Smjacob			error = ENOMEM;
192353259Smjacob			goto exit;
192453259Smjacob		}
192556981Smjacob
192656981Smjacob		if ((softc->quirks & SA_QUIRK_NODREAD) == 0) {
192756981Smjacob			scsi_sa_read_write(&ccb->csio, 0, sadone,
192856981Smjacob			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,
192956981Smjacob			    (void *) rblim, 8192, SSD_FULL_SIZE,
193079100Smjacob			    IO_TIMEOUT);
193156981Smjacob			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1932112006Sphk			    softc->device_stats);
193356981Smjacob			QFRLS(ccb);
193456981Smjacob			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
193556981Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
193674840Sken			error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
193774840Sken			    SF_NO_PRINT | SF_RETRY_UA,
1938112006Sphk			    softc->device_stats);
193956981Smjacob			QFRLS(ccb);
194056981Smjacob			if (error) {
1941164906Smjacob				xpt_print(periph->path,
1942164906Smjacob				    "unable to rewind after test read\n");
194356981Smjacob				xpt_release_ccb(ccb);
194456981Smjacob				goto exit;
194556981Smjacob			}
194653259Smjacob		}
194739213Sgibbs
194853259Smjacob		/*
194953259Smjacob		 * Next off, determine block limits.
195053259Smjacob		 */
195153259Smjacob		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
195279100Smjacob		    rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
195339213Sgibbs
195474840Sken		error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
1955112006Sphk		    SF_NO_PRINT | SF_RETRY_UA, softc->device_stats);
195674840Sken
195753259Smjacob		QFRLS(ccb);
195839213Sgibbs		xpt_release_ccb(ccb);
195939213Sgibbs
196041674Smjacob		if (error != 0) {
196141674Smjacob			/*
196241674Smjacob			 * If it's less than SCSI-2, READ BLOCK LIMITS is not
196341674Smjacob			 * a MANDATORY command. Anyway- it doesn't matter-
196441674Smjacob			 * we can proceed anyway.
196541674Smjacob			 */
196641674Smjacob			softc->blk_gran = 0;
196741674Smjacob			softc->max_blk = ~0;
196841674Smjacob			softc->min_blk = 0;
196941674Smjacob		} else {
197074840Sken			if (softc->scsi_rev >= SCSI_REV_SPC) {
197141674Smjacob				softc->blk_gran = RBL_GRAN(rblim);
197241674Smjacob			} else {
197341674Smjacob				softc->blk_gran = 0;
197441674Smjacob			}
197541674Smjacob			/*
197641674Smjacob			 * We take max_blk == min_blk to mean a default to
197741674Smjacob			 * fixed mode- but note that whatever we get out of
197841674Smjacob			 * sagetparams below will actually determine whether
197941674Smjacob			 * we are actually *in* fixed mode.
198041674Smjacob			 */
198141674Smjacob			softc->max_blk = scsi_3btoul(rblim->maximum);
198241674Smjacob			softc->min_blk = scsi_2btoul(rblim->minimum);
198341674Smjacob
198441674Smjacob
198541674Smjacob		}
198641674Smjacob		/*
198741674Smjacob		 * Next, perform a mode sense to determine
198841674Smjacob		 * current density, blocksize, compression etc.
198941674Smjacob		 */
199041674Smjacob		error = sagetparams(periph, SA_PARAM_ALL,
199141674Smjacob				    &softc->media_blksize,
199241674Smjacob				    &softc->media_density,
199341674Smjacob				    &softc->media_numblks,
199441674Smjacob				    &softc->buffer_mode, &write_protect,
199541674Smjacob				    &softc->speed, &comp_supported,
199641674Smjacob				    &comp_enabled, &softc->comp_algorithm,
199741674Smjacob				    NULL);
199841674Smjacob
199941674Smjacob		if (error != 0) {
200041674Smjacob			/*
200141674Smjacob			 * We could work a little harder here. We could
200241674Smjacob			 * adjust our attempts to get information. It
200341674Smjacob			 * might be an ancient tape drive. If someone
200441674Smjacob			 * nudges us, we'll do that.
200541674Smjacob			 */
200639213Sgibbs			goto exit;
200741674Smjacob		}
200839213Sgibbs
200941906Smjacob		/*
201041906Smjacob		 * If no quirk has determined that this is a device that is
201141906Smjacob		 * preferred to be in fixed or variable mode, now is the time
201241906Smjacob		 * to find out.
201341906Smjacob	 	 */
201441906Smjacob		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) == 0) {
201541906Smjacob			guessing = 1;
201643636Smjacob			/*
201743636Smjacob			 * This could be expensive to find out. Luckily we
201843636Smjacob			 * only need to do this once. If we start out in
201943636Smjacob			 * 'default' mode, try and set ourselves to one
202043636Smjacob			 * of the densities that would determine a wad
202143636Smjacob			 * of other stuff. Go from highest to lowest.
202243636Smjacob			 */
202343636Smjacob			if (softc->media_density == SCSI_DEFAULT_DENSITY) {
202443636Smjacob				int i;
202543636Smjacob				static u_int8_t ctry[] = {
202643636Smjacob					SCSI_DENSITY_HALFINCH_PE,
202743636Smjacob					SCSI_DENSITY_HALFINCH_6250C,
202843636Smjacob					SCSI_DENSITY_HALFINCH_6250,
202943636Smjacob					SCSI_DENSITY_HALFINCH_1600,
203043636Smjacob					SCSI_DENSITY_HALFINCH_800,
203146962Smjacob					SCSI_DENSITY_QIC_4GB,
203246962Smjacob					SCSI_DENSITY_QIC_2GB,
203343636Smjacob					SCSI_DENSITY_QIC_525_320,
203443636Smjacob					SCSI_DENSITY_QIC_150,
203543636Smjacob					SCSI_DENSITY_QIC_120,
203643636Smjacob					SCSI_DENSITY_QIC_24,
203743636Smjacob					SCSI_DENSITY_QIC_11_9TRK,
203843636Smjacob					SCSI_DENSITY_QIC_11_4TRK,
203946962Smjacob					SCSI_DENSITY_QIC_1320,
204046962Smjacob					SCSI_DENSITY_QIC_3080,
204143636Smjacob					0
204243636Smjacob				};
204343636Smjacob				for (i = 0; ctry[i]; i++) {
204443636Smjacob					error = sasetparams(periph,
204543636Smjacob					    SA_PARAM_DENSITY, 0, ctry[i],
204643636Smjacob					    0, SF_NO_PRINT);
204743636Smjacob					if (error == 0) {
204843636Smjacob						softc->media_density = ctry[i];
204943636Smjacob						break;
205043636Smjacob					}
205143636Smjacob				}
205243636Smjacob			}
205341906Smjacob			switch (softc->media_density) {
205441906Smjacob			case SCSI_DENSITY_QIC_11_4TRK:
205541906Smjacob			case SCSI_DENSITY_QIC_11_9TRK:
205641906Smjacob			case SCSI_DENSITY_QIC_24:
205741906Smjacob			case SCSI_DENSITY_QIC_120:
205841906Smjacob			case SCSI_DENSITY_QIC_150:
205965861Smjacob			case SCSI_DENSITY_QIC_525_320:
206043636Smjacob			case SCSI_DENSITY_QIC_1320:
206143636Smjacob			case SCSI_DENSITY_QIC_3080:
206246962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
206343636Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
206441906Smjacob				softc->last_media_blksize = 512;
206541906Smjacob				break;
206646962Smjacob			case SCSI_DENSITY_QIC_4GB:
206746962Smjacob			case SCSI_DENSITY_QIC_2GB:
206846962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
206946962Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
207046962Smjacob				softc->last_media_blksize = 1024;
207146962Smjacob				break;
207241906Smjacob			default:
207341906Smjacob				softc->last_media_blksize =
207441906Smjacob				    softc->media_blksize;
207541906Smjacob				softc->quirks |= SA_QUIRK_VARIABLE;
207641906Smjacob				break;
207741906Smjacob			}
207841906Smjacob		}
207942563Smjacob
208041906Smjacob		/*
208141906Smjacob		 * If no quirk has determined that this is a device that needs
208241906Smjacob		 * to have 2 Filemarks at EOD, now is the time to find out.
208341906Smjacob		 */
208442563Smjacob
208542735Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0) {
208641906Smjacob			switch (softc->media_density) {
208741906Smjacob			case SCSI_DENSITY_HALFINCH_800:
208841906Smjacob			case SCSI_DENSITY_HALFINCH_1600:
208941906Smjacob			case SCSI_DENSITY_HALFINCH_6250:
209041906Smjacob			case SCSI_DENSITY_HALFINCH_6250C:
209141906Smjacob			case SCSI_DENSITY_HALFINCH_PE:
209246962Smjacob				softc->quirks &= ~SA_QUIRK_1FM;
209341906Smjacob				softc->quirks |= SA_QUIRK_2FM;
209441906Smjacob				break;
209541906Smjacob			default:
209641906Smjacob				break;
209741906Smjacob			}
209841906Smjacob		}
209941906Smjacob
210041906Smjacob		/*
210141906Smjacob		 * Now validate that some info we got makes sense.
210241906Smjacob		 */
210341674Smjacob		if ((softc->max_blk < softc->media_blksize) ||
210441674Smjacob		    (softc->min_blk > softc->media_blksize &&
210541674Smjacob		    softc->media_blksize)) {
2106164906Smjacob			xpt_print(periph->path,
2107164906Smjacob			    "BLOCK LIMITS (%d..%d) could not match current "
210841674Smjacob			    "block settings (%d)- adjusting\n", softc->min_blk,
210941674Smjacob			    softc->max_blk, softc->media_blksize);
211041674Smjacob			softc->max_blk = softc->min_blk =
211141674Smjacob			    softc->media_blksize;
211241674Smjacob		}
211341906Smjacob
211441674Smjacob		/*
211541906Smjacob		 * Now put ourselves into the right frame of mind based
211641906Smjacob		 * upon quirks...
211741906Smjacob		 */
211841906Smjacobtryagain:
211942563Smjacob		/*
212042563Smjacob		 * If we want to be in FIXED mode and our current blocksize
212142563Smjacob		 * is not equal to our last blocksize (if nonzero), try and
212242563Smjacob		 * set ourselves to this last blocksize (as the 'preferred'
212342563Smjacob		 * block size).  The initial quirkmatch at registry sets the
212442563Smjacob		 * initial 'last' blocksize. If, for whatever reason, this
212542563Smjacob		 * 'last' blocksize is zero, set the blocksize to 512,
212642563Smjacob		 * or min_blk if that's larger.
212742563Smjacob		 */
212841906Smjacob		if ((softc->quirks & SA_QUIRK_FIXED) &&
212960235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0 &&
213042563Smjacob		    (softc->media_blksize != softc->last_media_blksize)) {
213141906Smjacob			softc->media_blksize = softc->last_media_blksize;
213241906Smjacob			if (softc->media_blksize == 0) {
213342563Smjacob				softc->media_blksize = 512;
213441906Smjacob				if (softc->media_blksize < softc->min_blk) {
213541906Smjacob					softc->media_blksize = softc->min_blk;
213641906Smjacob				}
213741906Smjacob			}
213841906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
213942563Smjacob			    softc->media_blksize, 0, 0, SF_NO_PRINT);
214041906Smjacob			if (error) {
2141164906Smjacob				xpt_print(periph->path,
2142164906Smjacob				    "unable to set fixed blocksize to %d\n",
2143164906Smjacob				    softc->media_blksize);
214441906Smjacob				goto exit;
214541906Smjacob			}
214641906Smjacob		}
214741906Smjacob
214841906Smjacob		if ((softc->quirks & SA_QUIRK_VARIABLE) &&
214941906Smjacob		    (softc->media_blksize != 0)) {
215041906Smjacob			softc->last_media_blksize = softc->media_blksize;
215141906Smjacob			softc->media_blksize = 0;
215241906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
215342563Smjacob			    0, 0, 0, SF_NO_PRINT);
215441906Smjacob			if (error) {
215541906Smjacob				/*
215641906Smjacob				 * If this fails and we were guessing, just
215741906Smjacob				 * assume that we got it wrong and go try
215842563Smjacob				 * fixed block mode. Don't even check against
215942563Smjacob				 * density code at this point.
216041906Smjacob				 */
216142563Smjacob				if (guessing) {
216241906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
216341906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
216441906Smjacob					if (softc->last_media_blksize == 0)
216541906Smjacob						softc->last_media_blksize = 512;
216641906Smjacob					goto tryagain;
216741906Smjacob				}
2168164906Smjacob				xpt_print(periph->path,
2169164906Smjacob				    "unable to set variable blocksize\n");
217041906Smjacob				goto exit;
217141906Smjacob			}
217241906Smjacob		}
217341906Smjacob
217441906Smjacob		/*
217541674Smjacob		 * Now that we have the current block size,
217641674Smjacob		 * set up some parameters for sastart's usage.
217741674Smjacob		 */
217841674Smjacob		if (softc->media_blksize) {
217939213Sgibbs			softc->flags |= SA_FLAG_FIXED;
218041674Smjacob			if (powerof2(softc->media_blksize)) {
218141674Smjacob				softc->blk_shift =
218241674Smjacob				    ffs(softc->media_blksize) - 1;
218341674Smjacob				softc->blk_mask = softc->media_blksize - 1;
218439213Sgibbs			} else {
218539213Sgibbs				softc->blk_mask = ~0;
218639213Sgibbs				softc->blk_shift = 0;
218739213Sgibbs			}
218839213Sgibbs		} else {
218939213Sgibbs			/*
219041674Smjacob			 * The SCSI-3 spec allows 0 to mean "unspecified".
219141674Smjacob			 * The SCSI-1 spec allows 0 to mean 'infinite'.
219241674Smjacob			 *
219341674Smjacob			 * Either works here.
219439213Sgibbs			 */
219539213Sgibbs			if (softc->max_blk == 0) {
219639213Sgibbs				softc->max_blk = ~0;
219739213Sgibbs			}
219839213Sgibbs			softc->blk_shift = 0;
219939213Sgibbs			if (softc->blk_gran != 0) {
220039213Sgibbs				softc->blk_mask = softc->blk_gran - 1;
220139213Sgibbs			} else {
220239213Sgibbs				softc->blk_mask = 0;
220339213Sgibbs			}
220439213Sgibbs		}
220539213Sgibbs
220639213Sgibbs		if (write_protect)
220739213Sgibbs			softc->flags |= SA_FLAG_TAPE_WP;
220839213Sgibbs
220939213Sgibbs		if (comp_supported) {
221043636Smjacob			if (softc->saved_comp_algorithm == 0)
221143636Smjacob				softc->saved_comp_algorithm =
221243636Smjacob				    softc->comp_algorithm;
221346962Smjacob			softc->flags |= SA_FLAG_COMP_SUPP;
221446962Smjacob			if (comp_enabled)
221546962Smjacob				softc->flags |= SA_FLAG_COMP_ENABLED;
221639213Sgibbs		} else
221739213Sgibbs			softc->flags |= SA_FLAG_COMP_UNSUPP;
221839213Sgibbs
221960235Smjacob		if ((softc->buffer_mode == SMH_SA_BUF_MODE_NOBUF) &&
222060235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0) {
222141906Smjacob			error = sasetparams(periph, SA_PARAM_BUFF_MODE, 0,
222242563Smjacob			    0, 0, SF_NO_PRINT);
222383473Smjacob			if (error == 0) {
222441906Smjacob				softc->buffer_mode = SMH_SA_BUF_MODE_SIBUF;
222583473Smjacob			} else {
2226164906Smjacob				xpt_print(periph->path,
2227164906Smjacob				    "unable to set buffered mode\n");
222883473Smjacob			}
222960235Smjacob			error = 0;	/* not an error */
223041906Smjacob		}
223139213Sgibbs
223239213Sgibbs
223344354Smjacob		if (error == 0) {
223441906Smjacob			softc->flags |= SA_FLAG_TAPE_MOUNTED;
223544354Smjacob		}
223639213Sgibbsexit:
223739213Sgibbs		if (rblim != NULL)
2238169562Sscottl			free(rblim, M_SCSISA);
223939213Sgibbs
224043636Smjacob		if (error != 0) {
224143636Smjacob			softc->dsreg = MTIO_DSREG_NIL;
224244354Smjacob		} else {
224344354Smjacob			softc->fileno = softc->blkno = 0;
224443636Smjacob			softc->dsreg = MTIO_DSREG_REST;
224544354Smjacob		}
224651875Smjacob#ifdef	SA_1FM_AT_EOD
224751875Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0)
224851875Smjacob			softc->quirks |= SA_QUIRK_1FM;
224951875Smjacob#else
225046962Smjacob		if ((softc->quirks & SA_QUIRK_1FM) == 0)
225143636Smjacob			softc->quirks |= SA_QUIRK_2FM;
225243636Smjacob#endif
225339213Sgibbs	} else
225439213Sgibbs		xpt_release_ccb(ccb);
225539213Sgibbs
225653259Smjacob	/*
225753259Smjacob	 * If we return an error, we're not mounted any more,
225853259Smjacob	 * so release any device reservation.
225953259Smjacob	 */
226053259Smjacob	if (error != 0) {
226153259Smjacob		(void) sareservereleaseunit(periph, FALSE);
226282575Smjacob	} else {
226382575Smjacob		/*
226482575Smjacob		 * Clear I/O residual.
226582575Smjacob		 */
226682575Smjacob		softc->last_io_resid = 0;
226782575Smjacob		softc->last_ctl_resid = 0;
226853259Smjacob	}
226954099Smjacob	return (error);
227039213Sgibbs}
227139213Sgibbs
227268114Smjacob/*
227368114Smjacob * How many filemarks do we need to write if we were to terminate the
227468114Smjacob * tape session right now? Note that this can be a negative number
227568114Smjacob */
227668114Smjacob
227739213Sgibbsstatic int
227868114Smjacobsamarkswanted(struct cam_periph *periph)
227939213Sgibbs{
228039213Sgibbs	int	markswanted;
228139213Sgibbs	struct	sa_softc *softc;
228239213Sgibbs
228339213Sgibbs	softc = (struct sa_softc *)periph->softc;
228439213Sgibbs	markswanted = 0;
228539213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) != 0) {
228639213Sgibbs		markswanted++;
228746962Smjacob		if (softc->quirks & SA_QUIRK_2FM)
228839213Sgibbs			markswanted++;
228939213Sgibbs	}
229068114Smjacob	markswanted -= softc->filemarks;
229168114Smjacob	return (markswanted);
229268114Smjacob}
229339213Sgibbs
229468114Smjacobstatic int
229568114Smjacobsacheckeod(struct cam_periph *periph)
229668114Smjacob{
229768114Smjacob	int	error;
229868114Smjacob	int	markswanted;
229968114Smjacob
230068114Smjacob	markswanted = samarkswanted(periph);
230168114Smjacob
230268114Smjacob	if (markswanted > 0) {
230341906Smjacob		error = sawritefilemarks(periph, markswanted, FALSE);
230439213Sgibbs	} else {
230539213Sgibbs		error = 0;
230639213Sgibbs	}
230739213Sgibbs	return (error);
230839213Sgibbs}
230939213Sgibbs
231039213Sgibbsstatic int
231146962Smjacobsaerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
231239213Sgibbs{
231346962Smjacob	static const char *toobig =
231498449Srobert	    "%d-byte tape record bigger than supplied buffer\n";
231539213Sgibbs	struct	cam_periph *periph;
231639213Sgibbs	struct	sa_softc *softc;
231739213Sgibbs	struct	ccb_scsiio *csio;
231839213Sgibbs	struct	scsi_sense_data *sense;
231953259Smjacob	u_int32_t resid = 0;
232053259Smjacob	int32_t	info = 0;
232182575Smjacob	cam_status status;
232282575Smjacob	int error_code, sense_key, asc, ascq, error, aqvalid;
232339213Sgibbs
232439213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
232539213Sgibbs	softc = (struct sa_softc *)periph->softc;
232639213Sgibbs	csio = &ccb->csio;
232739213Sgibbs	sense = &csio->sense_data;
232839213Sgibbs	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
232982575Smjacob	aqvalid = sense->extra_len >= 6;
233039213Sgibbs	error = 0;
233146962Smjacob
233282575Smjacob	status = csio->ccb_h.status & CAM_STATUS_MASK;
233382575Smjacob
233446962Smjacob	/*
233582575Smjacob	 * Calculate/latch up, any residuals... We do this in a funny 2-step
233682575Smjacob	 * so we can print stuff here if we have CAM_DEBUG enabled for this
233782575Smjacob	 * unit.
233846962Smjacob	 */
233982575Smjacob	if (status == CAM_SCSI_STATUS_ERROR) {
234039213Sgibbs		if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
234146962Smjacob			info = (int32_t) scsi_4btoul(sense->info);
234239213Sgibbs			resid = info;
234339213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0)
234439213Sgibbs				resid *= softc->media_blksize;
234539213Sgibbs		} else {
234639213Sgibbs			resid = csio->dxfer_len;
234739213Sgibbs			info = resid;
234841674Smjacob			if ((softc->flags & SA_FLAG_FIXED) != 0) {
234941674Smjacob				if (softc->media_blksize)
235041674Smjacob					info /= softc->media_blksize;
235141674Smjacob			}
235239213Sgibbs		}
235371082Smjacob		if (CCB_Type(csio) == SA_CCB_BUFFER_IO) {
235441948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,
235541948Smjacob			    sizeof (struct scsi_sense_data));
235642009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,
235742009Smjacob			    (int) csio->cdb_len);
235841948Smjacob			softc->last_io_resid = resid;
235971268Smjacob			softc->last_resid_was_io = 1;
236041948Smjacob		} else {
236141948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,
236241948Smjacob			    sizeof (struct scsi_sense_data));
236342009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,
236442009Smjacob			    (int) csio->cdb_len);
236541948Smjacob			softc->last_ctl_resid = resid;
236671268Smjacob			softc->last_resid_was_io = 0;
236741948Smjacob		}
236882575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=0x%x Key 0x%x "
236982575Smjacob		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d "
237082575Smjacob		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,
237182575Smjacob		    sense_key, asc, ascq, status,
237282575Smjacob		    sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len));
237353259Smjacob	} else {
237482575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
237582575Smjacob		    ("Cam Status 0x%x\n", status));
237641948Smjacob	}
237741948Smjacob
237882575Smjacob	switch (status) {
237982575Smjacob	case CAM_REQ_CMP:
238082575Smjacob		return (0);
238182575Smjacob	case CAM_SCSI_STATUS_ERROR:
238282575Smjacob		/*
238382575Smjacob		 * If a read/write command, we handle it here.
238482575Smjacob		 */
238582575Smjacob		if (CCB_Type(csio) != SA_CCB_WAITING) {
238682575Smjacob			break;
238782575Smjacob		}
238882575Smjacob		/*
238982575Smjacob		 * If this was just EOM/EOP, Filemark, Setmark or ILI detected
239082575Smjacob		 * on a non read/write command, we assume it's not an error
239182575Smjacob		 * and propagate the residule and return.
239282575Smjacob		 */
239382575Smjacob		if ((aqvalid && asc == 0 && ascq > 0 && ascq <= 5) ||
239482575Smjacob		    (aqvalid == 0 && sense_key == SSD_KEY_NO_SENSE)) {
239582575Smjacob			csio->resid = resid;
239682575Smjacob			QFRLS(ccb);
239782575Smjacob			return (0);
239882575Smjacob		}
239982575Smjacob		/*
240082575Smjacob		 * Otherwise, we let the common code handle this.
240182575Smjacob		 */
240246962Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
240341948Smjacob
240446962Smjacob	/*
240582575Smjacob	 * XXX: To Be Fixed
240682575Smjacob	 * We cannot depend upon CAM honoring retry counts for these.
240746962Smjacob	 */
240882575Smjacob	case CAM_SCSI_BUS_RESET:
240982575Smjacob	case CAM_BDR_SENT:
241082575Smjacob		if (ccb->ccb_h.retry_count <= 0) {
241182575Smjacob			return (EIO);
241282575Smjacob		}
241382575Smjacob		/* FALLTHROUGH */
241482575Smjacob	default:
241582575Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
241646962Smjacob	}
241746962Smjacob
241846962Smjacob	/*
241946962Smjacob	 * Handle filemark, end of tape, mismatched record sizes....
242046962Smjacob	 * From this point out, we're only handling read/write cases.
242146962Smjacob	 * Handle writes && reads differently.
242246962Smjacob	 */
242382575Smjacob
242446962Smjacob	if (csio->cdb_io.cdb_bytes[0] == SA_WRITE) {
242582575Smjacob		if (sense_key == SSD_KEY_VOLUME_OVERFLOW) {
242639213Sgibbs			csio->resid = resid;
242782575Smjacob			error = ENOSPC;
242882575Smjacob		} else if (sense->flags & SSD_EOM) {
242982575Smjacob			softc->flags |= SA_FLAG_EOM_PENDING;
243082575Smjacob			/*
243182575Smjacob			 * Grotesque as it seems, the few times
243282575Smjacob			 * I've actually seen a non-zero resid,
243382575Smjacob			 * the tape drive actually lied and had
2434124645Sjohan			 * written all the data!.
243582575Smjacob			 */
243682575Smjacob			csio->resid = 0;
243739213Sgibbs		}
243846962Smjacob	} else {
243982575Smjacob		csio->resid = resid;
244046962Smjacob		if (sense_key == SSD_KEY_BLANK_CHECK) {
244182575Smjacob			if (softc->quirks & SA_QUIRK_1FM) {
244282575Smjacob				error = 0;
244346962Smjacob				softc->flags |= SA_FLAG_EOM_PENDING;
244446962Smjacob			} else {
244546962Smjacob				error = EIO;
244646962Smjacob			}
244782575Smjacob		} else if (sense->flags & SSD_FILEMARK) {
244882575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
244946962Smjacob				error = -1;
245039213Sgibbs				softc->flags |= SA_FLAG_EOF_PENDING;
245146962Smjacob			}
245246962Smjacob			/*
245346962Smjacob			 * Unconditionally, if we detected a filemark on a read,
245446962Smjacob			 * mark that we've run moved a file ahead.
245546962Smjacob			 */
245643636Smjacob			if (softc->fileno != (daddr_t) -1) {
245743636Smjacob				softc->fileno++;
245843636Smjacob				softc->blkno = 0;
245971082Smjacob				csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED;
246043636Smjacob			}
246139213Sgibbs		}
246246962Smjacob	}
246382575Smjacob
246446962Smjacob	/*
246546962Smjacob	 * Incorrect Length usually applies to read, but can apply to writes.
246646962Smjacob	 */
246746962Smjacob	if (error == 0 && (sense->flags & SSD_ILI)) {
246846962Smjacob		if (info < 0) {
2469164906Smjacob			xpt_print(csio->ccb_h.path, toobig,
2470164906Smjacob			    csio->dxfer_len - info);
247146962Smjacob			csio->resid = csio->dxfer_len;
247246962Smjacob			error = EIO;
247346962Smjacob		} else {
247446962Smjacob			csio->resid = resid;
247582575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
247682575Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
247746962Smjacob			}
247846962Smjacob			/*
247946962Smjacob			 * Bump the block number if we hadn't seen a filemark.
248046962Smjacob			 * Do this independent of errors (we've moved anyway).
248146962Smjacob			 */
248246962Smjacob			if ((sense->flags & SSD_FILEMARK) == 0) {
248346962Smjacob				if (softc->blkno != (daddr_t) -1) {
248446962Smjacob					softc->blkno++;
248571082Smjacob					csio->ccb_h.ccb_pflags |=
248671082Smjacob					   SA_POSITION_UPDATED;
248739213Sgibbs				}
248839213Sgibbs			}
248939213Sgibbs		}
249039213Sgibbs	}
249182575Smjacob
249282575Smjacob	if (error <= 0) {
249382575Smjacob		/*
249482575Smjacob		 * Unfreeze the queue if frozen as we're not returning anything
249582575Smjacob		 * to our waiters that would indicate an I/O error has occurred
249682575Smjacob		 * (yet).
249782575Smjacob		 */
249882575Smjacob		QFRLS(ccb);
249982575Smjacob		error = 0;
250080575Smjacob	}
250182575Smjacob	return (error);
250239213Sgibbs}
250339213Sgibbs
250439213Sgibbsstatic int
250539213Sgibbssagetparams(struct cam_periph *periph, sa_params params_to_get,
250639213Sgibbs	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,
250739213Sgibbs	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,
250839213Sgibbs	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,
250946962Smjacob	    sa_comp_t *tcs)
251039213Sgibbs{
251139213Sgibbs	union ccb *ccb;
251239213Sgibbs	void *mode_buffer;
251339213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
251439213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
251539213Sgibbs	int mode_buffer_len;
251639213Sgibbs	struct sa_softc *softc;
251746962Smjacob	u_int8_t cpage;
251839213Sgibbs	int error;
251939213Sgibbs	cam_status status;
252039213Sgibbs
252139213Sgibbs	softc = (struct sa_softc *)periph->softc;
252246962Smjacob	ccb = cam_periph_getccb(periph, 1);
252371082Smjacob	if (softc->quirks & SA_QUIRK_NO_CPAGE)
252471082Smjacob		cpage = SA_DEVICE_CONFIGURATION_PAGE;
252571082Smjacob	else
252671082Smjacob		cpage = SA_DATA_COMPRESSION_PAGE;
252739213Sgibbs
252839213Sgibbsretry:
252939213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
253039213Sgibbs
253139213Sgibbs	if (params_to_get & SA_PARAM_COMPRESSION) {
253239213Sgibbs		if (softc->quirks & SA_QUIRK_NOCOMP) {
253339213Sgibbs			*comp_supported = FALSE;
253439213Sgibbs			params_to_get &= ~SA_PARAM_COMPRESSION;
253539213Sgibbs		} else
253646962Smjacob			mode_buffer_len += sizeof (sa_comp_t);
253739213Sgibbs	}
253854099Smjacob
2539170829Sscottl	/* XXX Fix M_NOWAIT */
2540170829Sscottl	mode_buffer = malloc(mode_buffer_len, M_SCSISA, M_NOWAIT | M_ZERO);
2541170829Sscottl	if (mode_buffer == NULL) {
2542170829Sscottl		xpt_release_ccb(ccb);
2543170829Sscottl		return (ENOMEM);
2544170829Sscottl	}
254539213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
254639213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
254739213Sgibbs
254842716Smjacob	/* it is safe to retry this */
254942716Smjacob	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
255042716Smjacob	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?
255146962Smjacob	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,
255279100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
255339213Sgibbs
255454099Smjacob	error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2555112006Sphk	    softc->device_stats);
255654099Smjacob	QFRLS(ccb);
255739213Sgibbs
255839213Sgibbs	status = ccb->ccb_h.status & CAM_STATUS_MASK;
255939213Sgibbs
256042716Smjacob	if (error == EINVAL && (params_to_get & SA_PARAM_COMPRESSION) != 0) {
256139213Sgibbs		/*
256246962Smjacob		 * Hmm. Let's see if we can try another page...
256346962Smjacob		 * If we've already done that, give up on compression
256446962Smjacob		 * for this device and remember this for the future
256546962Smjacob		 * and attempt the request without asking for compression
256646962Smjacob		 * info.
256739213Sgibbs		 */
256846962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
256946962Smjacob			cpage = SA_DEVICE_CONFIGURATION_PAGE;
257046962Smjacob			goto retry;
257146962Smjacob		}
257239213Sgibbs		softc->quirks |= SA_QUIRK_NOCOMP;
2573169562Sscottl		free(mode_buffer, M_SCSISA);
257439213Sgibbs		goto retry;
257546962Smjacob	} else if (status == CAM_SCSI_STATUS_ERROR) {
257646962Smjacob		/* Tell the user about the fatal error. */
257746962Smjacob		scsi_sense_print(&ccb->csio);
257846962Smjacob		goto sagetparamsexit;
257946962Smjacob	}
258039213Sgibbs
258146962Smjacob	/*
258246962Smjacob	 * If the user only wants the compression information, and
258346962Smjacob	 * the device doesn't send back the block descriptor, it's
258446962Smjacob	 * no big deal.  If the user wants more than just
258546962Smjacob	 * compression, though, and the device doesn't pass back the
258646962Smjacob	 * block descriptor, we need to send another mode sense to
258746962Smjacob	 * get the block descriptor.
258846962Smjacob	 */
258946962Smjacob	if ((mode_hdr->blk_desc_len == 0) &&
259046962Smjacob	    (params_to_get & SA_PARAM_COMPRESSION) &&
259146962Smjacob	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {
259239213Sgibbs
259339213Sgibbs		/*
259446962Smjacob		 * Decrease the mode buffer length by the size of
259546962Smjacob		 * the compression page, to make sure the data
259646962Smjacob		 * there doesn't get overwritten.
259739213Sgibbs		 */
259846962Smjacob		mode_buffer_len -= sizeof (sa_comp_t);
259939213Sgibbs
260046962Smjacob		/*
260146962Smjacob		 * Now move the compression page that we presumably
260246962Smjacob		 * got back down the memory chunk a little bit so
260346962Smjacob		 * it doesn't get spammed.
260446962Smjacob		 */
260554099Smjacob		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));
260654099Smjacob		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));
260739213Sgibbs
260846962Smjacob		/*
260946962Smjacob		 * Now, we issue another mode sense and just ask
261046962Smjacob		 * for the block descriptor, etc.
261146962Smjacob		 */
261239213Sgibbs
261346962Smjacob		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
261446962Smjacob		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,
261579100Smjacob		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE,
261679100Smjacob		    SCSIOP_TIMEOUT);
261739213Sgibbs
261854099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2619112006Sphk		    softc->device_stats);
262054099Smjacob		QFRLS(ccb);
262139213Sgibbs
262246962Smjacob		if (error != 0)
262346962Smjacob			goto sagetparamsexit;
262446962Smjacob	}
262539213Sgibbs
262646962Smjacob	if (params_to_get & SA_PARAM_BLOCKSIZE)
262746962Smjacob		*blocksize = scsi_3btoul(mode_blk->blklen);
262839213Sgibbs
262946962Smjacob	if (params_to_get & SA_PARAM_NUMBLOCKS)
263046962Smjacob		*numblocks = scsi_3btoul(mode_blk->nblocks);
263139213Sgibbs
263246962Smjacob	if (params_to_get & SA_PARAM_BUFF_MODE)
263346962Smjacob		*buff_mode = mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;
263439213Sgibbs
263546962Smjacob	if (params_to_get & SA_PARAM_DENSITY)
263646962Smjacob		*density = mode_blk->density;
263739213Sgibbs
263846962Smjacob	if (params_to_get & SA_PARAM_WP)
263946962Smjacob		*write_protect = (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;
264039213Sgibbs
264146962Smjacob	if (params_to_get & SA_PARAM_SPEED)
264246962Smjacob		*speed = mode_hdr->dev_spec & SMH_SA_SPEED_MASK;
264339213Sgibbs
264446962Smjacob	if (params_to_get & SA_PARAM_COMPRESSION) {
264554099Smjacob		sa_comp_t *ntcs = (sa_comp_t *) &mode_blk[1];
264646962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
264746962Smjacob			struct scsi_data_compression_page *cp = &ntcs->dcomp;
264846962Smjacob			*comp_supported =
264946962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;
265046962Smjacob			*comp_enabled =
265146962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;
265246962Smjacob			*comp_algorithm = scsi_4btoul(cp->comp_algorithm);
265346962Smjacob		} else {
265446962Smjacob			struct scsi_dev_conf_page *cp = &ntcs->dconf;
265546962Smjacob			/*
265646962Smjacob			 * We don't really know whether this device supports
265746962Smjacob			 * Data Compression if the the algorithm field is
265846962Smjacob			 * zero. Just say we do.
265946962Smjacob			 */
266046962Smjacob			*comp_supported = TRUE;
266146962Smjacob			*comp_enabled =
266246962Smjacob			    (cp->sel_comp_alg != SA_COMP_NONE)? TRUE : FALSE;
266346962Smjacob			*comp_algorithm = cp->sel_comp_alg;
266441906Smjacob		}
266546962Smjacob		if (tcs != NULL)
266654099Smjacob			bcopy(ntcs, tcs, sizeof (sa_comp_t));
266739213Sgibbs	}
266839213Sgibbs
266946962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
267046962Smjacob		int idx;
267146962Smjacob		char *xyz = mode_buffer;
267246962Smjacob		xpt_print_path(periph->path);
267346962Smjacob		printf("Mode Sense Data=");
267446962Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
267546962Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
267646962Smjacob		printf("\n");
267746962Smjacob	}
267846962Smjacob
267939213Sgibbssagetparamsexit:
268039213Sgibbs
268139213Sgibbs	xpt_release_ccb(ccb);
2682169562Sscottl	free(mode_buffer, M_SCSISA);
268354099Smjacob	return (error);
268439213Sgibbs}
268539213Sgibbs
268639213Sgibbs/*
268739213Sgibbs * The purpose of this function is to set one of four different parameters
268839213Sgibbs * for a tape drive:
268939213Sgibbs *	- blocksize
269039213Sgibbs *	- density
269139213Sgibbs *	- compression / compression algorithm
269239213Sgibbs *	- buffering mode
269339213Sgibbs *
269439213Sgibbs * The assumption is that this will be called from saioctl(), and therefore
269539213Sgibbs * from a process context.  Thus the waiting malloc calls below.  If that
269639213Sgibbs * assumption ever changes, the malloc calls should be changed to be
269739213Sgibbs * NOWAIT mallocs.
269839213Sgibbs *
269939213Sgibbs * Any or all of the four parameters may be set when this function is
270039213Sgibbs * called.  It should handle setting more than one parameter at once.
270139213Sgibbs */
270239213Sgibbsstatic int
270339213Sgibbssasetparams(struct cam_periph *periph, sa_params params_to_set,
270446962Smjacob	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,
270542563Smjacob	    u_int32_t sense_flags)
270639213Sgibbs{
270739213Sgibbs	struct sa_softc *softc;
270839213Sgibbs	u_int32_t current_blocksize;
270946962Smjacob	u_int32_t current_calg;
271039213Sgibbs	u_int8_t current_density;
271139213Sgibbs	u_int8_t current_speed;
271239213Sgibbs	int comp_enabled, comp_supported;
271339213Sgibbs	void *mode_buffer;
271439213Sgibbs	int mode_buffer_len;
271539213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
271639213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
271746962Smjacob	sa_comp_t *ccomp, *cpage;
271839213Sgibbs	int buff_mode;
271946962Smjacob	union ccb *ccb = NULL;
272039213Sgibbs	int error;
272139213Sgibbs
272239213Sgibbs	softc = (struct sa_softc *)periph->softc;
272339213Sgibbs
2724170830Sscottl	ccomp = malloc(sizeof (sa_comp_t), M_SCSISA, M_NOWAIT);
2725170830Sscottl	if (ccomp == NULL)
2726170830Sscottl		return (ENOMEM);
272739213Sgibbs
272839213Sgibbs	/*
272939213Sgibbs	 * Since it doesn't make sense to set the number of blocks, or
273039213Sgibbs	 * write protection, we won't try to get the current value.  We
273139213Sgibbs	 * always want to get the blocksize, so we can set it back to the
273239213Sgibbs	 * proper value.
273339213Sgibbs	 */
273446962Smjacob	error = sagetparams(periph,
273546962Smjacob	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,
273646962Smjacob	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,
273746962Smjacob	    &current_speed, &comp_supported, &comp_enabled,
273846962Smjacob	    &current_calg, ccomp);
273939213Sgibbs
274039213Sgibbs	if (error != 0) {
2741169562Sscottl		free(ccomp, M_SCSISA);
274254099Smjacob		return (error);
274339213Sgibbs	}
274439213Sgibbs
274539213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
274639213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION)
274746962Smjacob		mode_buffer_len += sizeof (sa_comp_t);
274839213Sgibbs
2749170830Sscottl	mode_buffer = malloc(mode_buffer_len, M_SCSISA, M_NOWAIT | M_ZERO);
2750170830Sscottl	if (mode_buffer == NULL) {
2751170830Sscottl		free(ccomp, M_SCSISA);
2752170830Sscottl		return (ENOMEM);
2753170830Sscottl	}
275439213Sgibbs
275539213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
275639213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
275739213Sgibbs
275853259Smjacob	ccb = cam_periph_getccb(periph, 1);
275953259Smjacob
276053259Smjacobretry:
276153259Smjacob
276239213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION) {
276353259Smjacob		if (mode_blk) {
276453259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
276553259Smjacob		} else {
276653259Smjacob			cpage = (sa_comp_t *)&mode_hdr[1];
276753259Smjacob		}
276846962Smjacob		bcopy(ccomp, cpage, sizeof (sa_comp_t));
276954099Smjacob		cpage->hdr.pagecode &= ~0x80;
277039213Sgibbs	} else
277146962Smjacob		cpage = NULL;
277239213Sgibbs
277339213Sgibbs	/*
277439213Sgibbs	 * If the caller wants us to set the blocksize, use the one they
277539213Sgibbs	 * pass in.  Otherwise, use the blocksize we got back from the
277639213Sgibbs	 * mode select above.
277739213Sgibbs	 */
277853259Smjacob	if (mode_blk) {
277953259Smjacob		if (params_to_set & SA_PARAM_BLOCKSIZE)
278053259Smjacob			scsi_ulto3b(blocksize, mode_blk->blklen);
278153259Smjacob		else
278253259Smjacob			scsi_ulto3b(current_blocksize, mode_blk->blklen);
278339213Sgibbs
278453259Smjacob		/*
278553259Smjacob		 * Set density if requested, else preserve old density.
278653259Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
278753259Smjacob		 * devices, else density we've latched up in our softc.
278853259Smjacob		 */
278953259Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
279053259Smjacob			mode_blk->density = density;
279153259Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
279253259Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
279353259Smjacob		} else {
279453259Smjacob			mode_blk->density = softc->media_density;
279553259Smjacob		}
279641674Smjacob	}
279739213Sgibbs
279839213Sgibbs	/*
279939213Sgibbs	 * For mode selects, these two fields must be zero.
280039213Sgibbs	 */
280139213Sgibbs	mode_hdr->data_length = 0;
280239213Sgibbs	mode_hdr->medium_type = 0;
280339213Sgibbs
280439213Sgibbs	/* set the speed to the current value */
280539213Sgibbs	mode_hdr->dev_spec = current_speed;
280639213Sgibbs
2807120019Smjacob	/* if set, set single-initiator buffering mode */
2808120019Smjacob	if (softc->buffer_mode == SMH_SA_BUF_MODE_SIBUF) {
2809120019Smjacob		mode_hdr->dev_spec |= SMH_SA_BUF_MODE_SIBUF;
2810120019Smjacob	}
281139213Sgibbs
281253259Smjacob	if (mode_blk)
281353259Smjacob		mode_hdr->blk_desc_len = sizeof(struct scsi_mode_blk_desc);
281453259Smjacob	else
281553259Smjacob		mode_hdr->blk_desc_len = 0;
281639213Sgibbs
281739213Sgibbs	/*
281839213Sgibbs	 * First, if the user wants us to set the compression algorithm or
281939213Sgibbs	 * just turn compression on, check to make sure that this drive
282039213Sgibbs	 * supports compression.
282139213Sgibbs	 */
282246962Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
282339213Sgibbs		/*
282439213Sgibbs		 * If the compression algorithm is 0, disable compression.
282539213Sgibbs		 * If the compression algorithm is non-zero, enable
282639213Sgibbs		 * compression and set the compression type to the
282739213Sgibbs		 * specified compression algorithm, unless the algorithm is
282839213Sgibbs		 * MT_COMP_ENABLE.  In that case, we look at the
282939213Sgibbs		 * compression algorithm that is currently set and if it is
283039213Sgibbs		 * non-zero, we leave it as-is.  If it is zero, and we have
283139213Sgibbs		 * saved a compression algorithm from a time when
283239213Sgibbs		 * compression was enabled before, set the compression to
283339213Sgibbs		 * the saved value.
283439213Sgibbs		 */
283554099Smjacob		switch (ccomp->hdr.pagecode & ~0x80) {
2836115660Smjacob		case SA_DEVICE_CONFIGURATION_PAGE:
2837115660Smjacob		{
2838115660Smjacob			struct scsi_dev_conf_page *dcp = &cpage->dconf;
2839115660Smjacob			if (calg == 0) {
2840115660Smjacob				dcp->sel_comp_alg = SA_COMP_NONE;
2841115660Smjacob				break;
2842115660Smjacob			}
2843115660Smjacob			if (calg != MT_COMP_ENABLE) {
2844115660Smjacob				dcp->sel_comp_alg = calg;
2845115660Smjacob			} else if (dcp->sel_comp_alg == SA_COMP_NONE &&
2846115660Smjacob			    softc->saved_comp_algorithm != 0) {
2847115660Smjacob				dcp->sel_comp_alg = softc->saved_comp_algorithm;
2848115660Smjacob			}
2849115660Smjacob			break;
2850115660Smjacob		}
285146962Smjacob		case SA_DATA_COMPRESSION_PAGE:
285246962Smjacob		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {
285346962Smjacob			struct scsi_data_compression_page *dcp = &cpage->dcomp;
285446962Smjacob			if (calg == 0) {
285554099Smjacob				/*
285654099Smjacob				 * Disable compression, but leave the
285754099Smjacob				 * decompression and the capability bit
285854099Smjacob				 * alone.
285954099Smjacob				 */
286054099Smjacob				dcp->dce_and_dcc = SA_DCP_DCC;
286154099Smjacob				dcp->dde_and_red |= SA_DCP_DDE;
286246962Smjacob				break;
286346962Smjacob			}
286453259Smjacob			/* enable compression && decompression */
286554099Smjacob			dcp->dce_and_dcc = SA_DCP_DCE | SA_DCP_DCC;
286654099Smjacob			dcp->dde_and_red |= SA_DCP_DDE;
286753259Smjacob			/*
286853259Smjacob			 * If there, use compression algorithm from caller.
286953259Smjacob			 * Otherwise, if there's a saved compression algorithm
287053259Smjacob			 * and there is no current algorithm, use the saved
287153259Smjacob			 * algorithm. Else parrot back what we got and hope
287253259Smjacob			 * for the best.
287353259Smjacob			 */
287446962Smjacob			if (calg != MT_COMP_ENABLE) {
287546962Smjacob				scsi_ulto4b(calg, dcp->comp_algorithm);
287653259Smjacob				scsi_ulto4b(calg, dcp->decomp_algorithm);
287746962Smjacob			} else if (scsi_4btoul(dcp->comp_algorithm) == 0 &&
287846962Smjacob			    softc->saved_comp_algorithm != 0) {
287939213Sgibbs				scsi_ulto4b(softc->saved_comp_algorithm,
288046962Smjacob				    dcp->comp_algorithm);
288153259Smjacob				scsi_ulto4b(softc->saved_comp_algorithm,
288253259Smjacob				    dcp->decomp_algorithm);
288339213Sgibbs			}
288446962Smjacob			break;
288539213Sgibbs		}
2886115660Smjacob		/*
2887115660Smjacob		 * Compression does not appear to be supported-
2888115660Smjacob		 * at least via the DATA COMPRESSION page. It
2889115660Smjacob		 * would be too much to ask us to believe that
2890115660Smjacob		 * the page itself is supported, but incorrectly
2891115660Smjacob		 * reports an ability to manipulate data compression,
2892115660Smjacob		 * so we'll assume that this device doesn't support
2893115660Smjacob		 * compression. We can just fall through for that.
2894115660Smjacob		 */
2895115660Smjacob		/* FALLTHROUGH */
289646962Smjacob		default:
289746962Smjacob			/*
289854099Smjacob			 * The drive doesn't seem to support compression,
289946962Smjacob			 * so turn off the set compression bit.
290046962Smjacob			 */
290146962Smjacob			params_to_set &= ~SA_PARAM_COMPRESSION;
2902164906Smjacob			xpt_print(periph->path,
2903164906Smjacob			    "device does not seem to support compression\n");
290454099Smjacob
290546962Smjacob			/*
290646962Smjacob			 * If that was the only thing the user wanted us to set,
290746962Smjacob			 * clean up allocated resources and return with
290846962Smjacob			 * 'operation not supported'.
290946962Smjacob			 */
291046962Smjacob			if (params_to_set == SA_PARAM_NONE) {
2911169562Sscottl				free(mode_buffer, M_SCSISA);
291254099Smjacob				xpt_release_ccb(ccb);
291354099Smjacob				return (ENODEV);
291446962Smjacob			}
291546962Smjacob
291646962Smjacob			/*
291746962Smjacob			 * That wasn't the only thing the user wanted us to set.
291846962Smjacob			 * So, decrease the stated mode buffer length by the
291946962Smjacob			 * size of the compression mode page.
292046962Smjacob			 */
292146962Smjacob			mode_buffer_len -= sizeof(sa_comp_t);
292246962Smjacob		}
292339213Sgibbs	}
292439213Sgibbs
292546962Smjacob	/* It is safe to retry this operation */
292646962Smjacob	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
292746962Smjacob	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,
292879100Smjacob	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
292941674Smjacob
293046962Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
2931112006Sphk	    sense_flags, softc->device_stats);
293254099Smjacob	QFRLS(ccb);
293339213Sgibbs
293446962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
293541906Smjacob		int idx;
293641674Smjacob		char *xyz = mode_buffer;
293741674Smjacob		xpt_print_path(periph->path);
293842009Smjacob		printf("Err%d, Mode Select Data=", error);
293941906Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
294042009Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
294141674Smjacob		printf("\n");
294241674Smjacob	}
294341674Smjacob
294439213Sgibbs
294553259Smjacob	if (error) {
294639213Sgibbs		/*
294753259Smjacob		 * If we can, try without setting density/blocksize.
294853259Smjacob		 */
294953259Smjacob		if (mode_blk) {
295053259Smjacob			if ((params_to_set &
295153259Smjacob			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) == 0) {
295253259Smjacob				mode_blk = NULL;
295353259Smjacob				goto retry;
295453259Smjacob			}
295553259Smjacob		} else {
295653259Smjacob			mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
295753259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
295853259Smjacob		}
295953259Smjacob
296053259Smjacob		/*
296139213Sgibbs		 * If we were setting the blocksize, and that failed, we
296239213Sgibbs		 * want to set it to its original value.  If we weren't
296339213Sgibbs		 * setting the blocksize, we don't want to change it.
296439213Sgibbs		 */
296539213Sgibbs		scsi_ulto3b(current_blocksize, mode_blk->blklen);
296639213Sgibbs
296739213Sgibbs		/*
296841674Smjacob		 * Set density if requested, else preserve old density.
296941674Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
297041674Smjacob		 * devices, else density we've latched up in our softc.
297139213Sgibbs		 */
297241674Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
297339213Sgibbs			mode_blk->density = current_density;
297441674Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
297541674Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
297641674Smjacob		} else {
297741674Smjacob			mode_blk->density = softc->media_density;
297841674Smjacob		}
297939213Sgibbs
298039213Sgibbs		if (params_to_set & SA_PARAM_COMPRESSION)
298146962Smjacob			bcopy(ccomp, cpage, sizeof (sa_comp_t));
298239213Sgibbs
298339213Sgibbs		/*
298439213Sgibbs		 * The retry count is the only CCB field that might have been
298539213Sgibbs		 * changed that we care about, so reset it back to 1.
298639213Sgibbs		 */
298739213Sgibbs		ccb->ccb_h.retry_count = 1;
298854099Smjacob		cam_periph_runccb(ccb, saerror, 0, sense_flags,
2989112006Sphk		    softc->device_stats);
299054099Smjacob		QFRLS(ccb);
299139213Sgibbs	}
299239213Sgibbs
299353259Smjacob	xpt_release_ccb(ccb);
299453259Smjacob
299546962Smjacob	if (ccomp != NULL)
2996169562Sscottl		free(ccomp, M_SCSISA);
299739213Sgibbs
299841948Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
299941948Smjacob		if (error) {
300041948Smjacob			softc->flags &= ~SA_FLAG_COMP_ENABLED;
300146962Smjacob			/*
300246962Smjacob			 * Even if we get an error setting compression,
300346962Smjacob			 * do not say that we don't support it. We could
300446962Smjacob			 * have been wrong, or it may be media specific.
300546962Smjacob			 *	softc->flags &= ~SA_FLAG_COMP_SUPP;
300646962Smjacob			 */
300741948Smjacob			softc->saved_comp_algorithm = softc->comp_algorithm;
300841948Smjacob			softc->comp_algorithm = 0;
300941948Smjacob		} else {
301041948Smjacob			softc->flags |= SA_FLAG_COMP_ENABLED;
301146962Smjacob			softc->comp_algorithm = calg;
301241948Smjacob		}
301341948Smjacob	}
301441948Smjacob
3015169562Sscottl	free(mode_buffer, M_SCSISA);
301654099Smjacob	return (error);
301739213Sgibbs}
301839213Sgibbs
301939213Sgibbsstatic void
302039213Sgibbssaprevent(struct cam_periph *periph, int action)
302139213Sgibbs{
302239213Sgibbs	struct	sa_softc *softc;
302339213Sgibbs	union	ccb *ccb;
302442735Smjacob	int	error, sf;
302539213Sgibbs
302639213Sgibbs	softc = (struct sa_softc *)periph->softc;
302739213Sgibbs
302842735Smjacob	if ((action == PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) == 0)
302939213Sgibbs		return;
303042735Smjacob	if ((action == PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) != 0)
303142735Smjacob		return;
303239213Sgibbs
303356981Smjacob	/*
303456981Smjacob	 * We can be quiet about illegal requests.
303556981Smjacob	 */
303656981Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
303742735Smjacob		sf = 0;
303856981Smjacob	} else
303942735Smjacob		sf = SF_QUIET_IR;
304042735Smjacob
304142716Smjacob	ccb = cam_periph_getccb(periph, 1);
304239213Sgibbs
304342716Smjacob	/* It is safe to retry this operation */
304442716Smjacob	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,
304579100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
304639213Sgibbs
3047112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats);
304853522Smjacob	QFRLS(ccb);
304939213Sgibbs	if (error == 0) {
305039213Sgibbs		if (action == PR_ALLOW)
305139213Sgibbs			softc->flags &= ~SA_FLAG_TAPE_LOCKED;
305239213Sgibbs		else
305339213Sgibbs			softc->flags |= SA_FLAG_TAPE_LOCKED;
305439213Sgibbs	}
305539213Sgibbs
305639213Sgibbs	xpt_release_ccb(ccb);
305739213Sgibbs}
305839213Sgibbs
305939213Sgibbsstatic int
306039213Sgibbssarewind(struct cam_periph *periph)
306139213Sgibbs{
306239213Sgibbs	union	ccb *ccb;
306339213Sgibbs	struct	sa_softc *softc;
306439213Sgibbs	int	error;
306539213Sgibbs
306639213Sgibbs	softc = (struct sa_softc *)periph->softc;
306739213Sgibbs
306846962Smjacob	ccb = cam_periph_getccb(periph, 1);
306939213Sgibbs
307042716Smjacob	/* It is safe to retry this operation */
307146962Smjacob	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
307253259Smjacob	    SSD_FULL_SIZE, REWIND_TIMEOUT);
307339213Sgibbs
307443636Smjacob	softc->dsreg = MTIO_DSREG_REW;
3075112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
307643636Smjacob	softc->dsreg = MTIO_DSREG_REST;
307739213Sgibbs
307839213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
307942716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
308042716Smjacob
308139213Sgibbs	xpt_release_ccb(ccb);
308243636Smjacob	if (error == 0)
308343636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
308443636Smjacob	else
308543636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
308639213Sgibbs	return (error);
308739213Sgibbs}
308839213Sgibbs
308939213Sgibbsstatic int
309039213Sgibbssaspace(struct cam_periph *periph, int count, scsi_space_code code)
309139213Sgibbs{
309239213Sgibbs	union	ccb *ccb;
309339213Sgibbs	struct	sa_softc *softc;
309439213Sgibbs	int	error;
309539213Sgibbs
309639213Sgibbs	softc = (struct sa_softc *)periph->softc;
309739213Sgibbs
309846962Smjacob	ccb = cam_periph_getccb(periph, 1);
309939213Sgibbs
310042716Smjacob	/* This cannot be retried */
310139213Sgibbs
310242716Smjacob	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,
310353259Smjacob	    SSD_FULL_SIZE, SPACE_TIMEOUT);
310439213Sgibbs
310571087Smjacob	/*
310671087Smjacob	 * Clear residual because we will be using it.
310771087Smjacob	 */
310871087Smjacob	softc->last_ctl_resid = 0;
310971087Smjacob
311043636Smjacob	softc->dsreg = (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;
3111112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
311243636Smjacob	softc->dsreg = MTIO_DSREG_REST;
311342716Smjacob
311439213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
311542716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
311642716Smjacob
311739213Sgibbs	xpt_release_ccb(ccb);
311843636Smjacob
311942716Smjacob	/*
312043636Smjacob	 * If a spacing operation has failed, we need to invalidate
312143636Smjacob	 * this mount.
312243636Smjacob	 *
312343636Smjacob	 * If the spacing operation was setmarks or to end of recorded data,
312443636Smjacob	 * we no longer know our relative position.
312543636Smjacob	 *
312671087Smjacob	 * If the spacing operations was spacing files in reverse, we
312771087Smjacob	 * take account of the residual, but still check against less
312871087Smjacob	 * than zero- if we've gone negative, we must have hit BOT.
312971087Smjacob	 *
313071087Smjacob	 * If the spacing operations was spacing records in reverse and
313171087Smjacob	 * we have a residual, we've either hit BOT or hit a filemark.
313271087Smjacob	 * In the former case, we know our new record number (0). In
313371087Smjacob	 * the latter case, we have absolutely no idea what the real
313471087Smjacob	 * record number is- we've stopped between the end of the last
313571087Smjacob	 * record in the previous file and the filemark that stopped
313671087Smjacob	 * our spacing backwards.
313742716Smjacob	 */
313843636Smjacob	if (error) {
313943636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
314043636Smjacob	} else if (code == SS_SETMARKS || code == SS_EOD) {
314143636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
314243636Smjacob	} else if (code == SS_FILEMARKS && softc->fileno != (daddr_t) -1) {
314371087Smjacob		softc->fileno += (count - softc->last_ctl_resid);
314471087Smjacob		if (softc->fileno < 0)	/* we must of hit BOT */
314571087Smjacob			softc->fileno = 0;
314643636Smjacob		softc->blkno = 0;
314743636Smjacob	} else if (code == SS_BLOCKS && softc->blkno != (daddr_t) -1) {
314871087Smjacob		softc->blkno += (count - softc->last_ctl_resid);
314971087Smjacob		if (count < 0) {
315071087Smjacob			if (softc->last_ctl_resid || softc->blkno < 0) {
315171087Smjacob				if (softc->fileno == 0) {
315271087Smjacob					softc->blkno = 0;
315371087Smjacob				} else {
315471087Smjacob					softc->blkno = (daddr_t) -1;
315571087Smjacob				}
315671087Smjacob			}
315771087Smjacob		}
315843636Smjacob	}
315939213Sgibbs	return (error);
316039213Sgibbs}
316139213Sgibbs
316239213Sgibbsstatic int
316339213Sgibbssawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)
316439213Sgibbs{
316539213Sgibbs	union	ccb *ccb;
316639213Sgibbs	struct	sa_softc *softc;
316771087Smjacob	int	error, nwm = 0;
316839213Sgibbs
316939213Sgibbs	softc = (struct sa_softc *)periph->softc;
3170154360Smjacob	if (softc->open_rdonly)
3171154360Smjacob		return (EBADF);
317239213Sgibbs
317346962Smjacob	ccb = cam_periph_getccb(periph, 1);
317471087Smjacob	/*
317571087Smjacob	 * Clear residual because we will be using it.
317671087Smjacob	 */
317771087Smjacob	softc->last_ctl_resid = 0;
317839213Sgibbs
317943636Smjacob	softc->dsreg = MTIO_DSREG_FMK;
318042716Smjacob	/* this *must* not be retried */
318142716Smjacob	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,
318279100Smjacob	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT);
318343636Smjacob	softc->dsreg = MTIO_DSREG_REST;
318439213Sgibbs
318543636Smjacob
3186112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
318739213Sgibbs
318839213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
318942716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
319039213Sgibbs
319141918Smjacob	if (error == 0 && nmarks) {
319241918Smjacob		struct sa_softc *softc = (struct sa_softc *)periph->softc;
319371087Smjacob		nwm = nmarks - softc->last_ctl_resid;
319471087Smjacob		softc->filemarks += nwm;
319539213Sgibbs	}
319671087Smjacob
319741918Smjacob	xpt_release_ccb(ccb);
319843636Smjacob
319943636Smjacob	/*
320043636Smjacob	 * Update relative positions (if we're doing that).
320143636Smjacob	 */
320243636Smjacob	if (error) {
320343636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
320443636Smjacob	} else if (softc->fileno != (daddr_t) -1) {
320571087Smjacob		softc->fileno += nwm;
320643636Smjacob		softc->blkno = 0;
320743636Smjacob	}
320841918Smjacob	return (error);
320941918Smjacob}
321039213Sgibbs
321141918Smjacobstatic int
321241918Smjacobsardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
321341918Smjacob{
321441918Smjacob	struct scsi_tape_position_data loc;
321541918Smjacob	union ccb *ccb;
321646962Smjacob	struct sa_softc *softc = (struct sa_softc *)periph->softc;
321741918Smjacob	int error;
321841918Smjacob
321941918Smjacob	/*
322071082Smjacob	 * We try and flush any buffered writes here if we were writing
322171082Smjacob	 * and we're trying to get hardware block position. It eats
322271082Smjacob	 * up performance substantially, but I'm wary of drive firmware.
322346962Smjacob	 *
322471082Smjacob	 * I think that *logical* block position is probably okay-
322571082Smjacob	 * but hardware block position might have to wait for data
322671082Smjacob	 * to hit media to be valid. Caveat Emptor.
322741918Smjacob	 */
322841918Smjacob
322971082Smjacob	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {
323046962Smjacob		error = sawritefilemarks(periph, 0, 0);
323146962Smjacob		if (error && error != EACCES)
323246962Smjacob			return (error);
323346962Smjacob	}
323441918Smjacob
323566678Smjacob	ccb = cam_periph_getccb(periph, 1);
323641918Smjacob	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
323779100Smjacob	    hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
323843636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
3239112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
324043636Smjacob	softc->dsreg = MTIO_DSREG_REST;
324141918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
324241918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
324341918Smjacob
324441918Smjacob	if (error == 0) {
324541918Smjacob		if (loc.flags & SA_RPOS_UNCERTAIN) {
324641918Smjacob			error = EINVAL;		/* nothing is certain */
324741918Smjacob		} else {
324841918Smjacob			*blkptr = scsi_4btoul(loc.firstblk);
324941918Smjacob		}
325041918Smjacob	}
325141918Smjacob
325239213Sgibbs	xpt_release_ccb(ccb);
325339213Sgibbs	return (error);
325439213Sgibbs}
325539213Sgibbs
325639213Sgibbsstatic int
325741918Smjacobsasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
325841918Smjacob{
325941918Smjacob	union ccb *ccb;
326041918Smjacob	struct sa_softc *softc;
326141918Smjacob	int error;
326241918Smjacob
326341918Smjacob	/*
326446962Smjacob	 * We used to try and flush any buffered writes here.
326546962Smjacob	 * Now we push this onto user applications to either
326646962Smjacob	 * flush the pending writes themselves (via a zero count
326746962Smjacob	 * WRITE FILEMARKS command) or they can trust their tape
326846962Smjacob	 * drive to do this correctly for them.
326946962Smjacob 	 */
327041918Smjacob
327141918Smjacob	softc = (struct sa_softc *)periph->softc;
327246962Smjacob	ccb = cam_periph_getccb(periph, 1);
327341918Smjacob
327443636Smjacob
327541918Smjacob	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
327679100Smjacob	    hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT);
327743636Smjacob
327879100Smjacob
327943636Smjacob	softc->dsreg = MTIO_DSREG_POS;
3280112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
328143636Smjacob	softc->dsreg = MTIO_DSREG_REST;
328241918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
328341918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
328441918Smjacob	xpt_release_ccb(ccb);
328541918Smjacob	/*
328646962Smjacob	 * Note relative file && block number position as now unknown.
328741918Smjacob	 */
328843636Smjacob	softc->fileno = softc->blkno = (daddr_t) -1;
328941918Smjacob	return (error);
329041918Smjacob}
329141918Smjacob
329241918Smjacobstatic int
329339213Sgibbssaretension(struct cam_periph *periph)
329439213Sgibbs{
329539213Sgibbs	union ccb *ccb;
329639213Sgibbs	struct sa_softc *softc;
329739213Sgibbs	int error;
329839213Sgibbs
329939213Sgibbs	softc = (struct sa_softc *)periph->softc;
330039213Sgibbs
330146962Smjacob	ccb = cam_periph_getccb(periph, 1);
330239213Sgibbs
330342716Smjacob	/* It is safe to retry this operation */
330442716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
330553259Smjacob	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);
330639213Sgibbs
330743636Smjacob	softc->dsreg = MTIO_DSREG_TEN;
3308112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
330943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
331039213Sgibbs
331139213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
331242716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
331339213Sgibbs	xpt_release_ccb(ccb);
331443636Smjacob	if (error == 0)
331543636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
331643636Smjacob	else
331743636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
331854099Smjacob	return (error);
331939213Sgibbs}
332039213Sgibbs
332139213Sgibbsstatic int
332239213Sgibbssareservereleaseunit(struct cam_periph *periph, int reserve)
332339213Sgibbs{
332439213Sgibbs	union ccb *ccb;
332539213Sgibbs	struct sa_softc *softc;
332654099Smjacob	int error;
332739213Sgibbs
332842009Smjacob	softc = (struct sa_softc *)periph->softc;
332942716Smjacob	ccb = cam_periph_getccb(periph,  1);
333039213Sgibbs
333142716Smjacob	/* It is safe to retry this operation */
333254099Smjacob	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
333379100Smjacob	    FALSE,  0, SSD_FULL_SIZE,  SCSIOP_TIMEOUT, reserve);
333443636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
333554099Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
3336112006Sphk	    SF_RETRY_UA | SF_NO_PRINT, softc->device_stats);
333743636Smjacob	softc->dsreg = MTIO_DSREG_REST;
333854099Smjacob	QFRLS(ccb);
333939213Sgibbs	xpt_release_ccb(ccb);
334039213Sgibbs
334141674Smjacob	/*
334241674Smjacob	 * If the error was Illegal Request, then the device doesn't support
334341674Smjacob	 * RESERVE/RELEASE. This is not an error.
334441674Smjacob	 */
334542009Smjacob	if (error == EINVAL) {
334641674Smjacob		error = 0;
334742009Smjacob	}
334841674Smjacob
334939213Sgibbs	return (error);
335039213Sgibbs}
335139213Sgibbs
335239213Sgibbsstatic int
335339213Sgibbssaloadunload(struct cam_periph *periph, int load)
335439213Sgibbs{
335539213Sgibbs	union	ccb *ccb;
335639213Sgibbs	struct	sa_softc *softc;
335739213Sgibbs	int	error;
335839213Sgibbs
335939213Sgibbs	softc = (struct sa_softc *)periph->softc;
336039213Sgibbs
336146962Smjacob	ccb = cam_periph_getccb(periph, 1);
336239213Sgibbs
336342716Smjacob	/* It is safe to retry this operation */
336442716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
336554099Smjacob	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);
336639213Sgibbs
336743636Smjacob	softc->dsreg = (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;
3368112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
336943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
337054099Smjacob	QFRLS(ccb);
337139213Sgibbs	xpt_release_ccb(ccb);
337243636Smjacob
337343636Smjacob	if (error || load == 0)
337443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
337543636Smjacob	else if (error == 0)
337643636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
337739213Sgibbs	return (error);
337839213Sgibbs}
337939213Sgibbs
338039213Sgibbsstatic int
338139213Sgibbssaerase(struct cam_periph *periph, int longerase)
338239213Sgibbs{
338339213Sgibbs
338439213Sgibbs	union	ccb *ccb;
338539213Sgibbs	struct	sa_softc *softc;
338639213Sgibbs	int error;
338739213Sgibbs
338839213Sgibbs	softc = (struct sa_softc *)periph->softc;
3389154360Smjacob	if (softc->open_rdonly)
3390154360Smjacob		return (EBADF);
339139213Sgibbs
339246962Smjacob	ccb = cam_periph_getccb(periph, 1);
339339213Sgibbs
339443636Smjacob	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,
339553259Smjacob	    SSD_FULL_SIZE, ERASE_TIMEOUT);
339639213Sgibbs
339743636Smjacob	softc->dsreg = MTIO_DSREG_ZER;
3398112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
339943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
340039213Sgibbs
340139213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
340243636Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
340339213Sgibbs	xpt_release_ccb(ccb);
340439213Sgibbs	return (error);
340539213Sgibbs}
340639213Sgibbs
340755205Speter#endif /* _KERNEL */
340839213Sgibbs
340939213Sgibbs/*
341039213Sgibbs * Read tape block limits command.
341139213Sgibbs */
341239213Sgibbsvoid
341339213Sgibbsscsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,
341439213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
341539213Sgibbs		   u_int8_t tag_action,
341639213Sgibbs		   struct scsi_read_block_limits_data *rlimit_buf,
341739213Sgibbs		   u_int8_t sense_len, u_int32_t timeout)
341839213Sgibbs{
341939213Sgibbs	struct scsi_read_block_limits *scsi_cmd;
342039213Sgibbs
342146962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
342246962Smjacob	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,
342346962Smjacob	     sizeof(*scsi_cmd), timeout);
342439213Sgibbs
342539213Sgibbs	scsi_cmd = (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;
342639213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
342739213Sgibbs	scsi_cmd->opcode = READ_BLOCK_LIMITS;
342839213Sgibbs}
342939213Sgibbs
343039213Sgibbsvoid
343139213Sgibbsscsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,
343239213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
343339213Sgibbs		   u_int8_t tag_action, int readop, int sli,
343439213Sgibbs		   int fixed, u_int32_t length, u_int8_t *data_ptr,
343539213Sgibbs		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)
343639213Sgibbs{
343739213Sgibbs	struct scsi_sa_rw *scsi_cmd;
343839213Sgibbs
343939213Sgibbs	scsi_cmd = (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;
344039213Sgibbs	scsi_cmd->opcode = readop ? SA_READ : SA_WRITE;
344139213Sgibbs	scsi_cmd->sli_fixed = 0;
344239213Sgibbs	if (sli && readop)
344339213Sgibbs		scsi_cmd->sli_fixed |= SAR_SLI;
344439213Sgibbs	if (fixed)
344539213Sgibbs		scsi_cmd->sli_fixed |= SARW_FIXED;
344639213Sgibbs	scsi_ulto3b(length, scsi_cmd->length);
344739213Sgibbs	scsi_cmd->control = 0;
344839213Sgibbs
344946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, readop ? CAM_DIR_IN : CAM_DIR_OUT,
345046962Smjacob	    tag_action, data_ptr, dxfer_len, sense_len,
345146962Smjacob	    sizeof(*scsi_cmd), timeout);
345239213Sgibbs}
345339213Sgibbs
345439213Sgibbsvoid
345539213Sgibbsscsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,
345639213Sgibbs		 void (*cbfcnp)(struct cam_periph *, union ccb *),
345739213Sgibbs		 u_int8_t tag_action, int immediate, int eot,
345839213Sgibbs		 int reten, int load, u_int8_t sense_len,
345939213Sgibbs		 u_int32_t timeout)
346039213Sgibbs{
346139213Sgibbs	struct scsi_load_unload *scsi_cmd;
346239213Sgibbs
346339213Sgibbs	scsi_cmd = (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;
346439213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
346539213Sgibbs	scsi_cmd->opcode = LOAD_UNLOAD;
346639213Sgibbs	if (immediate)
346739213Sgibbs		scsi_cmd->immediate = SLU_IMMED;
346839213Sgibbs	if (eot)
346939213Sgibbs		scsi_cmd->eot_reten_load |= SLU_EOT;
347039213Sgibbs	if (reten)
347139213Sgibbs		scsi_cmd->eot_reten_load |= SLU_RETEN;
347239213Sgibbs	if (load)
347339213Sgibbs		scsi_cmd->eot_reten_load |= SLU_LOAD;
347439213Sgibbs
347546962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
347646962Smjacob	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);
347739213Sgibbs}
347839213Sgibbs
347939213Sgibbsvoid
348039213Sgibbsscsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,
348139213Sgibbs	    void (*cbfcnp)(struct cam_periph *, union ccb *),
348239213Sgibbs	    u_int8_t tag_action, int immediate, u_int8_t sense_len,
348339213Sgibbs	    u_int32_t timeout)
348439213Sgibbs{
348539213Sgibbs	struct scsi_rewind *scsi_cmd;
348639213Sgibbs
348739213Sgibbs	scsi_cmd = (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;
348839213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
348939213Sgibbs	scsi_cmd->opcode = REWIND;
349039213Sgibbs	if (immediate)
349139213Sgibbs		scsi_cmd->immediate = SREW_IMMED;
349239213Sgibbs
349346962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
349446962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
349539213Sgibbs}
349639213Sgibbs
349739213Sgibbsvoid
349839213Sgibbsscsi_space(struct ccb_scsiio *csio, u_int32_t retries,
349939213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
350039213Sgibbs	   u_int8_t tag_action, scsi_space_code code,
350139213Sgibbs	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)
350239213Sgibbs{
350339213Sgibbs	struct scsi_space *scsi_cmd;
350439213Sgibbs
350539213Sgibbs	scsi_cmd = (struct scsi_space *)&csio->cdb_io.cdb_bytes;
350639213Sgibbs	scsi_cmd->opcode = SPACE;
350739213Sgibbs	scsi_cmd->code = code;
350839213Sgibbs	scsi_ulto3b(count, scsi_cmd->count);
350939213Sgibbs	scsi_cmd->control = 0;
351039213Sgibbs
351146962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
351246962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
351339213Sgibbs}
351439213Sgibbs
351539213Sgibbsvoid
351639213Sgibbsscsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,
351739213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
351839213Sgibbs		     u_int8_t tag_action, int immediate, int setmark,
351939213Sgibbs		     u_int32_t num_marks, u_int8_t sense_len,
352039213Sgibbs		     u_int32_t timeout)
352139213Sgibbs{
352239213Sgibbs	struct scsi_write_filemarks *scsi_cmd;
352339213Sgibbs
352439213Sgibbs	scsi_cmd = (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;
352539213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
352639213Sgibbs	scsi_cmd->opcode = WRITE_FILEMARKS;
352739213Sgibbs	if (immediate)
352839213Sgibbs		scsi_cmd->byte2 |= SWFMRK_IMMED;
352939213Sgibbs	if (setmark)
353039213Sgibbs		scsi_cmd->byte2 |= SWFMRK_WSMK;
353139213Sgibbs
353239213Sgibbs	scsi_ulto3b(num_marks, scsi_cmd->num_marks);
353339213Sgibbs
353446962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
353546962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
353639213Sgibbs}
353739213Sgibbs
353839213Sgibbs/*
353939213Sgibbs * The reserve and release unit commands differ only by their opcodes.
354039213Sgibbs */
354139213Sgibbsvoid
354239213Sgibbsscsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,
354339213Sgibbs			  void (*cbfcnp)(struct cam_periph *, union ccb *),
354439213Sgibbs			  u_int8_t tag_action, int third_party,
354539213Sgibbs			  int third_party_id, u_int8_t sense_len,
354639213Sgibbs			  u_int32_t timeout, int reserve)
354739213Sgibbs{
354839213Sgibbs	struct scsi_reserve_release_unit *scsi_cmd;
354939213Sgibbs
355039213Sgibbs	scsi_cmd = (struct scsi_reserve_release_unit *)&csio->cdb_io.cdb_bytes;
355139213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
355239213Sgibbs
355339213Sgibbs	if (reserve)
355439213Sgibbs		scsi_cmd->opcode = RESERVE_UNIT;
355539213Sgibbs	else
355639213Sgibbs		scsi_cmd->opcode = RELEASE_UNIT;
355739213Sgibbs
355839213Sgibbs	if (third_party) {
355939213Sgibbs		scsi_cmd->lun_thirdparty |= SRRU_3RD_PARTY;
356039213Sgibbs		scsi_cmd->lun_thirdparty |=
356139213Sgibbs			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);
356239213Sgibbs	}
356339213Sgibbs
356446962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
356546962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
356639213Sgibbs}
356739213Sgibbs
356839213Sgibbsvoid
356939213Sgibbsscsi_erase(struct ccb_scsiio *csio, u_int32_t retries,
357039213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
357139213Sgibbs	   u_int8_t tag_action, int immediate, int long_erase,
357239213Sgibbs	   u_int8_t sense_len, u_int32_t timeout)
357339213Sgibbs{
357439213Sgibbs	struct scsi_erase *scsi_cmd;
357539213Sgibbs
357639213Sgibbs	scsi_cmd = (struct scsi_erase *)&csio->cdb_io.cdb_bytes;
357739213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
357839213Sgibbs
357939213Sgibbs	scsi_cmd->opcode = ERASE;
358039213Sgibbs
358139213Sgibbs	if (immediate)
358239213Sgibbs		scsi_cmd->lun_imm_long |= SE_IMMED;
358339213Sgibbs
358439213Sgibbs	if (long_erase)
358539213Sgibbs		scsi_cmd->lun_imm_long |= SE_LONG;
358639213Sgibbs
358746962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
358846962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
358939213Sgibbs}
359041918Smjacob
359141918Smjacob/*
359241918Smjacob * Read Tape Position command.
359341918Smjacob */
359441918Smjacobvoid
359541918Smjacobscsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,
359641918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
359741918Smjacob		   u_int8_t tag_action, int hardsoft,
359841918Smjacob		   struct scsi_tape_position_data *sbp,
359941918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
360041918Smjacob{
360141918Smjacob	struct scsi_tape_read_position *scmd;
360241918Smjacob
360341918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
360441918Smjacob	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);
360541918Smjacob	scmd = (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;
360641918Smjacob	bzero(scmd, sizeof(*scmd));
360741918Smjacob	scmd->opcode = READ_POSITION;
360841918Smjacob	scmd->byte1 = hardsoft;
360941918Smjacob}
361041918Smjacob
361141918Smjacob/*
361241918Smjacob * Set Tape Position command.
361341918Smjacob */
361441918Smjacobvoid
361541918Smjacobscsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,
361641918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
361741918Smjacob		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,
361841918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
361941918Smjacob{
362041918Smjacob	struct scsi_tape_locate *scmd;
362141918Smjacob
362241918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
362341918Smjacob	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);
362441918Smjacob	scmd = (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;
362541918Smjacob	bzero(scmd, sizeof(*scmd));
362641918Smjacob	scmd->opcode = LOCATE;
362741918Smjacob	if (hardsoft)
362841918Smjacob		scmd->byte1 |= SA_SPOS_BT;
362941918Smjacob	scsi_ulto4b(blkno, scmd->blkaddr);
363041918Smjacob}
3631