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$");
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
105249132Smavstatic MALLOC_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
176251386Ssmh#define SA_QUIRK_BIT_STRING	\
177251386Ssmh	"\020"			\
178251386Ssmh	"\001NOCOMP"		\
179251386Ssmh	"\002FIXED"		\
180251386Ssmh	"\003VARIABLE"		\
181251386Ssmh	"\0042FM"		\
182251386Ssmh	"\0051FM"		\
183251386Ssmh	"\006NODREAD"		\
184251386Ssmh	"\007NO_MODESEL"	\
185251386Ssmh	"\010NO_CPAGE"
186251386Ssmh
187191304Sed#define	SAMODE(z)	(dev2unit(z) & 0x3)
188191304Sed#define	SADENSITY(z)	((dev2unit(z) >> 2) & 0x3)
189191304Sed#define	SA_IS_CTRL(z)	(dev2unit(z) & (1 << 4))
19053283Smjacob
19153259Smjacob#define SA_NOT_CTLDEV	0
19253259Smjacob#define SA_CTLDEV	1
19353259Smjacob
19453259Smjacob#define SA_ATYPE_R	0
19553259Smjacob#define SA_ATYPE_NR	1
19653259Smjacob#define SA_ATYPE_ER	2
19753259Smjacob
198191304Sed#define	SAMINOR(ctl, mode, access) \
199191304Sed	((ctl << 4) | (mode << 2) | (access & 0x3))
20053259Smjacob
20153259Smjacob#define SA_NUM_MODES	4
20253259Smjacobstruct sa_devs {
203130585Sphk	struct cdev *ctl_dev;
20453259Smjacob	struct sa_mode_devs {
205130585Sphk		struct cdev *r_dev;
206130585Sphk		struct cdev *nr_dev;
207130585Sphk		struct cdev *er_dev;
20853259Smjacob	} mode_devs[SA_NUM_MODES];
20953259Smjacob};
21053259Smjacob
21139213Sgibbsstruct sa_softc {
21239213Sgibbs	sa_state	state;
21339213Sgibbs	sa_flags	flags;
21439213Sgibbs	sa_quirks	quirks;
215255000Sken	u_int		si_flags;
21659249Sphk	struct		bio_queue_head bio_queue;
21746962Smjacob	int		queue_count;
218112006Sphk	struct		devstat *device_stats;
21953259Smjacob	struct sa_devs	devs;
22039213Sgibbs	int		blk_gran;
22139213Sgibbs	int		blk_mask;
22239213Sgibbs	int		blk_shift;
22339213Sgibbs	u_int32_t	max_blk;
22439213Sgibbs	u_int32_t	min_blk;
225255000Sken	u_int32_t	maxio;
22641674Smjacob	u_int32_t	comp_algorithm;
22741674Smjacob	u_int32_t	saved_comp_algorithm;
22839213Sgibbs	u_int32_t	media_blksize;
22941906Smjacob	u_int32_t	last_media_blksize;
23039213Sgibbs	u_int32_t	media_numblks;
23141674Smjacob	u_int8_t	media_density;
23239213Sgibbs	u_int8_t	speed;
23341674Smjacob	u_int8_t	scsi_rev;
23443636Smjacob	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */
23539213Sgibbs	int		buffer_mode;
23639213Sgibbs	int		filemarks;
23739213Sgibbs	union		ccb saved_ccb;
23871268Smjacob	int		last_resid_was_io;
23946962Smjacob
24041948Smjacob	/*
24143636Smjacob	 * Relative to BOT Location.
24243636Smjacob	 */
24343636Smjacob	daddr_t		fileno;
24443636Smjacob	daddr_t		blkno;
24543636Smjacob
24643636Smjacob	/*
24741948Smjacob	 * Latched Error Info
24841948Smjacob	 */
24942009Smjacob	struct {
25042009Smjacob		struct scsi_sense_data _last_io_sense;
251226067Sken		u_int64_t _last_io_resid;
25242009Smjacob		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];
25342009Smjacob		struct scsi_sense_data _last_ctl_sense;
254226067Sken		u_int64_t _last_ctl_resid;
25542009Smjacob		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];
25642009Smjacob#define	last_io_sense	errinfo._last_io_sense
25742009Smjacob#define	last_io_resid	errinfo._last_io_resid
25842009Smjacob#define	last_io_cdb	errinfo._last_io_cdb
25942009Smjacob#define	last_ctl_sense	errinfo._last_ctl_sense
26042009Smjacob#define	last_ctl_resid	errinfo._last_ctl_resid
26142009Smjacob#define	last_ctl_cdb	errinfo._last_ctl_cdb
26242009Smjacob	} errinfo;
26343636Smjacob	/*
26443636Smjacob	 * Misc other flags/state
26543636Smjacob	 */
26643636Smjacob	u_int32_t
267154360Smjacob					: 29,
268154360Smjacob		open_rdonly		: 1,	/* open read-only */
269154360Smjacob		open_pending_mount	: 1,	/* open pending mount */
270154360Smjacob		ctrl_mode		: 1;	/* control device open */
27139213Sgibbs};
27239213Sgibbs
27339213Sgibbsstruct sa_quirk_entry {
27442563Smjacob	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */
27542563Smjacob	sa_quirks quirks;	/* specific quirk type */
27642563Smjacob	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */
27739213Sgibbs};
27839213Sgibbs
27939213Sgibbsstatic struct sa_quirk_entry sa_quirk_table[] =
28039213Sgibbs{
28139213Sgibbs	{
28260235Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",
28360235Smjacob		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |
28460235Smjacob		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768
28560235Smjacob	},
28660235Smjacob	{
28739213Sgibbs		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28877810Snon		  "Python 06408*", "*"}, SA_QUIRK_NODREAD, 0
28977581Snon	},
29077581Snon	{
29177581Snon		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29256981Smjacob		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0
29341351Sjoerg	},
29441351Sjoerg	{
29542130Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29656981Smjacob		  "Python*", "*"}, SA_QUIRK_NODREAD, 0
29756981Smjacob	},
29856981Smjacob	{
29956981Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
30043636Smjacob		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
30142130Smjacob	},
30242130Smjacob	{
30342563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
30468500Smjacob		  "VIPER 2525 25462", "-011"},
30568500Smjacob		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0
30668500Smjacob	},
30768500Smjacob	{
30868500Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
30946962Smjacob		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
31042563Smjacob	},
31171082Smjacob#if	0
31242563Smjacob	{
31342533Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31471082Smjacob		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,
31571082Smjacob	},
31671082Smjacob#endif
317107943Strhodes 	{
318107943Strhodes 		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
319107943Strhodes		  "C56*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
320107943Strhodes	},
32171082Smjacob	{
32271082Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
32346962Smjacob		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
32446962Smjacob	},
32546962Smjacob	{
32646962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
32744354Smjacob		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
32842533Smjacob	},
32942533Smjacob	{
33042716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
33142716Smjacob		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
33242716Smjacob	},
33342716Smjacob	{
33442716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",
33542716Smjacob		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
33642716Smjacob	},
33742716Smjacob	{
33842716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",
33942716Smjacob		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
34042716Smjacob	},
34151744Smjacob	{	/* jreynold@primenet.com */
34251744Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
34351744Smjacob		"STT8000N*", "*"}, SA_QUIRK_1FM, 0
34451744Smjacob	},
34551875Smjacob	{	/* mike@sentex.net */
34651875Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
34751875Smjacob		"STT20000*", "*"}, SA_QUIRK_1FM, 0
34851875Smjacob	},
34942716Smjacob	{
350231321Seadler		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "SEAGATE",
351231321Seadler		"DAT    06241-XXX", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
352231321Seadler	},
353231321Seadler	{
35441351Sjoerg		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35543636Smjacob		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
35642563Smjacob	},
35742563Smjacob	{
35842563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35947519Smjacob		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
36047519Smjacob	},
36147519Smjacob	{
36247519Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
36348192Smjacob		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
36448192Smjacob	},
36548192Smjacob	{
36648192Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
36743636Smjacob		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
36842563Smjacob	},
36942563Smjacob	{
37046962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
37146962Smjacob		  " SLR*", "*"}, SA_QUIRK_1FM, 0
37246962Smjacob	},
37346962Smjacob	{
37442563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
37543636Smjacob		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
37645752Smjacob	},
37745752Smjacob	{
37845752Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
37945752Smjacob		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
38039213Sgibbs	}
38139213Sgibbs};
38239213Sgibbs
38339213Sgibbsstatic	d_open_t	saopen;
38439213Sgibbsstatic	d_close_t	saclose;
38539213Sgibbsstatic	d_strategy_t	sastrategy;
38639213Sgibbsstatic	d_ioctl_t	saioctl;
38739213Sgibbsstatic	periph_init_t	sainit;
38839213Sgibbsstatic	periph_ctor_t	saregister;
38940603Skenstatic	periph_oninv_t	saoninvalidate;
39039213Sgibbsstatic	periph_dtor_t	sacleanup;
39139213Sgibbsstatic	periph_start_t	sastart;
39239213Sgibbsstatic	void		saasync(void *callback_arg, u_int32_t code,
39339213Sgibbs				struct cam_path *path, void *arg);
39439213Sgibbsstatic	void		sadone(struct cam_periph *periph,
39539213Sgibbs			       union ccb *start_ccb);
39639213Sgibbsstatic  int		saerror(union ccb *ccb, u_int32_t cam_flags,
39739213Sgibbs				u_int32_t sense_flags);
39868114Smjacobstatic int		samarkswanted(struct cam_periph *);
39939213Sgibbsstatic int		sacheckeod(struct cam_periph *periph);
40039213Sgibbsstatic int		sagetparams(struct cam_periph *periph,
40139213Sgibbs				    sa_params params_to_get,
40239213Sgibbs				    u_int32_t *blocksize, u_int8_t *density,
40339213Sgibbs				    u_int32_t *numblocks, int *buff_mode,
40439213Sgibbs				    u_int8_t *write_protect, u_int8_t *speed,
40539213Sgibbs				    int *comp_supported, int *comp_enabled,
40639213Sgibbs				    u_int32_t *comp_algorithm,
40746962Smjacob				    sa_comp_t *comp_page);
40839213Sgibbsstatic int		sasetparams(struct cam_periph *periph,
40939213Sgibbs				    sa_params params_to_set,
41039213Sgibbs				    u_int32_t blocksize, u_int8_t density,
41142563Smjacob				    u_int32_t comp_algorithm,
41242563Smjacob				    u_int32_t sense_flags);
41339213Sgibbsstatic void		saprevent(struct cam_periph *periph, int action);
41439213Sgibbsstatic int		sarewind(struct cam_periph *periph);
41539213Sgibbsstatic int		saspace(struct cam_periph *periph, int count,
41639213Sgibbs				scsi_space_code code);
417130585Sphkstatic int		samount(struct cam_periph *, int, struct cdev *);
41839213Sgibbsstatic int		saretension(struct cam_periph *periph);
41939213Sgibbsstatic int		sareservereleaseunit(struct cam_periph *periph,
42039213Sgibbs					     int reserve);
42139213Sgibbsstatic int		saloadunload(struct cam_periph *periph, int load);
42239213Sgibbsstatic int		saerase(struct cam_periph *periph, int longerase);
42339213Sgibbsstatic int		sawritefilemarks(struct cam_periph *periph,
42439213Sgibbs					 int nmarks, int setmarks);
42541918Smjacobstatic int		sardpos(struct cam_periph *periph, int, u_int32_t *);
42641918Smjacobstatic int		sasetpos(struct cam_periph *periph, int, u_int32_t *);
42739213Sgibbs
42841918Smjacob
42939213Sgibbsstatic struct periph_driver sadriver =
43039213Sgibbs{
43139213Sgibbs	sainit, "sa",
43239213Sgibbs	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0
43339213Sgibbs};
43439213Sgibbs
43572119SpeterPERIPHDRIVER_DECLARE(sa, sadriver);
43639213Sgibbs
43739213Sgibbs/* For 2.2-stable support */
43839213Sgibbs#ifndef D_TAPE
43939213Sgibbs#define D_TAPE 0
44039213Sgibbs#endif
44139213Sgibbs
44239213Sgibbs
44347625Sphkstatic struct cdevsw sa_cdevsw = {
444126080Sphk	.d_version =	D_VERSION,
445111815Sphk	.d_open =	saopen,
446111815Sphk	.d_close =	saclose,
447111815Sphk	.d_read =	physread,
448111815Sphk	.d_write =	physwrite,
449111815Sphk	.d_ioctl =	saioctl,
450111815Sphk	.d_strategy =	sastrategy,
451111815Sphk	.d_name =	"sa",
452255000Sken	.d_flags =	D_TAPE,
45339213Sgibbs};
45439213Sgibbs
45539213Sgibbsstatic int
456130585Sphksaopen(struct cdev *dev, int flags, int fmt, struct thread *td)
45739213Sgibbs{
45839213Sgibbs	struct cam_periph *periph;
45939213Sgibbs	struct sa_softc *softc;
46039213Sgibbs	int error;
46139213Sgibbs
462101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
463168752Sscottl	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
464168752Sscottl		return (ENXIO);
46553259Smjacob	}
466168752Sscottl
467168752Sscottl	cam_periph_lock(periph);
468168752Sscottl
46939213Sgibbs	softc = (struct sa_softc *)periph->softc;
47039213Sgibbs
47146962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
472191304Sed	    ("saopen(%s): softc=0x%x\n", devtoname(dev), softc->flags));
47339213Sgibbs
47443636Smjacob	if (SA_IS_CTRL(dev)) {
47543636Smjacob		softc->ctrl_mode = 1;
47653259Smjacob		cam_periph_unlock(periph);
47743636Smjacob		return (0);
47843636Smjacob	}
47943636Smjacob
480168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
481168752Sscottl		cam_periph_unlock(periph);
482168752Sscottl		cam_periph_release(periph);
483168752Sscottl		return (error);
484168752Sscottl	}
485168752Sscottl
48653259Smjacob	if (softc->flags & SA_FLAG_OPEN) {
48753259Smjacob		error = EBUSY;
48853259Smjacob	} else if (softc->flags & SA_FLAG_INVALID) {
48953259Smjacob		error = ENXIO;
49053259Smjacob	} else {
49153259Smjacob		/*
492154360Smjacob		 * Preserve whether this is a read_only open.
493154360Smjacob		 */
494154360Smjacob		softc->open_rdonly = (flags & O_RDWR) == O_RDONLY;
495154360Smjacob
496154360Smjacob		/*
49753259Smjacob		 * The function samount ensures media is loaded and ready.
49853259Smjacob		 * It also does a device RESERVE if the tape isn't yet mounted.
499154360Smjacob		 *
500154360Smjacob		 * If the mount fails and this was a non-blocking open,
501154360Smjacob		 * make this a 'open_pending_mount' action.
50253259Smjacob		 */
50353259Smjacob		error = samount(periph, flags, dev);
504154360Smjacob		if (error && (flags & O_NONBLOCK)) {
505154360Smjacob			softc->flags |= SA_FLAG_OPEN;
506154360Smjacob			softc->open_pending_mount = 1;
507168752Sscottl			cam_periph_unhold(periph);
508154360Smjacob			cam_periph_unlock(periph);
509154360Smjacob			return (0);
510154360Smjacob		}
51139213Sgibbs	}
51239213Sgibbs
51353259Smjacob	if (error) {
514168752Sscottl		cam_periph_unhold(periph);
515168752Sscottl		cam_periph_unlock(periph);
51653259Smjacob		cam_periph_release(periph);
517168752Sscottl		return (error);
51839213Sgibbs	}
519168752Sscottl
520168752Sscottl	saprevent(periph, PR_PREVENT);
521168752Sscottl	softc->flags |= SA_FLAG_OPEN;
522168752Sscottl
523168752Sscottl	cam_periph_unhold(periph);
52439213Sgibbs	cam_periph_unlock(periph);
52539213Sgibbs	return (error);
52639213Sgibbs}
52739213Sgibbs
52839213Sgibbsstatic int
529130585Sphksaclose(struct cdev *dev, int flag, int fmt, struct thread *td)
53039213Sgibbs{
53139213Sgibbs	struct	cam_periph *periph;
53239213Sgibbs	struct	sa_softc *softc;
533191304Sed	int	mode, error, writing, tmp;
53442009Smjacob	int	closedbits = SA_FLAG_OPEN;
53539213Sgibbs
53639213Sgibbs	mode = SAMODE(dev);
537101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
53839213Sgibbs	if (periph == NULL)
53939213Sgibbs		return (ENXIO);
54039213Sgibbs
541168752Sscottl	cam_periph_lock(periph);
542168752Sscottl
54339213Sgibbs	softc = (struct sa_softc *)periph->softc;
54439213Sgibbs
54546962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
546191304Sed	    ("saclose(%s): softc=0x%x\n", devtoname(dev), softc->flags));
54746962Smjacob
54846962Smjacob
549154360Smjacob	softc->open_rdonly = 0;
55043636Smjacob	if (SA_IS_CTRL(dev)) {
55143636Smjacob		softc->ctrl_mode = 0;
552168752Sscottl		cam_periph_unlock(periph);
55353259Smjacob		cam_periph_release(periph);
55443636Smjacob		return (0);
55543636Smjacob	}
55643636Smjacob
557154360Smjacob	if (softc->open_pending_mount) {
558154360Smjacob		softc->flags &= ~SA_FLAG_OPEN;
559154360Smjacob		softc->open_pending_mount = 0;
560168752Sscottl		cam_periph_unlock(periph);
561154360Smjacob		cam_periph_release(periph);
562154360Smjacob		return (0);
563154360Smjacob	}
564154360Smjacob
565168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO)) != 0) {
566168752Sscottl		cam_periph_unlock(periph);
567168752Sscottl		return (error);
568168752Sscottl	}
569168752Sscottl
57041906Smjacob	/*
57146962Smjacob	 * Were we writing the tape?
57241906Smjacob	 */
57346962Smjacob	writing = (softc->flags & SA_FLAG_TAPE_WRITTEN) != 0;
57446962Smjacob
57546962Smjacob	/*
57646962Smjacob	 * See whether or not we need to write filemarks. If this
57746962Smjacob	 * fails, we probably have to assume we've lost tape
57846962Smjacob	 * position.
57946962Smjacob	 */
58041906Smjacob	error = sacheckeod(periph);
58141906Smjacob	if (error) {
582164906Smjacob		xpt_print(periph->path,
583164906Smjacob		    "failed to write terminating filemark(s)\n");
58446962Smjacob		softc->flags |= SA_FLAG_TAPE_FROZEN;
58541906Smjacob	}
58639213Sgibbs
58741906Smjacob	/*
58841906Smjacob	 * Whatever we end up doing, allow users to eject tapes from here on.
58941906Smjacob	 */
59039213Sgibbs	saprevent(periph, PR_ALLOW);
59139213Sgibbs
59241906Smjacob	/*
59341906Smjacob	 * Decide how to end...
59441906Smjacob	 */
59553522Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
59653522Smjacob		closedbits |= SA_FLAG_TAPE_FROZEN;
59753522Smjacob	} else switch (mode) {
59839213Sgibbs	case SA_MODE_OFFLINE:
59946962Smjacob		/*
60046962Smjacob		 * An 'offline' close is an unconditional release of
60146962Smjacob		 * frozen && mount conditions, irrespective of whether
60246962Smjacob		 * these operations succeeded. The reason for this is
60346962Smjacob		 * to allow at least some kind of programmatic way
60446962Smjacob		 * around our state getting all fouled up. If somebody
60546962Smjacob		 * issues an 'offline' command, that will be allowed
60646962Smjacob		 * to clear state.
60746962Smjacob		 */
60846962Smjacob		(void) sarewind(periph);
60946962Smjacob		(void) saloadunload(periph, FALSE);
61046962Smjacob		closedbits |= SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;
61139213Sgibbs		break;
61241906Smjacob	case SA_MODE_REWIND:
61346962Smjacob		/*
61446962Smjacob		 * If the rewind fails, return an error- if anyone cares,
61546962Smjacob		 * but not overwriting any previous error.
61646962Smjacob		 *
61746962Smjacob		 * We don't clear the notion of mounted here, but we do
61846962Smjacob		 * clear the notion of frozen if we successfully rewound.
61946962Smjacob		 */
62046962Smjacob		tmp = sarewind(periph);
62146962Smjacob		if (tmp) {
62246962Smjacob			if (error != 0)
62346962Smjacob				error = tmp;
62446962Smjacob		} else {
62546962Smjacob			closedbits |= SA_FLAG_TAPE_FROZEN;
62646962Smjacob		}
62741906Smjacob		break;
62839213Sgibbs	case SA_MODE_NOREWIND:
62941906Smjacob		/*
63041906Smjacob		 * If we're not rewinding/unloading the tape, find out
63141906Smjacob		 * whether we need to back up over one of two filemarks
63241906Smjacob		 * we wrote (if we wrote two filemarks) so that appends
63341906Smjacob		 * from this point on will be sane.
63441906Smjacob		 */
63546962Smjacob		if (error == 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {
63646962Smjacob			tmp = saspace(periph, -1, SS_FILEMARKS);
63746962Smjacob			if (tmp) {
638164906Smjacob				xpt_print(periph->path, "unable to backspace "
639164906Smjacob				    "over one of double filemarks at end of "
640164906Smjacob				    "tape\n");
641164906Smjacob				xpt_print(periph->path, "it is possible that "
642164906Smjacob				    "this device needs a SA_QUIRK_1FM quirk set"
643164906Smjacob				    "for it\n");
64446962Smjacob				softc->flags |= SA_FLAG_TAPE_FROZEN;
64541906Smjacob			}
64641906Smjacob		}
64739213Sgibbs		break;
64846962Smjacob	default:
649164906Smjacob		xpt_print(periph->path, "unknown mode 0x%x in saclose\n", mode);
65046962Smjacob		/* NOTREACHED */
65146962Smjacob		break;
65239213Sgibbs	}
65339213Sgibbs
65441906Smjacob	/*
65541948Smjacob	 * We wish to note here that there are no more filemarks to be written.
65641906Smjacob	 */
65741906Smjacob	softc->filemarks = 0;
65841948Smjacob	softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
65941906Smjacob
66041906Smjacob	/*
66141906Smjacob	 * And we are no longer open for business.
66241906Smjacob	 */
66342009Smjacob	softc->flags &= ~closedbits;
66446962Smjacob
66546962Smjacob	/*
66646962Smjacob	 * Inform users if tape state if frozen....
66746962Smjacob	 */
66846962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
669164906Smjacob		xpt_print(periph->path, "tape is now frozen- use an OFFLINE, "
670164906Smjacob		    "REWIND or MTEOM command to clear this state.\n");
67146962Smjacob	}
67239213Sgibbs
67353259Smjacob	/* release the device if it is no longer mounted */
67453259Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0)
67553259Smjacob		sareservereleaseunit(periph, FALSE);
67639213Sgibbs
677168752Sscottl	cam_periph_unhold(periph);
67839213Sgibbs	cam_periph_unlock(periph);
67939213Sgibbs	cam_periph_release(periph);
68039213Sgibbs
68146962Smjacob	return (error);
68239213Sgibbs}
68339213Sgibbs
68439213Sgibbs/*
68539213Sgibbs * Actually translate the requested transfer into one the physical driver
68639213Sgibbs * can understand.  The transfer is described by a buf and will include
68739213Sgibbs * only one physical transfer.
68839213Sgibbs */
68939213Sgibbsstatic void
69059249Sphksastrategy(struct bio *bp)
69139213Sgibbs{
69239213Sgibbs	struct cam_periph *periph;
69339213Sgibbs	struct sa_softc *softc;
69439213Sgibbs
69576362Sphk	bp->bio_resid = bp->bio_bcount;
69659249Sphk	if (SA_IS_CTRL(bp->bio_dev)) {
69776362Sphk		biofinish(bp, NULL, EINVAL);
69876362Sphk		return;
69943636Smjacob	}
700101940Snjl	periph = (struct cam_periph *)bp->bio_dev->si_drv1;
70139213Sgibbs	if (periph == NULL) {
70276362Sphk		biofinish(bp, NULL, ENXIO);
70376362Sphk		return;
70439213Sgibbs	}
705168752Sscottl	cam_periph_lock(periph);
706168752Sscottl
70739213Sgibbs	softc = (struct sa_softc *)periph->softc;
70839213Sgibbs
70940603Sken	if (softc->flags & SA_FLAG_INVALID) {
710168752Sscottl		cam_periph_unlock(periph);
71176362Sphk		biofinish(bp, NULL, ENXIO);
71276362Sphk		return;
71340603Sken	}
71440603Sken
71546962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
716168752Sscottl		cam_periph_unlock(periph);
71776362Sphk		biofinish(bp, NULL, EPERM);
71876362Sphk		return;
71946962Smjacob	}
72046962Smjacob
721154360Smjacob	/*
722154360Smjacob	 * This should actually never occur as the write(2)
723154360Smjacob	 * system call traps attempts to write to a read-only
724154360Smjacob	 * file descriptor.
725154360Smjacob	 */
726154360Smjacob	if (bp->bio_cmd == BIO_WRITE && softc->open_rdonly) {
727168752Sscottl		cam_periph_unlock(periph);
728154360Smjacob		biofinish(bp, NULL, EBADF);
729154360Smjacob		return;
730154360Smjacob	}
731154360Smjacob
732154360Smjacob	if (softc->open_pending_mount) {
733154360Smjacob		int error = samount(periph, 0, bp->bio_dev);
734154360Smjacob		if (error) {
735168752Sscottl			cam_periph_unlock(periph);
736154360Smjacob			biofinish(bp, NULL, ENXIO);
737154360Smjacob			return;
738154360Smjacob		}
739154360Smjacob		saprevent(periph, PR_PREVENT);
740154360Smjacob		softc->open_pending_mount = 0;
741154360Smjacob	}
742154360Smjacob
743154360Smjacob
74439213Sgibbs	/*
745154360Smjacob	 * If it's a null transfer, return immediately
74639213Sgibbs	 */
74776362Sphk	if (bp->bio_bcount == 0) {
748168752Sscottl		cam_periph_unlock(periph);
74976362Sphk		biodone(bp);
75076362Sphk		return;
75176362Sphk	}
75239213Sgibbs
75339213Sgibbs	/* valid request?  */
75439213Sgibbs	if (softc->flags & SA_FLAG_FIXED) {
75539213Sgibbs		/*
75639213Sgibbs		 * Fixed block device.  The byte count must
75739213Sgibbs		 * be a multiple of our block size.
75839213Sgibbs		 */
75942716Smjacob		if (((softc->blk_mask != ~0) &&
76059249Sphk		    ((bp->bio_bcount & softc->blk_mask) != 0)) ||
76142716Smjacob		    ((softc->blk_mask == ~0) &&
76259249Sphk		    ((bp->bio_bcount % softc->min_blk) != 0))) {
763164906Smjacob			xpt_print(periph->path, "Invalid request.  Fixed block "
764164906Smjacob			    "device requests must be a multiple of %d bytes\n",
765164906Smjacob			    softc->min_blk);
766168752Sscottl			cam_periph_unlock(periph);
76776362Sphk			biofinish(bp, NULL, EINVAL);
76876362Sphk			return;
76939213Sgibbs		}
77059249Sphk	} else if ((bp->bio_bcount > softc->max_blk) ||
77159249Sphk		   (bp->bio_bcount < softc->min_blk) ||
77259249Sphk		   (bp->bio_bcount & softc->blk_mask) != 0) {
77339213Sgibbs
77439213Sgibbs		xpt_print_path(periph->path);
775164906Smjacob		printf("Invalid request.  Variable block "
776164906Smjacob		    "device requests must be ");
77739213Sgibbs		if (softc->blk_mask != 0) {
77842716Smjacob			printf("a multiple of %d ", (0x1 << softc->blk_gran));
77939213Sgibbs		}
78042716Smjacob		printf("between %d and %d bytes\n", softc->min_blk,
78142716Smjacob		    softc->max_blk);
782168752Sscottl		cam_periph_unlock(periph);
78376362Sphk		biofinish(bp, NULL, EINVAL);
78476362Sphk		return;
78539213Sgibbs        }
78639213Sgibbs
78739213Sgibbs	/*
78842716Smjacob	 * Place it at the end of the queue.
78939213Sgibbs	 */
79059249Sphk	bioq_insert_tail(&softc->bio_queue, bp);
79146962Smjacob	softc->queue_count++;
792115660Smjacob#if	0
793115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
794115660Smjacob	    ("sastrategy: queuing a %ld %s byte %s\n", bp->bio_bcount,
795115660Smjacob 	    (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",
796115660Smjacob	    (bp->bio_cmd == BIO_READ)? "read" : "write"));
797115660Smjacob#endif
798115660Smjacob	if (softc->queue_count > 1) {
799115660Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
800115660Smjacob		    ("sastrategy: queue count now %d\n", softc->queue_count));
801115660Smjacob	}
80239213Sgibbs
80339213Sgibbs	/*
80439213Sgibbs	 * Schedule ourselves for performing the work.
80539213Sgibbs	 */
806198382Smav	xpt_schedule(periph, CAM_PRIORITY_NORMAL);
807168752Sscottl	cam_periph_unlock(periph);
80839213Sgibbs
80939213Sgibbs	return;
81039213Sgibbs}
81139213Sgibbs
812154360Smjacob
813154360Smjacob#define	PENDING_MOUNT_CHECK(softc, periph, dev)		\
814154360Smjacob	if (softc->open_pending_mount) {		\
815154360Smjacob		error = samount(periph, 0, dev);	\
816154360Smjacob		if (error) {				\
817154360Smjacob			break;				\
818154360Smjacob		}					\
819154360Smjacob		saprevent(periph, PR_PREVENT);		\
820154360Smjacob		softc->open_pending_mount = 0;		\
821154360Smjacob	}
822154360Smjacob
82339213Sgibbsstatic int
824130585Sphksaioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
82539213Sgibbs{
82639213Sgibbs	struct cam_periph *periph;
82739213Sgibbs	struct sa_softc *softc;
82848520Speter	scsi_space_code spaceop;
82943636Smjacob	int didlockperiph = 0;
83039213Sgibbs	int mode;
83153259Smjacob	int error = 0;
83239213Sgibbs
83339213Sgibbs	mode = SAMODE(dev);
83448520Speter	error = 0;		/* shut up gcc */
83548520Speter	spaceop = 0;		/* shut up gcc */
83639213Sgibbs
837101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
83839213Sgibbs	if (periph == NULL)
83939213Sgibbs		return (ENXIO);
84039213Sgibbs
841168752Sscottl	cam_periph_lock(periph);
84239213Sgibbs	softc = (struct sa_softc *)periph->softc;
84339213Sgibbs
84439213Sgibbs	/*
84543636Smjacob	 * Check for control mode accesses. We allow MTIOCGET and
84643636Smjacob	 * MTIOCERRSTAT (but need to be the only one open in order
84743636Smjacob	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY
84843636Smjacob	 * and MTCOMP (but need to be the only one accessing this
84943636Smjacob	 * device to run those).
85043636Smjacob	 */
85143636Smjacob
85243636Smjacob	if (SA_IS_CTRL(dev)) {
85343636Smjacob		switch (cmd) {
85446962Smjacob		case MTIOCGETEOTMODEL:
85543636Smjacob		case MTIOCGET:
85643636Smjacob			break;
85743636Smjacob		case MTIOCERRSTAT:
85843636Smjacob			/*
85943636Smjacob			 * If the periph isn't already locked, lock it
86043636Smjacob			 * so our MTIOCERRSTAT can reset latched error stats.
86143636Smjacob			 *
86243636Smjacob			 * If the periph is already locked, skip it because
86343636Smjacob			 * we're just getting status and it'll be up to the
86443636Smjacob			 * other thread that has this device open to do
86543636Smjacob			 * an MTIOCERRSTAT that would clear latched status.
86643636Smjacob			 */
86743636Smjacob			if ((periph->flags & CAM_PERIPH_LOCKED) == 0) {
868168752Sscottl				error = cam_periph_hold(periph, PRIBIO|PCATCH);
869226067Sken				if (error != 0) {
870226067Sken					cam_periph_unlock(periph);
87143636Smjacob					return (error);
872226067Sken				}
87343636Smjacob				didlockperiph = 1;
87443636Smjacob			}
87543636Smjacob			break;
87643636Smjacob
877145050Smjacob		case MTIOCTOP:
878145050Smjacob		{
879145050Smjacob			struct mtop *mt = (struct mtop *) arg;
880145050Smjacob
881145050Smjacob			/*
882145050Smjacob			 * Check to make sure it's an OP we can perform
883145050Smjacob			 * with no media inserted.
884145050Smjacob			 */
885145050Smjacob			switch (mt->mt_op) {
886145050Smjacob			case MTSETBSIZ:
887145050Smjacob			case MTSETDNSTY:
888145050Smjacob			case MTCOMP:
889145050Smjacob				mt = NULL;
890145050Smjacob				/* FALLTHROUGH */
891145050Smjacob			default:
892145050Smjacob				break;
893145050Smjacob			}
894145050Smjacob			if (mt != NULL) {
895145050Smjacob				break;
896145050Smjacob			}
897145050Smjacob			/* FALLTHROUGH */
898145050Smjacob		}
89946962Smjacob		case MTIOCSETEOTMODEL:
90043636Smjacob			/*
90143636Smjacob			 * We need to acquire the peripheral here rather
90243636Smjacob			 * than at open time because we are sharing writable
90343636Smjacob			 * access to data structures.
90443636Smjacob			 */
905168752Sscottl			error = cam_periph_hold(periph, PRIBIO|PCATCH);
906226067Sken			if (error != 0) {
907226067Sken				cam_periph_unlock(periph);
90843636Smjacob				return (error);
909226067Sken			}
91043636Smjacob			didlockperiph = 1;
91143636Smjacob			break;
91243636Smjacob
91343636Smjacob		default:
914226067Sken			cam_periph_unlock(periph);
91543636Smjacob			return (EINVAL);
91643636Smjacob		}
91743636Smjacob	}
91843636Smjacob
91943636Smjacob	/*
92039213Sgibbs	 * Find the device that the user is talking about
92139213Sgibbs	 */
92239213Sgibbs	switch (cmd) {
92339213Sgibbs	case MTIOCGET:
92439213Sgibbs	{
92539213Sgibbs		struct mtget *g = (struct mtget *)arg;
92639213Sgibbs
92753259Smjacob		/*
92853259Smjacob		 * If this isn't the control mode device, actually go out
92953259Smjacob		 * and ask the drive again what it's set to.
93053259Smjacob		 */
931154360Smjacob		if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) {
93253259Smjacob			u_int8_t write_protect;
93353259Smjacob			int comp_enabled, comp_supported;
93453259Smjacob			error = sagetparams(periph, SA_PARAM_ALL,
93553259Smjacob			    &softc->media_blksize, &softc->media_density,
93653259Smjacob			    &softc->media_numblks, &softc->buffer_mode,
93753259Smjacob			    &write_protect, &softc->speed, &comp_supported,
93853259Smjacob			    &comp_enabled, &softc->comp_algorithm, NULL);
93953259Smjacob			if (error)
94053259Smjacob				break;
94153259Smjacob			if (write_protect)
94253259Smjacob				softc->flags |= SA_FLAG_TAPE_WP;
94353259Smjacob			else
94453259Smjacob				softc->flags &= ~SA_FLAG_TAPE_WP;
94553259Smjacob			softc->flags &= ~(SA_FLAG_COMP_SUPP|
94653259Smjacob			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);
94753259Smjacob			if (comp_supported) {
94853259Smjacob				if (softc->saved_comp_algorithm == 0)
94953259Smjacob					softc->saved_comp_algorithm =
95053259Smjacob					    softc->comp_algorithm;
95153259Smjacob				softc->flags |= SA_FLAG_COMP_SUPP;
95253259Smjacob				if (comp_enabled)
95353259Smjacob					softc->flags |= SA_FLAG_COMP_ENABLED;
95453259Smjacob			} else
95553259Smjacob				softc->flags |= SA_FLAG_COMP_UNSUPP;
95653259Smjacob		}
95739213Sgibbs		bzero(g, sizeof(struct mtget));
95841948Smjacob		g->mt_type = MT_ISAR;
95939213Sgibbs		if (softc->flags & SA_FLAG_COMP_UNSUPP) {
96039213Sgibbs			g->mt_comp = MT_COMP_UNSUPP;
96139213Sgibbs			g->mt_comp0 = MT_COMP_UNSUPP;
96239213Sgibbs			g->mt_comp1 = MT_COMP_UNSUPP;
96339213Sgibbs			g->mt_comp2 = MT_COMP_UNSUPP;
96439213Sgibbs			g->mt_comp3 = MT_COMP_UNSUPP;
96539213Sgibbs		} else {
96646962Smjacob			if ((softc->flags & SA_FLAG_COMP_ENABLED) == 0) {
96746962Smjacob				g->mt_comp = MT_COMP_DISABLED;
96846962Smjacob			} else {
96946962Smjacob				g->mt_comp = softc->comp_algorithm;
97046962Smjacob			}
97139213Sgibbs			g->mt_comp0 = softc->comp_algorithm;
97239213Sgibbs			g->mt_comp1 = softc->comp_algorithm;
97339213Sgibbs			g->mt_comp2 = softc->comp_algorithm;
97439213Sgibbs			g->mt_comp3 = softc->comp_algorithm;
97539213Sgibbs		}
97646962Smjacob		g->mt_density = softc->media_density;
97739213Sgibbs		g->mt_density0 = softc->media_density;
97839213Sgibbs		g->mt_density1 = softc->media_density;
97939213Sgibbs		g->mt_density2 = softc->media_density;
98039213Sgibbs		g->mt_density3 = softc->media_density;
98146962Smjacob		g->mt_blksiz = softc->media_blksize;
98239213Sgibbs		g->mt_blksiz0 = softc->media_blksize;
98339213Sgibbs		g->mt_blksiz1 = softc->media_blksize;
98439213Sgibbs		g->mt_blksiz2 = softc->media_blksize;
98539213Sgibbs		g->mt_blksiz3 = softc->media_blksize;
98643636Smjacob		g->mt_fileno = softc->fileno;
98743636Smjacob		g->mt_blkno = softc->blkno;
98843636Smjacob		g->mt_dsreg = (short) softc->dsreg;
98971268Smjacob		/*
99071268Smjacob		 * Yes, we know that this is likely to overflow
99171268Smjacob		 */
99271268Smjacob		if (softc->last_resid_was_io) {
99371268Smjacob			if ((g->mt_resid = (short) softc->last_io_resid) != 0) {
99471268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
99571268Smjacob					softc->last_io_resid = 0;
99671268Smjacob				}
99771268Smjacob			}
99871268Smjacob		} else {
99971268Smjacob			if ((g->mt_resid = (short)softc->last_ctl_resid) != 0) {
100071268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
100171268Smjacob					softc->last_ctl_resid = 0;
100271268Smjacob				}
100371268Smjacob			}
100471268Smjacob		}
100539213Sgibbs		error = 0;
100639213Sgibbs		break;
100739213Sgibbs	}
100841948Smjacob	case MTIOCERRSTAT:
100941948Smjacob	{
101041948Smjacob		struct scsi_tape_errors *sep =
101141948Smjacob		    &((union mterrstat *)arg)->scsi_errstat;
101241948Smjacob
101341948Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
101441948Smjacob		    ("saioctl: MTIOCERRSTAT\n"));
101541948Smjacob
101641948Smjacob		bzero(sep, sizeof(*sep));
101741948Smjacob		sep->io_resid = softc->last_io_resid;
101841948Smjacob		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,
101941948Smjacob		    sizeof (sep->io_sense));
102042009Smjacob		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,
102142009Smjacob		    sizeof (sep->io_cdb));
102242009Smjacob		sep->ctl_resid = softc->last_ctl_resid;
102341948Smjacob		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,
102441948Smjacob		    sizeof (sep->ctl_sense));
102542009Smjacob		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
102642009Smjacob		    sizeof (sep->ctl_cdb));
102743636Smjacob
1028154360Smjacob		if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) ||
1029154360Smjacob		    didlockperiph)
103043636Smjacob			bzero((caddr_t) &softc->errinfo,
103143636Smjacob			    sizeof (softc->errinfo));
103241948Smjacob		error = 0;
103341948Smjacob		break;
103441948Smjacob	}
103539213Sgibbs	case MTIOCTOP:
103639213Sgibbs	{
103739213Sgibbs		struct mtop *mt;
103839213Sgibbs		int    count;
103939213Sgibbs
1040154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
1041154360Smjacob
104239213Sgibbs		mt = (struct mtop *)arg;
104339213Sgibbs
1044154360Smjacob
104539213Sgibbs		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
104639213Sgibbs			 ("saioctl: op=0x%x count=0x%x\n",
104739213Sgibbs			  mt->mt_op, mt->mt_count));
104839213Sgibbs
104939213Sgibbs		count = mt->mt_count;
105039213Sgibbs		switch (mt->mt_op) {
105141906Smjacob		case MTWEOF:	/* write an end-of-file marker */
105268114Smjacob			/*
105368114Smjacob			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN
105468114Smjacob			 * flag because by keeping track of filemarks
105568114Smjacob			 * we have last written we know ehether or not
105668114Smjacob			 * we need to write more when we close the device.
105768114Smjacob			 */
105841906Smjacob			error = sawritefilemarks(periph, count, FALSE);
105939213Sgibbs			break;
106042009Smjacob		case MTWSS:	/* write a setmark */
106142009Smjacob			error = sawritefilemarks(periph, count, TRUE);
106242009Smjacob			break;
106339213Sgibbs		case MTBSR:	/* backward space record */
106439213Sgibbs		case MTFSR:	/* forward space record */
106539213Sgibbs		case MTBSF:	/* backward space file */
106639213Sgibbs		case MTFSF:	/* forward space file */
106742009Smjacob		case MTBSS:	/* backward space setmark */
106842009Smjacob		case MTFSS:	/* forward space setmark */
106939213Sgibbs		case MTEOD:	/* space to end of recorded medium */
107039213Sgibbs		{
107139213Sgibbs			int nmarks;
107239213Sgibbs
107348520Speter			spaceop = SS_FILEMARKS;
107439213Sgibbs			nmarks = softc->filemarks;
107539213Sgibbs			error = sacheckeod(periph);
107641906Smjacob			if (error) {
1077164906Smjacob				xpt_print(periph->path,
1078164906Smjacob				    "EOD check prior to spacing failed\n");
107941906Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
108041906Smjacob				break;
108141906Smjacob			}
108239213Sgibbs			nmarks -= softc->filemarks;
108342009Smjacob			switch(mt->mt_op) {
108442009Smjacob			case MTBSR:
108539213Sgibbs				count = -count;
108642009Smjacob				/* FALLTHROUGH */
108742009Smjacob			case MTFSR:
108839213Sgibbs				spaceop = SS_BLOCKS;
108942009Smjacob				break;
109042009Smjacob			case MTBSF:
109142009Smjacob				count = -count;
109242009Smjacob				/* FALLTHROUGH */
109342009Smjacob			case MTFSF:
109442009Smjacob				break;
109542009Smjacob			case MTBSS:
109642009Smjacob				count = -count;
109742009Smjacob				/* FALLTHROUGH */
109842009Smjacob			case MTFSS:
109942009Smjacob				spaceop = SS_SETMARKS;
110042009Smjacob				break;
110142009Smjacob			case MTEOD:
110239213Sgibbs				spaceop = SS_EOD;
110339213Sgibbs				count = 0;
110439213Sgibbs				nmarks = 0;
110542009Smjacob				break;
110642009Smjacob			default:
110742009Smjacob				error = EINVAL;
110842009Smjacob				break;
110939213Sgibbs			}
111042009Smjacob			if (error)
111142009Smjacob				break;
111239213Sgibbs
111339213Sgibbs			nmarks = softc->filemarks;
111442009Smjacob			/*
111542009Smjacob			 * XXX: Why are we checking again?
111642009Smjacob			 */
111739213Sgibbs			error = sacheckeod(periph);
111842009Smjacob			if (error)
111942009Smjacob				break;
112039213Sgibbs			nmarks -= softc->filemarks;
112142009Smjacob			error = saspace(periph, count - nmarks, spaceop);
112241906Smjacob			/*
112341906Smjacob			 * At this point, clear that we've written the tape
112441906Smjacob			 * and that we've written any filemarks. We really
112541906Smjacob			 * don't know what the applications wishes to do next-
112641906Smjacob			 * the sacheckeod's will make sure we terminated the
112741906Smjacob			 * tape correctly if we'd been writing, but the next
112841906Smjacob			 * action the user application takes will set again
112941906Smjacob			 * whether we need to write filemarks.
113041906Smjacob			 */
113146962Smjacob			softc->flags &=
113246962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
113341906Smjacob			softc->filemarks = 0;
113439213Sgibbs			break;
113539213Sgibbs		}
113639213Sgibbs		case MTREW:	/* rewind */
1137154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
113841906Smjacob			(void) sacheckeod(periph);
113939213Sgibbs			error = sarewind(periph);
114041906Smjacob			/* see above */
114142009Smjacob			softc->flags &=
114246962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
114382575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
114441906Smjacob			softc->filemarks = 0;
114539213Sgibbs			break;
114639213Sgibbs		case MTERASE:	/* erase */
1147154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
114839213Sgibbs			error = saerase(periph, count);
114946962Smjacob			softc->flags &=
115046962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
115182575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
115239213Sgibbs			break;
115339213Sgibbs		case MTRETENS:	/* re-tension tape */
1154154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
115539213Sgibbs			error = saretension(periph);
115646962Smjacob			softc->flags &=
115746962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
115882575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
115939213Sgibbs			break;
116039213Sgibbs		case MTOFFL:	/* rewind and put the drive offline */
116141906Smjacob
1162154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1163154360Smjacob
116441906Smjacob			(void) sacheckeod(periph);
116541906Smjacob			/* see above */
116641906Smjacob			softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
116741906Smjacob			softc->filemarks = 0;
116841906Smjacob
116946962Smjacob			error = sarewind(periph);
117053522Smjacob			/* clear the frozen flag anyway */
117153522Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
117246962Smjacob
117339213Sgibbs			/*
117453522Smjacob			 * Be sure to allow media removal before ejecting.
117539213Sgibbs			 */
117646962Smjacob
117739213Sgibbs			saprevent(periph, PR_ALLOW);
117853522Smjacob			if (error == 0) {
117943636Smjacob				error = saloadunload(periph, FALSE);
118053522Smjacob				if (error == 0) {
118153522Smjacob					softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
118253522Smjacob				}
118353522Smjacob			}
118446962Smjacob			break;
118539213Sgibbs
118639213Sgibbs		case MTNOP:	/* no operation, sets status only */
118739213Sgibbs		case MTCACHE:	/* enable controller cache */
118839213Sgibbs		case MTNOCACHE:	/* disable controller cache */
118939213Sgibbs			error = 0;
119039213Sgibbs			break;
119146962Smjacob
119239213Sgibbs		case MTSETBSIZ:	/* Set block size for device */
119339213Sgibbs
1194154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1195154360Smjacob
119639213Sgibbs			error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
119742563Smjacob					    0, 0, 0);
119841674Smjacob			if (error == 0) {
119941906Smjacob				softc->last_media_blksize =
120041906Smjacob				    softc->media_blksize;
120141674Smjacob				softc->media_blksize = count;
120241674Smjacob				if (count) {
120341674Smjacob					softc->flags |= SA_FLAG_FIXED;
120441674Smjacob					if (powerof2(count)) {
120541674Smjacob						softc->blk_shift =
120641674Smjacob						    ffs(count) - 1;
120741674Smjacob						softc->blk_mask = count - 1;
120841674Smjacob					} else {
120941674Smjacob						softc->blk_mask = ~0;
121041674Smjacob						softc->blk_shift = 0;
121141674Smjacob					}
121241906Smjacob					/*
121341906Smjacob					 * Make the user's desire 'persistent'.
121441906Smjacob					 */
121541906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
121641906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
121741674Smjacob				} else {
121841674Smjacob					softc->flags &= ~SA_FLAG_FIXED;
121941674Smjacob					if (softc->max_blk == 0) {
122041674Smjacob						softc->max_blk = ~0;
122141674Smjacob					}
122241674Smjacob					softc->blk_shift = 0;
122341674Smjacob					if (softc->blk_gran != 0) {
122441674Smjacob						softc->blk_mask =
122541674Smjacob						    softc->blk_gran - 1;
122641674Smjacob					} else {
122741674Smjacob						softc->blk_mask = 0;
122841674Smjacob					}
122941906Smjacob					/*
123041906Smjacob					 * Make the user's desire 'persistent'.
123141906Smjacob					 */
123241906Smjacob					softc->quirks |= SA_QUIRK_VARIABLE;
123341906Smjacob					softc->quirks &= ~SA_QUIRK_FIXED;
123441674Smjacob				}
123541674Smjacob			}
123639213Sgibbs			break;
123739213Sgibbs		case MTSETDNSTY:	/* Set density for device and mode */
1238154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1239154360Smjacob
124039213Sgibbs			if (count > UCHAR_MAX) {
124139213Sgibbs				error = EINVAL;
124239213Sgibbs				break;
124339213Sgibbs			} else {
124439213Sgibbs				error = sasetparams(periph, SA_PARAM_DENSITY,
124542563Smjacob						    0, count, 0, 0);
124639213Sgibbs			}
124739213Sgibbs			break;
124839213Sgibbs		case MTCOMP:	/* enable compression */
1249154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
125039213Sgibbs			/*
125139213Sgibbs			 * Some devices don't support compression, and
125239213Sgibbs			 * don't like it if you ask them for the
125339213Sgibbs			 * compression page.
125439213Sgibbs			 */
125543636Smjacob			if ((softc->quirks & SA_QUIRK_NOCOMP) ||
125643636Smjacob			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {
125739213Sgibbs				error = ENODEV;
125839213Sgibbs				break;
125939213Sgibbs			}
126039213Sgibbs			error = sasetparams(periph, SA_PARAM_COMPRESSION,
126154099Smjacob			    0, 0, count, SF_NO_PRINT);
126239213Sgibbs			break;
126339213Sgibbs		default:
126439213Sgibbs			error = EINVAL;
126539213Sgibbs		}
126639213Sgibbs		break;
126739213Sgibbs	}
126839213Sgibbs	case MTIOCIEOT:
126939213Sgibbs	case MTIOCEEOT:
127039213Sgibbs		error = 0;
127139213Sgibbs		break;
127241918Smjacob	case MTIOCRDSPOS:
1273154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
127441918Smjacob		error = sardpos(periph, 0, (u_int32_t *) arg);
127541918Smjacob		break;
127641918Smjacob	case MTIOCRDHPOS:
1277154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
127841918Smjacob		error = sardpos(periph, 1, (u_int32_t *) arg);
127941918Smjacob		break;
128041918Smjacob	case MTIOCSLOCATE:
1281154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
128241918Smjacob		error = sasetpos(periph, 0, (u_int32_t *) arg);
128341918Smjacob		break;
128441918Smjacob	case MTIOCHLOCATE:
1285154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
128641918Smjacob		error = sasetpos(periph, 1, (u_int32_t *) arg);
128741918Smjacob		break;
128846962Smjacob	case MTIOCGETEOTMODEL:
128946962Smjacob		error = 0;
129046962Smjacob		if (softc->quirks & SA_QUIRK_1FM)
129146962Smjacob			mode = 1;
129246962Smjacob		else
129346962Smjacob			mode = 2;
129446962Smjacob		*((u_int32_t *) arg) = mode;
129546962Smjacob		break;
129646962Smjacob	case MTIOCSETEOTMODEL:
129746962Smjacob		error = 0;
129846962Smjacob		switch (*((u_int32_t *) arg)) {
129946962Smjacob		case 1:
130046962Smjacob			softc->quirks &= ~SA_QUIRK_2FM;
130146962Smjacob			softc->quirks |= SA_QUIRK_1FM;
130246962Smjacob			break;
130346962Smjacob		case 2:
130446962Smjacob			softc->quirks &= ~SA_QUIRK_1FM;
130546962Smjacob			softc->quirks |= SA_QUIRK_2FM;
130646962Smjacob			break;
130746962Smjacob		default:
130846962Smjacob			error = EINVAL;
130946962Smjacob			break;
131046962Smjacob		}
131146962Smjacob		break;
131239213Sgibbs	default:
131339213Sgibbs		error = cam_periph_ioctl(periph, cmd, arg, saerror);
131439213Sgibbs		break;
131539213Sgibbs	}
131671268Smjacob
131771268Smjacob	/*
131871268Smjacob	 * Check to see if we cleared a frozen state
131971268Smjacob	 */
132071268Smjacob	if (error == 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {
132171268Smjacob		switch(cmd) {
132271268Smjacob		case MTIOCRDSPOS:
132371268Smjacob		case MTIOCRDHPOS:
132471268Smjacob		case MTIOCSLOCATE:
132571268Smjacob		case MTIOCHLOCATE:
132671268Smjacob			softc->fileno = (daddr_t) -1;
132771268Smjacob			softc->blkno = (daddr_t) -1;
132871268Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
1329164906Smjacob			xpt_print(periph->path,
1330164906Smjacob			    "tape state now unfrozen.\n");
133171268Smjacob			break;
133271268Smjacob		default:
133371268Smjacob			break;
133471268Smjacob		}
133571268Smjacob	}
133643636Smjacob	if (didlockperiph) {
1337168752Sscottl		cam_periph_unhold(periph);
133843636Smjacob	}
1339168752Sscottl	cam_periph_unlock(periph);
134039213Sgibbs	return (error);
134139213Sgibbs}
134239213Sgibbs
134339213Sgibbsstatic void
134439213Sgibbssainit(void)
134539213Sgibbs{
134639213Sgibbs	cam_status status;
134739213Sgibbs
134839213Sgibbs	/*
134939213Sgibbs	 * Install a global async callback.
135039213Sgibbs	 */
1351169605Sscottl	status = xpt_register_async(AC_FOUND_DEVICE, saasync, NULL, NULL);
135239213Sgibbs
135339213Sgibbs	if (status != CAM_REQ_CMP) {
135439213Sgibbs		printf("sa: Failed to attach master async callback "
135539213Sgibbs		       "due to status 0x%x!\n", status);
135639213Sgibbs	}
135739213Sgibbs}
135839213Sgibbs
135939213Sgibbsstatic void
136040603Skensaoninvalidate(struct cam_periph *periph)
136140603Sken{
136240603Sken	struct sa_softc *softc;
136340603Sken
136440603Sken	softc = (struct sa_softc *)periph->softc;
136540603Sken
136640603Sken	/*
136740603Sken	 * De-register any async callbacks.
136840603Sken	 */
1369169605Sscottl	xpt_register_async(0, saasync, periph, periph->path);
137040603Sken
137140603Sken	softc->flags |= SA_FLAG_INVALID;
137240603Sken
137340603Sken	/*
137440603Sken	 * Return all queued I/O with ENXIO.
137540603Sken	 * XXX Handle any transactions queued to the card
137640603Sken	 *     with XPT_ABORT_CCB.
137740603Sken	 */
1378112946Sphk	bioq_flush(&softc->bio_queue, NULL, ENXIO);
137946962Smjacob	softc->queue_count = 0;
138040603Sken}
138140603Sken
138240603Skenstatic void
138339213Sgibbssacleanup(struct cam_periph *periph)
138439213Sgibbs{
138540603Sken	struct sa_softc *softc;
138653259Smjacob	int i;
138740603Sken
138840603Sken	softc = (struct sa_softc *)periph->softc;
138940603Sken
1390112006Sphk	devstat_remove_entry(softc->device_stats);
1391187028Strasz	cam_periph_unlock(periph);
139253259Smjacob	destroy_dev(softc->devs.ctl_dev);
139353259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
139453259Smjacob		destroy_dev(softc->devs.mode_devs[i].r_dev);
139553259Smjacob		destroy_dev(softc->devs.mode_devs[i].nr_dev);
139653259Smjacob		destroy_dev(softc->devs.mode_devs[i].er_dev);
139753259Smjacob	}
1398187028Strasz	cam_periph_lock(periph);
1399147723Savatar	free(softc, M_SCSISA);
140039213Sgibbs}
140139213Sgibbs
140239213Sgibbsstatic void
140339213Sgibbssaasync(void *callback_arg, u_int32_t code,
140439213Sgibbs	struct cam_path *path, void *arg)
140539213Sgibbs{
140639213Sgibbs	struct cam_periph *periph;
140739213Sgibbs
140839213Sgibbs	periph = (struct cam_periph *)callback_arg;
140939213Sgibbs	switch (code) {
141039213Sgibbs	case AC_FOUND_DEVICE:
141139213Sgibbs	{
141239213Sgibbs		struct ccb_getdev *cgd;
141339213Sgibbs		cam_status status;
141439213Sgibbs
141539213Sgibbs		cgd = (struct ccb_getdev *)arg;
141679177Smjacob		if (cgd == NULL)
141779177Smjacob			break;
141839213Sgibbs
1419195534Sscottl		if (cgd->protocol != PROTO_SCSI)
1420195534Sscottl			break;
1421195534Sscottl
142256148Smjacob		if (SID_TYPE(&cgd->inq_data) != T_SEQUENTIAL)
142339213Sgibbs			break;
142439213Sgibbs
142539213Sgibbs		/*
142639213Sgibbs		 * Allocate a peripheral instance for
142739213Sgibbs		 * this device and start the probe
142839213Sgibbs		 * process.
142939213Sgibbs		 */
143040603Sken		status = cam_periph_alloc(saregister, saoninvalidate,
143140603Sken					  sacleanup, sastart,
143239213Sgibbs					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,
143339213Sgibbs					  saasync, AC_FOUND_DEVICE, cgd);
143439213Sgibbs
143539213Sgibbs		if (status != CAM_REQ_CMP
143639213Sgibbs		 && status != CAM_REQ_INPROG)
143739213Sgibbs			printf("saasync: Unable to probe new device "
143839213Sgibbs				"due to status 0x%x\n", status);
143939213Sgibbs		break;
144039213Sgibbs	}
144139213Sgibbs	default:
144247413Sgibbs		cam_periph_async(periph, code, path, arg);
144339213Sgibbs		break;
144439213Sgibbs	}
144539213Sgibbs}
144639213Sgibbs
144739213Sgibbsstatic cam_status
144839213Sgibbssaregister(struct cam_periph *periph, void *arg)
144939213Sgibbs{
145039213Sgibbs	struct sa_softc *softc;
145139213Sgibbs	struct ccb_getdev *cgd;
1452220644Smav	struct ccb_pathinq cpi;
145339213Sgibbs	caddr_t match;
145453259Smjacob	int i;
145539213Sgibbs
145639213Sgibbs	cgd = (struct ccb_getdev *)arg;
145739213Sgibbs	if (cgd == NULL) {
145839213Sgibbs		printf("saregister: no getdev CCB, can't register device\n");
145954099Smjacob		return (CAM_REQ_CMP_ERR);
146039213Sgibbs	}
146139213Sgibbs
146267723Smjacob	softc = (struct sa_softc *)
1463147723Savatar	    malloc(sizeof (*softc), M_SCSISA, M_NOWAIT | M_ZERO);
146439213Sgibbs	if (softc == NULL) {
146539213Sgibbs		printf("saregister: Unable to probe new device. "
146639213Sgibbs		       "Unable to allocate softc\n");
146754099Smjacob		return (CAM_REQ_CMP_ERR);
146839213Sgibbs	}
146941674Smjacob	softc->scsi_rev = SID_ANSI_REV(&cgd->inq_data);
147039213Sgibbs	softc->state = SA_STATE_NORMAL;
147143636Smjacob	softc->fileno = (daddr_t) -1;
147243636Smjacob	softc->blkno = (daddr_t) -1;
147343636Smjacob
147459249Sphk	bioq_init(&softc->bio_queue);
147539213Sgibbs	periph->softc = softc;
147639213Sgibbs
147739213Sgibbs	/*
147839213Sgibbs	 * See if this device has any quirks.
147939213Sgibbs	 */
148039213Sgibbs	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
148139213Sgibbs			       (caddr_t)sa_quirk_table,
148239213Sgibbs			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),
148339213Sgibbs			       sizeof(*sa_quirk_table), scsi_inquiry_match);
148439213Sgibbs
148542563Smjacob	if (match != NULL) {
148639213Sgibbs		softc->quirks = ((struct sa_quirk_entry *)match)->quirks;
148742563Smjacob		softc->last_media_blksize =
148842563Smjacob		    ((struct sa_quirk_entry *)match)->prefblk;
148942563Smjacob	} else
149039213Sgibbs		softc->quirks = SA_QUIRK_NONE;
149139213Sgibbs
1492220644Smav	bzero(&cpi, sizeof(cpi));
1493220644Smav	xpt_setup_ccb(&cpi.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
1494220644Smav	cpi.ccb_h.func_code = XPT_PATH_INQ;
1495220644Smav	xpt_action((union ccb *)&cpi);
1496220644Smav
149739213Sgibbs	/*
1498220644Smav	 * The SA driver supports a blocksize, but we don't know the
149946962Smjacob	 * blocksize until we media is inserted.  So, set a flag to
150039213Sgibbs	 * indicate that the blocksize is unavailable right now.
150139213Sgibbs	 */
1502169605Sscottl	cam_periph_unlock(periph);
1503112006Sphk	softc->device_stats = devstat_new_entry("sa", periph->unit_number, 0,
150456148Smjacob	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |
1505220644Smav	    XPORT_DEVSTAT_TYPE(cpi.transport), DEVSTAT_PRIORITY_TAPE);
150653259Smjacob
1507255000Sken	/*
1508255000Sken	 * If maxio isn't set, we fall back to DFLTPHYS.  If it is set, we
1509255000Sken	 * take it whether or not it's larger than MAXPHYS.  physio will
1510255000Sken	 * break it down into pieces small enough to fit in a buffer.
1511255000Sken	 */
1512255000Sken	if (cpi.maxio == 0)
1513255000Sken		softc->maxio = DFLTPHYS;
1514255000Sken	else
1515255000Sken		softc->maxio = cpi.maxio;
1516255000Sken
1517255000Sken	/*
1518255000Sken	 * If the SIM supports unmapped I/O, let physio know that we can
1519255000Sken	 * handle unmapped buffers.
1520255000Sken	 */
1521255000Sken	if (cpi.hba_misc & PIM_UNMAPPED)
1522255000Sken		softc->si_flags = SI_UNMAPPED;
1523255000Sken
152453259Smjacob	softc->devs.ctl_dev = make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,
1525191304Sed	    0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR,
152672804Smjacob	    0660, "%s%d.ctl", periph->periph_name, periph->unit_number);
1527101940Snjl	softc->devs.ctl_dev->si_drv1 = periph;
1528255000Sken	softc->devs.ctl_dev->si_iosize_max = softc->maxio;
1529255000Sken	softc->devs.ctl_dev->si_flags |= softc->si_flags;
153053259Smjacob
153153259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
153253283Smjacob
153353259Smjacob		softc->devs.mode_devs[i].r_dev = make_dev(&sa_cdevsw,
1534191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_R),
153572804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d",
153653259Smjacob		    periph->periph_name, periph->unit_number, i);
1537101940Snjl		softc->devs.mode_devs[i].r_dev->si_drv1 = periph;
1538255000Sken		softc->devs.mode_devs[i].r_dev->si_iosize_max = softc->maxio;
1539255000Sken		softc->devs.mode_devs[i].r_dev->si_flags |= softc->si_flags;
154053283Smjacob
154153259Smjacob		softc->devs.mode_devs[i].nr_dev = make_dev(&sa_cdevsw,
1542191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_NR),
154372804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d",
154453259Smjacob		    periph->periph_name, periph->unit_number, i);
1545101940Snjl		softc->devs.mode_devs[i].nr_dev->si_drv1 = periph;
1546255000Sken		softc->devs.mode_devs[i].nr_dev->si_iosize_max = softc->maxio;
1547255000Sken		softc->devs.mode_devs[i].nr_dev->si_flags |= softc->si_flags;
154853259Smjacob
154953283Smjacob		softc->devs.mode_devs[i].er_dev = make_dev(&sa_cdevsw,
1550191304Sed		    SAMINOR(SA_NOT_CTLDEV, i, SA_ATYPE_ER),
155172804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d",
155253259Smjacob		    periph->periph_name, periph->unit_number, i);
1553101940Snjl		softc->devs.mode_devs[i].er_dev->si_drv1 = periph;
1554255000Sken		softc->devs.mode_devs[i].er_dev->si_iosize_max = softc->maxio;
1555255000Sken		softc->devs.mode_devs[i].er_dev->si_flags |= softc->si_flags;
155665838Smjacob
155765838Smjacob		/*
155865838Smjacob		 * Make the (well known) aliases for the first mode.
155965838Smjacob		 */
156065838Smjacob		if (i == 0) {
1561130585Sphk			struct cdev *alias;
1562101940Snjl
1563101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].r_dev,
156472804Smjacob			   "%s%d", periph->periph_name, periph->unit_number);
1565101940Snjl			alias->si_drv1 = periph;
1566255000Sken			alias->si_iosize_max = softc->maxio;
1567255000Sken			alias->si_flags |= softc->si_flags;
1568255000Sken
1569101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].nr_dev,
157072804Smjacob			    "n%s%d", periph->periph_name, periph->unit_number);
1571101940Snjl			alias->si_drv1 = periph;
1572255000Sken			alias->si_iosize_max = softc->maxio;
1573255000Sken			alias->si_flags |= softc->si_flags;
1574255000Sken
1575101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].er_dev,
157672804Smjacob			    "e%s%d", periph->periph_name, periph->unit_number);
1577101940Snjl			alias->si_drv1 = periph;
1578255000Sken			alias->si_iosize_max = softc->maxio;
1579255000Sken			alias->si_flags |= softc->si_flags;
158065838Smjacob		}
158153259Smjacob	}
1582168872Sscottl	cam_periph_lock(periph);
158353259Smjacob
158439213Sgibbs	/*
158539213Sgibbs	 * Add an async callback so that we get
158639213Sgibbs	 * notified if this device goes away.
158739213Sgibbs	 */
1588169605Sscottl	xpt_register_async(AC_LOST_DEVICE, saasync, periph, periph->path);
158939213Sgibbs
159039213Sgibbs	xpt_announce_periph(periph, NULL);
1591251386Ssmh	xpt_announce_quirks(periph, softc->quirks, SA_QUIRK_BIT_STRING);
159239213Sgibbs
159354099Smjacob	return (CAM_REQ_CMP);
159439213Sgibbs}
159539213Sgibbs
159639213Sgibbsstatic void
159739213Sgibbssastart(struct cam_periph *periph, union ccb *start_ccb)
159839213Sgibbs{
159939213Sgibbs	struct sa_softc *softc;
160039213Sgibbs
160139213Sgibbs	softc = (struct sa_softc *)periph->softc;
160239213Sgibbs
1603115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sastart\n"));
160471082Smjacob
160539213Sgibbs
160639213Sgibbs	switch (softc->state) {
160739213Sgibbs	case SA_STATE_NORMAL:
160839213Sgibbs	{
160939213Sgibbs		/* Pull a buffer from the queue and get going on it */
161059249Sphk		struct bio *bp;
161139213Sgibbs
161239213Sgibbs		/*
161339213Sgibbs		 * See if there is a buf with work for us to do..
161439213Sgibbs		 */
161559249Sphk		bp = bioq_first(&softc->bio_queue);
161639213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority) {
161739213Sgibbs			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
161839213Sgibbs					("queuing for immediate ccb\n"));
161971082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_WAITING);
162039213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
162139213Sgibbs					  periph_links.sle);
162239213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
162339213Sgibbs			wakeup(&periph->ccb_list);
162439213Sgibbs		} else if (bp == NULL) {
162539213Sgibbs			xpt_release_ccb(start_ccb);
162639213Sgibbs		} else if ((softc->flags & SA_FLAG_ERR_PENDING) != 0) {
162759249Sphk			struct bio *done_bp;
162882575Smjacobagain:
162946962Smjacob			softc->queue_count--;
163059249Sphk			bioq_remove(&softc->bio_queue, bp);
163159249Sphk			bp->bio_resid = bp->bio_bcount;
163282575Smjacob			done_bp = bp;
163339213Sgibbs			if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) {
163482575Smjacob				/*
163582575Smjacob				 * We now just clear errors in this case
163682575Smjacob				 * and let the residual be the notifier.
163782575Smjacob				 */
163882575Smjacob				bp->bio_error = 0;
163982575Smjacob			} else if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) {
164082575Smjacob				/*
164182575Smjacob				 * This can only happen if we're reading
164282575Smjacob				 * in fixed length mode. In this case,
164382575Smjacob				 * we dump the rest of the list the
164482575Smjacob				 * same way.
164582575Smjacob				 */
164682575Smjacob				bp->bio_error = 0;
164782575Smjacob				if (bioq_first(&softc->bio_queue) != NULL) {
164882575Smjacob					biodone(done_bp);
164982575Smjacob					goto again;
165082575Smjacob				}
165182575Smjacob			} else if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) {
165259249Sphk				bp->bio_error = EIO;
165382575Smjacob				bp->bio_flags |= BIO_ERROR;
165441948Smjacob			}
165559249Sphk			bp = bioq_first(&softc->bio_queue);
165644354Smjacob			/*
165744354Smjacob			 * Only if we have no other buffers queued up
165844354Smjacob			 * do we clear the pending error flag.
165944354Smjacob			 */
166044354Smjacob			if (bp == NULL)
166144354Smjacob				softc->flags &= ~SA_FLAG_ERR_PENDING;
166244354Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
166346962Smjacob			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "
166446962Smjacob			    "%d more buffers queued up\n",
166544354Smjacob			    (softc->flags & SA_FLAG_ERR_PENDING),
166646962Smjacob			    (bp != NULL)? "not " : " ", softc->queue_count));
166744354Smjacob			xpt_release_ccb(start_ccb);
166841948Smjacob			biodone(done_bp);
166939213Sgibbs		} else {
167039213Sgibbs			u_int32_t length;
167139213Sgibbs
167259249Sphk			bioq_remove(&softc->bio_queue, bp);
167346962Smjacob			softc->queue_count--;
167439213Sgibbs
167539213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0) {
167639213Sgibbs				if (softc->blk_shift != 0) {
167739213Sgibbs					length =
167859249Sphk					    bp->bio_bcount >> softc->blk_shift;
167943636Smjacob				} else if (softc->media_blksize != 0) {
168071082Smjacob					length = bp->bio_bcount /
168171082Smjacob					    softc->media_blksize;
168243636Smjacob				} else {
168359249Sphk					bp->bio_error = EIO;
1684164906Smjacob					xpt_print(periph->path, "zero blocksize"
1685164906Smjacob					    " for FIXED length writes?\n");
168643636Smjacob					biodone(bp);
168743636Smjacob					break;
168839213Sgibbs				}
1689115660Smjacob#if	0
1690115660Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1691115660Smjacob				    ("issuing a %d fixed record %s\n",
1692115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1693115660Smjacob				    "write"));
1694115660Smjacob#endif
169539213Sgibbs			} else {
169659249Sphk				length = bp->bio_bcount;
1697115660Smjacob#if	0
169841906Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1699115660Smjacob				    ("issuing a %d variable byte %s\n",
1700115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1701115660Smjacob				    "write"));
1702115660Smjacob#endif
170339213Sgibbs			}
1704112260Sphk			devstat_start_transaction_bio(softc->device_stats, bp);
170539213Sgibbs			/*
170641906Smjacob			 * Some people have theorized that we should
170739213Sgibbs			 * suppress illegal length indication if we are
170839213Sgibbs			 * running in variable block mode so that we don't
170939213Sgibbs			 * have to request sense every time our requested
171039213Sgibbs			 * block size is larger than the written block.
171139213Sgibbs			 * The residual information from the ccb allows
171239213Sgibbs			 * us to identify this situation anyway.  The only
171339213Sgibbs			 * problem with this is that we will not get
171439213Sgibbs			 * information about blocks that are larger than
171539213Sgibbs			 * our read buffer unless we set the block size
171639213Sgibbs			 * in the mode page to something other than 0.
171741906Smjacob			 *
171841906Smjacob			 * I believe that this is a non-issue. If user apps
171941906Smjacob			 * don't adjust their read size to match our record
172041906Smjacob			 * size, that's just life. Anyway, the typical usage
172141906Smjacob			 * would be to issue, e.g., 64KB reads and occasionally
172241906Smjacob			 * have to do deal with 512 byte or 1KB intermediate
172341906Smjacob			 * records.
172439213Sgibbs			 */
172559249Sphk			softc->dsreg = (bp->bio_cmd == BIO_READ)?
172643636Smjacob			    MTIO_DSREG_RD : MTIO_DSREG_WR;
172746962Smjacob			scsi_sa_read_write(&start_ccb->csio, 0, sadone,
1728255000Sken			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd == BIO_READ ?
1729255000Sken			    SCSI_RW_READ : SCSI_RW_WRITE) |
1730255000Sken			    ((bp->bio_flags & BIO_UNMAPPED) != 0 ?
1731255000Sken			    SCSI_RW_BIO : 0), FALSE,
1732255000Sken			    (softc->flags & SA_FLAG_FIXED) != 0, length,
1733255000Sken			    (bp->bio_flags & BIO_UNMAPPED) != 0 ? (void *)bp :
1734255000Sken			    bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,
173579100Smjacob			    IO_TIMEOUT);
173671082Smjacob			start_ccb->ccb_h.ccb_pflags &= ~SA_POSITION_UPDATED;
173771082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);
173839213Sgibbs			start_ccb->ccb_h.ccb_bp = bp;
173959249Sphk			bp = bioq_first(&softc->bio_queue);
174039213Sgibbs			xpt_action(start_ccb);
174139213Sgibbs		}
174239213Sgibbs
174339213Sgibbs		if (bp != NULL) {
174439213Sgibbs			/* Have more work to do, so ensure we stay scheduled */
1745198382Smav			xpt_schedule(periph, CAM_PRIORITY_NORMAL);
174639213Sgibbs		}
174739213Sgibbs		break;
174839213Sgibbs	}
174946962Smjacob	case SA_STATE_ABNORMAL:
175046962Smjacob	default:
175146962Smjacob		panic("state 0x%x in sastart", softc->state);
175246962Smjacob		break;
175339213Sgibbs	}
175439213Sgibbs}
175539213Sgibbs
175639213Sgibbs
175739213Sgibbsstatic void
175839213Sgibbssadone(struct cam_periph *periph, union ccb *done_ccb)
175939213Sgibbs{
176039213Sgibbs	struct sa_softc *softc;
176139213Sgibbs	struct ccb_scsiio *csio;
176239213Sgibbs
176339213Sgibbs	softc = (struct sa_softc *)periph->softc;
176439213Sgibbs	csio = &done_ccb->csio;
176571082Smjacob	switch (CCB_Type(csio)) {
176639213Sgibbs	case SA_CCB_BUFFER_IO:
176739213Sgibbs	{
176859249Sphk		struct bio *bp;
176939213Sgibbs		int error;
177039213Sgibbs
177143636Smjacob		softc->dsreg = MTIO_DSREG_REST;
177259249Sphk		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
177339213Sgibbs		error = 0;
177439213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
177539213Sgibbs			if ((error = saerror(done_ccb, 0, 0)) == ERESTART) {
177639213Sgibbs				/*
177741948Smjacob				 * A retry was scheduled, so just return.
177839213Sgibbs				 */
177939213Sgibbs				return;
178039213Sgibbs			}
178139213Sgibbs		}
178239213Sgibbs
178339213Sgibbs		if (error == EIO) {
178439213Sgibbs
178539213Sgibbs			/*
178653522Smjacob			 * Catastrophic error. Mark the tape as frozen
178753522Smjacob			 * (we no longer know tape position).
178853522Smjacob			 *
178944354Smjacob			 * Return all queued I/O with EIO, and unfreeze
179039213Sgibbs			 * our queue so that future transactions that
179139213Sgibbs			 * attempt to fix this problem can get to the
179239213Sgibbs			 * device.
179339213Sgibbs			 *
179439213Sgibbs			 */
179539213Sgibbs
179653522Smjacob			softc->flags |= SA_FLAG_TAPE_FROZEN;
1797112946Sphk			bioq_flush(&softc->bio_queue, NULL, EIO);
179839213Sgibbs		}
179939213Sgibbs		if (error != 0) {
180059249Sphk			bp->bio_resid = bp->bio_bcount;
180159249Sphk			bp->bio_error = error;
180259249Sphk			bp->bio_flags |= BIO_ERROR;
180343636Smjacob			/*
180443636Smjacob			 * In the error case, position is updated in saerror.
180543636Smjacob			 */
180639213Sgibbs		} else {
180759249Sphk			bp->bio_resid = csio->resid;
180859249Sphk			bp->bio_error = 0;
180939213Sgibbs			if (csio->resid != 0) {
181059249Sphk				bp->bio_flags |= BIO_ERROR;
181139213Sgibbs			}
181259249Sphk			if (bp->bio_cmd == BIO_WRITE) {
181339213Sgibbs				softc->flags |= SA_FLAG_TAPE_WRITTEN;
181439213Sgibbs				softc->filemarks = 0;
181539213Sgibbs			}
181671082Smjacob			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&
181771082Smjacob			    (softc->blkno != (daddr_t) -1)) {
181843636Smjacob				if ((softc->flags & SA_FLAG_FIXED) != 0) {
181943636Smjacob					u_int32_t l;
182043636Smjacob					if (softc->blk_shift != 0) {
182159249Sphk						l = bp->bio_bcount >>
182243636Smjacob							softc->blk_shift;
182343636Smjacob					} else {
182459249Sphk						l = bp->bio_bcount /
182543636Smjacob							softc->media_blksize;
182643636Smjacob					}
182743636Smjacob					softc->blkno += (daddr_t) l;
182843636Smjacob				} else {
182943636Smjacob					softc->blkno++;
183043636Smjacob				}
183143636Smjacob			}
183239213Sgibbs		}
183346962Smjacob		/*
183446962Smjacob		 * If we had an error (immediate or pending),
183546962Smjacob		 * release the device queue now.
183646962Smjacob		 */
183746962Smjacob		if (error || (softc->flags & SA_FLAG_ERR_PENDING))
183846962Smjacob			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);
183959249Sphk		if (error || bp->bio_resid) {
184041674Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
184141674Smjacob			    	  ("error %d resid %ld count %ld\n", error,
184259249Sphk				  bp->bio_resid, bp->bio_bcount));
184341674Smjacob		}
1844112006Sphk		biofinish(bp, softc->device_stats, 0);
184539213Sgibbs		break;
184639213Sgibbs	}
184739213Sgibbs	case SA_CCB_WAITING:
184839213Sgibbs	{
184939213Sgibbs		/* Caller will release the CCB */
185039213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
185139213Sgibbs		return;
185239213Sgibbs	}
185339213Sgibbs	}
185439213Sgibbs	xpt_release_ccb(done_ccb);
185539213Sgibbs}
185639213Sgibbs
185741906Smjacob/*
185841906Smjacob * Mount the tape (make sure it's ready for I/O).
185941906Smjacob */
186039213Sgibbsstatic int
1861130585Sphksamount(struct cam_periph *periph, int oflags, struct cdev *dev)
186239213Sgibbs{
186339213Sgibbs	struct	sa_softc *softc;
186439213Sgibbs	union	ccb *ccb;
186539213Sgibbs	int	error;
186639213Sgibbs
186741906Smjacob	/*
186841906Smjacob	 * oflags can be checked for 'kind' of open (read-only check) - later
186941906Smjacob	 * dev can be checked for a control-mode or compression open - later
187041906Smjacob	 */
187141906Smjacob	UNUSED_PARAMETER(oflags);
187241906Smjacob	UNUSED_PARAMETER(dev);
187341906Smjacob
187441906Smjacob
187539213Sgibbs	softc = (struct sa_softc *)periph->softc;
187639213Sgibbs
187739213Sgibbs	/*
187853259Smjacob	 * This should determine if something has happend since the last
187953259Smjacob	 * open/mount that would invalidate the mount. We do *not* want
188053259Smjacob	 * to retry this command- we just want the status. But we only
188153259Smjacob	 * do this if we're mounted already- if we're not mounted,
188253259Smjacob	 * we don't care about the unit read state and can instead use
188353259Smjacob	 * this opportunity to attempt to reserve the tape unit.
188439213Sgibbs	 */
188553259Smjacob
188653259Smjacob	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {
188753259Smjacob		ccb = cam_periph_getccb(periph, 1);
188853259Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
188979100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
189054099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1891112006Sphk		    softc->device_stats);
189253259Smjacob		if (error == ENXIO) {
189353259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
189453259Smjacob			scsi_test_unit_ready(&ccb->csio, 0, sadone,
189579100Smjacob			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
189654099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1897112006Sphk			    softc->device_stats);
189853259Smjacob		} else if (error) {
189954099Smjacob			/*
190054099Smjacob			 * We don't need to freeze the tape because we
190154099Smjacob			 * will now attempt to rewind/load it.
190254099Smjacob			 */
190353259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
1904144430Ssam			if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
1905164906Smjacob				xpt_print(periph->path,
1906164906Smjacob				    "error %d on TUR in samount\n", error);
190754099Smjacob			}
190853259Smjacob		}
190953259Smjacob	} else {
191053259Smjacob		error = sareservereleaseunit(periph, TRUE);
191153259Smjacob		if (error) {
191253259Smjacob			return (error);
191353259Smjacob		}
191453259Smjacob		ccb = cam_periph_getccb(periph, 1);
191554105Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
191679100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
191754105Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1918112006Sphk		    softc->device_stats);
191953259Smjacob	}
192039213Sgibbs
192139213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
192253259Smjacob		struct scsi_read_block_limits_data *rblim = NULL;
192353259Smjacob		int comp_enabled, comp_supported;
192441906Smjacob		u_int8_t write_protect, guessing = 0;
192539213Sgibbs
192639213Sgibbs		/*
192739213Sgibbs		 * Clear out old state.
192839213Sgibbs		 */
192939213Sgibbs		softc->flags &= ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|
193039213Sgibbs				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|
193146962Smjacob				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);
193239213Sgibbs		softc->filemarks = 0;
193339213Sgibbs
193439213Sgibbs		/*
193543636Smjacob		 * *Very* first off, make sure we're loaded to BOT.
193639213Sgibbs		 */
193743636Smjacob		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
193854099Smjacob		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);
193954099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1940112006Sphk		    softc->device_stats);
194153259Smjacob
194243636Smjacob		/*
194344354Smjacob		 * In case this doesn't work, do a REWIND instead
194443636Smjacob		 */
194544354Smjacob		if (error) {
194653259Smjacob			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
194753259Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
194854099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1949112006Sphk				softc->device_stats);
195043636Smjacob		}
195143636Smjacob		if (error) {
195243636Smjacob			xpt_release_ccb(ccb);
195343636Smjacob			goto exit;
195443636Smjacob		}
195543636Smjacob
195643636Smjacob		/*
195753259Smjacob		 * Do a dummy test read to force access to the
195853259Smjacob		 * media so that the drive will really know what's
195954099Smjacob		 * there. We actually don't really care what the
196054099Smjacob		 * blocksize on tape is and don't expect to really
196154099Smjacob		 * read a full record.
196243636Smjacob		 */
196339213Sgibbs		rblim = (struct  scsi_read_block_limits_data *)
1964170830Sscottl		    malloc(8192, M_SCSISA, M_NOWAIT);
196553259Smjacob		if (rblim == NULL) {
1966164906Smjacob			xpt_print(periph->path, "no memory for test read\n");
196753259Smjacob			xpt_release_ccb(ccb);
196853259Smjacob			error = ENOMEM;
196953259Smjacob			goto exit;
197053259Smjacob		}
197156981Smjacob
197256981Smjacob		if ((softc->quirks & SA_QUIRK_NODREAD) == 0) {
197356981Smjacob			scsi_sa_read_write(&ccb->csio, 0, sadone,
197456981Smjacob			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,
197556981Smjacob			    (void *) rblim, 8192, SSD_FULL_SIZE,
197679100Smjacob			    IO_TIMEOUT);
197756981Smjacob			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1978112006Sphk			    softc->device_stats);
197956981Smjacob			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
198056981Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
198174840Sken			error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
198274840Sken			    SF_NO_PRINT | SF_RETRY_UA,
1983112006Sphk			    softc->device_stats);
198456981Smjacob			if (error) {
1985164906Smjacob				xpt_print(periph->path,
1986164906Smjacob				    "unable to rewind after test read\n");
198756981Smjacob				xpt_release_ccb(ccb);
198856981Smjacob				goto exit;
198956981Smjacob			}
199053259Smjacob		}
199139213Sgibbs
199253259Smjacob		/*
199353259Smjacob		 * Next off, determine block limits.
199453259Smjacob		 */
199553259Smjacob		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
199679100Smjacob		    rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
199739213Sgibbs
199874840Sken		error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
1999112006Sphk		    SF_NO_PRINT | SF_RETRY_UA, softc->device_stats);
200074840Sken
200139213Sgibbs		xpt_release_ccb(ccb);
200239213Sgibbs
200341674Smjacob		if (error != 0) {
200441674Smjacob			/*
200541674Smjacob			 * If it's less than SCSI-2, READ BLOCK LIMITS is not
200641674Smjacob			 * a MANDATORY command. Anyway- it doesn't matter-
200741674Smjacob			 * we can proceed anyway.
200841674Smjacob			 */
200941674Smjacob			softc->blk_gran = 0;
201041674Smjacob			softc->max_blk = ~0;
201141674Smjacob			softc->min_blk = 0;
201241674Smjacob		} else {
201374840Sken			if (softc->scsi_rev >= SCSI_REV_SPC) {
201441674Smjacob				softc->blk_gran = RBL_GRAN(rblim);
201541674Smjacob			} else {
201641674Smjacob				softc->blk_gran = 0;
201741674Smjacob			}
201841674Smjacob			/*
201941674Smjacob			 * We take max_blk == min_blk to mean a default to
202041674Smjacob			 * fixed mode- but note that whatever we get out of
202141674Smjacob			 * sagetparams below will actually determine whether
202241674Smjacob			 * we are actually *in* fixed mode.
202341674Smjacob			 */
202441674Smjacob			softc->max_blk = scsi_3btoul(rblim->maximum);
202541674Smjacob			softc->min_blk = scsi_2btoul(rblim->minimum);
202641674Smjacob
202741674Smjacob
202841674Smjacob		}
202941674Smjacob		/*
203041674Smjacob		 * Next, perform a mode sense to determine
203141674Smjacob		 * current density, blocksize, compression etc.
203241674Smjacob		 */
203341674Smjacob		error = sagetparams(periph, SA_PARAM_ALL,
203441674Smjacob				    &softc->media_blksize,
203541674Smjacob				    &softc->media_density,
203641674Smjacob				    &softc->media_numblks,
203741674Smjacob				    &softc->buffer_mode, &write_protect,
203841674Smjacob				    &softc->speed, &comp_supported,
203941674Smjacob				    &comp_enabled, &softc->comp_algorithm,
204041674Smjacob				    NULL);
204141674Smjacob
204241674Smjacob		if (error != 0) {
204341674Smjacob			/*
204441674Smjacob			 * We could work a little harder here. We could
204541674Smjacob			 * adjust our attempts to get information. It
204641674Smjacob			 * might be an ancient tape drive. If someone
204741674Smjacob			 * nudges us, we'll do that.
204841674Smjacob			 */
204939213Sgibbs			goto exit;
205041674Smjacob		}
205139213Sgibbs
205241906Smjacob		/*
205341906Smjacob		 * If no quirk has determined that this is a device that is
205441906Smjacob		 * preferred to be in fixed or variable mode, now is the time
205541906Smjacob		 * to find out.
205641906Smjacob	 	 */
205741906Smjacob		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) == 0) {
205841906Smjacob			guessing = 1;
205943636Smjacob			/*
206043636Smjacob			 * This could be expensive to find out. Luckily we
206143636Smjacob			 * only need to do this once. If we start out in
206243636Smjacob			 * 'default' mode, try and set ourselves to one
206343636Smjacob			 * of the densities that would determine a wad
206443636Smjacob			 * of other stuff. Go from highest to lowest.
206543636Smjacob			 */
206643636Smjacob			if (softc->media_density == SCSI_DEFAULT_DENSITY) {
206743636Smjacob				int i;
206843636Smjacob				static u_int8_t ctry[] = {
206943636Smjacob					SCSI_DENSITY_HALFINCH_PE,
207043636Smjacob					SCSI_DENSITY_HALFINCH_6250C,
207143636Smjacob					SCSI_DENSITY_HALFINCH_6250,
207243636Smjacob					SCSI_DENSITY_HALFINCH_1600,
207343636Smjacob					SCSI_DENSITY_HALFINCH_800,
207446962Smjacob					SCSI_DENSITY_QIC_4GB,
207546962Smjacob					SCSI_DENSITY_QIC_2GB,
207643636Smjacob					SCSI_DENSITY_QIC_525_320,
207743636Smjacob					SCSI_DENSITY_QIC_150,
207843636Smjacob					SCSI_DENSITY_QIC_120,
207943636Smjacob					SCSI_DENSITY_QIC_24,
208043636Smjacob					SCSI_DENSITY_QIC_11_9TRK,
208143636Smjacob					SCSI_DENSITY_QIC_11_4TRK,
208246962Smjacob					SCSI_DENSITY_QIC_1320,
208346962Smjacob					SCSI_DENSITY_QIC_3080,
208443636Smjacob					0
208543636Smjacob				};
208643636Smjacob				for (i = 0; ctry[i]; i++) {
208743636Smjacob					error = sasetparams(periph,
208843636Smjacob					    SA_PARAM_DENSITY, 0, ctry[i],
208943636Smjacob					    0, SF_NO_PRINT);
209043636Smjacob					if (error == 0) {
209143636Smjacob						softc->media_density = ctry[i];
209243636Smjacob						break;
209343636Smjacob					}
209443636Smjacob				}
209543636Smjacob			}
209641906Smjacob			switch (softc->media_density) {
209741906Smjacob			case SCSI_DENSITY_QIC_11_4TRK:
209841906Smjacob			case SCSI_DENSITY_QIC_11_9TRK:
209941906Smjacob			case SCSI_DENSITY_QIC_24:
210041906Smjacob			case SCSI_DENSITY_QIC_120:
210141906Smjacob			case SCSI_DENSITY_QIC_150:
210265861Smjacob			case SCSI_DENSITY_QIC_525_320:
210343636Smjacob			case SCSI_DENSITY_QIC_1320:
210443636Smjacob			case SCSI_DENSITY_QIC_3080:
210546962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
210643636Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
210741906Smjacob				softc->last_media_blksize = 512;
210841906Smjacob				break;
210946962Smjacob			case SCSI_DENSITY_QIC_4GB:
211046962Smjacob			case SCSI_DENSITY_QIC_2GB:
211146962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
211246962Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
211346962Smjacob				softc->last_media_blksize = 1024;
211446962Smjacob				break;
211541906Smjacob			default:
211641906Smjacob				softc->last_media_blksize =
211741906Smjacob				    softc->media_blksize;
211841906Smjacob				softc->quirks |= SA_QUIRK_VARIABLE;
211941906Smjacob				break;
212041906Smjacob			}
212141906Smjacob		}
212242563Smjacob
212341906Smjacob		/*
212441906Smjacob		 * If no quirk has determined that this is a device that needs
212541906Smjacob		 * to have 2 Filemarks at EOD, now is the time to find out.
212641906Smjacob		 */
212742563Smjacob
212842735Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0) {
212941906Smjacob			switch (softc->media_density) {
213041906Smjacob			case SCSI_DENSITY_HALFINCH_800:
213141906Smjacob			case SCSI_DENSITY_HALFINCH_1600:
213241906Smjacob			case SCSI_DENSITY_HALFINCH_6250:
213341906Smjacob			case SCSI_DENSITY_HALFINCH_6250C:
213441906Smjacob			case SCSI_DENSITY_HALFINCH_PE:
213546962Smjacob				softc->quirks &= ~SA_QUIRK_1FM;
213641906Smjacob				softc->quirks |= SA_QUIRK_2FM;
213741906Smjacob				break;
213841906Smjacob			default:
213941906Smjacob				break;
214041906Smjacob			}
214141906Smjacob		}
214241906Smjacob
214341906Smjacob		/*
214441906Smjacob		 * Now validate that some info we got makes sense.
214541906Smjacob		 */
214641674Smjacob		if ((softc->max_blk < softc->media_blksize) ||
214741674Smjacob		    (softc->min_blk > softc->media_blksize &&
214841674Smjacob		    softc->media_blksize)) {
2149164906Smjacob			xpt_print(periph->path,
2150164906Smjacob			    "BLOCK LIMITS (%d..%d) could not match current "
215141674Smjacob			    "block settings (%d)- adjusting\n", softc->min_blk,
215241674Smjacob			    softc->max_blk, softc->media_blksize);
215341674Smjacob			softc->max_blk = softc->min_blk =
215441674Smjacob			    softc->media_blksize;
215541674Smjacob		}
215641906Smjacob
215741674Smjacob		/*
215841906Smjacob		 * Now put ourselves into the right frame of mind based
215941906Smjacob		 * upon quirks...
216041906Smjacob		 */
216141906Smjacobtryagain:
216242563Smjacob		/*
216342563Smjacob		 * If we want to be in FIXED mode and our current blocksize
216442563Smjacob		 * is not equal to our last blocksize (if nonzero), try and
216542563Smjacob		 * set ourselves to this last blocksize (as the 'preferred'
216642563Smjacob		 * block size).  The initial quirkmatch at registry sets the
216742563Smjacob		 * initial 'last' blocksize. If, for whatever reason, this
216842563Smjacob		 * 'last' blocksize is zero, set the blocksize to 512,
216942563Smjacob		 * or min_blk if that's larger.
217042563Smjacob		 */
217141906Smjacob		if ((softc->quirks & SA_QUIRK_FIXED) &&
217260235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0 &&
217342563Smjacob		    (softc->media_blksize != softc->last_media_blksize)) {
217441906Smjacob			softc->media_blksize = softc->last_media_blksize;
217541906Smjacob			if (softc->media_blksize == 0) {
217642563Smjacob				softc->media_blksize = 512;
217741906Smjacob				if (softc->media_blksize < softc->min_blk) {
217841906Smjacob					softc->media_blksize = softc->min_blk;
217941906Smjacob				}
218041906Smjacob			}
218141906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
218242563Smjacob			    softc->media_blksize, 0, 0, SF_NO_PRINT);
218341906Smjacob			if (error) {
2184164906Smjacob				xpt_print(periph->path,
2185164906Smjacob				    "unable to set fixed blocksize to %d\n",
2186164906Smjacob				    softc->media_blksize);
218741906Smjacob				goto exit;
218841906Smjacob			}
218941906Smjacob		}
219041906Smjacob
219141906Smjacob		if ((softc->quirks & SA_QUIRK_VARIABLE) &&
219241906Smjacob		    (softc->media_blksize != 0)) {
219341906Smjacob			softc->last_media_blksize = softc->media_blksize;
219441906Smjacob			softc->media_blksize = 0;
219541906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
219642563Smjacob			    0, 0, 0, SF_NO_PRINT);
219741906Smjacob			if (error) {
219841906Smjacob				/*
219941906Smjacob				 * If this fails and we were guessing, just
220041906Smjacob				 * assume that we got it wrong and go try
220142563Smjacob				 * fixed block mode. Don't even check against
220242563Smjacob				 * density code at this point.
220341906Smjacob				 */
220442563Smjacob				if (guessing) {
220541906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
220641906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
220741906Smjacob					if (softc->last_media_blksize == 0)
220841906Smjacob						softc->last_media_blksize = 512;
220941906Smjacob					goto tryagain;
221041906Smjacob				}
2211164906Smjacob				xpt_print(periph->path,
2212164906Smjacob				    "unable to set variable blocksize\n");
221341906Smjacob				goto exit;
221441906Smjacob			}
221541906Smjacob		}
221641906Smjacob
221741906Smjacob		/*
221841674Smjacob		 * Now that we have the current block size,
221941674Smjacob		 * set up some parameters for sastart's usage.
222041674Smjacob		 */
222141674Smjacob		if (softc->media_blksize) {
222239213Sgibbs			softc->flags |= SA_FLAG_FIXED;
222341674Smjacob			if (powerof2(softc->media_blksize)) {
222441674Smjacob				softc->blk_shift =
222541674Smjacob				    ffs(softc->media_blksize) - 1;
222641674Smjacob				softc->blk_mask = softc->media_blksize - 1;
222739213Sgibbs			} else {
222839213Sgibbs				softc->blk_mask = ~0;
222939213Sgibbs				softc->blk_shift = 0;
223039213Sgibbs			}
223139213Sgibbs		} else {
223239213Sgibbs			/*
223341674Smjacob			 * The SCSI-3 spec allows 0 to mean "unspecified".
223441674Smjacob			 * The SCSI-1 spec allows 0 to mean 'infinite'.
223541674Smjacob			 *
223641674Smjacob			 * Either works here.
223739213Sgibbs			 */
223839213Sgibbs			if (softc->max_blk == 0) {
223939213Sgibbs				softc->max_blk = ~0;
224039213Sgibbs			}
224139213Sgibbs			softc->blk_shift = 0;
224239213Sgibbs			if (softc->blk_gran != 0) {
224339213Sgibbs				softc->blk_mask = softc->blk_gran - 1;
224439213Sgibbs			} else {
224539213Sgibbs				softc->blk_mask = 0;
224639213Sgibbs			}
224739213Sgibbs		}
224839213Sgibbs
224939213Sgibbs		if (write_protect)
225039213Sgibbs			softc->flags |= SA_FLAG_TAPE_WP;
225139213Sgibbs
225239213Sgibbs		if (comp_supported) {
225343636Smjacob			if (softc->saved_comp_algorithm == 0)
225443636Smjacob				softc->saved_comp_algorithm =
225543636Smjacob				    softc->comp_algorithm;
225646962Smjacob			softc->flags |= SA_FLAG_COMP_SUPP;
225746962Smjacob			if (comp_enabled)
225846962Smjacob				softc->flags |= SA_FLAG_COMP_ENABLED;
225939213Sgibbs		} else
226039213Sgibbs			softc->flags |= SA_FLAG_COMP_UNSUPP;
226139213Sgibbs
226260235Smjacob		if ((softc->buffer_mode == SMH_SA_BUF_MODE_NOBUF) &&
226360235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0) {
226441906Smjacob			error = sasetparams(periph, SA_PARAM_BUFF_MODE, 0,
226542563Smjacob			    0, 0, SF_NO_PRINT);
226683473Smjacob			if (error == 0) {
226741906Smjacob				softc->buffer_mode = SMH_SA_BUF_MODE_SIBUF;
226883473Smjacob			} else {
2269164906Smjacob				xpt_print(periph->path,
2270164906Smjacob				    "unable to set buffered mode\n");
227183473Smjacob			}
227260235Smjacob			error = 0;	/* not an error */
227341906Smjacob		}
227439213Sgibbs
227539213Sgibbs
227644354Smjacob		if (error == 0) {
227741906Smjacob			softc->flags |= SA_FLAG_TAPE_MOUNTED;
227844354Smjacob		}
227939213Sgibbsexit:
228039213Sgibbs		if (rblim != NULL)
2281169562Sscottl			free(rblim, M_SCSISA);
228239213Sgibbs
228343636Smjacob		if (error != 0) {
228443636Smjacob			softc->dsreg = MTIO_DSREG_NIL;
228544354Smjacob		} else {
228644354Smjacob			softc->fileno = softc->blkno = 0;
228743636Smjacob			softc->dsreg = MTIO_DSREG_REST;
228844354Smjacob		}
228951875Smjacob#ifdef	SA_1FM_AT_EOD
229051875Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0)
229151875Smjacob			softc->quirks |= SA_QUIRK_1FM;
229251875Smjacob#else
229346962Smjacob		if ((softc->quirks & SA_QUIRK_1FM) == 0)
229443636Smjacob			softc->quirks |= SA_QUIRK_2FM;
229543636Smjacob#endif
229639213Sgibbs	} else
229739213Sgibbs		xpt_release_ccb(ccb);
229839213Sgibbs
229953259Smjacob	/*
230053259Smjacob	 * If we return an error, we're not mounted any more,
230153259Smjacob	 * so release any device reservation.
230253259Smjacob	 */
230353259Smjacob	if (error != 0) {
230453259Smjacob		(void) sareservereleaseunit(periph, FALSE);
230582575Smjacob	} else {
230682575Smjacob		/*
230782575Smjacob		 * Clear I/O residual.
230882575Smjacob		 */
230982575Smjacob		softc->last_io_resid = 0;
231082575Smjacob		softc->last_ctl_resid = 0;
231153259Smjacob	}
231254099Smjacob	return (error);
231339213Sgibbs}
231439213Sgibbs
231568114Smjacob/*
231668114Smjacob * How many filemarks do we need to write if we were to terminate the
231768114Smjacob * tape session right now? Note that this can be a negative number
231868114Smjacob */
231968114Smjacob
232039213Sgibbsstatic int
232168114Smjacobsamarkswanted(struct cam_periph *periph)
232239213Sgibbs{
232339213Sgibbs	int	markswanted;
232439213Sgibbs	struct	sa_softc *softc;
232539213Sgibbs
232639213Sgibbs	softc = (struct sa_softc *)periph->softc;
232739213Sgibbs	markswanted = 0;
232839213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) != 0) {
232939213Sgibbs		markswanted++;
233046962Smjacob		if (softc->quirks & SA_QUIRK_2FM)
233139213Sgibbs			markswanted++;
233239213Sgibbs	}
233368114Smjacob	markswanted -= softc->filemarks;
233468114Smjacob	return (markswanted);
233568114Smjacob}
233639213Sgibbs
233768114Smjacobstatic int
233868114Smjacobsacheckeod(struct cam_periph *periph)
233968114Smjacob{
234068114Smjacob	int	error;
234168114Smjacob	int	markswanted;
234268114Smjacob
234368114Smjacob	markswanted = samarkswanted(periph);
234468114Smjacob
234568114Smjacob	if (markswanted > 0) {
234641906Smjacob		error = sawritefilemarks(periph, markswanted, FALSE);
234739213Sgibbs	} else {
234839213Sgibbs		error = 0;
234939213Sgibbs	}
235039213Sgibbs	return (error);
235139213Sgibbs}
235239213Sgibbs
235339213Sgibbsstatic int
235446962Smjacobsaerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
235539213Sgibbs{
235646962Smjacob	static const char *toobig =
235798449Srobert	    "%d-byte tape record bigger than supplied buffer\n";
235839213Sgibbs	struct	cam_periph *periph;
235939213Sgibbs	struct	sa_softc *softc;
236039213Sgibbs	struct	ccb_scsiio *csio;
236139213Sgibbs	struct	scsi_sense_data *sense;
2362226067Sken	uint64_t resid = 0;
2363226067Sken	int64_t	info = 0;
236482575Smjacob	cam_status status;
2365226067Sken	int error_code, sense_key, asc, ascq, error, aqvalid, stream_valid;
2366226067Sken	int sense_len;
2367226067Sken	uint8_t stream_bits;
236839213Sgibbs
236939213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
237039213Sgibbs	softc = (struct sa_softc *)periph->softc;
237139213Sgibbs	csio = &ccb->csio;
237239213Sgibbs	sense = &csio->sense_data;
2373226067Sken	sense_len = csio->sense_len - csio->sense_resid;
2374226067Sken	scsi_extract_sense_len(sense, sense_len, &error_code, &sense_key,
2375226067Sken	    &asc, &ascq, /*show_errors*/ 1);
2376226067Sken	if (asc != -1 && ascq != -1)
2377226067Sken		aqvalid = 1;
2378226067Sken	else
2379226067Sken		aqvalid = 0;
2380226067Sken	if (scsi_get_stream_info(sense, sense_len, NULL, &stream_bits) == 0)
2381226067Sken		stream_valid = 1;
2382226067Sken	else
2383226067Sken		stream_valid = 0;
238439213Sgibbs	error = 0;
238546962Smjacob
238682575Smjacob	status = csio->ccb_h.status & CAM_STATUS_MASK;
238782575Smjacob
238846962Smjacob	/*
238982575Smjacob	 * Calculate/latch up, any residuals... We do this in a funny 2-step
239082575Smjacob	 * so we can print stuff here if we have CAM_DEBUG enabled for this
239182575Smjacob	 * unit.
239246962Smjacob	 */
239382575Smjacob	if (status == CAM_SCSI_STATUS_ERROR) {
2394226067Sken		if (scsi_get_sense_info(sense, sense_len, SSD_DESC_INFO, &resid,
2395226067Sken					&info) == 0) {
239639213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0)
239739213Sgibbs				resid *= softc->media_blksize;
239839213Sgibbs		} else {
239939213Sgibbs			resid = csio->dxfer_len;
240039213Sgibbs			info = resid;
240141674Smjacob			if ((softc->flags & SA_FLAG_FIXED) != 0) {
240241674Smjacob				if (softc->media_blksize)
240341674Smjacob					info /= softc->media_blksize;
240441674Smjacob			}
240539213Sgibbs		}
240671082Smjacob		if (CCB_Type(csio) == SA_CCB_BUFFER_IO) {
240741948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,
240841948Smjacob			    sizeof (struct scsi_sense_data));
240942009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,
241042009Smjacob			    (int) csio->cdb_len);
241141948Smjacob			softc->last_io_resid = resid;
241271268Smjacob			softc->last_resid_was_io = 1;
241341948Smjacob		} else {
241441948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,
241541948Smjacob			    sizeof (struct scsi_sense_data));
241642009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,
241742009Smjacob			    (int) csio->cdb_len);
241841948Smjacob			softc->last_ctl_resid = resid;
241971268Smjacob			softc->last_resid_was_io = 0;
242041948Smjacob		}
242182575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=0x%x Key 0x%x "
2422226067Sken		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %jd "
242382575Smjacob		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,
242482575Smjacob		    sense_key, asc, ascq, status,
2425226067Sken		    (stream_valid) ? stream_bits : 0, (intmax_t)resid,
2426226067Sken		    csio->dxfer_len));
242753259Smjacob	} else {
242882575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
242982575Smjacob		    ("Cam Status 0x%x\n", status));
243041948Smjacob	}
243141948Smjacob
243282575Smjacob	switch (status) {
243382575Smjacob	case CAM_REQ_CMP:
243482575Smjacob		return (0);
243582575Smjacob	case CAM_SCSI_STATUS_ERROR:
243682575Smjacob		/*
243782575Smjacob		 * If a read/write command, we handle it here.
243882575Smjacob		 */
243982575Smjacob		if (CCB_Type(csio) != SA_CCB_WAITING) {
244082575Smjacob			break;
244182575Smjacob		}
244282575Smjacob		/*
244382575Smjacob		 * If this was just EOM/EOP, Filemark, Setmark or ILI detected
244482575Smjacob		 * on a non read/write command, we assume it's not an error
244582575Smjacob		 * and propagate the residule and return.
244682575Smjacob		 */
244782575Smjacob		if ((aqvalid && asc == 0 && ascq > 0 && ascq <= 5) ||
244882575Smjacob		    (aqvalid == 0 && sense_key == SSD_KEY_NO_SENSE)) {
244982575Smjacob			csio->resid = resid;
245082575Smjacob			QFRLS(ccb);
245182575Smjacob			return (0);
245282575Smjacob		}
245382575Smjacob		/*
245482575Smjacob		 * Otherwise, we let the common code handle this.
245582575Smjacob		 */
245646962Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
245741948Smjacob
245846962Smjacob	/*
245982575Smjacob	 * XXX: To Be Fixed
246082575Smjacob	 * We cannot depend upon CAM honoring retry counts for these.
246146962Smjacob	 */
246282575Smjacob	case CAM_SCSI_BUS_RESET:
246382575Smjacob	case CAM_BDR_SENT:
246482575Smjacob		if (ccb->ccb_h.retry_count <= 0) {
246582575Smjacob			return (EIO);
246682575Smjacob		}
246782575Smjacob		/* FALLTHROUGH */
246882575Smjacob	default:
246982575Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
247046962Smjacob	}
247146962Smjacob
247246962Smjacob	/*
247346962Smjacob	 * Handle filemark, end of tape, mismatched record sizes....
247446962Smjacob	 * From this point out, we're only handling read/write cases.
247546962Smjacob	 * Handle writes && reads differently.
247646962Smjacob	 */
247782575Smjacob
247846962Smjacob	if (csio->cdb_io.cdb_bytes[0] == SA_WRITE) {
247982575Smjacob		if (sense_key == SSD_KEY_VOLUME_OVERFLOW) {
248039213Sgibbs			csio->resid = resid;
248182575Smjacob			error = ENOSPC;
2482226067Sken		} else if ((stream_valid != 0) && (stream_bits & SSD_EOM)) {
248382575Smjacob			softc->flags |= SA_FLAG_EOM_PENDING;
248482575Smjacob			/*
248582575Smjacob			 * Grotesque as it seems, the few times
248682575Smjacob			 * I've actually seen a non-zero resid,
248782575Smjacob			 * the tape drive actually lied and had
2488124645Sjohan			 * written all the data!.
248982575Smjacob			 */
249082575Smjacob			csio->resid = 0;
249139213Sgibbs		}
249246962Smjacob	} else {
249382575Smjacob		csio->resid = resid;
249446962Smjacob		if (sense_key == SSD_KEY_BLANK_CHECK) {
249582575Smjacob			if (softc->quirks & SA_QUIRK_1FM) {
249682575Smjacob				error = 0;
249746962Smjacob				softc->flags |= SA_FLAG_EOM_PENDING;
249846962Smjacob			} else {
249946962Smjacob				error = EIO;
250046962Smjacob			}
2501226067Sken		} else if ((stream_valid != 0) && (stream_bits & SSD_FILEMARK)){
250282575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
250346962Smjacob				error = -1;
250439213Sgibbs				softc->flags |= SA_FLAG_EOF_PENDING;
250546962Smjacob			}
250646962Smjacob			/*
250746962Smjacob			 * Unconditionally, if we detected a filemark on a read,
250846962Smjacob			 * mark that we've run moved a file ahead.
250946962Smjacob			 */
251043636Smjacob			if (softc->fileno != (daddr_t) -1) {
251143636Smjacob				softc->fileno++;
251243636Smjacob				softc->blkno = 0;
251371082Smjacob				csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED;
251443636Smjacob			}
251539213Sgibbs		}
251646962Smjacob	}
251782575Smjacob
251846962Smjacob	/*
251946962Smjacob	 * Incorrect Length usually applies to read, but can apply to writes.
252046962Smjacob	 */
2521226067Sken	if (error == 0 && (stream_valid != 0) && (stream_bits & SSD_ILI)) {
252246962Smjacob		if (info < 0) {
2523164906Smjacob			xpt_print(csio->ccb_h.path, toobig,
2524164906Smjacob			    csio->dxfer_len - info);
252546962Smjacob			csio->resid = csio->dxfer_len;
252646962Smjacob			error = EIO;
252746962Smjacob		} else {
252846962Smjacob			csio->resid = resid;
252982575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
253082575Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
253146962Smjacob			}
253246962Smjacob			/*
253346962Smjacob			 * Bump the block number if we hadn't seen a filemark.
253446962Smjacob			 * Do this independent of errors (we've moved anyway).
253546962Smjacob			 */
2536226067Sken			if ((stream_valid == 0) ||
2537226067Sken			    (stream_bits & SSD_FILEMARK) == 0) {
253846962Smjacob				if (softc->blkno != (daddr_t) -1) {
253946962Smjacob					softc->blkno++;
254071082Smjacob					csio->ccb_h.ccb_pflags |=
254171082Smjacob					   SA_POSITION_UPDATED;
254239213Sgibbs				}
254339213Sgibbs			}
254439213Sgibbs		}
254539213Sgibbs	}
254682575Smjacob
254782575Smjacob	if (error <= 0) {
254882575Smjacob		/*
254982575Smjacob		 * Unfreeze the queue if frozen as we're not returning anything
255082575Smjacob		 * to our waiters that would indicate an I/O error has occurred
255182575Smjacob		 * (yet).
255282575Smjacob		 */
255382575Smjacob		QFRLS(ccb);
255482575Smjacob		error = 0;
255580575Smjacob	}
255682575Smjacob	return (error);
255739213Sgibbs}
255839213Sgibbs
255939213Sgibbsstatic int
256039213Sgibbssagetparams(struct cam_periph *periph, sa_params params_to_get,
256139213Sgibbs	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,
256239213Sgibbs	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,
256339213Sgibbs	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,
256446962Smjacob	    sa_comp_t *tcs)
256539213Sgibbs{
256639213Sgibbs	union ccb *ccb;
256739213Sgibbs	void *mode_buffer;
256839213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
256939213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
257039213Sgibbs	int mode_buffer_len;
257139213Sgibbs	struct sa_softc *softc;
257246962Smjacob	u_int8_t cpage;
257339213Sgibbs	int error;
257439213Sgibbs	cam_status status;
257539213Sgibbs
257639213Sgibbs	softc = (struct sa_softc *)periph->softc;
257746962Smjacob	ccb = cam_periph_getccb(periph, 1);
257871082Smjacob	if (softc->quirks & SA_QUIRK_NO_CPAGE)
257971082Smjacob		cpage = SA_DEVICE_CONFIGURATION_PAGE;
258071082Smjacob	else
258171082Smjacob		cpage = SA_DATA_COMPRESSION_PAGE;
258239213Sgibbs
258339213Sgibbsretry:
258439213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
258539213Sgibbs
258639213Sgibbs	if (params_to_get & SA_PARAM_COMPRESSION) {
258739213Sgibbs		if (softc->quirks & SA_QUIRK_NOCOMP) {
258839213Sgibbs			*comp_supported = FALSE;
258939213Sgibbs			params_to_get &= ~SA_PARAM_COMPRESSION;
259039213Sgibbs		} else
259146962Smjacob			mode_buffer_len += sizeof (sa_comp_t);
259239213Sgibbs	}
259354099Smjacob
2594170829Sscottl	/* XXX Fix M_NOWAIT */
2595170829Sscottl	mode_buffer = malloc(mode_buffer_len, M_SCSISA, M_NOWAIT | M_ZERO);
2596170829Sscottl	if (mode_buffer == NULL) {
2597170829Sscottl		xpt_release_ccb(ccb);
2598170829Sscottl		return (ENOMEM);
2599170829Sscottl	}
260039213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
260139213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
260239213Sgibbs
260342716Smjacob	/* it is safe to retry this */
260442716Smjacob	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
260542716Smjacob	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?
260646962Smjacob	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,
260779100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
260839213Sgibbs
260954099Smjacob	error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2610112006Sphk	    softc->device_stats);
261139213Sgibbs
261239213Sgibbs	status = ccb->ccb_h.status & CAM_STATUS_MASK;
261339213Sgibbs
261442716Smjacob	if (error == EINVAL && (params_to_get & SA_PARAM_COMPRESSION) != 0) {
261539213Sgibbs		/*
261646962Smjacob		 * Hmm. Let's see if we can try another page...
261746962Smjacob		 * If we've already done that, give up on compression
261846962Smjacob		 * for this device and remember this for the future
261946962Smjacob		 * and attempt the request without asking for compression
262046962Smjacob		 * info.
262139213Sgibbs		 */
262246962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
262346962Smjacob			cpage = SA_DEVICE_CONFIGURATION_PAGE;
262446962Smjacob			goto retry;
262546962Smjacob		}
262639213Sgibbs		softc->quirks |= SA_QUIRK_NOCOMP;
2627169562Sscottl		free(mode_buffer, M_SCSISA);
262839213Sgibbs		goto retry;
262946962Smjacob	} else if (status == CAM_SCSI_STATUS_ERROR) {
263046962Smjacob		/* Tell the user about the fatal error. */
263146962Smjacob		scsi_sense_print(&ccb->csio);
263246962Smjacob		goto sagetparamsexit;
263346962Smjacob	}
263439213Sgibbs
263546962Smjacob	/*
263646962Smjacob	 * If the user only wants the compression information, and
263746962Smjacob	 * the device doesn't send back the block descriptor, it's
263846962Smjacob	 * no big deal.  If the user wants more than just
263946962Smjacob	 * compression, though, and the device doesn't pass back the
264046962Smjacob	 * block descriptor, we need to send another mode sense to
264146962Smjacob	 * get the block descriptor.
264246962Smjacob	 */
264346962Smjacob	if ((mode_hdr->blk_desc_len == 0) &&
264446962Smjacob	    (params_to_get & SA_PARAM_COMPRESSION) &&
264546962Smjacob	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {
264639213Sgibbs
264739213Sgibbs		/*
264846962Smjacob		 * Decrease the mode buffer length by the size of
264946962Smjacob		 * the compression page, to make sure the data
265046962Smjacob		 * there doesn't get overwritten.
265139213Sgibbs		 */
265246962Smjacob		mode_buffer_len -= sizeof (sa_comp_t);
265339213Sgibbs
265446962Smjacob		/*
265546962Smjacob		 * Now move the compression page that we presumably
265646962Smjacob		 * got back down the memory chunk a little bit so
265746962Smjacob		 * it doesn't get spammed.
265846962Smjacob		 */
265954099Smjacob		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));
266054099Smjacob		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));
266139213Sgibbs
266246962Smjacob		/*
266346962Smjacob		 * Now, we issue another mode sense and just ask
266446962Smjacob		 * for the block descriptor, etc.
266546962Smjacob		 */
266639213Sgibbs
266746962Smjacob		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
266846962Smjacob		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,
266979100Smjacob		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE,
267079100Smjacob		    SCSIOP_TIMEOUT);
267139213Sgibbs
267254099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2673112006Sphk		    softc->device_stats);
267439213Sgibbs
267546962Smjacob		if (error != 0)
267646962Smjacob			goto sagetparamsexit;
267746962Smjacob	}
267839213Sgibbs
267946962Smjacob	if (params_to_get & SA_PARAM_BLOCKSIZE)
268046962Smjacob		*blocksize = scsi_3btoul(mode_blk->blklen);
268139213Sgibbs
268246962Smjacob	if (params_to_get & SA_PARAM_NUMBLOCKS)
268346962Smjacob		*numblocks = scsi_3btoul(mode_blk->nblocks);
268439213Sgibbs
268546962Smjacob	if (params_to_get & SA_PARAM_BUFF_MODE)
268646962Smjacob		*buff_mode = mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;
268739213Sgibbs
268846962Smjacob	if (params_to_get & SA_PARAM_DENSITY)
268946962Smjacob		*density = mode_blk->density;
269039213Sgibbs
269146962Smjacob	if (params_to_get & SA_PARAM_WP)
269246962Smjacob		*write_protect = (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;
269339213Sgibbs
269446962Smjacob	if (params_to_get & SA_PARAM_SPEED)
269546962Smjacob		*speed = mode_hdr->dev_spec & SMH_SA_SPEED_MASK;
269639213Sgibbs
269746962Smjacob	if (params_to_get & SA_PARAM_COMPRESSION) {
269854099Smjacob		sa_comp_t *ntcs = (sa_comp_t *) &mode_blk[1];
269946962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
270046962Smjacob			struct scsi_data_compression_page *cp = &ntcs->dcomp;
270146962Smjacob			*comp_supported =
270246962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;
270346962Smjacob			*comp_enabled =
270446962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;
270546962Smjacob			*comp_algorithm = scsi_4btoul(cp->comp_algorithm);
270646962Smjacob		} else {
270746962Smjacob			struct scsi_dev_conf_page *cp = &ntcs->dconf;
270846962Smjacob			/*
270946962Smjacob			 * We don't really know whether this device supports
2710218909Sbrucec			 * Data Compression if the algorithm field is
271146962Smjacob			 * zero. Just say we do.
271246962Smjacob			 */
271346962Smjacob			*comp_supported = TRUE;
271446962Smjacob			*comp_enabled =
271546962Smjacob			    (cp->sel_comp_alg != SA_COMP_NONE)? TRUE : FALSE;
271646962Smjacob			*comp_algorithm = cp->sel_comp_alg;
271741906Smjacob		}
271846962Smjacob		if (tcs != NULL)
271954099Smjacob			bcopy(ntcs, tcs, sizeof (sa_comp_t));
272039213Sgibbs	}
272139213Sgibbs
272246962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
272346962Smjacob		int idx;
272446962Smjacob		char *xyz = mode_buffer;
272546962Smjacob		xpt_print_path(periph->path);
272646962Smjacob		printf("Mode Sense Data=");
272746962Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
272846962Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
272946962Smjacob		printf("\n");
273046962Smjacob	}
273146962Smjacob
273239213Sgibbssagetparamsexit:
273339213Sgibbs
273439213Sgibbs	xpt_release_ccb(ccb);
2735169562Sscottl	free(mode_buffer, M_SCSISA);
273654099Smjacob	return (error);
273739213Sgibbs}
273839213Sgibbs
273939213Sgibbs/*
274039213Sgibbs * The purpose of this function is to set one of four different parameters
274139213Sgibbs * for a tape drive:
274239213Sgibbs *	- blocksize
274339213Sgibbs *	- density
274439213Sgibbs *	- compression / compression algorithm
274539213Sgibbs *	- buffering mode
274639213Sgibbs *
274739213Sgibbs * The assumption is that this will be called from saioctl(), and therefore
274839213Sgibbs * from a process context.  Thus the waiting malloc calls below.  If that
274939213Sgibbs * assumption ever changes, the malloc calls should be changed to be
275039213Sgibbs * NOWAIT mallocs.
275139213Sgibbs *
275239213Sgibbs * Any or all of the four parameters may be set when this function is
275339213Sgibbs * called.  It should handle setting more than one parameter at once.
275439213Sgibbs */
275539213Sgibbsstatic int
275639213Sgibbssasetparams(struct cam_periph *periph, sa_params params_to_set,
275746962Smjacob	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,
275842563Smjacob	    u_int32_t sense_flags)
275939213Sgibbs{
276039213Sgibbs	struct sa_softc *softc;
276139213Sgibbs	u_int32_t current_blocksize;
276246962Smjacob	u_int32_t current_calg;
276339213Sgibbs	u_int8_t current_density;
276439213Sgibbs	u_int8_t current_speed;
276539213Sgibbs	int comp_enabled, comp_supported;
276639213Sgibbs	void *mode_buffer;
276739213Sgibbs	int mode_buffer_len;
276839213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
276939213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
277046962Smjacob	sa_comp_t *ccomp, *cpage;
277139213Sgibbs	int buff_mode;
277246962Smjacob	union ccb *ccb = NULL;
277339213Sgibbs	int error;
277439213Sgibbs
277539213Sgibbs	softc = (struct sa_softc *)periph->softc;
277639213Sgibbs
2777170830Sscottl	ccomp = malloc(sizeof (sa_comp_t), M_SCSISA, M_NOWAIT);
2778170830Sscottl	if (ccomp == NULL)
2779170830Sscottl		return (ENOMEM);
278039213Sgibbs
278139213Sgibbs	/*
278239213Sgibbs	 * Since it doesn't make sense to set the number of blocks, or
278339213Sgibbs	 * write protection, we won't try to get the current value.  We
278439213Sgibbs	 * always want to get the blocksize, so we can set it back to the
278539213Sgibbs	 * proper value.
278639213Sgibbs	 */
278746962Smjacob	error = sagetparams(periph,
278846962Smjacob	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,
278946962Smjacob	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,
279046962Smjacob	    &current_speed, &comp_supported, &comp_enabled,
279146962Smjacob	    &current_calg, ccomp);
279239213Sgibbs
279339213Sgibbs	if (error != 0) {
2794169562Sscottl		free(ccomp, M_SCSISA);
279554099Smjacob		return (error);
279639213Sgibbs	}
279739213Sgibbs
279839213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
279939213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION)
280046962Smjacob		mode_buffer_len += sizeof (sa_comp_t);
280139213Sgibbs
2802170830Sscottl	mode_buffer = malloc(mode_buffer_len, M_SCSISA, M_NOWAIT | M_ZERO);
2803170830Sscottl	if (mode_buffer == NULL) {
2804170830Sscottl		free(ccomp, M_SCSISA);
2805170830Sscottl		return (ENOMEM);
2806170830Sscottl	}
280739213Sgibbs
280839213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
280939213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
281039213Sgibbs
281153259Smjacob	ccb = cam_periph_getccb(periph, 1);
281253259Smjacob
281353259Smjacobretry:
281453259Smjacob
281539213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION) {
281653259Smjacob		if (mode_blk) {
281753259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
281853259Smjacob		} else {
281953259Smjacob			cpage = (sa_comp_t *)&mode_hdr[1];
282053259Smjacob		}
282146962Smjacob		bcopy(ccomp, cpage, sizeof (sa_comp_t));
282254099Smjacob		cpage->hdr.pagecode &= ~0x80;
282339213Sgibbs	} else
282446962Smjacob		cpage = NULL;
282539213Sgibbs
282639213Sgibbs	/*
282739213Sgibbs	 * If the caller wants us to set the blocksize, use the one they
282839213Sgibbs	 * pass in.  Otherwise, use the blocksize we got back from the
282939213Sgibbs	 * mode select above.
283039213Sgibbs	 */
283153259Smjacob	if (mode_blk) {
283253259Smjacob		if (params_to_set & SA_PARAM_BLOCKSIZE)
283353259Smjacob			scsi_ulto3b(blocksize, mode_blk->blklen);
283453259Smjacob		else
283553259Smjacob			scsi_ulto3b(current_blocksize, mode_blk->blklen);
283639213Sgibbs
283753259Smjacob		/*
283853259Smjacob		 * Set density if requested, else preserve old density.
283953259Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
284053259Smjacob		 * devices, else density we've latched up in our softc.
284153259Smjacob		 */
284253259Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
284353259Smjacob			mode_blk->density = density;
284453259Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
284553259Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
284653259Smjacob		} else {
284753259Smjacob			mode_blk->density = softc->media_density;
284853259Smjacob		}
284941674Smjacob	}
285039213Sgibbs
285139213Sgibbs	/*
285239213Sgibbs	 * For mode selects, these two fields must be zero.
285339213Sgibbs	 */
285439213Sgibbs	mode_hdr->data_length = 0;
285539213Sgibbs	mode_hdr->medium_type = 0;
285639213Sgibbs
285739213Sgibbs	/* set the speed to the current value */
285839213Sgibbs	mode_hdr->dev_spec = current_speed;
285939213Sgibbs
2860120019Smjacob	/* if set, set single-initiator buffering mode */
2861120019Smjacob	if (softc->buffer_mode == SMH_SA_BUF_MODE_SIBUF) {
2862120019Smjacob		mode_hdr->dev_spec |= SMH_SA_BUF_MODE_SIBUF;
2863120019Smjacob	}
286439213Sgibbs
286553259Smjacob	if (mode_blk)
286653259Smjacob		mode_hdr->blk_desc_len = sizeof(struct scsi_mode_blk_desc);
286753259Smjacob	else
286853259Smjacob		mode_hdr->blk_desc_len = 0;
286939213Sgibbs
287039213Sgibbs	/*
287139213Sgibbs	 * First, if the user wants us to set the compression algorithm or
287239213Sgibbs	 * just turn compression on, check to make sure that this drive
287339213Sgibbs	 * supports compression.
287439213Sgibbs	 */
287546962Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
287639213Sgibbs		/*
287739213Sgibbs		 * If the compression algorithm is 0, disable compression.
287839213Sgibbs		 * If the compression algorithm is non-zero, enable
287939213Sgibbs		 * compression and set the compression type to the
288039213Sgibbs		 * specified compression algorithm, unless the algorithm is
288139213Sgibbs		 * MT_COMP_ENABLE.  In that case, we look at the
288239213Sgibbs		 * compression algorithm that is currently set and if it is
288339213Sgibbs		 * non-zero, we leave it as-is.  If it is zero, and we have
288439213Sgibbs		 * saved a compression algorithm from a time when
288539213Sgibbs		 * compression was enabled before, set the compression to
288639213Sgibbs		 * the saved value.
288739213Sgibbs		 */
288854099Smjacob		switch (ccomp->hdr.pagecode & ~0x80) {
2889115660Smjacob		case SA_DEVICE_CONFIGURATION_PAGE:
2890115660Smjacob		{
2891115660Smjacob			struct scsi_dev_conf_page *dcp = &cpage->dconf;
2892115660Smjacob			if (calg == 0) {
2893115660Smjacob				dcp->sel_comp_alg = SA_COMP_NONE;
2894115660Smjacob				break;
2895115660Smjacob			}
2896115660Smjacob			if (calg != MT_COMP_ENABLE) {
2897115660Smjacob				dcp->sel_comp_alg = calg;
2898115660Smjacob			} else if (dcp->sel_comp_alg == SA_COMP_NONE &&
2899115660Smjacob			    softc->saved_comp_algorithm != 0) {
2900115660Smjacob				dcp->sel_comp_alg = softc->saved_comp_algorithm;
2901115660Smjacob			}
2902115660Smjacob			break;
2903115660Smjacob		}
290446962Smjacob		case SA_DATA_COMPRESSION_PAGE:
290546962Smjacob		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {
290646962Smjacob			struct scsi_data_compression_page *dcp = &cpage->dcomp;
290746962Smjacob			if (calg == 0) {
290854099Smjacob				/*
290954099Smjacob				 * Disable compression, but leave the
291054099Smjacob				 * decompression and the capability bit
291154099Smjacob				 * alone.
291254099Smjacob				 */
291354099Smjacob				dcp->dce_and_dcc = SA_DCP_DCC;
291454099Smjacob				dcp->dde_and_red |= SA_DCP_DDE;
291546962Smjacob				break;
291646962Smjacob			}
291753259Smjacob			/* enable compression && decompression */
291854099Smjacob			dcp->dce_and_dcc = SA_DCP_DCE | SA_DCP_DCC;
291954099Smjacob			dcp->dde_and_red |= SA_DCP_DDE;
292053259Smjacob			/*
292153259Smjacob			 * If there, use compression algorithm from caller.
292253259Smjacob			 * Otherwise, if there's a saved compression algorithm
292353259Smjacob			 * and there is no current algorithm, use the saved
292453259Smjacob			 * algorithm. Else parrot back what we got and hope
292553259Smjacob			 * for the best.
292653259Smjacob			 */
292746962Smjacob			if (calg != MT_COMP_ENABLE) {
292846962Smjacob				scsi_ulto4b(calg, dcp->comp_algorithm);
292953259Smjacob				scsi_ulto4b(calg, dcp->decomp_algorithm);
293046962Smjacob			} else if (scsi_4btoul(dcp->comp_algorithm) == 0 &&
293146962Smjacob			    softc->saved_comp_algorithm != 0) {
293239213Sgibbs				scsi_ulto4b(softc->saved_comp_algorithm,
293346962Smjacob				    dcp->comp_algorithm);
293453259Smjacob				scsi_ulto4b(softc->saved_comp_algorithm,
293553259Smjacob				    dcp->decomp_algorithm);
293639213Sgibbs			}
293746962Smjacob			break;
293839213Sgibbs		}
2939115660Smjacob		/*
2940115660Smjacob		 * Compression does not appear to be supported-
2941115660Smjacob		 * at least via the DATA COMPRESSION page. It
2942115660Smjacob		 * would be too much to ask us to believe that
2943115660Smjacob		 * the page itself is supported, but incorrectly
2944115660Smjacob		 * reports an ability to manipulate data compression,
2945115660Smjacob		 * so we'll assume that this device doesn't support
2946115660Smjacob		 * compression. We can just fall through for that.
2947115660Smjacob		 */
2948115660Smjacob		/* FALLTHROUGH */
294946962Smjacob		default:
295046962Smjacob			/*
295154099Smjacob			 * The drive doesn't seem to support compression,
295246962Smjacob			 * so turn off the set compression bit.
295346962Smjacob			 */
295446962Smjacob			params_to_set &= ~SA_PARAM_COMPRESSION;
2955164906Smjacob			xpt_print(periph->path,
2956164906Smjacob			    "device does not seem to support compression\n");
295754099Smjacob
295846962Smjacob			/*
295946962Smjacob			 * If that was the only thing the user wanted us to set,
296046962Smjacob			 * clean up allocated resources and return with
296146962Smjacob			 * 'operation not supported'.
296246962Smjacob			 */
296346962Smjacob			if (params_to_set == SA_PARAM_NONE) {
2964169562Sscottl				free(mode_buffer, M_SCSISA);
296554099Smjacob				xpt_release_ccb(ccb);
296654099Smjacob				return (ENODEV);
296746962Smjacob			}
296846962Smjacob
296946962Smjacob			/*
297046962Smjacob			 * That wasn't the only thing the user wanted us to set.
297146962Smjacob			 * So, decrease the stated mode buffer length by the
297246962Smjacob			 * size of the compression mode page.
297346962Smjacob			 */
297446962Smjacob			mode_buffer_len -= sizeof(sa_comp_t);
297546962Smjacob		}
297639213Sgibbs	}
297739213Sgibbs
297846962Smjacob	/* It is safe to retry this operation */
297946962Smjacob	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
298046962Smjacob	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,
298179100Smjacob	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
298241674Smjacob
298346962Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
2984112006Sphk	    sense_flags, softc->device_stats);
298539213Sgibbs
298646962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
298741906Smjacob		int idx;
298841674Smjacob		char *xyz = mode_buffer;
298941674Smjacob		xpt_print_path(periph->path);
299042009Smjacob		printf("Err%d, Mode Select Data=", error);
299141906Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
299242009Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
299341674Smjacob		printf("\n");
299441674Smjacob	}
299541674Smjacob
299639213Sgibbs
299753259Smjacob	if (error) {
299839213Sgibbs		/*
299953259Smjacob		 * If we can, try without setting density/blocksize.
300053259Smjacob		 */
300153259Smjacob		if (mode_blk) {
300253259Smjacob			if ((params_to_set &
300353259Smjacob			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) == 0) {
300453259Smjacob				mode_blk = NULL;
300553259Smjacob				goto retry;
300653259Smjacob			}
300753259Smjacob		} else {
300853259Smjacob			mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
300953259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
301053259Smjacob		}
301153259Smjacob
301253259Smjacob		/*
301339213Sgibbs		 * If we were setting the blocksize, and that failed, we
301439213Sgibbs		 * want to set it to its original value.  If we weren't
301539213Sgibbs		 * setting the blocksize, we don't want to change it.
301639213Sgibbs		 */
301739213Sgibbs		scsi_ulto3b(current_blocksize, mode_blk->blklen);
301839213Sgibbs
301939213Sgibbs		/*
302041674Smjacob		 * Set density if requested, else preserve old density.
302141674Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
302241674Smjacob		 * devices, else density we've latched up in our softc.
302339213Sgibbs		 */
302441674Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
302539213Sgibbs			mode_blk->density = current_density;
302641674Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
302741674Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
302841674Smjacob		} else {
302941674Smjacob			mode_blk->density = softc->media_density;
303041674Smjacob		}
303139213Sgibbs
303239213Sgibbs		if (params_to_set & SA_PARAM_COMPRESSION)
303346962Smjacob			bcopy(ccomp, cpage, sizeof (sa_comp_t));
303439213Sgibbs
303539213Sgibbs		/*
303639213Sgibbs		 * The retry count is the only CCB field that might have been
303739213Sgibbs		 * changed that we care about, so reset it back to 1.
303839213Sgibbs		 */
303939213Sgibbs		ccb->ccb_h.retry_count = 1;
304054099Smjacob		cam_periph_runccb(ccb, saerror, 0, sense_flags,
3041112006Sphk		    softc->device_stats);
304239213Sgibbs	}
304339213Sgibbs
304453259Smjacob	xpt_release_ccb(ccb);
304553259Smjacob
304646962Smjacob	if (ccomp != NULL)
3047169562Sscottl		free(ccomp, M_SCSISA);
304839213Sgibbs
304941948Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
305041948Smjacob		if (error) {
305141948Smjacob			softc->flags &= ~SA_FLAG_COMP_ENABLED;
305246962Smjacob			/*
305346962Smjacob			 * Even if we get an error setting compression,
305446962Smjacob			 * do not say that we don't support it. We could
305546962Smjacob			 * have been wrong, or it may be media specific.
305646962Smjacob			 *	softc->flags &= ~SA_FLAG_COMP_SUPP;
305746962Smjacob			 */
305841948Smjacob			softc->saved_comp_algorithm = softc->comp_algorithm;
305941948Smjacob			softc->comp_algorithm = 0;
306041948Smjacob		} else {
306141948Smjacob			softc->flags |= SA_FLAG_COMP_ENABLED;
306246962Smjacob			softc->comp_algorithm = calg;
306341948Smjacob		}
306441948Smjacob	}
306541948Smjacob
3066169562Sscottl	free(mode_buffer, M_SCSISA);
306754099Smjacob	return (error);
306839213Sgibbs}
306939213Sgibbs
307039213Sgibbsstatic void
307139213Sgibbssaprevent(struct cam_periph *periph, int action)
307239213Sgibbs{
307339213Sgibbs	struct	sa_softc *softc;
307439213Sgibbs	union	ccb *ccb;
307542735Smjacob	int	error, sf;
307639213Sgibbs
307739213Sgibbs	softc = (struct sa_softc *)periph->softc;
307839213Sgibbs
307942735Smjacob	if ((action == PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) == 0)
308039213Sgibbs		return;
308142735Smjacob	if ((action == PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) != 0)
308242735Smjacob		return;
308339213Sgibbs
308456981Smjacob	/*
308556981Smjacob	 * We can be quiet about illegal requests.
308656981Smjacob	 */
308756981Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
308842735Smjacob		sf = 0;
308956981Smjacob	} else
309042735Smjacob		sf = SF_QUIET_IR;
309142735Smjacob
309242716Smjacob	ccb = cam_periph_getccb(periph, 1);
309339213Sgibbs
309442716Smjacob	/* It is safe to retry this operation */
309542716Smjacob	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,
309679100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
309739213Sgibbs
3098112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats);
309939213Sgibbs	if (error == 0) {
310039213Sgibbs		if (action == PR_ALLOW)
310139213Sgibbs			softc->flags &= ~SA_FLAG_TAPE_LOCKED;
310239213Sgibbs		else
310339213Sgibbs			softc->flags |= SA_FLAG_TAPE_LOCKED;
310439213Sgibbs	}
310539213Sgibbs
310639213Sgibbs	xpt_release_ccb(ccb);
310739213Sgibbs}
310839213Sgibbs
310939213Sgibbsstatic int
311039213Sgibbssarewind(struct cam_periph *periph)
311139213Sgibbs{
311239213Sgibbs	union	ccb *ccb;
311339213Sgibbs	struct	sa_softc *softc;
311439213Sgibbs	int	error;
311539213Sgibbs
311639213Sgibbs	softc = (struct sa_softc *)periph->softc;
311739213Sgibbs
311846962Smjacob	ccb = cam_periph_getccb(periph, 1);
311939213Sgibbs
312042716Smjacob	/* It is safe to retry this operation */
312146962Smjacob	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
312253259Smjacob	    SSD_FULL_SIZE, REWIND_TIMEOUT);
312339213Sgibbs
312443636Smjacob	softc->dsreg = MTIO_DSREG_REW;
3125112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
312643636Smjacob	softc->dsreg = MTIO_DSREG_REST;
312739213Sgibbs
312839213Sgibbs	xpt_release_ccb(ccb);
312943636Smjacob	if (error == 0)
313043636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
313143636Smjacob	else
313243636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
313339213Sgibbs	return (error);
313439213Sgibbs}
313539213Sgibbs
313639213Sgibbsstatic int
313739213Sgibbssaspace(struct cam_periph *periph, int count, scsi_space_code code)
313839213Sgibbs{
313939213Sgibbs	union	ccb *ccb;
314039213Sgibbs	struct	sa_softc *softc;
314139213Sgibbs	int	error;
314239213Sgibbs
314339213Sgibbs	softc = (struct sa_softc *)periph->softc;
314439213Sgibbs
314546962Smjacob	ccb = cam_periph_getccb(periph, 1);
314639213Sgibbs
314742716Smjacob	/* This cannot be retried */
314839213Sgibbs
314942716Smjacob	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,
315053259Smjacob	    SSD_FULL_SIZE, SPACE_TIMEOUT);
315139213Sgibbs
315271087Smjacob	/*
315371087Smjacob	 * Clear residual because we will be using it.
315471087Smjacob	 */
315571087Smjacob	softc->last_ctl_resid = 0;
315671087Smjacob
315743636Smjacob	softc->dsreg = (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;
3158112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
315943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
316042716Smjacob
316139213Sgibbs	xpt_release_ccb(ccb);
316243636Smjacob
316342716Smjacob	/*
316443636Smjacob	 * If a spacing operation has failed, we need to invalidate
316543636Smjacob	 * this mount.
316643636Smjacob	 *
316743636Smjacob	 * If the spacing operation was setmarks or to end of recorded data,
316843636Smjacob	 * we no longer know our relative position.
316943636Smjacob	 *
317071087Smjacob	 * If the spacing operations was spacing files in reverse, we
317171087Smjacob	 * take account of the residual, but still check against less
317271087Smjacob	 * than zero- if we've gone negative, we must have hit BOT.
317371087Smjacob	 *
317471087Smjacob	 * If the spacing operations was spacing records in reverse and
317571087Smjacob	 * we have a residual, we've either hit BOT or hit a filemark.
317671087Smjacob	 * In the former case, we know our new record number (0). In
317771087Smjacob	 * the latter case, we have absolutely no idea what the real
317871087Smjacob	 * record number is- we've stopped between the end of the last
317971087Smjacob	 * record in the previous file and the filemark that stopped
318071087Smjacob	 * our spacing backwards.
318142716Smjacob	 */
318243636Smjacob	if (error) {
318343636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
318443636Smjacob	} else if (code == SS_SETMARKS || code == SS_EOD) {
318543636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
318643636Smjacob	} else if (code == SS_FILEMARKS && softc->fileno != (daddr_t) -1) {
318771087Smjacob		softc->fileno += (count - softc->last_ctl_resid);
318871087Smjacob		if (softc->fileno < 0)	/* we must of hit BOT */
318971087Smjacob			softc->fileno = 0;
319043636Smjacob		softc->blkno = 0;
319143636Smjacob	} else if (code == SS_BLOCKS && softc->blkno != (daddr_t) -1) {
319271087Smjacob		softc->blkno += (count - softc->last_ctl_resid);
319371087Smjacob		if (count < 0) {
319471087Smjacob			if (softc->last_ctl_resid || softc->blkno < 0) {
319571087Smjacob				if (softc->fileno == 0) {
319671087Smjacob					softc->blkno = 0;
319771087Smjacob				} else {
319871087Smjacob					softc->blkno = (daddr_t) -1;
319971087Smjacob				}
320071087Smjacob			}
320171087Smjacob		}
320243636Smjacob	}
320339213Sgibbs	return (error);
320439213Sgibbs}
320539213Sgibbs
320639213Sgibbsstatic int
320739213Sgibbssawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)
320839213Sgibbs{
320939213Sgibbs	union	ccb *ccb;
321039213Sgibbs	struct	sa_softc *softc;
321171087Smjacob	int	error, nwm = 0;
321239213Sgibbs
321339213Sgibbs	softc = (struct sa_softc *)periph->softc;
3214154360Smjacob	if (softc->open_rdonly)
3215154360Smjacob		return (EBADF);
321639213Sgibbs
321746962Smjacob	ccb = cam_periph_getccb(periph, 1);
321871087Smjacob	/*
321971087Smjacob	 * Clear residual because we will be using it.
322071087Smjacob	 */
322171087Smjacob	softc->last_ctl_resid = 0;
322239213Sgibbs
322343636Smjacob	softc->dsreg = MTIO_DSREG_FMK;
322442716Smjacob	/* this *must* not be retried */
322542716Smjacob	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,
322679100Smjacob	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT);
322743636Smjacob	softc->dsreg = MTIO_DSREG_REST;
322839213Sgibbs
322943636Smjacob
3230112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
323139213Sgibbs
323241918Smjacob	if (error == 0 && nmarks) {
323341918Smjacob		struct sa_softc *softc = (struct sa_softc *)periph->softc;
323471087Smjacob		nwm = nmarks - softc->last_ctl_resid;
323571087Smjacob		softc->filemarks += nwm;
323639213Sgibbs	}
323771087Smjacob
323841918Smjacob	xpt_release_ccb(ccb);
323943636Smjacob
324043636Smjacob	/*
324143636Smjacob	 * Update relative positions (if we're doing that).
324243636Smjacob	 */
324343636Smjacob	if (error) {
324443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
324543636Smjacob	} else if (softc->fileno != (daddr_t) -1) {
324671087Smjacob		softc->fileno += nwm;
324743636Smjacob		softc->blkno = 0;
324843636Smjacob	}
324941918Smjacob	return (error);
325041918Smjacob}
325139213Sgibbs
325241918Smjacobstatic int
325341918Smjacobsardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
325441918Smjacob{
325541918Smjacob	struct scsi_tape_position_data loc;
325641918Smjacob	union ccb *ccb;
325746962Smjacob	struct sa_softc *softc = (struct sa_softc *)periph->softc;
325841918Smjacob	int error;
325941918Smjacob
326041918Smjacob	/*
326171082Smjacob	 * We try and flush any buffered writes here if we were writing
326271082Smjacob	 * and we're trying to get hardware block position. It eats
326371082Smjacob	 * up performance substantially, but I'm wary of drive firmware.
326446962Smjacob	 *
326571082Smjacob	 * I think that *logical* block position is probably okay-
326671082Smjacob	 * but hardware block position might have to wait for data
326771082Smjacob	 * to hit media to be valid. Caveat Emptor.
326841918Smjacob	 */
326941918Smjacob
327071082Smjacob	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {
327146962Smjacob		error = sawritefilemarks(periph, 0, 0);
327246962Smjacob		if (error && error != EACCES)
327346962Smjacob			return (error);
327446962Smjacob	}
327541918Smjacob
327666678Smjacob	ccb = cam_periph_getccb(periph, 1);
327741918Smjacob	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
327879100Smjacob	    hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
327943636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
3280112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
328143636Smjacob	softc->dsreg = MTIO_DSREG_REST;
328241918Smjacob
328341918Smjacob	if (error == 0) {
328441918Smjacob		if (loc.flags & SA_RPOS_UNCERTAIN) {
328541918Smjacob			error = EINVAL;		/* nothing is certain */
328641918Smjacob		} else {
328741918Smjacob			*blkptr = scsi_4btoul(loc.firstblk);
328841918Smjacob		}
328941918Smjacob	}
329041918Smjacob
329139213Sgibbs	xpt_release_ccb(ccb);
329239213Sgibbs	return (error);
329339213Sgibbs}
329439213Sgibbs
329539213Sgibbsstatic int
329641918Smjacobsasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
329741918Smjacob{
329841918Smjacob	union ccb *ccb;
329941918Smjacob	struct sa_softc *softc;
330041918Smjacob	int error;
330141918Smjacob
330241918Smjacob	/*
330346962Smjacob	 * We used to try and flush any buffered writes here.
330446962Smjacob	 * Now we push this onto user applications to either
330546962Smjacob	 * flush the pending writes themselves (via a zero count
330646962Smjacob	 * WRITE FILEMARKS command) or they can trust their tape
330746962Smjacob	 * drive to do this correctly for them.
330846962Smjacob 	 */
330941918Smjacob
331041918Smjacob	softc = (struct sa_softc *)periph->softc;
331146962Smjacob	ccb = cam_periph_getccb(periph, 1);
331241918Smjacob
331343636Smjacob
331441918Smjacob	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
331579100Smjacob	    hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT);
331643636Smjacob
331779100Smjacob
331843636Smjacob	softc->dsreg = MTIO_DSREG_POS;
3319112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
332043636Smjacob	softc->dsreg = MTIO_DSREG_REST;
332141918Smjacob	xpt_release_ccb(ccb);
332241918Smjacob	/*
332346962Smjacob	 * Note relative file && block number position as now unknown.
332441918Smjacob	 */
332543636Smjacob	softc->fileno = softc->blkno = (daddr_t) -1;
332641918Smjacob	return (error);
332741918Smjacob}
332841918Smjacob
332941918Smjacobstatic int
333039213Sgibbssaretension(struct cam_periph *periph)
333139213Sgibbs{
333239213Sgibbs	union ccb *ccb;
333339213Sgibbs	struct sa_softc *softc;
333439213Sgibbs	int error;
333539213Sgibbs
333639213Sgibbs	softc = (struct sa_softc *)periph->softc;
333739213Sgibbs
333846962Smjacob	ccb = cam_periph_getccb(periph, 1);
333939213Sgibbs
334042716Smjacob	/* It is safe to retry this operation */
334142716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
334253259Smjacob	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);
334339213Sgibbs
334443636Smjacob	softc->dsreg = MTIO_DSREG_TEN;
3345112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
334643636Smjacob	softc->dsreg = MTIO_DSREG_REST;
334739213Sgibbs
334839213Sgibbs	xpt_release_ccb(ccb);
334943636Smjacob	if (error == 0)
335043636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
335143636Smjacob	else
335243636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
335354099Smjacob	return (error);
335439213Sgibbs}
335539213Sgibbs
335639213Sgibbsstatic int
335739213Sgibbssareservereleaseunit(struct cam_periph *periph, int reserve)
335839213Sgibbs{
335939213Sgibbs	union ccb *ccb;
336039213Sgibbs	struct sa_softc *softc;
336154099Smjacob	int error;
336239213Sgibbs
336342009Smjacob	softc = (struct sa_softc *)periph->softc;
336442716Smjacob	ccb = cam_periph_getccb(periph,  1);
336539213Sgibbs
336642716Smjacob	/* It is safe to retry this operation */
336754099Smjacob	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
336879100Smjacob	    FALSE,  0, SSD_FULL_SIZE,  SCSIOP_TIMEOUT, reserve);
336943636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
337054099Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
3371112006Sphk	    SF_RETRY_UA | SF_NO_PRINT, softc->device_stats);
337243636Smjacob	softc->dsreg = MTIO_DSREG_REST;
337339213Sgibbs	xpt_release_ccb(ccb);
337439213Sgibbs
337541674Smjacob	/*
337641674Smjacob	 * If the error was Illegal Request, then the device doesn't support
337741674Smjacob	 * RESERVE/RELEASE. This is not an error.
337841674Smjacob	 */
337942009Smjacob	if (error == EINVAL) {
338041674Smjacob		error = 0;
338142009Smjacob	}
338241674Smjacob
338339213Sgibbs	return (error);
338439213Sgibbs}
338539213Sgibbs
338639213Sgibbsstatic int
338739213Sgibbssaloadunload(struct cam_periph *periph, int load)
338839213Sgibbs{
338939213Sgibbs	union	ccb *ccb;
339039213Sgibbs	struct	sa_softc *softc;
339139213Sgibbs	int	error;
339239213Sgibbs
339339213Sgibbs	softc = (struct sa_softc *)periph->softc;
339439213Sgibbs
339546962Smjacob	ccb = cam_periph_getccb(periph, 1);
339639213Sgibbs
339742716Smjacob	/* It is safe to retry this operation */
339842716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
339954099Smjacob	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);
340039213Sgibbs
340143636Smjacob	softc->dsreg = (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;
3402112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
340343636Smjacob	softc->dsreg = MTIO_DSREG_REST;
340439213Sgibbs	xpt_release_ccb(ccb);
340543636Smjacob
340643636Smjacob	if (error || load == 0)
340743636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
340843636Smjacob	else if (error == 0)
340943636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
341039213Sgibbs	return (error);
341139213Sgibbs}
341239213Sgibbs
341339213Sgibbsstatic int
341439213Sgibbssaerase(struct cam_periph *periph, int longerase)
341539213Sgibbs{
341639213Sgibbs
341739213Sgibbs	union	ccb *ccb;
341839213Sgibbs	struct	sa_softc *softc;
341939213Sgibbs	int error;
342039213Sgibbs
342139213Sgibbs	softc = (struct sa_softc *)periph->softc;
3422154360Smjacob	if (softc->open_rdonly)
3423154360Smjacob		return (EBADF);
342439213Sgibbs
342546962Smjacob	ccb = cam_periph_getccb(periph, 1);
342639213Sgibbs
342743636Smjacob	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,
342853259Smjacob	    SSD_FULL_SIZE, ERASE_TIMEOUT);
342939213Sgibbs
343043636Smjacob	softc->dsreg = MTIO_DSREG_ZER;
3431112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
343243636Smjacob	softc->dsreg = MTIO_DSREG_REST;
343339213Sgibbs
343439213Sgibbs	xpt_release_ccb(ccb);
343539213Sgibbs	return (error);
343639213Sgibbs}
343739213Sgibbs
343855205Speter#endif /* _KERNEL */
343939213Sgibbs
344039213Sgibbs/*
344139213Sgibbs * Read tape block limits command.
344239213Sgibbs */
344339213Sgibbsvoid
344439213Sgibbsscsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,
344539213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
344639213Sgibbs		   u_int8_t tag_action,
344739213Sgibbs		   struct scsi_read_block_limits_data *rlimit_buf,
344839213Sgibbs		   u_int8_t sense_len, u_int32_t timeout)
344939213Sgibbs{
345039213Sgibbs	struct scsi_read_block_limits *scsi_cmd;
345139213Sgibbs
345246962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
345346962Smjacob	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,
345446962Smjacob	     sizeof(*scsi_cmd), timeout);
345539213Sgibbs
345639213Sgibbs	scsi_cmd = (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;
345739213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
345839213Sgibbs	scsi_cmd->opcode = READ_BLOCK_LIMITS;
345939213Sgibbs}
346039213Sgibbs
346139213Sgibbsvoid
346239213Sgibbsscsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,
346339213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
346439213Sgibbs		   u_int8_t tag_action, int readop, int sli,
346539213Sgibbs		   int fixed, u_int32_t length, u_int8_t *data_ptr,
346639213Sgibbs		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)
346739213Sgibbs{
346839213Sgibbs	struct scsi_sa_rw *scsi_cmd;
3469255000Sken	int read;
347039213Sgibbs
3471255000Sken	read = (readop & SCSI_RW_DIRMASK) == SCSI_RW_READ;
3472255000Sken
347339213Sgibbs	scsi_cmd = (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;
3474255000Sken	scsi_cmd->opcode = read ? SA_READ : SA_WRITE;
347539213Sgibbs	scsi_cmd->sli_fixed = 0;
3476255000Sken	if (sli && read)
347739213Sgibbs		scsi_cmd->sli_fixed |= SAR_SLI;
347839213Sgibbs	if (fixed)
347939213Sgibbs		scsi_cmd->sli_fixed |= SARW_FIXED;
348039213Sgibbs	scsi_ulto3b(length, scsi_cmd->length);
348139213Sgibbs	scsi_cmd->control = 0;
348239213Sgibbs
3483255000Sken	cam_fill_csio(csio, retries, cbfcnp, (read ? CAM_DIR_IN : CAM_DIR_OUT) |
3484255000Sken	    ((readop & SCSI_RW_BIO) != 0 ? CAM_DATA_BIO : 0),
348546962Smjacob	    tag_action, data_ptr, dxfer_len, sense_len,
348646962Smjacob	    sizeof(*scsi_cmd), timeout);
348739213Sgibbs}
348839213Sgibbs
348939213Sgibbsvoid
349039213Sgibbsscsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,
349139213Sgibbs		 void (*cbfcnp)(struct cam_periph *, union ccb *),
349239213Sgibbs		 u_int8_t tag_action, int immediate, int eot,
349339213Sgibbs		 int reten, int load, u_int8_t sense_len,
349439213Sgibbs		 u_int32_t timeout)
349539213Sgibbs{
349639213Sgibbs	struct scsi_load_unload *scsi_cmd;
349739213Sgibbs
349839213Sgibbs	scsi_cmd = (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;
349939213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
350039213Sgibbs	scsi_cmd->opcode = LOAD_UNLOAD;
350139213Sgibbs	if (immediate)
350239213Sgibbs		scsi_cmd->immediate = SLU_IMMED;
350339213Sgibbs	if (eot)
350439213Sgibbs		scsi_cmd->eot_reten_load |= SLU_EOT;
350539213Sgibbs	if (reten)
350639213Sgibbs		scsi_cmd->eot_reten_load |= SLU_RETEN;
350739213Sgibbs	if (load)
350839213Sgibbs		scsi_cmd->eot_reten_load |= SLU_LOAD;
350939213Sgibbs
351046962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
351146962Smjacob	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);
351239213Sgibbs}
351339213Sgibbs
351439213Sgibbsvoid
351539213Sgibbsscsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,
351639213Sgibbs	    void (*cbfcnp)(struct cam_periph *, union ccb *),
351739213Sgibbs	    u_int8_t tag_action, int immediate, u_int8_t sense_len,
351839213Sgibbs	    u_int32_t timeout)
351939213Sgibbs{
352039213Sgibbs	struct scsi_rewind *scsi_cmd;
352139213Sgibbs
352239213Sgibbs	scsi_cmd = (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;
352339213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
352439213Sgibbs	scsi_cmd->opcode = REWIND;
352539213Sgibbs	if (immediate)
352639213Sgibbs		scsi_cmd->immediate = SREW_IMMED;
352739213Sgibbs
352846962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
352946962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
353039213Sgibbs}
353139213Sgibbs
353239213Sgibbsvoid
353339213Sgibbsscsi_space(struct ccb_scsiio *csio, u_int32_t retries,
353439213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
353539213Sgibbs	   u_int8_t tag_action, scsi_space_code code,
353639213Sgibbs	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)
353739213Sgibbs{
353839213Sgibbs	struct scsi_space *scsi_cmd;
353939213Sgibbs
354039213Sgibbs	scsi_cmd = (struct scsi_space *)&csio->cdb_io.cdb_bytes;
354139213Sgibbs	scsi_cmd->opcode = SPACE;
354239213Sgibbs	scsi_cmd->code = code;
354339213Sgibbs	scsi_ulto3b(count, scsi_cmd->count);
354439213Sgibbs	scsi_cmd->control = 0;
354539213Sgibbs
354646962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
354746962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
354839213Sgibbs}
354939213Sgibbs
355039213Sgibbsvoid
355139213Sgibbsscsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,
355239213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
355339213Sgibbs		     u_int8_t tag_action, int immediate, int setmark,
355439213Sgibbs		     u_int32_t num_marks, u_int8_t sense_len,
355539213Sgibbs		     u_int32_t timeout)
355639213Sgibbs{
355739213Sgibbs	struct scsi_write_filemarks *scsi_cmd;
355839213Sgibbs
355939213Sgibbs	scsi_cmd = (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;
356039213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
356139213Sgibbs	scsi_cmd->opcode = WRITE_FILEMARKS;
356239213Sgibbs	if (immediate)
356339213Sgibbs		scsi_cmd->byte2 |= SWFMRK_IMMED;
356439213Sgibbs	if (setmark)
356539213Sgibbs		scsi_cmd->byte2 |= SWFMRK_WSMK;
356639213Sgibbs
356739213Sgibbs	scsi_ulto3b(num_marks, scsi_cmd->num_marks);
356839213Sgibbs
356946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
357046962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
357139213Sgibbs}
357239213Sgibbs
357339213Sgibbs/*
357439213Sgibbs * The reserve and release unit commands differ only by their opcodes.
357539213Sgibbs */
357639213Sgibbsvoid
357739213Sgibbsscsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,
357839213Sgibbs			  void (*cbfcnp)(struct cam_periph *, union ccb *),
357939213Sgibbs			  u_int8_t tag_action, int third_party,
358039213Sgibbs			  int third_party_id, u_int8_t sense_len,
358139213Sgibbs			  u_int32_t timeout, int reserve)
358239213Sgibbs{
358339213Sgibbs	struct scsi_reserve_release_unit *scsi_cmd;
358439213Sgibbs
358539213Sgibbs	scsi_cmd = (struct scsi_reserve_release_unit *)&csio->cdb_io.cdb_bytes;
358639213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
358739213Sgibbs
358839213Sgibbs	if (reserve)
358939213Sgibbs		scsi_cmd->opcode = RESERVE_UNIT;
359039213Sgibbs	else
359139213Sgibbs		scsi_cmd->opcode = RELEASE_UNIT;
359239213Sgibbs
359339213Sgibbs	if (third_party) {
359439213Sgibbs		scsi_cmd->lun_thirdparty |= SRRU_3RD_PARTY;
359539213Sgibbs		scsi_cmd->lun_thirdparty |=
359639213Sgibbs			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);
359739213Sgibbs	}
359839213Sgibbs
359946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
360046962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
360139213Sgibbs}
360239213Sgibbs
360339213Sgibbsvoid
360439213Sgibbsscsi_erase(struct ccb_scsiio *csio, u_int32_t retries,
360539213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
360639213Sgibbs	   u_int8_t tag_action, int immediate, int long_erase,
360739213Sgibbs	   u_int8_t sense_len, u_int32_t timeout)
360839213Sgibbs{
360939213Sgibbs	struct scsi_erase *scsi_cmd;
361039213Sgibbs
361139213Sgibbs	scsi_cmd = (struct scsi_erase *)&csio->cdb_io.cdb_bytes;
361239213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
361339213Sgibbs
361439213Sgibbs	scsi_cmd->opcode = ERASE;
361539213Sgibbs
361639213Sgibbs	if (immediate)
361739213Sgibbs		scsi_cmd->lun_imm_long |= SE_IMMED;
361839213Sgibbs
361939213Sgibbs	if (long_erase)
362039213Sgibbs		scsi_cmd->lun_imm_long |= SE_LONG;
362139213Sgibbs
362246962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
362346962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
362439213Sgibbs}
362541918Smjacob
362641918Smjacob/*
362741918Smjacob * Read Tape Position command.
362841918Smjacob */
362941918Smjacobvoid
363041918Smjacobscsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,
363141918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
363241918Smjacob		   u_int8_t tag_action, int hardsoft,
363341918Smjacob		   struct scsi_tape_position_data *sbp,
363441918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
363541918Smjacob{
363641918Smjacob	struct scsi_tape_read_position *scmd;
363741918Smjacob
363841918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
363941918Smjacob	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);
364041918Smjacob	scmd = (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;
364141918Smjacob	bzero(scmd, sizeof(*scmd));
364241918Smjacob	scmd->opcode = READ_POSITION;
364341918Smjacob	scmd->byte1 = hardsoft;
364441918Smjacob}
364541918Smjacob
364641918Smjacob/*
364741918Smjacob * Set Tape Position command.
364841918Smjacob */
364941918Smjacobvoid
365041918Smjacobscsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,
365141918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
365241918Smjacob		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,
365341918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
365441918Smjacob{
365541918Smjacob	struct scsi_tape_locate *scmd;
365641918Smjacob
365741918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
365841918Smjacob	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);
365941918Smjacob	scmd = (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;
366041918Smjacob	bzero(scmd, sizeof(*scmd));
366141918Smjacob	scmd->opcode = LOCATE;
366241918Smjacob	if (hardsoft)
366341918Smjacob		scmd->byte1 |= SA_SPOS_BT;
366441918Smjacob	scsi_ulto4b(blkno, scmd->blkaddr);
366541918Smjacob}
3666