scsi_sa.c revision 168752
1139743Simp/*-
239213Sgibbs * Implementation of SCSI Sequential Access Peripheral driver for CAM.
339213Sgibbs *
458251Smjacob * Copyright (c) 1999, 2000 Matthew Jacob
539213Sgibbs * All rights reserved.
639213Sgibbs *
739213Sgibbs * Redistribution and use in source and binary forms, with or without
839213Sgibbs * modification, are permitted provided that the following conditions
939213Sgibbs * are met:
1039213Sgibbs * 1. Redistributions of source code must retain the above copyright
1139213Sgibbs *    notice, this list of conditions, and the following disclaimer,
1239213Sgibbs *    without modification, immediately at the beginning of the file.
1339213Sgibbs * 2. The name of the author may not be used to endorse or promote products
1439213Sgibbs *    derived from this software without specific prior written permission.
1539213Sgibbs *
1639213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1739213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1839213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1939213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2039213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2139213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2239213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2339213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2439213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2539213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2639213Sgibbs * SUCH DAMAGE.
2739213Sgibbs */
2839213Sgibbs
29116162Sobrien#include <sys/cdefs.h>
30116162Sobrien__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_sa.c 168752 2007-04-15 08:49:19Z scottl $");
31116162Sobrien
3239213Sgibbs#include <sys/param.h>
3339213Sgibbs#include <sys/queue.h>
3455205Speter#ifdef _KERNEL
3539213Sgibbs#include <sys/systm.h>
3639213Sgibbs#include <sys/kernel.h>
3739213Sgibbs#endif
3839213Sgibbs#include <sys/types.h>
39110517Sphk#include <sys/time.h>
4060041Sphk#include <sys/bio.h>
41114216Skan#include <sys/limits.h>
4239213Sgibbs#include <sys/malloc.h>
4339213Sgibbs#include <sys/mtio.h>
4465061Speter#ifdef _KERNEL
4539213Sgibbs#include <sys/conf.h>
4665061Speter#endif
47154360Smjacob#include <sys/fcntl.h>
4839213Sgibbs#include <sys/devicestat.h>
4939213Sgibbs
5055205Speter#ifndef _KERNEL
5139213Sgibbs#include <stdio.h>
5239213Sgibbs#include <string.h>
5339213Sgibbs#endif
5439213Sgibbs
5539213Sgibbs#include <cam/cam.h>
5639213Sgibbs#include <cam/cam_ccb.h>
5739213Sgibbs#include <cam/cam_periph.h>
5839213Sgibbs#include <cam/cam_xpt_periph.h>
5939213Sgibbs#include <cam/cam_debug.h>
6039213Sgibbs
6139213Sgibbs#include <cam/scsi/scsi_all.h>
6239213Sgibbs#include <cam/scsi/scsi_message.h>
6339213Sgibbs#include <cam/scsi/scsi_sa.h>
6439213Sgibbs
6555205Speter#ifdef _KERNEL
6639213Sgibbs
6739884Sken#include <opt_sa.h>
6839884Sken
6979100Smjacob#ifndef SA_IO_TIMEOUT
7079100Smjacob#define SA_IO_TIMEOUT		4
7179100Smjacob#endif
7239884Sken#ifndef SA_SPACE_TIMEOUT
7339884Sken#define SA_SPACE_TIMEOUT	1 * 60
7439884Sken#endif
7539884Sken#ifndef SA_REWIND_TIMEOUT
7639884Sken#define SA_REWIND_TIMEOUT	2 * 60
7739884Sken#endif
7839884Sken#ifndef SA_ERASE_TIMEOUT
7939884Sken#define SA_ERASE_TIMEOUT	4 * 60
8039884Sken#endif
8153259Smjacob
8279100Smjacob#define	SCSIOP_TIMEOUT		(60 * 1000)	/* not an option */
8379100Smjacob
8479100Smjacob#define	IO_TIMEOUT		(SA_IO_TIMEOUT * 60 * 1000)
8554099Smjacob#define	REWIND_TIMEOUT		(SA_REWIND_TIMEOUT * 60 * 1000)
8654099Smjacob#define	ERASE_TIMEOUT		(SA_ERASE_TIMEOUT * 60 * 1000)
8754099Smjacob#define	SPACE_TIMEOUT		(SA_SPACE_TIMEOUT * 60 * 1000)
8853259Smjacob
8943636Smjacob/*
9051875Smjacob * Additional options that can be set for config: SA_1FM_AT_EOT
9143636Smjacob */
9253259Smjacob
9341906Smjacob#ifndef	UNUSED_PARAMETER
9441906Smjacob#define	UNUSED_PARAMETER(x)	x = x
9541906Smjacob#endif
9641906Smjacob
9753259Smjacob#define	QFRLS(ccb)	\
9853259Smjacob	if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) != 0)	\
9953259Smjacob		cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE)
10053259Smjacob
10153259Smjacob/*
10253259Smjacob * Driver states
10353259Smjacob */
10453259Smjacob
105147723SavatarMALLOC_DEFINE(M_SCSISA, "SCSI sa", "SCSI sequential access buffers");
10653259Smjacob
10739213Sgibbstypedef enum {
10846962Smjacob	SA_STATE_NORMAL, SA_STATE_ABNORMAL
10939213Sgibbs} sa_state;
11039213Sgibbs
11171082Smjacob#define ccb_pflags	ppriv_field0
11271082Smjacob#define ccb_bp	 	ppriv_ptr1
11339213Sgibbs
11471082Smjacob#define	SA_CCB_BUFFER_IO	0x0
11571082Smjacob#define	SA_CCB_WAITING		0x1
11671082Smjacob#define	SA_CCB_TYPEMASK		0x1
11771082Smjacob#define	SA_POSITION_UPDATED	0x2
11839213Sgibbs
11971082Smjacob#define	Set_CCB_Type(x, type)				\
12071082Smjacob	x->ccb_h.ccb_pflags &= ~SA_CCB_TYPEMASK;	\
12171082Smjacob	x->ccb_h.ccb_pflags |= type
12271082Smjacob
12371082Smjacob#define	CCB_Type(x)	(x->ccb_h.ccb_pflags & SA_CCB_TYPEMASK)
12471082Smjacob
12571082Smjacob
12671082Smjacob
12739213Sgibbstypedef enum {
12839213Sgibbs	SA_FLAG_OPEN		= 0x0001,
12939213Sgibbs	SA_FLAG_FIXED		= 0x0002,
13039213Sgibbs	SA_FLAG_TAPE_LOCKED	= 0x0004,
13139213Sgibbs	SA_FLAG_TAPE_MOUNTED	= 0x0008,
13239213Sgibbs	SA_FLAG_TAPE_WP		= 0x0010,
13339213Sgibbs	SA_FLAG_TAPE_WRITTEN	= 0x0020,
13441906Smjacob	SA_FLAG_EOM_PENDING	= 0x0040,
13541906Smjacob	SA_FLAG_EIO_PENDING	= 0x0080,
13641906Smjacob	SA_FLAG_EOF_PENDING	= 0x0100,
13739213Sgibbs	SA_FLAG_ERR_PENDING	= (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING|
13839213Sgibbs				   SA_FLAG_EOF_PENDING),
13941906Smjacob	SA_FLAG_INVALID		= 0x0200,
14041906Smjacob	SA_FLAG_COMP_ENABLED	= 0x0400,
14146962Smjacob	SA_FLAG_COMP_SUPP	= 0x0800,
14246962Smjacob	SA_FLAG_COMP_UNSUPP	= 0x1000,
14346962Smjacob	SA_FLAG_TAPE_FROZEN	= 0x2000
14439213Sgibbs} sa_flags;
14539213Sgibbs
14639213Sgibbstypedef enum {
14739213Sgibbs	SA_MODE_REWIND		= 0x00,
14839213Sgibbs	SA_MODE_NOREWIND	= 0x01,
14939213Sgibbs	SA_MODE_OFFLINE		= 0x02
15039213Sgibbs} sa_mode;
15139213Sgibbs
15239213Sgibbstypedef enum {
15339213Sgibbs	SA_PARAM_NONE		= 0x00,
15439213Sgibbs	SA_PARAM_BLOCKSIZE	= 0x01,
15539213Sgibbs	SA_PARAM_DENSITY	= 0x02,
15639213Sgibbs	SA_PARAM_COMPRESSION	= 0x04,
15739213Sgibbs	SA_PARAM_BUFF_MODE	= 0x08,
15839213Sgibbs	SA_PARAM_NUMBLOCKS	= 0x10,
15939213Sgibbs	SA_PARAM_WP		= 0x20,
16039213Sgibbs	SA_PARAM_SPEED		= 0x40,
16139213Sgibbs	SA_PARAM_ALL		= 0x7f
16239213Sgibbs} sa_params;
16339213Sgibbs
16439213Sgibbstypedef enum {
16539213Sgibbs	SA_QUIRK_NONE		= 0x00,
16660235Smjacob	SA_QUIRK_NOCOMP		= 0x01,	/* Can't deal with compression at all */
16760235Smjacob	SA_QUIRK_FIXED		= 0x02,	/* Force fixed mode */
16860235Smjacob	SA_QUIRK_VARIABLE	= 0x04,	/* Force variable mode */
16943636Smjacob	SA_QUIRK_2FM		= 0x08,	/* Needs Two File Marks at EOD */
17056981Smjacob	SA_QUIRK_1FM		= 0x10,	/* No more than 1 File Mark at EOD */
17160235Smjacob	SA_QUIRK_NODREAD	= 0x20,	/* Don't try and dummy read density */
17271082Smjacob	SA_QUIRK_NO_MODESEL	= 0x40,	/* Don't do mode select at all */
17371082Smjacob	SA_QUIRK_NO_CPAGE	= 0x80	/* Don't use DEVICE COMPRESSION page */
17439213Sgibbs} sa_quirks;
17539213Sgibbs
17653283Smjacob/* units are bits 4-7, 16-21 (1024 units) */
17753283Smjacob#define SAUNIT(DEV) \
17853283Smjacob	(((minor(DEV) & 0xF0) >> 4) |  ((minor(DEV) & 0x3f0000) >> 16))
17953283Smjacob
18053283Smjacob#define SAMODE(z) ((minor(z) & 0x3))
18153283Smjacob#define SADENSITY(z) (((minor(z) >> 2) & 0x3))
18253283Smjacob#define	SA_IS_CTRL(z) (minor(z) & (1 << 29))
18353283Smjacob
18453259Smjacob#define SA_NOT_CTLDEV	0
18553259Smjacob#define SA_CTLDEV	1
18653259Smjacob
18753259Smjacob#define SA_ATYPE_R	0
18853259Smjacob#define SA_ATYPE_NR	1
18953259Smjacob#define SA_ATYPE_ER	2
19053259Smjacob
19153259Smjacob#define SAMINOR(ctl, unit, mode, access) \
19253283Smjacob	((ctl << 29) | ((unit & 0x3f0) << 16) | ((unit & 0xf) << 4) | \
19353283Smjacob	(mode << 0x2) | (access & 0x3))
19453259Smjacob
19553259Smjacob#define SA_NUM_MODES	4
19653259Smjacobstruct sa_devs {
197130585Sphk	struct cdev *ctl_dev;
19853259Smjacob	struct sa_mode_devs {
199130585Sphk		struct cdev *r_dev;
200130585Sphk		struct cdev *nr_dev;
201130585Sphk		struct cdev *er_dev;
20253259Smjacob	} mode_devs[SA_NUM_MODES];
20353259Smjacob};
20453259Smjacob
20539213Sgibbsstruct sa_softc {
20639213Sgibbs	sa_state	state;
20739213Sgibbs	sa_flags	flags;
20839213Sgibbs	sa_quirks	quirks;
20959249Sphk	struct		bio_queue_head bio_queue;
21046962Smjacob	int		queue_count;
211112006Sphk	struct		devstat *device_stats;
21253259Smjacob	struct sa_devs	devs;
21339213Sgibbs	int		blk_gran;
21439213Sgibbs	int		blk_mask;
21539213Sgibbs	int		blk_shift;
21639213Sgibbs	u_int32_t	max_blk;
21739213Sgibbs	u_int32_t	min_blk;
21841674Smjacob	u_int32_t	comp_algorithm;
21941674Smjacob	u_int32_t	saved_comp_algorithm;
22039213Sgibbs	u_int32_t	media_blksize;
22141906Smjacob	u_int32_t	last_media_blksize;
22239213Sgibbs	u_int32_t	media_numblks;
22341674Smjacob	u_int8_t	media_density;
22439213Sgibbs	u_int8_t	speed;
22541674Smjacob	u_int8_t	scsi_rev;
22643636Smjacob	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */
22739213Sgibbs	int		buffer_mode;
22839213Sgibbs	int		filemarks;
22939213Sgibbs	union		ccb saved_ccb;
23071268Smjacob	int		last_resid_was_io;
23146962Smjacob
23241948Smjacob	/*
23343636Smjacob	 * Relative to BOT Location.
23443636Smjacob	 */
23543636Smjacob	daddr_t		fileno;
23643636Smjacob	daddr_t		blkno;
23743636Smjacob
23843636Smjacob	/*
23941948Smjacob	 * Latched Error Info
24041948Smjacob	 */
24142009Smjacob	struct {
24242009Smjacob		struct scsi_sense_data _last_io_sense;
24342009Smjacob		u_int32_t _last_io_resid;
24442009Smjacob		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];
24542009Smjacob		struct scsi_sense_data _last_ctl_sense;
24642009Smjacob		u_int32_t _last_ctl_resid;
24742009Smjacob		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];
24842009Smjacob#define	last_io_sense	errinfo._last_io_sense
24942009Smjacob#define	last_io_resid	errinfo._last_io_resid
25042009Smjacob#define	last_io_cdb	errinfo._last_io_cdb
25142009Smjacob#define	last_ctl_sense	errinfo._last_ctl_sense
25242009Smjacob#define	last_ctl_resid	errinfo._last_ctl_resid
25342009Smjacob#define	last_ctl_cdb	errinfo._last_ctl_cdb
25442009Smjacob	} errinfo;
25543636Smjacob	/*
25643636Smjacob	 * Misc other flags/state
25743636Smjacob	 */
25843636Smjacob	u_int32_t
259154360Smjacob					: 29,
260154360Smjacob		open_rdonly		: 1,	/* open read-only */
261154360Smjacob		open_pending_mount	: 1,	/* open pending mount */
262154360Smjacob		ctrl_mode		: 1;	/* control device open */
26339213Sgibbs};
26439213Sgibbs
26539213Sgibbsstruct sa_quirk_entry {
26642563Smjacob	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */
26742563Smjacob	sa_quirks quirks;	/* specific quirk type */
26842563Smjacob	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */
26939213Sgibbs};
27039213Sgibbs
27139213Sgibbsstatic struct sa_quirk_entry sa_quirk_table[] =
27239213Sgibbs{
27339213Sgibbs	{
27460235Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",
27560235Smjacob		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |
27660235Smjacob		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768
27760235Smjacob	},
27860235Smjacob	{
27939213Sgibbs		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28077810Snon		  "Python 06408*", "*"}, SA_QUIRK_NODREAD, 0
28177581Snon	},
28277581Snon	{
28377581Snon		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28456981Smjacob		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0
28541351Sjoerg	},
28641351Sjoerg	{
28742130Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28856981Smjacob		  "Python*", "*"}, SA_QUIRK_NODREAD, 0
28956981Smjacob	},
29056981Smjacob	{
29156981Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29243636Smjacob		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
29342130Smjacob	},
29442130Smjacob	{
29542563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
29668500Smjacob		  "VIPER 2525 25462", "-011"},
29768500Smjacob		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0
29868500Smjacob	},
29968500Smjacob	{
30068500Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
30146962Smjacob		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
30242563Smjacob	},
30371082Smjacob#if	0
30442563Smjacob	{
30542533Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
30671082Smjacob		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,
30771082Smjacob	},
30871082Smjacob#endif
309107943Strhodes 	{
310107943Strhodes 		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
311107943Strhodes		  "C56*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
312107943Strhodes	},
31371082Smjacob	{
31471082Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31546962Smjacob		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
31646962Smjacob	},
31746962Smjacob	{
31846962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
31944354Smjacob		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
32042533Smjacob	},
32142533Smjacob	{
32242716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
32342716Smjacob		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
32442716Smjacob	},
32542716Smjacob	{
32642716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",
32742716Smjacob		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
32842716Smjacob	},
32942716Smjacob	{
33042716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",
33142716Smjacob		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
33242716Smjacob	},
33351744Smjacob	{	/* jreynold@primenet.com */
33451744Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
33551744Smjacob		"STT8000N*", "*"}, SA_QUIRK_1FM, 0
33651744Smjacob	},
33751875Smjacob	{	/* mike@sentex.net */
33851875Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
33951875Smjacob		"STT20000*", "*"}, SA_QUIRK_1FM, 0
34051875Smjacob	},
34142716Smjacob	{
34241351Sjoerg		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
34343636Smjacob		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
34442563Smjacob	},
34542563Smjacob	{
34642563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
34747519Smjacob		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
34847519Smjacob	},
34947519Smjacob	{
35047519Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35148192Smjacob		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
35248192Smjacob	},
35348192Smjacob	{
35448192Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35543636Smjacob		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
35642563Smjacob	},
35742563Smjacob	{
35846962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
35946962Smjacob		  " SLR*", "*"}, SA_QUIRK_1FM, 0
36046962Smjacob	},
36146962Smjacob	{
36242563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
36343636Smjacob		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
36445752Smjacob	},
36545752Smjacob	{
36645752Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
36745752Smjacob		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
36839213Sgibbs	}
36939213Sgibbs};
37039213Sgibbs
37139213Sgibbsstatic	d_open_t	saopen;
37239213Sgibbsstatic	d_close_t	saclose;
37339213Sgibbsstatic	d_strategy_t	sastrategy;
37439213Sgibbsstatic	d_ioctl_t	saioctl;
37539213Sgibbsstatic	periph_init_t	sainit;
37639213Sgibbsstatic	periph_ctor_t	saregister;
37740603Skenstatic	periph_oninv_t	saoninvalidate;
37839213Sgibbsstatic	periph_dtor_t	sacleanup;
37939213Sgibbsstatic	periph_start_t	sastart;
38039213Sgibbsstatic	void		saasync(void *callback_arg, u_int32_t code,
38139213Sgibbs				struct cam_path *path, void *arg);
38239213Sgibbsstatic	void		sadone(struct cam_periph *periph,
38339213Sgibbs			       union ccb *start_ccb);
38439213Sgibbsstatic  int		saerror(union ccb *ccb, u_int32_t cam_flags,
38539213Sgibbs				u_int32_t sense_flags);
38668114Smjacobstatic int		samarkswanted(struct cam_periph *);
38739213Sgibbsstatic int		sacheckeod(struct cam_periph *periph);
38839213Sgibbsstatic int		sagetparams(struct cam_periph *periph,
38939213Sgibbs				    sa_params params_to_get,
39039213Sgibbs				    u_int32_t *blocksize, u_int8_t *density,
39139213Sgibbs				    u_int32_t *numblocks, int *buff_mode,
39239213Sgibbs				    u_int8_t *write_protect, u_int8_t *speed,
39339213Sgibbs				    int *comp_supported, int *comp_enabled,
39439213Sgibbs				    u_int32_t *comp_algorithm,
39546962Smjacob				    sa_comp_t *comp_page);
39639213Sgibbsstatic int		sasetparams(struct cam_periph *periph,
39739213Sgibbs				    sa_params params_to_set,
39839213Sgibbs				    u_int32_t blocksize, u_int8_t density,
39942563Smjacob				    u_int32_t comp_algorithm,
40042563Smjacob				    u_int32_t sense_flags);
40139213Sgibbsstatic void		saprevent(struct cam_periph *periph, int action);
40239213Sgibbsstatic int		sarewind(struct cam_periph *periph);
40339213Sgibbsstatic int		saspace(struct cam_periph *periph, int count,
40439213Sgibbs				scsi_space_code code);
405130585Sphkstatic int		samount(struct cam_periph *, int, struct cdev *);
40639213Sgibbsstatic int		saretension(struct cam_periph *periph);
40739213Sgibbsstatic int		sareservereleaseunit(struct cam_periph *periph,
40839213Sgibbs					     int reserve);
40939213Sgibbsstatic int		saloadunload(struct cam_periph *periph, int load);
41039213Sgibbsstatic int		saerase(struct cam_periph *periph, int longerase);
41139213Sgibbsstatic int		sawritefilemarks(struct cam_periph *periph,
41239213Sgibbs					 int nmarks, int setmarks);
41341918Smjacobstatic int		sardpos(struct cam_periph *periph, int, u_int32_t *);
41441918Smjacobstatic int		sasetpos(struct cam_periph *periph, int, u_int32_t *);
41539213Sgibbs
41641918Smjacob
41739213Sgibbsstatic struct periph_driver sadriver =
41839213Sgibbs{
41939213Sgibbs	sainit, "sa",
42039213Sgibbs	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0
42139213Sgibbs};
42239213Sgibbs
42372119SpeterPERIPHDRIVER_DECLARE(sa, sadriver);
42439213Sgibbs
42539213Sgibbs/* For 2.2-stable support */
42639213Sgibbs#ifndef D_TAPE
42739213Sgibbs#define D_TAPE 0
42839213Sgibbs#endif
42939213Sgibbs
43039213Sgibbs
43147625Sphkstatic struct cdevsw sa_cdevsw = {
432126080Sphk	.d_version =	D_VERSION,
433111815Sphk	.d_open =	saopen,
434111815Sphk	.d_close =	saclose,
435111815Sphk	.d_read =	physread,
436111815Sphk	.d_write =	physwrite,
437111815Sphk	.d_ioctl =	saioctl,
438111815Sphk	.d_strategy =	sastrategy,
439111815Sphk	.d_name =	"sa",
440126080Sphk	.d_flags =	D_TAPE | D_NEEDGIANT,
44139213Sgibbs};
44239213Sgibbs
44339213Sgibbsstatic int
444130585Sphksaopen(struct cdev *dev, int flags, int fmt, struct thread *td)
44539213Sgibbs{
44639213Sgibbs	struct cam_periph *periph;
44739213Sgibbs	struct sa_softc *softc;
44839213Sgibbs	int unit;
44939213Sgibbs	int error;
45039213Sgibbs
45139213Sgibbs	unit = SAUNIT(dev);
45239213Sgibbs
453101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
454168752Sscottl	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
455168752Sscottl		return (ENXIO);
45653259Smjacob	}
457168752Sscottl
458168752Sscottl	cam_periph_lock(periph);
459168752Sscottl
46039213Sgibbs	softc = (struct sa_softc *)periph->softc;
46139213Sgibbs
46246962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
46346962Smjacob	    ("saopen(%d): dev=0x%x softc=0x%x\n", unit, unit, softc->flags));
46439213Sgibbs
46543636Smjacob	if (SA_IS_CTRL(dev)) {
46643636Smjacob		softc->ctrl_mode = 1;
46753259Smjacob		cam_periph_unlock(periph);
46843636Smjacob		return (0);
46943636Smjacob	}
47043636Smjacob
471168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO|PCATCH)) != 0) {
472168752Sscottl		cam_periph_unlock(periph);
473168752Sscottl		cam_periph_release(periph);
474168752Sscottl		return (error);
475168752Sscottl	}
476168752Sscottl
47753259Smjacob	if (softc->flags & SA_FLAG_OPEN) {
47853259Smjacob		error = EBUSY;
47953259Smjacob	} else if (softc->flags & SA_FLAG_INVALID) {
48053259Smjacob		error = ENXIO;
48153259Smjacob	} else {
48253259Smjacob		/*
483154360Smjacob		 * Preserve whether this is a read_only open.
484154360Smjacob		 */
485154360Smjacob		softc->open_rdonly = (flags & O_RDWR) == O_RDONLY;
486154360Smjacob
487154360Smjacob		/*
48853259Smjacob		 * The function samount ensures media is loaded and ready.
48953259Smjacob		 * It also does a device RESERVE if the tape isn't yet mounted.
490154360Smjacob		 *
491154360Smjacob		 * If the mount fails and this was a non-blocking open,
492154360Smjacob		 * make this a 'open_pending_mount' action.
49353259Smjacob		 */
49453259Smjacob		error = samount(periph, flags, dev);
495154360Smjacob		if (error && (flags & O_NONBLOCK)) {
496154360Smjacob			softc->flags |= SA_FLAG_OPEN;
497154360Smjacob			softc->open_pending_mount = 1;
498168752Sscottl			cam_periph_unhold(periph);
499154360Smjacob			cam_periph_unlock(periph);
500154360Smjacob			return (0);
501154360Smjacob		}
50239213Sgibbs	}
50339213Sgibbs
50453259Smjacob	if (error) {
505168752Sscottl		cam_periph_unhold(periph);
506168752Sscottl		cam_periph_unlock(periph);
50753259Smjacob		cam_periph_release(periph);
508168752Sscottl		return (error);
50939213Sgibbs	}
510168752Sscottl
511168752Sscottl	saprevent(periph, PR_PREVENT);
512168752Sscottl	softc->flags |= SA_FLAG_OPEN;
513168752Sscottl
514168752Sscottl	cam_periph_unhold(periph);
51539213Sgibbs	cam_periph_unlock(periph);
51639213Sgibbs	return (error);
51739213Sgibbs}
51839213Sgibbs
51939213Sgibbsstatic int
520130585Sphksaclose(struct cdev *dev, int flag, int fmt, struct thread *td)
52139213Sgibbs{
52239213Sgibbs	struct	cam_periph *periph;
52339213Sgibbs	struct	sa_softc *softc;
52446962Smjacob	int	unit, mode, error, writing, tmp;
52542009Smjacob	int	closedbits = SA_FLAG_OPEN;
52639213Sgibbs
52739213Sgibbs	unit = SAUNIT(dev);
52839213Sgibbs	mode = SAMODE(dev);
529101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
53039213Sgibbs	if (periph == NULL)
53139213Sgibbs		return (ENXIO);
53239213Sgibbs
533168752Sscottl	cam_periph_lock(periph);
534168752Sscottl
53539213Sgibbs	softc = (struct sa_softc *)periph->softc;
53639213Sgibbs
53746962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
53846962Smjacob	    ("saclose(%d): dev=0x%x softc=0x%x\n", unit, unit, softc->flags));
53946962Smjacob
54046962Smjacob
541154360Smjacob	softc->open_rdonly = 0;
54243636Smjacob	if (SA_IS_CTRL(dev)) {
54343636Smjacob		softc->ctrl_mode = 0;
544168752Sscottl		cam_periph_unlock(periph);
54553259Smjacob		cam_periph_release(periph);
54643636Smjacob		return (0);
54743636Smjacob	}
54843636Smjacob
549154360Smjacob	if (softc->open_pending_mount) {
550154360Smjacob		softc->flags &= ~SA_FLAG_OPEN;
551154360Smjacob		softc->open_pending_mount = 0;
552168752Sscottl		cam_periph_unlock(periph);
553154360Smjacob		cam_periph_release(periph);
554154360Smjacob		return (0);
555154360Smjacob	}
556154360Smjacob
557168752Sscottl	if ((error = cam_periph_hold(periph, PRIBIO)) != 0) {
558168752Sscottl		cam_periph_unlock(periph);
559168752Sscottl		return (error);
560168752Sscottl	}
561168752Sscottl
56241906Smjacob	/*
56346962Smjacob	 * Were we writing the tape?
56441906Smjacob	 */
56546962Smjacob	writing = (softc->flags & SA_FLAG_TAPE_WRITTEN) != 0;
56646962Smjacob
56746962Smjacob	/*
56846962Smjacob	 * See whether or not we need to write filemarks. If this
56946962Smjacob	 * fails, we probably have to assume we've lost tape
57046962Smjacob	 * position.
57146962Smjacob	 */
57241906Smjacob	error = sacheckeod(periph);
57341906Smjacob	if (error) {
574164906Smjacob		xpt_print(periph->path,
575164906Smjacob		    "failed to write terminating filemark(s)\n");
57646962Smjacob		softc->flags |= SA_FLAG_TAPE_FROZEN;
57741906Smjacob	}
57839213Sgibbs
57941906Smjacob	/*
58041906Smjacob	 * Whatever we end up doing, allow users to eject tapes from here on.
58141906Smjacob	 */
58239213Sgibbs	saprevent(periph, PR_ALLOW);
58339213Sgibbs
58441906Smjacob	/*
58541906Smjacob	 * Decide how to end...
58641906Smjacob	 */
58753522Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
58853522Smjacob		closedbits |= SA_FLAG_TAPE_FROZEN;
58953522Smjacob	} else switch (mode) {
59039213Sgibbs	case SA_MODE_OFFLINE:
59146962Smjacob		/*
59246962Smjacob		 * An 'offline' close is an unconditional release of
59346962Smjacob		 * frozen && mount conditions, irrespective of whether
59446962Smjacob		 * these operations succeeded. The reason for this is
59546962Smjacob		 * to allow at least some kind of programmatic way
59646962Smjacob		 * around our state getting all fouled up. If somebody
59746962Smjacob		 * issues an 'offline' command, that will be allowed
59846962Smjacob		 * to clear state.
59946962Smjacob		 */
60046962Smjacob		(void) sarewind(periph);
60146962Smjacob		(void) saloadunload(periph, FALSE);
60246962Smjacob		closedbits |= SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;
60339213Sgibbs		break;
60441906Smjacob	case SA_MODE_REWIND:
60546962Smjacob		/*
60646962Smjacob		 * If the rewind fails, return an error- if anyone cares,
60746962Smjacob		 * but not overwriting any previous error.
60846962Smjacob		 *
60946962Smjacob		 * We don't clear the notion of mounted here, but we do
61046962Smjacob		 * clear the notion of frozen if we successfully rewound.
61146962Smjacob		 */
61246962Smjacob		tmp = sarewind(periph);
61346962Smjacob		if (tmp) {
61446962Smjacob			if (error != 0)
61546962Smjacob				error = tmp;
61646962Smjacob		} else {
61746962Smjacob			closedbits |= SA_FLAG_TAPE_FROZEN;
61846962Smjacob		}
61941906Smjacob		break;
62039213Sgibbs	case SA_MODE_NOREWIND:
62141906Smjacob		/*
62241906Smjacob		 * If we're not rewinding/unloading the tape, find out
62341906Smjacob		 * whether we need to back up over one of two filemarks
62441906Smjacob		 * we wrote (if we wrote two filemarks) so that appends
62541906Smjacob		 * from this point on will be sane.
62641906Smjacob		 */
62746962Smjacob		if (error == 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {
62846962Smjacob			tmp = saspace(periph, -1, SS_FILEMARKS);
62946962Smjacob			if (tmp) {
630164906Smjacob				xpt_print(periph->path, "unable to backspace "
631164906Smjacob				    "over one of double filemarks at end of "
632164906Smjacob				    "tape\n");
633164906Smjacob				xpt_print(periph->path, "it is possible that "
634164906Smjacob				    "this device needs a SA_QUIRK_1FM quirk set"
635164906Smjacob				    "for it\n");
63646962Smjacob				softc->flags |= SA_FLAG_TAPE_FROZEN;
63741906Smjacob			}
63841906Smjacob		}
63939213Sgibbs		break;
64046962Smjacob	default:
641164906Smjacob		xpt_print(periph->path, "unknown mode 0x%x in saclose\n", mode);
64246962Smjacob		/* NOTREACHED */
64346962Smjacob		break;
64439213Sgibbs	}
64539213Sgibbs
64641906Smjacob	/*
64741948Smjacob	 * We wish to note here that there are no more filemarks to be written.
64841906Smjacob	 */
64941906Smjacob	softc->filemarks = 0;
65041948Smjacob	softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
65141906Smjacob
65241906Smjacob	/*
65341906Smjacob	 * And we are no longer open for business.
65441906Smjacob	 */
65542009Smjacob	softc->flags &= ~closedbits;
65646962Smjacob
65746962Smjacob	/*
65846962Smjacob	 * Inform users if tape state if frozen....
65946962Smjacob	 */
66046962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
661164906Smjacob		xpt_print(periph->path, "tape is now frozen- use an OFFLINE, "
662164906Smjacob		    "REWIND or MTEOM command to clear this state.\n");
66346962Smjacob	}
66439213Sgibbs
66553259Smjacob	/* release the device if it is no longer mounted */
66653259Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0)
66753259Smjacob		sareservereleaseunit(periph, FALSE);
66839213Sgibbs
669168752Sscottl	cam_periph_unhold(periph);
67039213Sgibbs	cam_periph_unlock(periph);
67139213Sgibbs	cam_periph_release(periph);
67239213Sgibbs
67346962Smjacob	return (error);
67439213Sgibbs}
67539213Sgibbs
67639213Sgibbs/*
67739213Sgibbs * Actually translate the requested transfer into one the physical driver
67839213Sgibbs * can understand.  The transfer is described by a buf and will include
67939213Sgibbs * only one physical transfer.
68039213Sgibbs */
68139213Sgibbsstatic void
68259249Sphksastrategy(struct bio *bp)
68339213Sgibbs{
68439213Sgibbs	struct cam_periph *periph;
68539213Sgibbs	struct sa_softc *softc;
68639213Sgibbs
68776362Sphk	bp->bio_resid = bp->bio_bcount;
68859249Sphk	if (SA_IS_CTRL(bp->bio_dev)) {
68976362Sphk		biofinish(bp, NULL, EINVAL);
69076362Sphk		return;
69143636Smjacob	}
692101940Snjl	periph = (struct cam_periph *)bp->bio_dev->si_drv1;
69339213Sgibbs	if (periph == NULL) {
69476362Sphk		biofinish(bp, NULL, ENXIO);
69576362Sphk		return;
69639213Sgibbs	}
697168752Sscottl	cam_periph_lock(periph);
698168752Sscottl
69939213Sgibbs	softc = (struct sa_softc *)periph->softc;
70039213Sgibbs
70140603Sken	if (softc->flags & SA_FLAG_INVALID) {
702168752Sscottl		cam_periph_unlock(periph);
70376362Sphk		biofinish(bp, NULL, ENXIO);
70476362Sphk		return;
70540603Sken	}
70640603Sken
70746962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
708168752Sscottl		cam_periph_unlock(periph);
70976362Sphk		biofinish(bp, NULL, EPERM);
71076362Sphk		return;
71146962Smjacob	}
71246962Smjacob
713154360Smjacob	/*
714154360Smjacob	 * This should actually never occur as the write(2)
715154360Smjacob	 * system call traps attempts to write to a read-only
716154360Smjacob	 * file descriptor.
717154360Smjacob	 */
718154360Smjacob	if (bp->bio_cmd == BIO_WRITE && softc->open_rdonly) {
719168752Sscottl		cam_periph_unlock(periph);
720154360Smjacob		biofinish(bp, NULL, EBADF);
721154360Smjacob		return;
722154360Smjacob	}
723154360Smjacob
724154360Smjacob	if (softc->open_pending_mount) {
725154360Smjacob		int error = samount(periph, 0, bp->bio_dev);
726154360Smjacob		if (error) {
727168752Sscottl			cam_periph_unlock(periph);
728154360Smjacob			biofinish(bp, NULL, ENXIO);
729154360Smjacob			return;
730154360Smjacob		}
731154360Smjacob		saprevent(periph, PR_PREVENT);
732154360Smjacob		softc->open_pending_mount = 0;
733154360Smjacob	}
734154360Smjacob
735154360Smjacob
73639213Sgibbs	/*
737154360Smjacob	 * If it's a null transfer, return immediately
73839213Sgibbs	 */
73976362Sphk	if (bp->bio_bcount == 0) {
740168752Sscottl		cam_periph_unlock(periph);
74176362Sphk		biodone(bp);
74276362Sphk		return;
74376362Sphk	}
74439213Sgibbs
74539213Sgibbs	/* valid request?  */
74639213Sgibbs	if (softc->flags & SA_FLAG_FIXED) {
74739213Sgibbs		/*
74839213Sgibbs		 * Fixed block device.  The byte count must
74939213Sgibbs		 * be a multiple of our block size.
75039213Sgibbs		 */
75142716Smjacob		if (((softc->blk_mask != ~0) &&
75259249Sphk		    ((bp->bio_bcount & softc->blk_mask) != 0)) ||
75342716Smjacob		    ((softc->blk_mask == ~0) &&
75459249Sphk		    ((bp->bio_bcount % softc->min_blk) != 0))) {
755164906Smjacob			xpt_print(periph->path, "Invalid request.  Fixed block "
756164906Smjacob			    "device requests must be a multiple of %d bytes\n",
757164906Smjacob			    softc->min_blk);
758168752Sscottl			cam_periph_unlock(periph);
75976362Sphk			biofinish(bp, NULL, EINVAL);
76076362Sphk			return;
76139213Sgibbs		}
76259249Sphk	} else if ((bp->bio_bcount > softc->max_blk) ||
76359249Sphk		   (bp->bio_bcount < softc->min_blk) ||
76459249Sphk		   (bp->bio_bcount & softc->blk_mask) != 0) {
76539213Sgibbs
76639213Sgibbs		xpt_print_path(periph->path);
767164906Smjacob		printf("Invalid request.  Variable block "
768164906Smjacob		    "device requests must be ");
76939213Sgibbs		if (softc->blk_mask != 0) {
77042716Smjacob			printf("a multiple of %d ", (0x1 << softc->blk_gran));
77139213Sgibbs		}
77242716Smjacob		printf("between %d and %d bytes\n", softc->min_blk,
77342716Smjacob		    softc->max_blk);
774168752Sscottl		cam_periph_unlock(periph);
77576362Sphk		biofinish(bp, NULL, EINVAL);
77676362Sphk		return;
77739213Sgibbs        }
77839213Sgibbs
77939213Sgibbs	/*
78042716Smjacob	 * Place it at the end of the queue.
78139213Sgibbs	 */
78259249Sphk	bioq_insert_tail(&softc->bio_queue, bp);
78346962Smjacob	softc->queue_count++;
784115660Smjacob#if	0
785115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
786115660Smjacob	    ("sastrategy: queuing a %ld %s byte %s\n", bp->bio_bcount,
787115660Smjacob 	    (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",
788115660Smjacob	    (bp->bio_cmd == BIO_READ)? "read" : "write"));
789115660Smjacob#endif
790115660Smjacob	if (softc->queue_count > 1) {
791115660Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
792115660Smjacob		    ("sastrategy: queue count now %d\n", softc->queue_count));
793115660Smjacob	}
79439213Sgibbs
79539213Sgibbs	/*
79639213Sgibbs	 * Schedule ourselves for performing the work.
79739213Sgibbs	 */
79842716Smjacob	xpt_schedule(periph, 1);
799168752Sscottl	cam_periph_unlock(periph);
80039213Sgibbs
80139213Sgibbs	return;
80239213Sgibbs}
80339213Sgibbs
804154360Smjacob
805154360Smjacob#define	PENDING_MOUNT_CHECK(softc, periph, dev)		\
806154360Smjacob	if (softc->open_pending_mount) {		\
807154360Smjacob		error = samount(periph, 0, dev);	\
808154360Smjacob		if (error) {				\
809154360Smjacob			break;				\
810154360Smjacob		}					\
811154360Smjacob		saprevent(periph, PR_PREVENT);		\
812154360Smjacob		softc->open_pending_mount = 0;		\
813154360Smjacob	}
814154360Smjacob
81539213Sgibbsstatic int
816130585Sphksaioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td)
81739213Sgibbs{
81839213Sgibbs	struct cam_periph *periph;
81939213Sgibbs	struct sa_softc *softc;
82048520Speter	scsi_space_code spaceop;
82143636Smjacob	int didlockperiph = 0;
82239213Sgibbs	int mode;
82353259Smjacob	int error = 0;
82439213Sgibbs
82539213Sgibbs	mode = SAMODE(dev);
82648520Speter	error = 0;		/* shut up gcc */
82748520Speter	spaceop = 0;		/* shut up gcc */
82839213Sgibbs
829101940Snjl	periph = (struct cam_periph *)dev->si_drv1;
83039213Sgibbs	if (periph == NULL)
83139213Sgibbs		return (ENXIO);
83239213Sgibbs
833168752Sscottl	cam_periph_lock(periph);
83439213Sgibbs	softc = (struct sa_softc *)periph->softc;
83539213Sgibbs
83639213Sgibbs	/*
83743636Smjacob	 * Check for control mode accesses. We allow MTIOCGET and
83843636Smjacob	 * MTIOCERRSTAT (but need to be the only one open in order
83943636Smjacob	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY
84043636Smjacob	 * and MTCOMP (but need to be the only one accessing this
84143636Smjacob	 * device to run those).
84243636Smjacob	 */
84343636Smjacob
84443636Smjacob	if (SA_IS_CTRL(dev)) {
84543636Smjacob		switch (cmd) {
84646962Smjacob		case MTIOCGETEOTMODEL:
84743636Smjacob		case MTIOCGET:
84843636Smjacob			break;
84943636Smjacob		case MTIOCERRSTAT:
85043636Smjacob			/*
85143636Smjacob			 * If the periph isn't already locked, lock it
85243636Smjacob			 * so our MTIOCERRSTAT can reset latched error stats.
85343636Smjacob			 *
85443636Smjacob			 * If the periph is already locked, skip it because
85543636Smjacob			 * we're just getting status and it'll be up to the
85643636Smjacob			 * other thread that has this device open to do
85743636Smjacob			 * an MTIOCERRSTAT that would clear latched status.
85843636Smjacob			 */
85943636Smjacob			if ((periph->flags & CAM_PERIPH_LOCKED) == 0) {
860168752Sscottl				error = cam_periph_hold(periph, PRIBIO|PCATCH);
861168752Sscottl				 if (error != 0)
86243636Smjacob					return (error);
86343636Smjacob				didlockperiph = 1;
86443636Smjacob			}
86543636Smjacob			break;
86643636Smjacob
867145050Smjacob		case MTIOCTOP:
868145050Smjacob		{
869145050Smjacob			struct mtop *mt = (struct mtop *) arg;
870145050Smjacob
871145050Smjacob			/*
872145050Smjacob			 * Check to make sure it's an OP we can perform
873145050Smjacob			 * with no media inserted.
874145050Smjacob			 */
875145050Smjacob			switch (mt->mt_op) {
876145050Smjacob			case MTSETBSIZ:
877145050Smjacob			case MTSETDNSTY:
878145050Smjacob			case MTCOMP:
879145050Smjacob				mt = NULL;
880145050Smjacob				/* FALLTHROUGH */
881145050Smjacob			default:
882145050Smjacob				break;
883145050Smjacob			}
884145050Smjacob			if (mt != NULL) {
885145050Smjacob				break;
886145050Smjacob			}
887145050Smjacob			/* FALLTHROUGH */
888145050Smjacob		}
88946962Smjacob		case MTIOCSETEOTMODEL:
89043636Smjacob			/*
89143636Smjacob			 * We need to acquire the peripheral here rather
89243636Smjacob			 * than at open time because we are sharing writable
89343636Smjacob			 * access to data structures.
89443636Smjacob			 */
895168752Sscottl			error = cam_periph_hold(periph, PRIBIO|PCATCH);
896168752Sscottl			if (error != 0)
89743636Smjacob				return (error);
89843636Smjacob			didlockperiph = 1;
89943636Smjacob			break;
90043636Smjacob
90143636Smjacob		default:
90243636Smjacob			return (EINVAL);
90343636Smjacob		}
90443636Smjacob	}
90543636Smjacob
90643636Smjacob	/*
90739213Sgibbs	 * Find the device that the user is talking about
90839213Sgibbs	 */
90939213Sgibbs	switch (cmd) {
91039213Sgibbs	case MTIOCGET:
91139213Sgibbs	{
91239213Sgibbs		struct mtget *g = (struct mtget *)arg;
91339213Sgibbs
91453259Smjacob		/*
91553259Smjacob		 * If this isn't the control mode device, actually go out
91653259Smjacob		 * and ask the drive again what it's set to.
91753259Smjacob		 */
918154360Smjacob		if (!SA_IS_CTRL(dev) && !softc->open_pending_mount) {
91953259Smjacob			u_int8_t write_protect;
92053259Smjacob			int comp_enabled, comp_supported;
92153259Smjacob			error = sagetparams(periph, SA_PARAM_ALL,
92253259Smjacob			    &softc->media_blksize, &softc->media_density,
92353259Smjacob			    &softc->media_numblks, &softc->buffer_mode,
92453259Smjacob			    &write_protect, &softc->speed, &comp_supported,
92553259Smjacob			    &comp_enabled, &softc->comp_algorithm, NULL);
92653259Smjacob			if (error)
92753259Smjacob				break;
92853259Smjacob			if (write_protect)
92953259Smjacob				softc->flags |= SA_FLAG_TAPE_WP;
93053259Smjacob			else
93153259Smjacob				softc->flags &= ~SA_FLAG_TAPE_WP;
93253259Smjacob			softc->flags &= ~(SA_FLAG_COMP_SUPP|
93353259Smjacob			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);
93453259Smjacob			if (comp_supported) {
93553259Smjacob				if (softc->saved_comp_algorithm == 0)
93653259Smjacob					softc->saved_comp_algorithm =
93753259Smjacob					    softc->comp_algorithm;
93853259Smjacob				softc->flags |= SA_FLAG_COMP_SUPP;
93953259Smjacob				if (comp_enabled)
94053259Smjacob					softc->flags |= SA_FLAG_COMP_ENABLED;
94153259Smjacob			} else
94253259Smjacob				softc->flags |= SA_FLAG_COMP_UNSUPP;
94353259Smjacob		}
94439213Sgibbs		bzero(g, sizeof(struct mtget));
94541948Smjacob		g->mt_type = MT_ISAR;
94639213Sgibbs		if (softc->flags & SA_FLAG_COMP_UNSUPP) {
94739213Sgibbs			g->mt_comp = MT_COMP_UNSUPP;
94839213Sgibbs			g->mt_comp0 = MT_COMP_UNSUPP;
94939213Sgibbs			g->mt_comp1 = MT_COMP_UNSUPP;
95039213Sgibbs			g->mt_comp2 = MT_COMP_UNSUPP;
95139213Sgibbs			g->mt_comp3 = MT_COMP_UNSUPP;
95239213Sgibbs		} else {
95346962Smjacob			if ((softc->flags & SA_FLAG_COMP_ENABLED) == 0) {
95446962Smjacob				g->mt_comp = MT_COMP_DISABLED;
95546962Smjacob			} else {
95646962Smjacob				g->mt_comp = softc->comp_algorithm;
95746962Smjacob			}
95839213Sgibbs			g->mt_comp0 = softc->comp_algorithm;
95939213Sgibbs			g->mt_comp1 = softc->comp_algorithm;
96039213Sgibbs			g->mt_comp2 = softc->comp_algorithm;
96139213Sgibbs			g->mt_comp3 = softc->comp_algorithm;
96239213Sgibbs		}
96346962Smjacob		g->mt_density = softc->media_density;
96439213Sgibbs		g->mt_density0 = softc->media_density;
96539213Sgibbs		g->mt_density1 = softc->media_density;
96639213Sgibbs		g->mt_density2 = softc->media_density;
96739213Sgibbs		g->mt_density3 = softc->media_density;
96846962Smjacob		g->mt_blksiz = softc->media_blksize;
96939213Sgibbs		g->mt_blksiz0 = softc->media_blksize;
97039213Sgibbs		g->mt_blksiz1 = softc->media_blksize;
97139213Sgibbs		g->mt_blksiz2 = softc->media_blksize;
97239213Sgibbs		g->mt_blksiz3 = softc->media_blksize;
97343636Smjacob		g->mt_fileno = softc->fileno;
97443636Smjacob		g->mt_blkno = softc->blkno;
97543636Smjacob		g->mt_dsreg = (short) softc->dsreg;
97671268Smjacob		/*
97771268Smjacob		 * Yes, we know that this is likely to overflow
97871268Smjacob		 */
97971268Smjacob		if (softc->last_resid_was_io) {
98071268Smjacob			if ((g->mt_resid = (short) softc->last_io_resid) != 0) {
98171268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
98271268Smjacob					softc->last_io_resid = 0;
98371268Smjacob				}
98471268Smjacob			}
98571268Smjacob		} else {
98671268Smjacob			if ((g->mt_resid = (short)softc->last_ctl_resid) != 0) {
98771268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
98871268Smjacob					softc->last_ctl_resid = 0;
98971268Smjacob				}
99071268Smjacob			}
99171268Smjacob		}
99239213Sgibbs		error = 0;
99339213Sgibbs		break;
99439213Sgibbs	}
99541948Smjacob	case MTIOCERRSTAT:
99641948Smjacob	{
99741948Smjacob		struct scsi_tape_errors *sep =
99841948Smjacob		    &((union mterrstat *)arg)->scsi_errstat;
99941948Smjacob
100041948Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
100141948Smjacob		    ("saioctl: MTIOCERRSTAT\n"));
100241948Smjacob
100341948Smjacob		bzero(sep, sizeof(*sep));
100441948Smjacob		sep->io_resid = softc->last_io_resid;
100541948Smjacob		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,
100641948Smjacob		    sizeof (sep->io_sense));
100742009Smjacob		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,
100842009Smjacob		    sizeof (sep->io_cdb));
100942009Smjacob		sep->ctl_resid = softc->last_ctl_resid;
101041948Smjacob		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,
101141948Smjacob		    sizeof (sep->ctl_sense));
101242009Smjacob		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
101342009Smjacob		    sizeof (sep->ctl_cdb));
101443636Smjacob
1015154360Smjacob		if ((SA_IS_CTRL(dev) == 0 && softc->open_pending_mount) ||
1016154360Smjacob		    didlockperiph)
101743636Smjacob			bzero((caddr_t) &softc->errinfo,
101843636Smjacob			    sizeof (softc->errinfo));
101941948Smjacob		error = 0;
102041948Smjacob		break;
102141948Smjacob	}
102239213Sgibbs	case MTIOCTOP:
102339213Sgibbs	{
102439213Sgibbs		struct mtop *mt;
102539213Sgibbs		int    count;
102639213Sgibbs
1027154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
1028154360Smjacob
102939213Sgibbs		mt = (struct mtop *)arg;
103039213Sgibbs
1031154360Smjacob
103239213Sgibbs		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
103339213Sgibbs			 ("saioctl: op=0x%x count=0x%x\n",
103439213Sgibbs			  mt->mt_op, mt->mt_count));
103539213Sgibbs
103639213Sgibbs		count = mt->mt_count;
103739213Sgibbs		switch (mt->mt_op) {
103841906Smjacob		case MTWEOF:	/* write an end-of-file marker */
103968114Smjacob			/*
104068114Smjacob			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN
104168114Smjacob			 * flag because by keeping track of filemarks
104268114Smjacob			 * we have last written we know ehether or not
104368114Smjacob			 * we need to write more when we close the device.
104468114Smjacob			 */
104541906Smjacob			error = sawritefilemarks(periph, count, FALSE);
104639213Sgibbs			break;
104742009Smjacob		case MTWSS:	/* write a setmark */
104842009Smjacob			error = sawritefilemarks(periph, count, TRUE);
104942009Smjacob			break;
105039213Sgibbs		case MTBSR:	/* backward space record */
105139213Sgibbs		case MTFSR:	/* forward space record */
105239213Sgibbs		case MTBSF:	/* backward space file */
105339213Sgibbs		case MTFSF:	/* forward space file */
105442009Smjacob		case MTBSS:	/* backward space setmark */
105542009Smjacob		case MTFSS:	/* forward space setmark */
105639213Sgibbs		case MTEOD:	/* space to end of recorded medium */
105739213Sgibbs		{
105839213Sgibbs			int nmarks;
105939213Sgibbs
106048520Speter			spaceop = SS_FILEMARKS;
106139213Sgibbs			nmarks = softc->filemarks;
106239213Sgibbs			error = sacheckeod(periph);
106341906Smjacob			if (error) {
1064164906Smjacob				xpt_print(periph->path,
1065164906Smjacob				    "EOD check prior to spacing failed\n");
106641906Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
106741906Smjacob				break;
106841906Smjacob			}
106939213Sgibbs			nmarks -= softc->filemarks;
107042009Smjacob			switch(mt->mt_op) {
107142009Smjacob			case MTBSR:
107239213Sgibbs				count = -count;
107342009Smjacob				/* FALLTHROUGH */
107442009Smjacob			case MTFSR:
107539213Sgibbs				spaceop = SS_BLOCKS;
107642009Smjacob				break;
107742009Smjacob			case MTBSF:
107842009Smjacob				count = -count;
107942009Smjacob				/* FALLTHROUGH */
108042009Smjacob			case MTFSF:
108142009Smjacob				break;
108242009Smjacob			case MTBSS:
108342009Smjacob				count = -count;
108442009Smjacob				/* FALLTHROUGH */
108542009Smjacob			case MTFSS:
108642009Smjacob				spaceop = SS_SETMARKS;
108742009Smjacob				break;
108842009Smjacob			case MTEOD:
108939213Sgibbs				spaceop = SS_EOD;
109039213Sgibbs				count = 0;
109139213Sgibbs				nmarks = 0;
109242009Smjacob				break;
109342009Smjacob			default:
109442009Smjacob				error = EINVAL;
109542009Smjacob				break;
109639213Sgibbs			}
109742009Smjacob			if (error)
109842009Smjacob				break;
109939213Sgibbs
110039213Sgibbs			nmarks = softc->filemarks;
110142009Smjacob			/*
110242009Smjacob			 * XXX: Why are we checking again?
110342009Smjacob			 */
110439213Sgibbs			error = sacheckeod(periph);
110542009Smjacob			if (error)
110642009Smjacob				break;
110739213Sgibbs			nmarks -= softc->filemarks;
110842009Smjacob			error = saspace(periph, count - nmarks, spaceop);
110941906Smjacob			/*
111041906Smjacob			 * At this point, clear that we've written the tape
111141906Smjacob			 * and that we've written any filemarks. We really
111241906Smjacob			 * don't know what the applications wishes to do next-
111341906Smjacob			 * the sacheckeod's will make sure we terminated the
111441906Smjacob			 * tape correctly if we'd been writing, but the next
111541906Smjacob			 * action the user application takes will set again
111641906Smjacob			 * whether we need to write filemarks.
111741906Smjacob			 */
111846962Smjacob			softc->flags &=
111946962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
112041906Smjacob			softc->filemarks = 0;
112139213Sgibbs			break;
112239213Sgibbs		}
112339213Sgibbs		case MTREW:	/* rewind */
1124154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
112541906Smjacob			(void) sacheckeod(periph);
112639213Sgibbs			error = sarewind(periph);
112741906Smjacob			/* see above */
112842009Smjacob			softc->flags &=
112946962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
113082575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
113141906Smjacob			softc->filemarks = 0;
113239213Sgibbs			break;
113339213Sgibbs		case MTERASE:	/* erase */
1134154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
113539213Sgibbs			error = saerase(periph, count);
113646962Smjacob			softc->flags &=
113746962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
113882575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
113939213Sgibbs			break;
114039213Sgibbs		case MTRETENS:	/* re-tension tape */
1141154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
114239213Sgibbs			error = saretension(periph);
114346962Smjacob			softc->flags &=
114446962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
114582575Smjacob			softc->flags &= ~SA_FLAG_ERR_PENDING;
114639213Sgibbs			break;
114739213Sgibbs		case MTOFFL:	/* rewind and put the drive offline */
114841906Smjacob
1149154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1150154360Smjacob
115141906Smjacob			(void) sacheckeod(periph);
115241906Smjacob			/* see above */
115341906Smjacob			softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
115441906Smjacob			softc->filemarks = 0;
115541906Smjacob
115646962Smjacob			error = sarewind(periph);
115753522Smjacob			/* clear the frozen flag anyway */
115853522Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
115946962Smjacob
116039213Sgibbs			/*
116153522Smjacob			 * Be sure to allow media removal before ejecting.
116239213Sgibbs			 */
116346962Smjacob
116439213Sgibbs			saprevent(periph, PR_ALLOW);
116553522Smjacob			if (error == 0) {
116643636Smjacob				error = saloadunload(periph, FALSE);
116753522Smjacob				if (error == 0) {
116853522Smjacob					softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
116953522Smjacob				}
117053522Smjacob			}
117146962Smjacob			break;
117239213Sgibbs
117339213Sgibbs		case MTNOP:	/* no operation, sets status only */
117439213Sgibbs		case MTCACHE:	/* enable controller cache */
117539213Sgibbs		case MTNOCACHE:	/* disable controller cache */
117639213Sgibbs			error = 0;
117739213Sgibbs			break;
117846962Smjacob
117939213Sgibbs		case MTSETBSIZ:	/* Set block size for device */
118039213Sgibbs
1181154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1182154360Smjacob
118339213Sgibbs			error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
118442563Smjacob					    0, 0, 0);
118541674Smjacob			if (error == 0) {
118641906Smjacob				softc->last_media_blksize =
118741906Smjacob				    softc->media_blksize;
118841674Smjacob				softc->media_blksize = count;
118941674Smjacob				if (count) {
119041674Smjacob					softc->flags |= SA_FLAG_FIXED;
119141674Smjacob					if (powerof2(count)) {
119241674Smjacob						softc->blk_shift =
119341674Smjacob						    ffs(count) - 1;
119441674Smjacob						softc->blk_mask = count - 1;
119541674Smjacob					} else {
119641674Smjacob						softc->blk_mask = ~0;
119741674Smjacob						softc->blk_shift = 0;
119841674Smjacob					}
119941906Smjacob					/*
120041906Smjacob					 * Make the user's desire 'persistent'.
120141906Smjacob					 */
120241906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
120341906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
120441674Smjacob				} else {
120541674Smjacob					softc->flags &= ~SA_FLAG_FIXED;
120641674Smjacob					if (softc->max_blk == 0) {
120741674Smjacob						softc->max_blk = ~0;
120841674Smjacob					}
120941674Smjacob					softc->blk_shift = 0;
121041674Smjacob					if (softc->blk_gran != 0) {
121141674Smjacob						softc->blk_mask =
121241674Smjacob						    softc->blk_gran - 1;
121341674Smjacob					} else {
121441674Smjacob						softc->blk_mask = 0;
121541674Smjacob					}
121641906Smjacob					/*
121741906Smjacob					 * Make the user's desire 'persistent'.
121841906Smjacob					 */
121941906Smjacob					softc->quirks |= SA_QUIRK_VARIABLE;
122041906Smjacob					softc->quirks &= ~SA_QUIRK_FIXED;
122141674Smjacob				}
122241674Smjacob			}
122339213Sgibbs			break;
122439213Sgibbs		case MTSETDNSTY:	/* Set density for device and mode */
1225154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
1226154360Smjacob
122739213Sgibbs			if (count > UCHAR_MAX) {
122839213Sgibbs				error = EINVAL;
122939213Sgibbs				break;
123039213Sgibbs			} else {
123139213Sgibbs				error = sasetparams(periph, SA_PARAM_DENSITY,
123242563Smjacob						    0, count, 0, 0);
123339213Sgibbs			}
123439213Sgibbs			break;
123539213Sgibbs		case MTCOMP:	/* enable compression */
1236154360Smjacob			PENDING_MOUNT_CHECK(softc, periph, dev);
123739213Sgibbs			/*
123839213Sgibbs			 * Some devices don't support compression, and
123939213Sgibbs			 * don't like it if you ask them for the
124039213Sgibbs			 * compression page.
124139213Sgibbs			 */
124243636Smjacob			if ((softc->quirks & SA_QUIRK_NOCOMP) ||
124343636Smjacob			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {
124439213Sgibbs				error = ENODEV;
124539213Sgibbs				break;
124639213Sgibbs			}
124739213Sgibbs			error = sasetparams(periph, SA_PARAM_COMPRESSION,
124854099Smjacob			    0, 0, count, SF_NO_PRINT);
124939213Sgibbs			break;
125039213Sgibbs		default:
125139213Sgibbs			error = EINVAL;
125239213Sgibbs		}
125339213Sgibbs		break;
125439213Sgibbs	}
125539213Sgibbs	case MTIOCIEOT:
125639213Sgibbs	case MTIOCEEOT:
125739213Sgibbs		error = 0;
125839213Sgibbs		break;
125941918Smjacob	case MTIOCRDSPOS:
1260154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
126141918Smjacob		error = sardpos(periph, 0, (u_int32_t *) arg);
126241918Smjacob		break;
126341918Smjacob	case MTIOCRDHPOS:
1264154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
126541918Smjacob		error = sardpos(periph, 1, (u_int32_t *) arg);
126641918Smjacob		break;
126741918Smjacob	case MTIOCSLOCATE:
1268154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
126941918Smjacob		error = sasetpos(periph, 0, (u_int32_t *) arg);
127041918Smjacob		break;
127141918Smjacob	case MTIOCHLOCATE:
1272154360Smjacob		PENDING_MOUNT_CHECK(softc, periph, dev);
127341918Smjacob		error = sasetpos(periph, 1, (u_int32_t *) arg);
127441918Smjacob		break;
127546962Smjacob	case MTIOCGETEOTMODEL:
127646962Smjacob		error = 0;
127746962Smjacob		if (softc->quirks & SA_QUIRK_1FM)
127846962Smjacob			mode = 1;
127946962Smjacob		else
128046962Smjacob			mode = 2;
128146962Smjacob		*((u_int32_t *) arg) = mode;
128246962Smjacob		break;
128346962Smjacob	case MTIOCSETEOTMODEL:
128446962Smjacob		error = 0;
128546962Smjacob		switch (*((u_int32_t *) arg)) {
128646962Smjacob		case 1:
128746962Smjacob			softc->quirks &= ~SA_QUIRK_2FM;
128846962Smjacob			softc->quirks |= SA_QUIRK_1FM;
128946962Smjacob			break;
129046962Smjacob		case 2:
129146962Smjacob			softc->quirks &= ~SA_QUIRK_1FM;
129246962Smjacob			softc->quirks |= SA_QUIRK_2FM;
129346962Smjacob			break;
129446962Smjacob		default:
129546962Smjacob			error = EINVAL;
129646962Smjacob			break;
129746962Smjacob		}
129846962Smjacob		break;
129939213Sgibbs	default:
130039213Sgibbs		error = cam_periph_ioctl(periph, cmd, arg, saerror);
130139213Sgibbs		break;
130239213Sgibbs	}
130371268Smjacob
130471268Smjacob	/*
130571268Smjacob	 * Check to see if we cleared a frozen state
130671268Smjacob	 */
130771268Smjacob	if (error == 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {
130871268Smjacob		switch(cmd) {
130971268Smjacob		case MTIOCRDSPOS:
131071268Smjacob		case MTIOCRDHPOS:
131171268Smjacob		case MTIOCSLOCATE:
131271268Smjacob		case MTIOCHLOCATE:
131371268Smjacob			softc->fileno = (daddr_t) -1;
131471268Smjacob			softc->blkno = (daddr_t) -1;
131571268Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
1316164906Smjacob			xpt_print(periph->path,
1317164906Smjacob			    "tape state now unfrozen.\n");
131871268Smjacob			break;
131971268Smjacob		default:
132071268Smjacob			break;
132171268Smjacob		}
132271268Smjacob	}
132343636Smjacob	if (didlockperiph) {
1324168752Sscottl		cam_periph_unhold(periph);
132543636Smjacob	}
1326168752Sscottl	cam_periph_unlock(periph);
132739213Sgibbs	return (error);
132839213Sgibbs}
132939213Sgibbs
133039213Sgibbsstatic void
133139213Sgibbssainit(void)
133239213Sgibbs{
133339213Sgibbs	cam_status status;
133439213Sgibbs	struct cam_path *path;
133539213Sgibbs
133639213Sgibbs	/*
133739213Sgibbs	 * Install a global async callback.
133839213Sgibbs	 */
133939213Sgibbs	status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID,
134039213Sgibbs				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
134139213Sgibbs
134239213Sgibbs	if (status == CAM_REQ_CMP) {
134339213Sgibbs		/* Register the async callbacks of interrest */
134439213Sgibbs		struct ccb_setasync csa; /*
134539213Sgibbs					  * This is an immediate CCB,
134639213Sgibbs					  * so using the stack is OK
134739213Sgibbs					  */
134846962Smjacob		xpt_setup_ccb(&csa.ccb_h, path, 5);
134939213Sgibbs		csa.ccb_h.func_code = XPT_SASYNC_CB;
135039213Sgibbs		csa.event_enable = AC_FOUND_DEVICE;
135139213Sgibbs		csa.callback = saasync;
135239213Sgibbs		csa.callback_arg = NULL;
135339213Sgibbs		xpt_action((union ccb *)&csa);
135439213Sgibbs		status = csa.ccb_h.status;
135539213Sgibbs		xpt_free_path(path);
135639213Sgibbs	}
135739213Sgibbs
135839213Sgibbs	if (status != CAM_REQ_CMP) {
135939213Sgibbs		printf("sa: Failed to attach master async callback "
136039213Sgibbs		       "due to status 0x%x!\n", status);
136139213Sgibbs	}
136239213Sgibbs}
136339213Sgibbs
136439213Sgibbsstatic void
136540603Skensaoninvalidate(struct cam_periph *periph)
136640603Sken{
136740603Sken	struct sa_softc *softc;
136840603Sken	struct ccb_setasync csa;
136940603Sken
137040603Sken	softc = (struct sa_softc *)periph->softc;
137140603Sken
137240603Sken	/*
137340603Sken	 * De-register any async callbacks.
137440603Sken	 */
137540603Sken	xpt_setup_ccb(&csa.ccb_h, periph->path,
137640603Sken		      /* priority */ 5);
137740603Sken	csa.ccb_h.func_code = XPT_SASYNC_CB;
137840603Sken	csa.event_enable = 0;
137940603Sken	csa.callback = saasync;
138040603Sken	csa.callback_arg = periph;
138140603Sken	xpt_action((union ccb *)&csa);
138240603Sken
138340603Sken	softc->flags |= SA_FLAG_INVALID;
138440603Sken
138540603Sken	/*
138640603Sken	 * Return all queued I/O with ENXIO.
138740603Sken	 * XXX Handle any transactions queued to the card
138840603Sken	 *     with XPT_ABORT_CCB.
138940603Sken	 */
1390112946Sphk	bioq_flush(&softc->bio_queue, NULL, ENXIO);
139146962Smjacob	softc->queue_count = 0;
139240603Sken
1393164906Smjacob	xpt_print(periph->path, "lost device\n");
139440603Sken
139540603Sken}
139640603Sken
139740603Skenstatic void
139839213Sgibbssacleanup(struct cam_periph *periph)
139939213Sgibbs{
140040603Sken	struct sa_softc *softc;
140153259Smjacob	int i;
140240603Sken
140340603Sken	softc = (struct sa_softc *)periph->softc;
140440603Sken
1405112006Sphk	devstat_remove_entry(softc->device_stats);
140653259Smjacob
140753259Smjacob	destroy_dev(softc->devs.ctl_dev);
140853259Smjacob
140953259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
141053259Smjacob		destroy_dev(softc->devs.mode_devs[i].r_dev);
141153259Smjacob		destroy_dev(softc->devs.mode_devs[i].nr_dev);
141253259Smjacob		destroy_dev(softc->devs.mode_devs[i].er_dev);
141353259Smjacob	}
141453259Smjacob
1415164906Smjacob	xpt_print(periph->path, "removing device entry\n");
1416147723Savatar	free(softc, M_SCSISA);
141739213Sgibbs}
141839213Sgibbs
141939213Sgibbsstatic void
142039213Sgibbssaasync(void *callback_arg, u_int32_t code,
142139213Sgibbs	struct cam_path *path, void *arg)
142239213Sgibbs{
142339213Sgibbs	struct cam_periph *periph;
142439213Sgibbs
142539213Sgibbs	periph = (struct cam_periph *)callback_arg;
142639213Sgibbs	switch (code) {
142739213Sgibbs	case AC_FOUND_DEVICE:
142839213Sgibbs	{
142939213Sgibbs		struct ccb_getdev *cgd;
143039213Sgibbs		cam_status status;
143139213Sgibbs
143239213Sgibbs		cgd = (struct ccb_getdev *)arg;
143379177Smjacob		if (cgd == NULL)
143479177Smjacob			break;
143539213Sgibbs
143656148Smjacob		if (SID_TYPE(&cgd->inq_data) != T_SEQUENTIAL)
143739213Sgibbs			break;
143839213Sgibbs
143939213Sgibbs		/*
144039213Sgibbs		 * Allocate a peripheral instance for
144139213Sgibbs		 * this device and start the probe
144239213Sgibbs		 * process.
144339213Sgibbs		 */
144440603Sken		status = cam_periph_alloc(saregister, saoninvalidate,
144540603Sken					  sacleanup, sastart,
144639213Sgibbs					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,
144739213Sgibbs					  saasync, AC_FOUND_DEVICE, cgd);
144839213Sgibbs
144939213Sgibbs		if (status != CAM_REQ_CMP
145039213Sgibbs		 && status != CAM_REQ_INPROG)
145139213Sgibbs			printf("saasync: Unable to probe new device "
145239213Sgibbs				"due to status 0x%x\n", status);
145339213Sgibbs		break;
145439213Sgibbs	}
145539213Sgibbs	default:
145647413Sgibbs		cam_periph_async(periph, code, path, arg);
145739213Sgibbs		break;
145839213Sgibbs	}
145939213Sgibbs}
146039213Sgibbs
146139213Sgibbsstatic cam_status
146239213Sgibbssaregister(struct cam_periph *periph, void *arg)
146339213Sgibbs{
146439213Sgibbs	struct sa_softc *softc;
146539213Sgibbs	struct ccb_setasync csa;
146639213Sgibbs	struct ccb_getdev *cgd;
146739213Sgibbs	caddr_t match;
146853259Smjacob	int i;
146939213Sgibbs
147039213Sgibbs	cgd = (struct ccb_getdev *)arg;
147139213Sgibbs	if (periph == NULL) {
147239213Sgibbs		printf("saregister: periph was NULL!!\n");
147354099Smjacob		return (CAM_REQ_CMP_ERR);
147439213Sgibbs	}
147539213Sgibbs
147639213Sgibbs	if (cgd == NULL) {
147739213Sgibbs		printf("saregister: no getdev CCB, can't register device\n");
147854099Smjacob		return (CAM_REQ_CMP_ERR);
147939213Sgibbs	}
148039213Sgibbs
148167723Smjacob	softc = (struct sa_softc *)
1482147723Savatar	    malloc(sizeof (*softc), M_SCSISA, M_NOWAIT | M_ZERO);
148339213Sgibbs	if (softc == NULL) {
148439213Sgibbs		printf("saregister: Unable to probe new device. "
148539213Sgibbs		       "Unable to allocate softc\n");
148654099Smjacob		return (CAM_REQ_CMP_ERR);
148739213Sgibbs	}
148841674Smjacob	softc->scsi_rev = SID_ANSI_REV(&cgd->inq_data);
148939213Sgibbs	softc->state = SA_STATE_NORMAL;
149043636Smjacob	softc->fileno = (daddr_t) -1;
149143636Smjacob	softc->blkno = (daddr_t) -1;
149243636Smjacob
149359249Sphk	bioq_init(&softc->bio_queue);
149439213Sgibbs	periph->softc = softc;
149539213Sgibbs
149639213Sgibbs	/*
149739213Sgibbs	 * See if this device has any quirks.
149839213Sgibbs	 */
149939213Sgibbs	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
150039213Sgibbs			       (caddr_t)sa_quirk_table,
150139213Sgibbs			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),
150239213Sgibbs			       sizeof(*sa_quirk_table), scsi_inquiry_match);
150339213Sgibbs
150442563Smjacob	if (match != NULL) {
150539213Sgibbs		softc->quirks = ((struct sa_quirk_entry *)match)->quirks;
150642563Smjacob		softc->last_media_blksize =
150742563Smjacob		    ((struct sa_quirk_entry *)match)->prefblk;
150842563Smjacob#ifdef	CAMDEBUG
1509164906Smjacob		xpt_print(periph->path, "found quirk entry %d\n",
1510164906Smjacob		    (int) (((struct sa_quirk_entry *) match) - sa_quirk_table));
151142563Smjacob#endif
151242563Smjacob	} else
151339213Sgibbs		softc->quirks = SA_QUIRK_NONE;
151439213Sgibbs
151539213Sgibbs	/*
151639213Sgibbs 	 * The SA driver supports a blocksize, but we don't know the
151746962Smjacob	 * blocksize until we media is inserted.  So, set a flag to
151839213Sgibbs	 * indicate that the blocksize is unavailable right now.
151939213Sgibbs	 */
1520112006Sphk	softc->device_stats = devstat_new_entry("sa", periph->unit_number, 0,
152156148Smjacob	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |
152256148Smjacob	    DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_TAPE);
152353259Smjacob
152453259Smjacob	softc->devs.ctl_dev = make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,
152553259Smjacob	    periph->unit_number, 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;
152853259Smjacob
152953259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
153053283Smjacob
153153259Smjacob		softc->devs.mode_devs[i].r_dev = make_dev(&sa_cdevsw,
153253259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_R),
153372804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "%s%d.%d",
153453259Smjacob		    periph->periph_name, periph->unit_number, i);
1535101940Snjl		softc->devs.mode_devs[i].r_dev->si_drv1 = periph;
153653283Smjacob
153753259Smjacob		softc->devs.mode_devs[i].nr_dev = make_dev(&sa_cdevsw,
153853259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_NR),
153972804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "n%s%d.%d",
154053259Smjacob		    periph->periph_name, periph->unit_number, i);
1541101940Snjl		softc->devs.mode_devs[i].nr_dev->si_drv1 = periph;
154253259Smjacob
154353283Smjacob		softc->devs.mode_devs[i].er_dev = make_dev(&sa_cdevsw,
154453259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_ER),
154572804Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "e%s%d.%d",
154653259Smjacob		    periph->periph_name, periph->unit_number, i);
1547101940Snjl		softc->devs.mode_devs[i].er_dev->si_drv1 = periph;
154865838Smjacob
154965838Smjacob		/*
155065838Smjacob		 * Make the (well known) aliases for the first mode.
155165838Smjacob		 */
155265838Smjacob		if (i == 0) {
1553130585Sphk			struct cdev *alias;
1554101940Snjl
1555101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].r_dev,
155672804Smjacob			   "%s%d", periph->periph_name, periph->unit_number);
1557101940Snjl			alias->si_drv1 = periph;
1558101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].nr_dev,
155972804Smjacob			    "n%s%d", periph->periph_name, periph->unit_number);
1560101940Snjl			alias->si_drv1 = periph;
1561101940Snjl			alias = make_dev_alias(softc->devs.mode_devs[i].er_dev,
156272804Smjacob			    "e%s%d", periph->periph_name, periph->unit_number);
1563101940Snjl			alias->si_drv1 = periph;
156465838Smjacob		}
156553259Smjacob	}
156653259Smjacob
156739213Sgibbs	/*
156839213Sgibbs	 * Add an async callback so that we get
156939213Sgibbs	 * notified if this device goes away.
157039213Sgibbs	 */
157139213Sgibbs	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
157239213Sgibbs	csa.ccb_h.func_code = XPT_SASYNC_CB;
157339213Sgibbs	csa.event_enable = AC_LOST_DEVICE;
157439213Sgibbs	csa.callback = saasync;
157539213Sgibbs	csa.callback_arg = periph;
157639213Sgibbs	xpt_action((union ccb *)&csa);
157739213Sgibbs
157839213Sgibbs	xpt_announce_periph(periph, NULL);
157939213Sgibbs
158054099Smjacob	return (CAM_REQ_CMP);
158139213Sgibbs}
158239213Sgibbs
158339213Sgibbsstatic void
158439213Sgibbssastart(struct cam_periph *periph, union ccb *start_ccb)
158539213Sgibbs{
158639213Sgibbs	struct sa_softc *softc;
158739213Sgibbs
158839213Sgibbs	softc = (struct sa_softc *)periph->softc;
158939213Sgibbs
1590115660Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("sastart\n"));
159171082Smjacob
159239213Sgibbs
159339213Sgibbs	switch (softc->state) {
159439213Sgibbs	case SA_STATE_NORMAL:
159539213Sgibbs	{
159639213Sgibbs		/* Pull a buffer from the queue and get going on it */
159759249Sphk		struct bio *bp;
159839213Sgibbs
159939213Sgibbs		/*
160039213Sgibbs		 * See if there is a buf with work for us to do..
160139213Sgibbs		 */
160259249Sphk		bp = bioq_first(&softc->bio_queue);
160339213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority) {
160439213Sgibbs			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
160539213Sgibbs					("queuing for immediate ccb\n"));
160671082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_WAITING);
160739213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
160839213Sgibbs					  periph_links.sle);
160939213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
161039213Sgibbs			wakeup(&periph->ccb_list);
161139213Sgibbs		} else if (bp == NULL) {
161239213Sgibbs			xpt_release_ccb(start_ccb);
161339213Sgibbs		} else if ((softc->flags & SA_FLAG_ERR_PENDING) != 0) {
161459249Sphk			struct bio *done_bp;
161582575Smjacobagain:
161646962Smjacob			softc->queue_count--;
161759249Sphk			bioq_remove(&softc->bio_queue, bp);
161859249Sphk			bp->bio_resid = bp->bio_bcount;
161982575Smjacob			done_bp = bp;
162039213Sgibbs			if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) {
162182575Smjacob				/*
162282575Smjacob				 * We now just clear errors in this case
162382575Smjacob				 * and let the residual be the notifier.
162482575Smjacob				 */
162582575Smjacob				bp->bio_error = 0;
162682575Smjacob			} else if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) {
162782575Smjacob				/*
162882575Smjacob				 * This can only happen if we're reading
162982575Smjacob				 * in fixed length mode. In this case,
163082575Smjacob				 * we dump the rest of the list the
163182575Smjacob				 * same way.
163282575Smjacob				 */
163382575Smjacob				bp->bio_error = 0;
163482575Smjacob				if (bioq_first(&softc->bio_queue) != NULL) {
163582575Smjacob					biodone(done_bp);
163682575Smjacob					goto again;
163782575Smjacob				}
163882575Smjacob			} else if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) {
163959249Sphk				bp->bio_error = EIO;
164082575Smjacob				bp->bio_flags |= BIO_ERROR;
164141948Smjacob			}
164259249Sphk			bp = bioq_first(&softc->bio_queue);
164344354Smjacob			/*
164444354Smjacob			 * Only if we have no other buffers queued up
164544354Smjacob			 * do we clear the pending error flag.
164644354Smjacob			 */
164744354Smjacob			if (bp == NULL)
164844354Smjacob				softc->flags &= ~SA_FLAG_ERR_PENDING;
164944354Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
165046962Smjacob			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "
165146962Smjacob			    "%d more buffers queued up\n",
165244354Smjacob			    (softc->flags & SA_FLAG_ERR_PENDING),
165346962Smjacob			    (bp != NULL)? "not " : " ", softc->queue_count));
165444354Smjacob			xpt_release_ccb(start_ccb);
165541948Smjacob			biodone(done_bp);
165639213Sgibbs		} else {
165739213Sgibbs			u_int32_t length;
165839213Sgibbs
165959249Sphk			bioq_remove(&softc->bio_queue, bp);
166046962Smjacob			softc->queue_count--;
166139213Sgibbs
166239213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0) {
166339213Sgibbs				if (softc->blk_shift != 0) {
166439213Sgibbs					length =
166559249Sphk					    bp->bio_bcount >> softc->blk_shift;
166643636Smjacob				} else if (softc->media_blksize != 0) {
166771082Smjacob					length = bp->bio_bcount /
166871082Smjacob					    softc->media_blksize;
166943636Smjacob				} else {
167059249Sphk					bp->bio_error = EIO;
1671164906Smjacob					xpt_print(periph->path, "zero blocksize"
1672164906Smjacob					    " for FIXED length writes?\n");
167343636Smjacob					biodone(bp);
167443636Smjacob					break;
167539213Sgibbs				}
1676115660Smjacob#if	0
1677115660Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1678115660Smjacob				    ("issuing a %d fixed record %s\n",
1679115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1680115660Smjacob				    "write"));
1681115660Smjacob#endif
168239213Sgibbs			} else {
168359249Sphk				length = bp->bio_bcount;
1684115660Smjacob#if	0
168541906Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
1686115660Smjacob				    ("issuing a %d variable byte %s\n",
1687115660Smjacob				    length,  (bp->bio_cmd == BIO_READ)? "read" :
1688115660Smjacob				    "write"));
1689115660Smjacob#endif
169039213Sgibbs			}
1691112260Sphk			devstat_start_transaction_bio(softc->device_stats, bp);
169239213Sgibbs			/*
169341906Smjacob			 * Some people have theorized that we should
169439213Sgibbs			 * suppress illegal length indication if we are
169539213Sgibbs			 * running in variable block mode so that we don't
169639213Sgibbs			 * have to request sense every time our requested
169739213Sgibbs			 * block size is larger than the written block.
169839213Sgibbs			 * The residual information from the ccb allows
169939213Sgibbs			 * us to identify this situation anyway.  The only
170039213Sgibbs			 * problem with this is that we will not get
170139213Sgibbs			 * information about blocks that are larger than
170239213Sgibbs			 * our read buffer unless we set the block size
170339213Sgibbs			 * in the mode page to something other than 0.
170441906Smjacob			 *
170541906Smjacob			 * I believe that this is a non-issue. If user apps
170641906Smjacob			 * don't adjust their read size to match our record
170741906Smjacob			 * size, that's just life. Anyway, the typical usage
170841906Smjacob			 * would be to issue, e.g., 64KB reads and occasionally
170941906Smjacob			 * have to do deal with 512 byte or 1KB intermediate
171041906Smjacob			 * records.
171139213Sgibbs			 */
171259249Sphk			softc->dsreg = (bp->bio_cmd == BIO_READ)?
171343636Smjacob			    MTIO_DSREG_RD : MTIO_DSREG_WR;
171446962Smjacob			scsi_sa_read_write(&start_ccb->csio, 0, sadone,
171559249Sphk			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd == BIO_READ),
171646962Smjacob			    FALSE, (softc->flags & SA_FLAG_FIXED) != 0,
171759249Sphk			    length, bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,
171879100Smjacob			    IO_TIMEOUT);
171971082Smjacob			start_ccb->ccb_h.ccb_pflags &= ~SA_POSITION_UPDATED;
172071082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);
172139213Sgibbs			start_ccb->ccb_h.ccb_bp = bp;
172259249Sphk			bp = bioq_first(&softc->bio_queue);
172339213Sgibbs			xpt_action(start_ccb);
172439213Sgibbs		}
172539213Sgibbs
172639213Sgibbs		if (bp != NULL) {
172739213Sgibbs			/* Have more work to do, so ensure we stay scheduled */
172846962Smjacob			xpt_schedule(periph, 1);
172939213Sgibbs		}
173039213Sgibbs		break;
173139213Sgibbs	}
173246962Smjacob	case SA_STATE_ABNORMAL:
173346962Smjacob	default:
173446962Smjacob		panic("state 0x%x in sastart", softc->state);
173546962Smjacob		break;
173639213Sgibbs	}
173739213Sgibbs}
173839213Sgibbs
173939213Sgibbs
174039213Sgibbsstatic void
174139213Sgibbssadone(struct cam_periph *periph, union ccb *done_ccb)
174239213Sgibbs{
174339213Sgibbs	struct sa_softc *softc;
174439213Sgibbs	struct ccb_scsiio *csio;
174539213Sgibbs
174639213Sgibbs	softc = (struct sa_softc *)periph->softc;
174739213Sgibbs	csio = &done_ccb->csio;
174871082Smjacob	switch (CCB_Type(csio)) {
174939213Sgibbs	case SA_CCB_BUFFER_IO:
175039213Sgibbs	{
175159249Sphk		struct bio *bp;
175239213Sgibbs		int error;
175339213Sgibbs
175443636Smjacob		softc->dsreg = MTIO_DSREG_REST;
175559249Sphk		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
175639213Sgibbs		error = 0;
175739213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
175839213Sgibbs			if ((error = saerror(done_ccb, 0, 0)) == ERESTART) {
175939213Sgibbs				/*
176041948Smjacob				 * A retry was scheduled, so just return.
176139213Sgibbs				 */
176239213Sgibbs				return;
176339213Sgibbs			}
176439213Sgibbs		}
176539213Sgibbs
176639213Sgibbs		if (error == EIO) {
176739213Sgibbs
176839213Sgibbs			/*
176953522Smjacob			 * Catastrophic error. Mark the tape as frozen
177053522Smjacob			 * (we no longer know tape position).
177153522Smjacob			 *
177244354Smjacob			 * Return all queued I/O with EIO, and unfreeze
177339213Sgibbs			 * our queue so that future transactions that
177439213Sgibbs			 * attempt to fix this problem can get to the
177539213Sgibbs			 * device.
177639213Sgibbs			 *
177739213Sgibbs			 */
177839213Sgibbs
177953522Smjacob			softc->flags |= SA_FLAG_TAPE_FROZEN;
1780112946Sphk			bioq_flush(&softc->bio_queue, NULL, EIO);
178139213Sgibbs		}
178239213Sgibbs		if (error != 0) {
178359249Sphk			bp->bio_resid = bp->bio_bcount;
178459249Sphk			bp->bio_error = error;
178559249Sphk			bp->bio_flags |= BIO_ERROR;
178643636Smjacob			/*
178743636Smjacob			 * In the error case, position is updated in saerror.
178843636Smjacob			 */
178939213Sgibbs		} else {
179059249Sphk			bp->bio_resid = csio->resid;
179159249Sphk			bp->bio_error = 0;
179239213Sgibbs			if (csio->resid != 0) {
179359249Sphk				bp->bio_flags |= BIO_ERROR;
179439213Sgibbs			}
179559249Sphk			if (bp->bio_cmd == BIO_WRITE) {
179639213Sgibbs				softc->flags |= SA_FLAG_TAPE_WRITTEN;
179739213Sgibbs				softc->filemarks = 0;
179839213Sgibbs			}
179971082Smjacob			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&
180071082Smjacob			    (softc->blkno != (daddr_t) -1)) {
180143636Smjacob				if ((softc->flags & SA_FLAG_FIXED) != 0) {
180243636Smjacob					u_int32_t l;
180343636Smjacob					if (softc->blk_shift != 0) {
180459249Sphk						l = bp->bio_bcount >>
180543636Smjacob							softc->blk_shift;
180643636Smjacob					} else {
180759249Sphk						l = bp->bio_bcount /
180843636Smjacob							softc->media_blksize;
180943636Smjacob					}
181043636Smjacob					softc->blkno += (daddr_t) l;
181143636Smjacob				} else {
181243636Smjacob					softc->blkno++;
181343636Smjacob				}
181443636Smjacob			}
181539213Sgibbs		}
181646962Smjacob		/*
181746962Smjacob		 * If we had an error (immediate or pending),
181846962Smjacob		 * release the device queue now.
181946962Smjacob		 */
182046962Smjacob		if (error || (softc->flags & SA_FLAG_ERR_PENDING))
182146962Smjacob			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);
182241674Smjacob#ifdef	CAMDEBUG
182359249Sphk		if (error || bp->bio_resid) {
182441674Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
182541674Smjacob			    	  ("error %d resid %ld count %ld\n", error,
182659249Sphk				  bp->bio_resid, bp->bio_bcount));
182741674Smjacob		}
182841674Smjacob#endif
1829112006Sphk		biofinish(bp, softc->device_stats, 0);
183039213Sgibbs		break;
183139213Sgibbs	}
183239213Sgibbs	case SA_CCB_WAITING:
183339213Sgibbs	{
183439213Sgibbs		/* Caller will release the CCB */
183539213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
183639213Sgibbs		return;
183739213Sgibbs	}
183839213Sgibbs	}
183939213Sgibbs	xpt_release_ccb(done_ccb);
184039213Sgibbs}
184139213Sgibbs
184241906Smjacob/*
184341906Smjacob * Mount the tape (make sure it's ready for I/O).
184441906Smjacob */
184539213Sgibbsstatic int
1846130585Sphksamount(struct cam_periph *periph, int oflags, struct cdev *dev)
184739213Sgibbs{
184839213Sgibbs	struct	sa_softc *softc;
184939213Sgibbs	union	ccb *ccb;
185039213Sgibbs	int	error;
185139213Sgibbs
185241906Smjacob	/*
185341906Smjacob	 * oflags can be checked for 'kind' of open (read-only check) - later
185441906Smjacob	 * dev can be checked for a control-mode or compression open - later
185541906Smjacob	 */
185641906Smjacob	UNUSED_PARAMETER(oflags);
185741906Smjacob	UNUSED_PARAMETER(dev);
185841906Smjacob
185941906Smjacob
186039213Sgibbs	softc = (struct sa_softc *)periph->softc;
186139213Sgibbs
186239213Sgibbs	/*
186353259Smjacob	 * This should determine if something has happend since the last
186453259Smjacob	 * open/mount that would invalidate the mount. We do *not* want
186553259Smjacob	 * to retry this command- we just want the status. But we only
186653259Smjacob	 * do this if we're mounted already- if we're not mounted,
186753259Smjacob	 * we don't care about the unit read state and can instead use
186853259Smjacob	 * this opportunity to attempt to reserve the tape unit.
186939213Sgibbs	 */
187053259Smjacob
187153259Smjacob	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {
187253259Smjacob		ccb = cam_periph_getccb(periph, 1);
187353259Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
187479100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
187554099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1876112006Sphk		    softc->device_stats);
187753259Smjacob		QFRLS(ccb);
187853259Smjacob		if (error == ENXIO) {
187953259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
188053259Smjacob			scsi_test_unit_ready(&ccb->csio, 0, sadone,
188179100Smjacob			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
188254099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1883112006Sphk			    softc->device_stats);
188453259Smjacob			QFRLS(ccb);
188553259Smjacob		} else if (error) {
188654099Smjacob			/*
188754099Smjacob			 * We don't need to freeze the tape because we
188854099Smjacob			 * will now attempt to rewind/load it.
188954099Smjacob			 */
189053259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
1891144430Ssam			if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
1892164906Smjacob				xpt_print(periph->path,
1893164906Smjacob				    "error %d on TUR in samount\n", error);
189454099Smjacob			}
189553259Smjacob		}
189653259Smjacob	} else {
189753259Smjacob		error = sareservereleaseunit(periph, TRUE);
189853259Smjacob		if (error) {
189953259Smjacob			return (error);
190053259Smjacob		}
190153259Smjacob		ccb = cam_periph_getccb(periph, 1);
190254105Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
190379100Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, IO_TIMEOUT);
190454105Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1905112006Sphk		    softc->device_stats);
190654105Smjacob		QFRLS(ccb);
190753259Smjacob	}
190839213Sgibbs
190939213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
191053259Smjacob		struct scsi_read_block_limits_data *rblim = NULL;
191153259Smjacob		int comp_enabled, comp_supported;
191241906Smjacob		u_int8_t write_protect, guessing = 0;
191339213Sgibbs
191439213Sgibbs		/*
191539213Sgibbs		 * Clear out old state.
191639213Sgibbs		 */
191739213Sgibbs		softc->flags &= ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|
191839213Sgibbs				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|
191946962Smjacob				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);
192039213Sgibbs		softc->filemarks = 0;
192139213Sgibbs
192239213Sgibbs		/*
192343636Smjacob		 * *Very* first off, make sure we're loaded to BOT.
192439213Sgibbs		 */
192543636Smjacob		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
192654099Smjacob		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);
192754099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1928112006Sphk		    softc->device_stats);
192953259Smjacob		QFRLS(ccb);
193053259Smjacob
193143636Smjacob		/*
193244354Smjacob		 * In case this doesn't work, do a REWIND instead
193343636Smjacob		 */
193444354Smjacob		if (error) {
193553259Smjacob			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
193653259Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
193754099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1938112006Sphk				softc->device_stats);
193953259Smjacob			QFRLS(ccb);
194043636Smjacob		}
194143636Smjacob		if (error) {
194243636Smjacob			xpt_release_ccb(ccb);
194343636Smjacob			goto exit;
194443636Smjacob		}
194543636Smjacob
194643636Smjacob		/*
194753259Smjacob		 * Do a dummy test read to force access to the
194853259Smjacob		 * media so that the drive will really know what's
194954099Smjacob		 * there. We actually don't really care what the
195054099Smjacob		 * blocksize on tape is and don't expect to really
195154099Smjacob		 * read a full record.
195243636Smjacob		 */
195339213Sgibbs		rblim = (struct  scsi_read_block_limits_data *)
1954111119Simp		    malloc(8192, M_TEMP, M_WAITOK);
195553259Smjacob		if (rblim == NULL) {
1956164906Smjacob			xpt_print(periph->path, "no memory for test read\n");
195753259Smjacob			xpt_release_ccb(ccb);
195853259Smjacob			error = ENOMEM;
195953259Smjacob			goto exit;
196053259Smjacob		}
196156981Smjacob
196256981Smjacob		if ((softc->quirks & SA_QUIRK_NODREAD) == 0) {
196356981Smjacob			scsi_sa_read_write(&ccb->csio, 0, sadone,
196456981Smjacob			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,
196556981Smjacob			    (void *) rblim, 8192, SSD_FULL_SIZE,
196679100Smjacob			    IO_TIMEOUT);
196756981Smjacob			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
1968112006Sphk			    softc->device_stats);
196956981Smjacob			QFRLS(ccb);
197056981Smjacob			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
197156981Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
197274840Sken			error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
197374840Sken			    SF_NO_PRINT | SF_RETRY_UA,
1974112006Sphk			    softc->device_stats);
197556981Smjacob			QFRLS(ccb);
197656981Smjacob			if (error) {
1977164906Smjacob				xpt_print(periph->path,
1978164906Smjacob				    "unable to rewind after test read\n");
197956981Smjacob				xpt_release_ccb(ccb);
198056981Smjacob				goto exit;
198156981Smjacob			}
198253259Smjacob		}
198339213Sgibbs
198453259Smjacob		/*
198553259Smjacob		 * Next off, determine block limits.
198653259Smjacob		 */
198753259Smjacob		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
198879100Smjacob		    rblim, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
198939213Sgibbs
199074840Sken		error = cam_periph_runccb(ccb, saerror, CAM_RETRY_SELTO,
1991112006Sphk		    SF_NO_PRINT | SF_RETRY_UA, softc->device_stats);
199274840Sken
199353259Smjacob		QFRLS(ccb);
199439213Sgibbs		xpt_release_ccb(ccb);
199539213Sgibbs
199641674Smjacob		if (error != 0) {
199741674Smjacob			/*
199841674Smjacob			 * If it's less than SCSI-2, READ BLOCK LIMITS is not
199941674Smjacob			 * a MANDATORY command. Anyway- it doesn't matter-
200041674Smjacob			 * we can proceed anyway.
200141674Smjacob			 */
200241674Smjacob			softc->blk_gran = 0;
200341674Smjacob			softc->max_blk = ~0;
200441674Smjacob			softc->min_blk = 0;
200541674Smjacob		} else {
200674840Sken			if (softc->scsi_rev >= SCSI_REV_SPC) {
200741674Smjacob				softc->blk_gran = RBL_GRAN(rblim);
200841674Smjacob			} else {
200941674Smjacob				softc->blk_gran = 0;
201041674Smjacob			}
201141674Smjacob			/*
201241674Smjacob			 * We take max_blk == min_blk to mean a default to
201341674Smjacob			 * fixed mode- but note that whatever we get out of
201441674Smjacob			 * sagetparams below will actually determine whether
201541674Smjacob			 * we are actually *in* fixed mode.
201641674Smjacob			 */
201741674Smjacob			softc->max_blk = scsi_3btoul(rblim->maximum);
201841674Smjacob			softc->min_blk = scsi_2btoul(rblim->minimum);
201941674Smjacob
202041674Smjacob
202141674Smjacob		}
202241674Smjacob		/*
202341674Smjacob		 * Next, perform a mode sense to determine
202441674Smjacob		 * current density, blocksize, compression etc.
202541674Smjacob		 */
202641674Smjacob		error = sagetparams(periph, SA_PARAM_ALL,
202741674Smjacob				    &softc->media_blksize,
202841674Smjacob				    &softc->media_density,
202941674Smjacob				    &softc->media_numblks,
203041674Smjacob				    &softc->buffer_mode, &write_protect,
203141674Smjacob				    &softc->speed, &comp_supported,
203241674Smjacob				    &comp_enabled, &softc->comp_algorithm,
203341674Smjacob				    NULL);
203441674Smjacob
203541674Smjacob		if (error != 0) {
203641674Smjacob			/*
203741674Smjacob			 * We could work a little harder here. We could
203841674Smjacob			 * adjust our attempts to get information. It
203941674Smjacob			 * might be an ancient tape drive. If someone
204041674Smjacob			 * nudges us, we'll do that.
204141674Smjacob			 */
204239213Sgibbs			goto exit;
204341674Smjacob		}
204439213Sgibbs
204541906Smjacob		/*
204641906Smjacob		 * If no quirk has determined that this is a device that is
204741906Smjacob		 * preferred to be in fixed or variable mode, now is the time
204841906Smjacob		 * to find out.
204941906Smjacob	 	 */
205041906Smjacob		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) == 0) {
205141906Smjacob			guessing = 1;
205243636Smjacob			/*
205343636Smjacob			 * This could be expensive to find out. Luckily we
205443636Smjacob			 * only need to do this once. If we start out in
205543636Smjacob			 * 'default' mode, try and set ourselves to one
205643636Smjacob			 * of the densities that would determine a wad
205743636Smjacob			 * of other stuff. Go from highest to lowest.
205843636Smjacob			 */
205943636Smjacob			if (softc->media_density == SCSI_DEFAULT_DENSITY) {
206043636Smjacob				int i;
206143636Smjacob				static u_int8_t ctry[] = {
206243636Smjacob					SCSI_DENSITY_HALFINCH_PE,
206343636Smjacob					SCSI_DENSITY_HALFINCH_6250C,
206443636Smjacob					SCSI_DENSITY_HALFINCH_6250,
206543636Smjacob					SCSI_DENSITY_HALFINCH_1600,
206643636Smjacob					SCSI_DENSITY_HALFINCH_800,
206746962Smjacob					SCSI_DENSITY_QIC_4GB,
206846962Smjacob					SCSI_DENSITY_QIC_2GB,
206943636Smjacob					SCSI_DENSITY_QIC_525_320,
207043636Smjacob					SCSI_DENSITY_QIC_150,
207143636Smjacob					SCSI_DENSITY_QIC_120,
207243636Smjacob					SCSI_DENSITY_QIC_24,
207343636Smjacob					SCSI_DENSITY_QIC_11_9TRK,
207443636Smjacob					SCSI_DENSITY_QIC_11_4TRK,
207546962Smjacob					SCSI_DENSITY_QIC_1320,
207646962Smjacob					SCSI_DENSITY_QIC_3080,
207743636Smjacob					0
207843636Smjacob				};
207943636Smjacob				for (i = 0; ctry[i]; i++) {
208043636Smjacob					error = sasetparams(periph,
208143636Smjacob					    SA_PARAM_DENSITY, 0, ctry[i],
208243636Smjacob					    0, SF_NO_PRINT);
208343636Smjacob					if (error == 0) {
208443636Smjacob						softc->media_density = ctry[i];
208543636Smjacob						break;
208643636Smjacob					}
208743636Smjacob				}
208843636Smjacob			}
208941906Smjacob			switch (softc->media_density) {
209041906Smjacob			case SCSI_DENSITY_QIC_11_4TRK:
209141906Smjacob			case SCSI_DENSITY_QIC_11_9TRK:
209241906Smjacob			case SCSI_DENSITY_QIC_24:
209341906Smjacob			case SCSI_DENSITY_QIC_120:
209441906Smjacob			case SCSI_DENSITY_QIC_150:
209565861Smjacob			case SCSI_DENSITY_QIC_525_320:
209643636Smjacob			case SCSI_DENSITY_QIC_1320:
209743636Smjacob			case SCSI_DENSITY_QIC_3080:
209846962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
209943636Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
210041906Smjacob				softc->last_media_blksize = 512;
210141906Smjacob				break;
210246962Smjacob			case SCSI_DENSITY_QIC_4GB:
210346962Smjacob			case SCSI_DENSITY_QIC_2GB:
210446962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
210546962Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
210646962Smjacob				softc->last_media_blksize = 1024;
210746962Smjacob				break;
210841906Smjacob			default:
210941906Smjacob				softc->last_media_blksize =
211041906Smjacob				    softc->media_blksize;
211141906Smjacob				softc->quirks |= SA_QUIRK_VARIABLE;
211241906Smjacob				break;
211341906Smjacob			}
211441906Smjacob		}
211542563Smjacob
211641906Smjacob		/*
211741906Smjacob		 * If no quirk has determined that this is a device that needs
211841906Smjacob		 * to have 2 Filemarks at EOD, now is the time to find out.
211941906Smjacob		 */
212042563Smjacob
212142735Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0) {
212241906Smjacob			switch (softc->media_density) {
212341906Smjacob			case SCSI_DENSITY_HALFINCH_800:
212441906Smjacob			case SCSI_DENSITY_HALFINCH_1600:
212541906Smjacob			case SCSI_DENSITY_HALFINCH_6250:
212641906Smjacob			case SCSI_DENSITY_HALFINCH_6250C:
212741906Smjacob			case SCSI_DENSITY_HALFINCH_PE:
212846962Smjacob				softc->quirks &= ~SA_QUIRK_1FM;
212941906Smjacob				softc->quirks |= SA_QUIRK_2FM;
213041906Smjacob				break;
213141906Smjacob			default:
213241906Smjacob				break;
213341906Smjacob			}
213441906Smjacob		}
213541906Smjacob
213641906Smjacob		/*
213741906Smjacob		 * Now validate that some info we got makes sense.
213841906Smjacob		 */
213941674Smjacob		if ((softc->max_blk < softc->media_blksize) ||
214041674Smjacob		    (softc->min_blk > softc->media_blksize &&
214141674Smjacob		    softc->media_blksize)) {
2142164906Smjacob			xpt_print(periph->path,
2143164906Smjacob			    "BLOCK LIMITS (%d..%d) could not match current "
214441674Smjacob			    "block settings (%d)- adjusting\n", softc->min_blk,
214541674Smjacob			    softc->max_blk, softc->media_blksize);
214641674Smjacob			softc->max_blk = softc->min_blk =
214741674Smjacob			    softc->media_blksize;
214841674Smjacob		}
214941906Smjacob
215041674Smjacob		/*
215141906Smjacob		 * Now put ourselves into the right frame of mind based
215241906Smjacob		 * upon quirks...
215341906Smjacob		 */
215441906Smjacobtryagain:
215542563Smjacob		/*
215642563Smjacob		 * If we want to be in FIXED mode and our current blocksize
215742563Smjacob		 * is not equal to our last blocksize (if nonzero), try and
215842563Smjacob		 * set ourselves to this last blocksize (as the 'preferred'
215942563Smjacob		 * block size).  The initial quirkmatch at registry sets the
216042563Smjacob		 * initial 'last' blocksize. If, for whatever reason, this
216142563Smjacob		 * 'last' blocksize is zero, set the blocksize to 512,
216242563Smjacob		 * or min_blk if that's larger.
216342563Smjacob		 */
216441906Smjacob		if ((softc->quirks & SA_QUIRK_FIXED) &&
216560235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0 &&
216642563Smjacob		    (softc->media_blksize != softc->last_media_blksize)) {
216741906Smjacob			softc->media_blksize = softc->last_media_blksize;
216841906Smjacob			if (softc->media_blksize == 0) {
216942563Smjacob				softc->media_blksize = 512;
217041906Smjacob				if (softc->media_blksize < softc->min_blk) {
217141906Smjacob					softc->media_blksize = softc->min_blk;
217241906Smjacob				}
217341906Smjacob			}
217441906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
217542563Smjacob			    softc->media_blksize, 0, 0, SF_NO_PRINT);
217641906Smjacob			if (error) {
2177164906Smjacob				xpt_print(periph->path,
2178164906Smjacob				    "unable to set fixed blocksize to %d\n",
2179164906Smjacob				    softc->media_blksize);
218041906Smjacob				goto exit;
218141906Smjacob			}
218241906Smjacob		}
218341906Smjacob
218441906Smjacob		if ((softc->quirks & SA_QUIRK_VARIABLE) &&
218541906Smjacob		    (softc->media_blksize != 0)) {
218641906Smjacob			softc->last_media_blksize = softc->media_blksize;
218741906Smjacob			softc->media_blksize = 0;
218841906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
218942563Smjacob			    0, 0, 0, SF_NO_PRINT);
219041906Smjacob			if (error) {
219141906Smjacob				/*
219241906Smjacob				 * If this fails and we were guessing, just
219341906Smjacob				 * assume that we got it wrong and go try
219442563Smjacob				 * fixed block mode. Don't even check against
219542563Smjacob				 * density code at this point.
219641906Smjacob				 */
219742563Smjacob				if (guessing) {
219841906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
219941906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
220041906Smjacob					if (softc->last_media_blksize == 0)
220141906Smjacob						softc->last_media_blksize = 512;
220241906Smjacob					goto tryagain;
220341906Smjacob				}
2204164906Smjacob				xpt_print(periph->path,
2205164906Smjacob				    "unable to set variable blocksize\n");
220641906Smjacob				goto exit;
220741906Smjacob			}
220841906Smjacob		}
220941906Smjacob
221041906Smjacob		/*
221141674Smjacob		 * Now that we have the current block size,
221241674Smjacob		 * set up some parameters for sastart's usage.
221341674Smjacob		 */
221441674Smjacob		if (softc->media_blksize) {
221539213Sgibbs			softc->flags |= SA_FLAG_FIXED;
221641674Smjacob			if (powerof2(softc->media_blksize)) {
221741674Smjacob				softc->blk_shift =
221841674Smjacob				    ffs(softc->media_blksize) - 1;
221941674Smjacob				softc->blk_mask = softc->media_blksize - 1;
222039213Sgibbs			} else {
222139213Sgibbs				softc->blk_mask = ~0;
222239213Sgibbs				softc->blk_shift = 0;
222339213Sgibbs			}
222439213Sgibbs		} else {
222539213Sgibbs			/*
222641674Smjacob			 * The SCSI-3 spec allows 0 to mean "unspecified".
222741674Smjacob			 * The SCSI-1 spec allows 0 to mean 'infinite'.
222841674Smjacob			 *
222941674Smjacob			 * Either works here.
223039213Sgibbs			 */
223139213Sgibbs			if (softc->max_blk == 0) {
223239213Sgibbs				softc->max_blk = ~0;
223339213Sgibbs			}
223439213Sgibbs			softc->blk_shift = 0;
223539213Sgibbs			if (softc->blk_gran != 0) {
223639213Sgibbs				softc->blk_mask = softc->blk_gran - 1;
223739213Sgibbs			} else {
223839213Sgibbs				softc->blk_mask = 0;
223939213Sgibbs			}
224039213Sgibbs		}
224139213Sgibbs
224239213Sgibbs		if (write_protect)
224339213Sgibbs			softc->flags |= SA_FLAG_TAPE_WP;
224439213Sgibbs
224539213Sgibbs		if (comp_supported) {
224643636Smjacob			if (softc->saved_comp_algorithm == 0)
224743636Smjacob				softc->saved_comp_algorithm =
224843636Smjacob				    softc->comp_algorithm;
224946962Smjacob			softc->flags |= SA_FLAG_COMP_SUPP;
225046962Smjacob			if (comp_enabled)
225146962Smjacob				softc->flags |= SA_FLAG_COMP_ENABLED;
225239213Sgibbs		} else
225339213Sgibbs			softc->flags |= SA_FLAG_COMP_UNSUPP;
225439213Sgibbs
225560235Smjacob		if ((softc->buffer_mode == SMH_SA_BUF_MODE_NOBUF) &&
225660235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0) {
225741906Smjacob			error = sasetparams(periph, SA_PARAM_BUFF_MODE, 0,
225842563Smjacob			    0, 0, SF_NO_PRINT);
225983473Smjacob			if (error == 0) {
226041906Smjacob				softc->buffer_mode = SMH_SA_BUF_MODE_SIBUF;
226183473Smjacob			} else {
2262164906Smjacob				xpt_print(periph->path,
2263164906Smjacob				    "unable to set buffered mode\n");
226483473Smjacob			}
226560235Smjacob			error = 0;	/* not an error */
226641906Smjacob		}
226739213Sgibbs
226839213Sgibbs
226944354Smjacob		if (error == 0) {
227041906Smjacob			softc->flags |= SA_FLAG_TAPE_MOUNTED;
227144354Smjacob		}
227239213Sgibbsexit:
227339213Sgibbs		if (rblim != NULL)
227439213Sgibbs			free(rblim, M_TEMP);
227539213Sgibbs
227643636Smjacob		if (error != 0) {
227743636Smjacob			softc->dsreg = MTIO_DSREG_NIL;
227844354Smjacob		} else {
227944354Smjacob			softc->fileno = softc->blkno = 0;
228043636Smjacob			softc->dsreg = MTIO_DSREG_REST;
228144354Smjacob		}
228251875Smjacob#ifdef	SA_1FM_AT_EOD
228351875Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0)
228451875Smjacob			softc->quirks |= SA_QUIRK_1FM;
228551875Smjacob#else
228646962Smjacob		if ((softc->quirks & SA_QUIRK_1FM) == 0)
228743636Smjacob			softc->quirks |= SA_QUIRK_2FM;
228843636Smjacob#endif
228939213Sgibbs	} else
229039213Sgibbs		xpt_release_ccb(ccb);
229139213Sgibbs
229253259Smjacob	/*
229353259Smjacob	 * If we return an error, we're not mounted any more,
229453259Smjacob	 * so release any device reservation.
229553259Smjacob	 */
229653259Smjacob	if (error != 0) {
229753259Smjacob		(void) sareservereleaseunit(periph, FALSE);
229882575Smjacob	} else {
229982575Smjacob		/*
230082575Smjacob		 * Clear I/O residual.
230182575Smjacob		 */
230282575Smjacob		softc->last_io_resid = 0;
230382575Smjacob		softc->last_ctl_resid = 0;
230453259Smjacob	}
230554099Smjacob	return (error);
230639213Sgibbs}
230739213Sgibbs
230868114Smjacob/*
230968114Smjacob * How many filemarks do we need to write if we were to terminate the
231068114Smjacob * tape session right now? Note that this can be a negative number
231168114Smjacob */
231268114Smjacob
231339213Sgibbsstatic int
231468114Smjacobsamarkswanted(struct cam_periph *periph)
231539213Sgibbs{
231639213Sgibbs	int	markswanted;
231739213Sgibbs	struct	sa_softc *softc;
231839213Sgibbs
231939213Sgibbs	softc = (struct sa_softc *)periph->softc;
232039213Sgibbs	markswanted = 0;
232139213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) != 0) {
232239213Sgibbs		markswanted++;
232346962Smjacob		if (softc->quirks & SA_QUIRK_2FM)
232439213Sgibbs			markswanted++;
232539213Sgibbs	}
232668114Smjacob	markswanted -= softc->filemarks;
232768114Smjacob	return (markswanted);
232868114Smjacob}
232939213Sgibbs
233068114Smjacobstatic int
233168114Smjacobsacheckeod(struct cam_periph *periph)
233268114Smjacob{
233368114Smjacob	int	error;
233468114Smjacob	int	markswanted;
233568114Smjacob
233668114Smjacob	markswanted = samarkswanted(periph);
233768114Smjacob
233868114Smjacob	if (markswanted > 0) {
233941906Smjacob		error = sawritefilemarks(periph, markswanted, FALSE);
234039213Sgibbs	} else {
234139213Sgibbs		error = 0;
234239213Sgibbs	}
234339213Sgibbs	return (error);
234439213Sgibbs}
234539213Sgibbs
234639213Sgibbsstatic int
234746962Smjacobsaerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
234839213Sgibbs{
234946962Smjacob	static const char *toobig =
235098449Srobert	    "%d-byte tape record bigger than supplied buffer\n";
235139213Sgibbs	struct	cam_periph *periph;
235239213Sgibbs	struct	sa_softc *softc;
235339213Sgibbs	struct	ccb_scsiio *csio;
235439213Sgibbs	struct	scsi_sense_data *sense;
235553259Smjacob	u_int32_t resid = 0;
235653259Smjacob	int32_t	info = 0;
235782575Smjacob	cam_status status;
235882575Smjacob	int error_code, sense_key, asc, ascq, error, aqvalid;
235939213Sgibbs
236039213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
236139213Sgibbs	softc = (struct sa_softc *)periph->softc;
236239213Sgibbs	csio = &ccb->csio;
236339213Sgibbs	sense = &csio->sense_data;
236439213Sgibbs	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
236582575Smjacob	aqvalid = sense->extra_len >= 6;
236639213Sgibbs	error = 0;
236746962Smjacob
236882575Smjacob	status = csio->ccb_h.status & CAM_STATUS_MASK;
236982575Smjacob
237046962Smjacob	/*
237182575Smjacob	 * Calculate/latch up, any residuals... We do this in a funny 2-step
237282575Smjacob	 * so we can print stuff here if we have CAM_DEBUG enabled for this
237382575Smjacob	 * unit.
237446962Smjacob	 */
237582575Smjacob	if (status == CAM_SCSI_STATUS_ERROR) {
237639213Sgibbs		if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
237746962Smjacob			info = (int32_t) scsi_4btoul(sense->info);
237839213Sgibbs			resid = info;
237939213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0)
238039213Sgibbs				resid *= softc->media_blksize;
238139213Sgibbs		} else {
238239213Sgibbs			resid = csio->dxfer_len;
238339213Sgibbs			info = resid;
238441674Smjacob			if ((softc->flags & SA_FLAG_FIXED) != 0) {
238541674Smjacob				if (softc->media_blksize)
238641674Smjacob					info /= softc->media_blksize;
238741674Smjacob			}
238839213Sgibbs		}
238971082Smjacob		if (CCB_Type(csio) == SA_CCB_BUFFER_IO) {
239041948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,
239141948Smjacob			    sizeof (struct scsi_sense_data));
239242009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,
239342009Smjacob			    (int) csio->cdb_len);
239441948Smjacob			softc->last_io_resid = resid;
239571268Smjacob			softc->last_resid_was_io = 1;
239641948Smjacob		} else {
239741948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,
239841948Smjacob			    sizeof (struct scsi_sense_data));
239942009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,
240042009Smjacob			    (int) csio->cdb_len);
240141948Smjacob			softc->last_ctl_resid = resid;
240271268Smjacob			softc->last_resid_was_io = 0;
240341948Smjacob		}
240482575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("CDB[0]=0x%x Key 0x%x "
240582575Smjacob		    "ASC/ASCQ 0x%x/0x%x CAM STATUS 0x%x flags 0x%x resid %d "
240682575Smjacob		    "dxfer_len %d\n", csio->cdb_io.cdb_bytes[0] & 0xff,
240782575Smjacob		    sense_key, asc, ascq, status,
240882575Smjacob		    sense->flags & ~SSD_KEY_RESERVED, resid, csio->dxfer_len));
240953259Smjacob	} else {
241082575Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
241182575Smjacob		    ("Cam Status 0x%x\n", status));
241241948Smjacob	}
241341948Smjacob
241482575Smjacob	switch (status) {
241582575Smjacob	case CAM_REQ_CMP:
241682575Smjacob		return (0);
241782575Smjacob	case CAM_SCSI_STATUS_ERROR:
241882575Smjacob		/*
241982575Smjacob		 * If a read/write command, we handle it here.
242082575Smjacob		 */
242182575Smjacob		if (CCB_Type(csio) != SA_CCB_WAITING) {
242282575Smjacob			break;
242382575Smjacob		}
242482575Smjacob		/*
242582575Smjacob		 * If this was just EOM/EOP, Filemark, Setmark or ILI detected
242682575Smjacob		 * on a non read/write command, we assume it's not an error
242782575Smjacob		 * and propagate the residule and return.
242882575Smjacob		 */
242982575Smjacob		if ((aqvalid && asc == 0 && ascq > 0 && ascq <= 5) ||
243082575Smjacob		    (aqvalid == 0 && sense_key == SSD_KEY_NO_SENSE)) {
243182575Smjacob			csio->resid = resid;
243282575Smjacob			QFRLS(ccb);
243382575Smjacob			return (0);
243482575Smjacob		}
243582575Smjacob		/*
243682575Smjacob		 * Otherwise, we let the common code handle this.
243782575Smjacob		 */
243846962Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
243941948Smjacob
244046962Smjacob	/*
244182575Smjacob	 * XXX: To Be Fixed
244282575Smjacob	 * We cannot depend upon CAM honoring retry counts for these.
244346962Smjacob	 */
244482575Smjacob	case CAM_SCSI_BUS_RESET:
244582575Smjacob	case CAM_BDR_SENT:
244682575Smjacob		if (ccb->ccb_h.retry_count <= 0) {
244782575Smjacob			return (EIO);
244882575Smjacob		}
244982575Smjacob		/* FALLTHROUGH */
245082575Smjacob	default:
245182575Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
245246962Smjacob	}
245346962Smjacob
245446962Smjacob	/*
245546962Smjacob	 * Handle filemark, end of tape, mismatched record sizes....
245646962Smjacob	 * From this point out, we're only handling read/write cases.
245746962Smjacob	 * Handle writes && reads differently.
245846962Smjacob	 */
245982575Smjacob
246046962Smjacob	if (csio->cdb_io.cdb_bytes[0] == SA_WRITE) {
246182575Smjacob		if (sense_key == SSD_KEY_VOLUME_OVERFLOW) {
246239213Sgibbs			csio->resid = resid;
246382575Smjacob			error = ENOSPC;
246482575Smjacob		} else if (sense->flags & SSD_EOM) {
246582575Smjacob			softc->flags |= SA_FLAG_EOM_PENDING;
246682575Smjacob			/*
246782575Smjacob			 * Grotesque as it seems, the few times
246882575Smjacob			 * I've actually seen a non-zero resid,
246982575Smjacob			 * the tape drive actually lied and had
2470124645Sjohan			 * written all the data!.
247182575Smjacob			 */
247282575Smjacob			csio->resid = 0;
247339213Sgibbs		}
247446962Smjacob	} else {
247582575Smjacob		csio->resid = resid;
247646962Smjacob		if (sense_key == SSD_KEY_BLANK_CHECK) {
247782575Smjacob			if (softc->quirks & SA_QUIRK_1FM) {
247882575Smjacob				error = 0;
247946962Smjacob				softc->flags |= SA_FLAG_EOM_PENDING;
248046962Smjacob			} else {
248146962Smjacob				error = EIO;
248246962Smjacob			}
248382575Smjacob		} else if (sense->flags & SSD_FILEMARK) {
248482575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
248546962Smjacob				error = -1;
248639213Sgibbs				softc->flags |= SA_FLAG_EOF_PENDING;
248746962Smjacob			}
248846962Smjacob			/*
248946962Smjacob			 * Unconditionally, if we detected a filemark on a read,
249046962Smjacob			 * mark that we've run moved a file ahead.
249146962Smjacob			 */
249243636Smjacob			if (softc->fileno != (daddr_t) -1) {
249343636Smjacob				softc->fileno++;
249443636Smjacob				softc->blkno = 0;
249571082Smjacob				csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED;
249643636Smjacob			}
249739213Sgibbs		}
249846962Smjacob	}
249982575Smjacob
250046962Smjacob	/*
250146962Smjacob	 * Incorrect Length usually applies to read, but can apply to writes.
250246962Smjacob	 */
250346962Smjacob	if (error == 0 && (sense->flags & SSD_ILI)) {
250446962Smjacob		if (info < 0) {
2505164906Smjacob			xpt_print(csio->ccb_h.path, toobig,
2506164906Smjacob			    csio->dxfer_len - info);
250746962Smjacob			csio->resid = csio->dxfer_len;
250846962Smjacob			error = EIO;
250946962Smjacob		} else {
251046962Smjacob			csio->resid = resid;
251182575Smjacob			if (softc->flags & SA_FLAG_FIXED) {
251282575Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
251346962Smjacob			}
251446962Smjacob			/*
251546962Smjacob			 * Bump the block number if we hadn't seen a filemark.
251646962Smjacob			 * Do this independent of errors (we've moved anyway).
251746962Smjacob			 */
251846962Smjacob			if ((sense->flags & SSD_FILEMARK) == 0) {
251946962Smjacob				if (softc->blkno != (daddr_t) -1) {
252046962Smjacob					softc->blkno++;
252171082Smjacob					csio->ccb_h.ccb_pflags |=
252271082Smjacob					   SA_POSITION_UPDATED;
252339213Sgibbs				}
252439213Sgibbs			}
252539213Sgibbs		}
252639213Sgibbs	}
252782575Smjacob
252882575Smjacob	if (error <= 0) {
252982575Smjacob		/*
253082575Smjacob		 * Unfreeze the queue if frozen as we're not returning anything
253182575Smjacob		 * to our waiters that would indicate an I/O error has occurred
253282575Smjacob		 * (yet).
253382575Smjacob		 */
253482575Smjacob		QFRLS(ccb);
253582575Smjacob		error = 0;
253680575Smjacob	}
253782575Smjacob	return (error);
253839213Sgibbs}
253939213Sgibbs
254039213Sgibbsstatic int
254139213Sgibbssagetparams(struct cam_periph *periph, sa_params params_to_get,
254239213Sgibbs	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,
254339213Sgibbs	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,
254439213Sgibbs	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,
254546962Smjacob	    sa_comp_t *tcs)
254639213Sgibbs{
254739213Sgibbs	union ccb *ccb;
254839213Sgibbs	void *mode_buffer;
254939213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
255039213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
255139213Sgibbs	int mode_buffer_len;
255239213Sgibbs	struct sa_softc *softc;
255346962Smjacob	u_int8_t cpage;
255439213Sgibbs	int error;
255539213Sgibbs	cam_status status;
255639213Sgibbs
255739213Sgibbs	softc = (struct sa_softc *)periph->softc;
255846962Smjacob	ccb = cam_periph_getccb(periph, 1);
255971082Smjacob	if (softc->quirks & SA_QUIRK_NO_CPAGE)
256071082Smjacob		cpage = SA_DEVICE_CONFIGURATION_PAGE;
256171082Smjacob	else
256271082Smjacob		cpage = SA_DATA_COMPRESSION_PAGE;
256339213Sgibbs
256439213Sgibbsretry:
256539213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
256639213Sgibbs
256739213Sgibbs	if (params_to_get & SA_PARAM_COMPRESSION) {
256839213Sgibbs		if (softc->quirks & SA_QUIRK_NOCOMP) {
256939213Sgibbs			*comp_supported = FALSE;
257039213Sgibbs			params_to_get &= ~SA_PARAM_COMPRESSION;
257139213Sgibbs		} else
257246962Smjacob			mode_buffer_len += sizeof (sa_comp_t);
257339213Sgibbs	}
257454099Smjacob
2575111119Simp	mode_buffer = malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);
257639213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
257739213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
257839213Sgibbs
257942716Smjacob	/* it is safe to retry this */
258042716Smjacob	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
258142716Smjacob	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?
258246962Smjacob	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,
258379100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
258439213Sgibbs
258554099Smjacob	error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2586112006Sphk	    softc->device_stats);
258754099Smjacob	QFRLS(ccb);
258839213Sgibbs
258939213Sgibbs	status = ccb->ccb_h.status & CAM_STATUS_MASK;
259039213Sgibbs
259142716Smjacob	if (error == EINVAL && (params_to_get & SA_PARAM_COMPRESSION) != 0) {
259239213Sgibbs		/*
259346962Smjacob		 * Hmm. Let's see if we can try another page...
259446962Smjacob		 * If we've already done that, give up on compression
259546962Smjacob		 * for this device and remember this for the future
259646962Smjacob		 * and attempt the request without asking for compression
259746962Smjacob		 * info.
259839213Sgibbs		 */
259946962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
260046962Smjacob			cpage = SA_DEVICE_CONFIGURATION_PAGE;
260146962Smjacob			goto retry;
260246962Smjacob		}
260339213Sgibbs		softc->quirks |= SA_QUIRK_NOCOMP;
260439213Sgibbs		free(mode_buffer, M_TEMP);
260539213Sgibbs		goto retry;
260646962Smjacob	} else if (status == CAM_SCSI_STATUS_ERROR) {
260746962Smjacob		/* Tell the user about the fatal error. */
260846962Smjacob		scsi_sense_print(&ccb->csio);
260946962Smjacob		goto sagetparamsexit;
261046962Smjacob	}
261139213Sgibbs
261246962Smjacob	/*
261346962Smjacob	 * If the user only wants the compression information, and
261446962Smjacob	 * the device doesn't send back the block descriptor, it's
261546962Smjacob	 * no big deal.  If the user wants more than just
261646962Smjacob	 * compression, though, and the device doesn't pass back the
261746962Smjacob	 * block descriptor, we need to send another mode sense to
261846962Smjacob	 * get the block descriptor.
261946962Smjacob	 */
262046962Smjacob	if ((mode_hdr->blk_desc_len == 0) &&
262146962Smjacob	    (params_to_get & SA_PARAM_COMPRESSION) &&
262246962Smjacob	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {
262339213Sgibbs
262439213Sgibbs		/*
262546962Smjacob		 * Decrease the mode buffer length by the size of
262646962Smjacob		 * the compression page, to make sure the data
262746962Smjacob		 * there doesn't get overwritten.
262839213Sgibbs		 */
262946962Smjacob		mode_buffer_len -= sizeof (sa_comp_t);
263039213Sgibbs
263146962Smjacob		/*
263246962Smjacob		 * Now move the compression page that we presumably
263346962Smjacob		 * got back down the memory chunk a little bit so
263446962Smjacob		 * it doesn't get spammed.
263546962Smjacob		 */
263654099Smjacob		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));
263754099Smjacob		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));
263839213Sgibbs
263946962Smjacob		/*
264046962Smjacob		 * Now, we issue another mode sense and just ask
264146962Smjacob		 * for the block descriptor, etc.
264246962Smjacob		 */
264339213Sgibbs
264446962Smjacob		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
264546962Smjacob		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,
264679100Smjacob		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE,
264779100Smjacob		    SCSIOP_TIMEOUT);
264839213Sgibbs
264954099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
2650112006Sphk		    softc->device_stats);
265154099Smjacob		QFRLS(ccb);
265239213Sgibbs
265346962Smjacob		if (error != 0)
265446962Smjacob			goto sagetparamsexit;
265546962Smjacob	}
265639213Sgibbs
265746962Smjacob	if (params_to_get & SA_PARAM_BLOCKSIZE)
265846962Smjacob		*blocksize = scsi_3btoul(mode_blk->blklen);
265939213Sgibbs
266046962Smjacob	if (params_to_get & SA_PARAM_NUMBLOCKS)
266146962Smjacob		*numblocks = scsi_3btoul(mode_blk->nblocks);
266239213Sgibbs
266346962Smjacob	if (params_to_get & SA_PARAM_BUFF_MODE)
266446962Smjacob		*buff_mode = mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;
266539213Sgibbs
266646962Smjacob	if (params_to_get & SA_PARAM_DENSITY)
266746962Smjacob		*density = mode_blk->density;
266839213Sgibbs
266946962Smjacob	if (params_to_get & SA_PARAM_WP)
267046962Smjacob		*write_protect = (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;
267139213Sgibbs
267246962Smjacob	if (params_to_get & SA_PARAM_SPEED)
267346962Smjacob		*speed = mode_hdr->dev_spec & SMH_SA_SPEED_MASK;
267439213Sgibbs
267546962Smjacob	if (params_to_get & SA_PARAM_COMPRESSION) {
267654099Smjacob		sa_comp_t *ntcs = (sa_comp_t *) &mode_blk[1];
267746962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
267846962Smjacob			struct scsi_data_compression_page *cp = &ntcs->dcomp;
267946962Smjacob			*comp_supported =
268046962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;
268146962Smjacob			*comp_enabled =
268246962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;
268346962Smjacob			*comp_algorithm = scsi_4btoul(cp->comp_algorithm);
268446962Smjacob		} else {
268546962Smjacob			struct scsi_dev_conf_page *cp = &ntcs->dconf;
268646962Smjacob			/*
268746962Smjacob			 * We don't really know whether this device supports
268846962Smjacob			 * Data Compression if the the algorithm field is
268946962Smjacob			 * zero. Just say we do.
269046962Smjacob			 */
269146962Smjacob			*comp_supported = TRUE;
269246962Smjacob			*comp_enabled =
269346962Smjacob			    (cp->sel_comp_alg != SA_COMP_NONE)? TRUE : FALSE;
269446962Smjacob			*comp_algorithm = cp->sel_comp_alg;
269541906Smjacob		}
269646962Smjacob		if (tcs != NULL)
269754099Smjacob			bcopy(ntcs, tcs, sizeof (sa_comp_t));
269839213Sgibbs	}
269939213Sgibbs
270046962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
270146962Smjacob		int idx;
270246962Smjacob		char *xyz = mode_buffer;
270346962Smjacob		xpt_print_path(periph->path);
270446962Smjacob		printf("Mode Sense Data=");
270546962Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
270646962Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
270746962Smjacob		printf("\n");
270846962Smjacob	}
270946962Smjacob
271039213Sgibbssagetparamsexit:
271139213Sgibbs
271239213Sgibbs	xpt_release_ccb(ccb);
271339213Sgibbs	free(mode_buffer, M_TEMP);
271454099Smjacob	return (error);
271539213Sgibbs}
271639213Sgibbs
271739213Sgibbs/*
271839213Sgibbs * The purpose of this function is to set one of four different parameters
271939213Sgibbs * for a tape drive:
272039213Sgibbs *	- blocksize
272139213Sgibbs *	- density
272239213Sgibbs *	- compression / compression algorithm
272339213Sgibbs *	- buffering mode
272439213Sgibbs *
272539213Sgibbs * The assumption is that this will be called from saioctl(), and therefore
272639213Sgibbs * from a process context.  Thus the waiting malloc calls below.  If that
272739213Sgibbs * assumption ever changes, the malloc calls should be changed to be
272839213Sgibbs * NOWAIT mallocs.
272939213Sgibbs *
273039213Sgibbs * Any or all of the four parameters may be set when this function is
273139213Sgibbs * called.  It should handle setting more than one parameter at once.
273239213Sgibbs */
273339213Sgibbsstatic int
273439213Sgibbssasetparams(struct cam_periph *periph, sa_params params_to_set,
273546962Smjacob	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,
273642563Smjacob	    u_int32_t sense_flags)
273739213Sgibbs{
273839213Sgibbs	struct sa_softc *softc;
273939213Sgibbs	u_int32_t current_blocksize;
274046962Smjacob	u_int32_t current_calg;
274139213Sgibbs	u_int8_t current_density;
274239213Sgibbs	u_int8_t current_speed;
274339213Sgibbs	int comp_enabled, comp_supported;
274439213Sgibbs	void *mode_buffer;
274539213Sgibbs	int mode_buffer_len;
274639213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
274739213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
274846962Smjacob	sa_comp_t *ccomp, *cpage;
274939213Sgibbs	int buff_mode;
275046962Smjacob	union ccb *ccb = NULL;
275139213Sgibbs	int error;
275239213Sgibbs
275339213Sgibbs	softc = (struct sa_softc *)periph->softc;
275439213Sgibbs
2755111119Simp	ccomp = malloc(sizeof (sa_comp_t), M_TEMP, M_WAITOK);
275639213Sgibbs
275739213Sgibbs	/*
275839213Sgibbs	 * Since it doesn't make sense to set the number of blocks, or
275939213Sgibbs	 * write protection, we won't try to get the current value.  We
276039213Sgibbs	 * always want to get the blocksize, so we can set it back to the
276139213Sgibbs	 * proper value.
276239213Sgibbs	 */
276346962Smjacob	error = sagetparams(periph,
276446962Smjacob	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,
276546962Smjacob	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,
276646962Smjacob	    &current_speed, &comp_supported, &comp_enabled,
276746962Smjacob	    &current_calg, ccomp);
276839213Sgibbs
276939213Sgibbs	if (error != 0) {
277046962Smjacob		free(ccomp, M_TEMP);
277154099Smjacob		return (error);
277239213Sgibbs	}
277339213Sgibbs
277439213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
277539213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION)
277646962Smjacob		mode_buffer_len += sizeof (sa_comp_t);
277739213Sgibbs
2778111119Simp	mode_buffer = malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);
277939213Sgibbs
278039213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
278139213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
278239213Sgibbs
278353259Smjacob	ccb = cam_periph_getccb(periph, 1);
278453259Smjacob
278553259Smjacobretry:
278653259Smjacob
278739213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION) {
278853259Smjacob		if (mode_blk) {
278953259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
279053259Smjacob		} else {
279153259Smjacob			cpage = (sa_comp_t *)&mode_hdr[1];
279253259Smjacob		}
279346962Smjacob		bcopy(ccomp, cpage, sizeof (sa_comp_t));
279454099Smjacob		cpage->hdr.pagecode &= ~0x80;
279539213Sgibbs	} else
279646962Smjacob		cpage = NULL;
279739213Sgibbs
279839213Sgibbs	/*
279939213Sgibbs	 * If the caller wants us to set the blocksize, use the one they
280039213Sgibbs	 * pass in.  Otherwise, use the blocksize we got back from the
280139213Sgibbs	 * mode select above.
280239213Sgibbs	 */
280353259Smjacob	if (mode_blk) {
280453259Smjacob		if (params_to_set & SA_PARAM_BLOCKSIZE)
280553259Smjacob			scsi_ulto3b(blocksize, mode_blk->blklen);
280653259Smjacob		else
280753259Smjacob			scsi_ulto3b(current_blocksize, mode_blk->blklen);
280839213Sgibbs
280953259Smjacob		/*
281053259Smjacob		 * Set density if requested, else preserve old density.
281153259Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
281253259Smjacob		 * devices, else density we've latched up in our softc.
281353259Smjacob		 */
281453259Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
281553259Smjacob			mode_blk->density = density;
281653259Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
281753259Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
281853259Smjacob		} else {
281953259Smjacob			mode_blk->density = softc->media_density;
282053259Smjacob		}
282141674Smjacob	}
282239213Sgibbs
282339213Sgibbs	/*
282439213Sgibbs	 * For mode selects, these two fields must be zero.
282539213Sgibbs	 */
282639213Sgibbs	mode_hdr->data_length = 0;
282739213Sgibbs	mode_hdr->medium_type = 0;
282839213Sgibbs
282939213Sgibbs	/* set the speed to the current value */
283039213Sgibbs	mode_hdr->dev_spec = current_speed;
283139213Sgibbs
2832120019Smjacob	/* if set, set single-initiator buffering mode */
2833120019Smjacob	if (softc->buffer_mode == SMH_SA_BUF_MODE_SIBUF) {
2834120019Smjacob		mode_hdr->dev_spec |= SMH_SA_BUF_MODE_SIBUF;
2835120019Smjacob	}
283639213Sgibbs
283753259Smjacob	if (mode_blk)
283853259Smjacob		mode_hdr->blk_desc_len = sizeof(struct scsi_mode_blk_desc);
283953259Smjacob	else
284053259Smjacob		mode_hdr->blk_desc_len = 0;
284139213Sgibbs
284239213Sgibbs	/*
284339213Sgibbs	 * First, if the user wants us to set the compression algorithm or
284439213Sgibbs	 * just turn compression on, check to make sure that this drive
284539213Sgibbs	 * supports compression.
284639213Sgibbs	 */
284746962Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
284839213Sgibbs		/*
284939213Sgibbs		 * If the compression algorithm is 0, disable compression.
285039213Sgibbs		 * If the compression algorithm is non-zero, enable
285139213Sgibbs		 * compression and set the compression type to the
285239213Sgibbs		 * specified compression algorithm, unless the algorithm is
285339213Sgibbs		 * MT_COMP_ENABLE.  In that case, we look at the
285439213Sgibbs		 * compression algorithm that is currently set and if it is
285539213Sgibbs		 * non-zero, we leave it as-is.  If it is zero, and we have
285639213Sgibbs		 * saved a compression algorithm from a time when
285739213Sgibbs		 * compression was enabled before, set the compression to
285839213Sgibbs		 * the saved value.
285939213Sgibbs		 */
286054099Smjacob		switch (ccomp->hdr.pagecode & ~0x80) {
2861115660Smjacob		case SA_DEVICE_CONFIGURATION_PAGE:
2862115660Smjacob		{
2863115660Smjacob			struct scsi_dev_conf_page *dcp = &cpage->dconf;
2864115660Smjacob			if (calg == 0) {
2865115660Smjacob				dcp->sel_comp_alg = SA_COMP_NONE;
2866115660Smjacob				break;
2867115660Smjacob			}
2868115660Smjacob			if (calg != MT_COMP_ENABLE) {
2869115660Smjacob				dcp->sel_comp_alg = calg;
2870115660Smjacob			} else if (dcp->sel_comp_alg == SA_COMP_NONE &&
2871115660Smjacob			    softc->saved_comp_algorithm != 0) {
2872115660Smjacob				dcp->sel_comp_alg = softc->saved_comp_algorithm;
2873115660Smjacob			}
2874115660Smjacob			break;
2875115660Smjacob		}
287646962Smjacob		case SA_DATA_COMPRESSION_PAGE:
287746962Smjacob		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {
287846962Smjacob			struct scsi_data_compression_page *dcp = &cpage->dcomp;
287946962Smjacob			if (calg == 0) {
288054099Smjacob				/*
288154099Smjacob				 * Disable compression, but leave the
288254099Smjacob				 * decompression and the capability bit
288354099Smjacob				 * alone.
288454099Smjacob				 */
288554099Smjacob				dcp->dce_and_dcc = SA_DCP_DCC;
288654099Smjacob				dcp->dde_and_red |= SA_DCP_DDE;
288746962Smjacob				break;
288846962Smjacob			}
288953259Smjacob			/* enable compression && decompression */
289054099Smjacob			dcp->dce_and_dcc = SA_DCP_DCE | SA_DCP_DCC;
289154099Smjacob			dcp->dde_and_red |= SA_DCP_DDE;
289253259Smjacob			/*
289353259Smjacob			 * If there, use compression algorithm from caller.
289453259Smjacob			 * Otherwise, if there's a saved compression algorithm
289553259Smjacob			 * and there is no current algorithm, use the saved
289653259Smjacob			 * algorithm. Else parrot back what we got and hope
289753259Smjacob			 * for the best.
289853259Smjacob			 */
289946962Smjacob			if (calg != MT_COMP_ENABLE) {
290046962Smjacob				scsi_ulto4b(calg, dcp->comp_algorithm);
290153259Smjacob				scsi_ulto4b(calg, dcp->decomp_algorithm);
290246962Smjacob			} else if (scsi_4btoul(dcp->comp_algorithm) == 0 &&
290346962Smjacob			    softc->saved_comp_algorithm != 0) {
290439213Sgibbs				scsi_ulto4b(softc->saved_comp_algorithm,
290546962Smjacob				    dcp->comp_algorithm);
290653259Smjacob				scsi_ulto4b(softc->saved_comp_algorithm,
290753259Smjacob				    dcp->decomp_algorithm);
290839213Sgibbs			}
290946962Smjacob			break;
291039213Sgibbs		}
2911115660Smjacob		/*
2912115660Smjacob		 * Compression does not appear to be supported-
2913115660Smjacob		 * at least via the DATA COMPRESSION page. It
2914115660Smjacob		 * would be too much to ask us to believe that
2915115660Smjacob		 * the page itself is supported, but incorrectly
2916115660Smjacob		 * reports an ability to manipulate data compression,
2917115660Smjacob		 * so we'll assume that this device doesn't support
2918115660Smjacob		 * compression. We can just fall through for that.
2919115660Smjacob		 */
2920115660Smjacob		/* FALLTHROUGH */
292146962Smjacob		default:
292246962Smjacob			/*
292354099Smjacob			 * The drive doesn't seem to support compression,
292446962Smjacob			 * so turn off the set compression bit.
292546962Smjacob			 */
292646962Smjacob			params_to_set &= ~SA_PARAM_COMPRESSION;
2927164906Smjacob			xpt_print(periph->path,
2928164906Smjacob			    "device does not seem to support compression\n");
292954099Smjacob
293046962Smjacob			/*
293146962Smjacob			 * If that was the only thing the user wanted us to set,
293246962Smjacob			 * clean up allocated resources and return with
293346962Smjacob			 * 'operation not supported'.
293446962Smjacob			 */
293546962Smjacob			if (params_to_set == SA_PARAM_NONE) {
293646962Smjacob				free(mode_buffer, M_TEMP);
293754099Smjacob				xpt_release_ccb(ccb);
293854099Smjacob				return (ENODEV);
293946962Smjacob			}
294046962Smjacob
294146962Smjacob			/*
294246962Smjacob			 * That wasn't the only thing the user wanted us to set.
294346962Smjacob			 * So, decrease the stated mode buffer length by the
294446962Smjacob			 * size of the compression mode page.
294546962Smjacob			 */
294646962Smjacob			mode_buffer_len -= sizeof(sa_comp_t);
294746962Smjacob		}
294839213Sgibbs	}
294939213Sgibbs
295046962Smjacob	/* It is safe to retry this operation */
295146962Smjacob	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
295246962Smjacob	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,
295379100Smjacob	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
295441674Smjacob
295546962Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
2956112006Sphk	    sense_flags, softc->device_stats);
295754099Smjacob	QFRLS(ccb);
295839213Sgibbs
295946962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
296041906Smjacob		int idx;
296141674Smjacob		char *xyz = mode_buffer;
296241674Smjacob		xpt_print_path(periph->path);
296342009Smjacob		printf("Err%d, Mode Select Data=", error);
296441906Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
296542009Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
296641674Smjacob		printf("\n");
296741674Smjacob	}
296841674Smjacob
296939213Sgibbs
297053259Smjacob	if (error) {
297139213Sgibbs		/*
297253259Smjacob		 * If we can, try without setting density/blocksize.
297353259Smjacob		 */
297453259Smjacob		if (mode_blk) {
297553259Smjacob			if ((params_to_set &
297653259Smjacob			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) == 0) {
297753259Smjacob				mode_blk = NULL;
297853259Smjacob				goto retry;
297953259Smjacob			}
298053259Smjacob		} else {
298153259Smjacob			mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
298253259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
298353259Smjacob		}
298453259Smjacob
298553259Smjacob		/*
298639213Sgibbs		 * If we were setting the blocksize, and that failed, we
298739213Sgibbs		 * want to set it to its original value.  If we weren't
298839213Sgibbs		 * setting the blocksize, we don't want to change it.
298939213Sgibbs		 */
299039213Sgibbs		scsi_ulto3b(current_blocksize, mode_blk->blklen);
299139213Sgibbs
299239213Sgibbs		/*
299341674Smjacob		 * Set density if requested, else preserve old density.
299441674Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
299541674Smjacob		 * devices, else density we've latched up in our softc.
299639213Sgibbs		 */
299741674Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
299839213Sgibbs			mode_blk->density = current_density;
299941674Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
300041674Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
300141674Smjacob		} else {
300241674Smjacob			mode_blk->density = softc->media_density;
300341674Smjacob		}
300439213Sgibbs
300539213Sgibbs		if (params_to_set & SA_PARAM_COMPRESSION)
300646962Smjacob			bcopy(ccomp, cpage, sizeof (sa_comp_t));
300739213Sgibbs
300839213Sgibbs		/*
300939213Sgibbs		 * The retry count is the only CCB field that might have been
301039213Sgibbs		 * changed that we care about, so reset it back to 1.
301139213Sgibbs		 */
301239213Sgibbs		ccb->ccb_h.retry_count = 1;
301354099Smjacob		cam_periph_runccb(ccb, saerror, 0, sense_flags,
3014112006Sphk		    softc->device_stats);
301554099Smjacob		QFRLS(ccb);
301639213Sgibbs	}
301739213Sgibbs
301853259Smjacob	xpt_release_ccb(ccb);
301953259Smjacob
302046962Smjacob	if (ccomp != NULL)
302146962Smjacob		free(ccomp, M_TEMP);
302239213Sgibbs
302341948Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
302441948Smjacob		if (error) {
302541948Smjacob			softc->flags &= ~SA_FLAG_COMP_ENABLED;
302646962Smjacob			/*
302746962Smjacob			 * Even if we get an error setting compression,
302846962Smjacob			 * do not say that we don't support it. We could
302946962Smjacob			 * have been wrong, or it may be media specific.
303046962Smjacob			 *	softc->flags &= ~SA_FLAG_COMP_SUPP;
303146962Smjacob			 */
303241948Smjacob			softc->saved_comp_algorithm = softc->comp_algorithm;
303341948Smjacob			softc->comp_algorithm = 0;
303441948Smjacob		} else {
303541948Smjacob			softc->flags |= SA_FLAG_COMP_ENABLED;
303646962Smjacob			softc->comp_algorithm = calg;
303741948Smjacob		}
303841948Smjacob	}
303941948Smjacob
304039213Sgibbs	free(mode_buffer, M_TEMP);
304154099Smjacob	return (error);
304239213Sgibbs}
304339213Sgibbs
304439213Sgibbsstatic void
304539213Sgibbssaprevent(struct cam_periph *periph, int action)
304639213Sgibbs{
304739213Sgibbs	struct	sa_softc *softc;
304839213Sgibbs	union	ccb *ccb;
304942735Smjacob	int	error, sf;
305039213Sgibbs
305139213Sgibbs	softc = (struct sa_softc *)periph->softc;
305239213Sgibbs
305342735Smjacob	if ((action == PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) == 0)
305439213Sgibbs		return;
305542735Smjacob	if ((action == PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) != 0)
305642735Smjacob		return;
305739213Sgibbs
305856981Smjacob	/*
305956981Smjacob	 * We can be quiet about illegal requests.
306056981Smjacob	 */
306156981Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
306242735Smjacob		sf = 0;
306356981Smjacob	} else
306442735Smjacob		sf = SF_QUIET_IR;
306542735Smjacob
306642716Smjacob	ccb = cam_periph_getccb(periph, 1);
306739213Sgibbs
306842716Smjacob	/* It is safe to retry this operation */
306942716Smjacob	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,
307079100Smjacob	    SSD_FULL_SIZE, SCSIOP_TIMEOUT);
307139213Sgibbs
3072112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, sf, softc->device_stats);
307353522Smjacob	QFRLS(ccb);
307439213Sgibbs	if (error == 0) {
307539213Sgibbs		if (action == PR_ALLOW)
307639213Sgibbs			softc->flags &= ~SA_FLAG_TAPE_LOCKED;
307739213Sgibbs		else
307839213Sgibbs			softc->flags |= SA_FLAG_TAPE_LOCKED;
307939213Sgibbs	}
308039213Sgibbs
308139213Sgibbs	xpt_release_ccb(ccb);
308239213Sgibbs}
308339213Sgibbs
308439213Sgibbsstatic int
308539213Sgibbssarewind(struct cam_periph *periph)
308639213Sgibbs{
308739213Sgibbs	union	ccb *ccb;
308839213Sgibbs	struct	sa_softc *softc;
308939213Sgibbs	int	error;
309039213Sgibbs
309139213Sgibbs	softc = (struct sa_softc *)periph->softc;
309239213Sgibbs
309346962Smjacob	ccb = cam_periph_getccb(periph, 1);
309439213Sgibbs
309542716Smjacob	/* It is safe to retry this operation */
309646962Smjacob	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
309753259Smjacob	    SSD_FULL_SIZE, REWIND_TIMEOUT);
309839213Sgibbs
309943636Smjacob	softc->dsreg = MTIO_DSREG_REW;
3100112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
310143636Smjacob	softc->dsreg = MTIO_DSREG_REST;
310239213Sgibbs
310339213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
310442716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
310542716Smjacob
310639213Sgibbs	xpt_release_ccb(ccb);
310743636Smjacob	if (error == 0)
310843636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
310943636Smjacob	else
311043636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
311139213Sgibbs	return (error);
311239213Sgibbs}
311339213Sgibbs
311439213Sgibbsstatic int
311539213Sgibbssaspace(struct cam_periph *periph, int count, scsi_space_code code)
311639213Sgibbs{
311739213Sgibbs	union	ccb *ccb;
311839213Sgibbs	struct	sa_softc *softc;
311939213Sgibbs	int	error;
312039213Sgibbs
312139213Sgibbs	softc = (struct sa_softc *)periph->softc;
312239213Sgibbs
312346962Smjacob	ccb = cam_periph_getccb(periph, 1);
312439213Sgibbs
312542716Smjacob	/* This cannot be retried */
312639213Sgibbs
312742716Smjacob	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,
312853259Smjacob	    SSD_FULL_SIZE, SPACE_TIMEOUT);
312939213Sgibbs
313071087Smjacob	/*
313171087Smjacob	 * Clear residual because we will be using it.
313271087Smjacob	 */
313371087Smjacob	softc->last_ctl_resid = 0;
313471087Smjacob
313543636Smjacob	softc->dsreg = (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;
3136112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
313743636Smjacob	softc->dsreg = MTIO_DSREG_REST;
313842716Smjacob
313939213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
314042716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
314142716Smjacob
314239213Sgibbs	xpt_release_ccb(ccb);
314343636Smjacob
314442716Smjacob	/*
314543636Smjacob	 * If a spacing operation has failed, we need to invalidate
314643636Smjacob	 * this mount.
314743636Smjacob	 *
314843636Smjacob	 * If the spacing operation was setmarks or to end of recorded data,
314943636Smjacob	 * we no longer know our relative position.
315043636Smjacob	 *
315171087Smjacob	 * If the spacing operations was spacing files in reverse, we
315271087Smjacob	 * take account of the residual, but still check against less
315371087Smjacob	 * than zero- if we've gone negative, we must have hit BOT.
315471087Smjacob	 *
315571087Smjacob	 * If the spacing operations was spacing records in reverse and
315671087Smjacob	 * we have a residual, we've either hit BOT or hit a filemark.
315771087Smjacob	 * In the former case, we know our new record number (0). In
315871087Smjacob	 * the latter case, we have absolutely no idea what the real
315971087Smjacob	 * record number is- we've stopped between the end of the last
316071087Smjacob	 * record in the previous file and the filemark that stopped
316171087Smjacob	 * our spacing backwards.
316242716Smjacob	 */
316343636Smjacob	if (error) {
316443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
316543636Smjacob	} else if (code == SS_SETMARKS || code == SS_EOD) {
316643636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
316743636Smjacob	} else if (code == SS_FILEMARKS && softc->fileno != (daddr_t) -1) {
316871087Smjacob		softc->fileno += (count - softc->last_ctl_resid);
316971087Smjacob		if (softc->fileno < 0)	/* we must of hit BOT */
317071087Smjacob			softc->fileno = 0;
317143636Smjacob		softc->blkno = 0;
317243636Smjacob	} else if (code == SS_BLOCKS && softc->blkno != (daddr_t) -1) {
317371087Smjacob		softc->blkno += (count - softc->last_ctl_resid);
317471087Smjacob		if (count < 0) {
317571087Smjacob			if (softc->last_ctl_resid || softc->blkno < 0) {
317671087Smjacob				if (softc->fileno == 0) {
317771087Smjacob					softc->blkno = 0;
317871087Smjacob				} else {
317971087Smjacob					softc->blkno = (daddr_t) -1;
318071087Smjacob				}
318171087Smjacob			}
318271087Smjacob		}
318343636Smjacob	}
318439213Sgibbs	return (error);
318539213Sgibbs}
318639213Sgibbs
318739213Sgibbsstatic int
318839213Sgibbssawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)
318939213Sgibbs{
319039213Sgibbs	union	ccb *ccb;
319139213Sgibbs	struct	sa_softc *softc;
319271087Smjacob	int	error, nwm = 0;
319339213Sgibbs
319439213Sgibbs	softc = (struct sa_softc *)periph->softc;
3195154360Smjacob	if (softc->open_rdonly)
3196154360Smjacob		return (EBADF);
319739213Sgibbs
319846962Smjacob	ccb = cam_periph_getccb(periph, 1);
319971087Smjacob	/*
320071087Smjacob	 * Clear residual because we will be using it.
320171087Smjacob	 */
320271087Smjacob	softc->last_ctl_resid = 0;
320339213Sgibbs
320443636Smjacob	softc->dsreg = MTIO_DSREG_FMK;
320542716Smjacob	/* this *must* not be retried */
320642716Smjacob	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,
320779100Smjacob	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, IO_TIMEOUT);
320843636Smjacob	softc->dsreg = MTIO_DSREG_REST;
320939213Sgibbs
321043636Smjacob
3211112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
321239213Sgibbs
321339213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
321442716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
321539213Sgibbs
321641918Smjacob	if (error == 0 && nmarks) {
321741918Smjacob		struct sa_softc *softc = (struct sa_softc *)periph->softc;
321871087Smjacob		nwm = nmarks - softc->last_ctl_resid;
321971087Smjacob		softc->filemarks += nwm;
322039213Sgibbs	}
322171087Smjacob
322241918Smjacob	xpt_release_ccb(ccb);
322343636Smjacob
322443636Smjacob	/*
322543636Smjacob	 * Update relative positions (if we're doing that).
322643636Smjacob	 */
322743636Smjacob	if (error) {
322843636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
322943636Smjacob	} else if (softc->fileno != (daddr_t) -1) {
323071087Smjacob		softc->fileno += nwm;
323143636Smjacob		softc->blkno = 0;
323243636Smjacob	}
323341918Smjacob	return (error);
323441918Smjacob}
323539213Sgibbs
323641918Smjacobstatic int
323741918Smjacobsardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
323841918Smjacob{
323941918Smjacob	struct scsi_tape_position_data loc;
324041918Smjacob	union ccb *ccb;
324146962Smjacob	struct sa_softc *softc = (struct sa_softc *)periph->softc;
324241918Smjacob	int error;
324341918Smjacob
324441918Smjacob	/*
324571082Smjacob	 * We try and flush any buffered writes here if we were writing
324671082Smjacob	 * and we're trying to get hardware block position. It eats
324771082Smjacob	 * up performance substantially, but I'm wary of drive firmware.
324846962Smjacob	 *
324971082Smjacob	 * I think that *logical* block position is probably okay-
325071082Smjacob	 * but hardware block position might have to wait for data
325171082Smjacob	 * to hit media to be valid. Caveat Emptor.
325241918Smjacob	 */
325341918Smjacob
325471082Smjacob	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {
325546962Smjacob		error = sawritefilemarks(periph, 0, 0);
325646962Smjacob		if (error && error != EACCES)
325746962Smjacob			return (error);
325846962Smjacob	}
325941918Smjacob
326066678Smjacob	ccb = cam_periph_getccb(periph, 1);
326141918Smjacob	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
326279100Smjacob	    hard, &loc, SSD_FULL_SIZE, SCSIOP_TIMEOUT);
326343636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
3264112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
326543636Smjacob	softc->dsreg = MTIO_DSREG_REST;
326641918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
326741918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
326841918Smjacob
326941918Smjacob	if (error == 0) {
327041918Smjacob		if (loc.flags & SA_RPOS_UNCERTAIN) {
327141918Smjacob			error = EINVAL;		/* nothing is certain */
327241918Smjacob		} else {
327341918Smjacob			*blkptr = scsi_4btoul(loc.firstblk);
327441918Smjacob		}
327541918Smjacob	}
327641918Smjacob
327739213Sgibbs	xpt_release_ccb(ccb);
327839213Sgibbs	return (error);
327939213Sgibbs}
328039213Sgibbs
328139213Sgibbsstatic int
328241918Smjacobsasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
328341918Smjacob{
328441918Smjacob	union ccb *ccb;
328541918Smjacob	struct sa_softc *softc;
328641918Smjacob	int error;
328741918Smjacob
328841918Smjacob	/*
328946962Smjacob	 * We used to try and flush any buffered writes here.
329046962Smjacob	 * Now we push this onto user applications to either
329146962Smjacob	 * flush the pending writes themselves (via a zero count
329246962Smjacob	 * WRITE FILEMARKS command) or they can trust their tape
329346962Smjacob	 * drive to do this correctly for them.
329446962Smjacob 	 */
329541918Smjacob
329641918Smjacob	softc = (struct sa_softc *)periph->softc;
329746962Smjacob	ccb = cam_periph_getccb(periph, 1);
329841918Smjacob
329943636Smjacob
330041918Smjacob	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
330179100Smjacob	    hard, *blkptr, SSD_FULL_SIZE, SPACE_TIMEOUT);
330243636Smjacob
330379100Smjacob
330443636Smjacob	softc->dsreg = MTIO_DSREG_POS;
3305112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
330643636Smjacob	softc->dsreg = MTIO_DSREG_REST;
330741918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
330841918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
330941918Smjacob	xpt_release_ccb(ccb);
331041918Smjacob	/*
331146962Smjacob	 * Note relative file && block number position as now unknown.
331241918Smjacob	 */
331343636Smjacob	softc->fileno = softc->blkno = (daddr_t) -1;
331441918Smjacob	return (error);
331541918Smjacob}
331641918Smjacob
331741918Smjacobstatic int
331839213Sgibbssaretension(struct cam_periph *periph)
331939213Sgibbs{
332039213Sgibbs	union ccb *ccb;
332139213Sgibbs	struct sa_softc *softc;
332239213Sgibbs	int error;
332339213Sgibbs
332439213Sgibbs	softc = (struct sa_softc *)periph->softc;
332539213Sgibbs
332646962Smjacob	ccb = cam_periph_getccb(periph, 1);
332739213Sgibbs
332842716Smjacob	/* It is safe to retry this operation */
332942716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
333053259Smjacob	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);
333139213Sgibbs
333243636Smjacob	softc->dsreg = MTIO_DSREG_TEN;
3333112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
333443636Smjacob	softc->dsreg = MTIO_DSREG_REST;
333539213Sgibbs
333639213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
333742716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
333839213Sgibbs	xpt_release_ccb(ccb);
333943636Smjacob	if (error == 0)
334043636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
334143636Smjacob	else
334243636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
334354099Smjacob	return (error);
334439213Sgibbs}
334539213Sgibbs
334639213Sgibbsstatic int
334739213Sgibbssareservereleaseunit(struct cam_periph *periph, int reserve)
334839213Sgibbs{
334939213Sgibbs	union ccb *ccb;
335039213Sgibbs	struct sa_softc *softc;
335154099Smjacob	int error;
335239213Sgibbs
335342009Smjacob	softc = (struct sa_softc *)periph->softc;
335442716Smjacob	ccb = cam_periph_getccb(periph,  1);
335539213Sgibbs
335642716Smjacob	/* It is safe to retry this operation */
335754099Smjacob	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
335879100Smjacob	    FALSE,  0, SSD_FULL_SIZE,  SCSIOP_TIMEOUT, reserve);
335943636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
336054099Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
3361112006Sphk	    SF_RETRY_UA | SF_NO_PRINT, softc->device_stats);
336243636Smjacob	softc->dsreg = MTIO_DSREG_REST;
336354099Smjacob	QFRLS(ccb);
336439213Sgibbs	xpt_release_ccb(ccb);
336539213Sgibbs
336641674Smjacob	/*
336741674Smjacob	 * If the error was Illegal Request, then the device doesn't support
336841674Smjacob	 * RESERVE/RELEASE. This is not an error.
336941674Smjacob	 */
337042009Smjacob	if (error == EINVAL) {
337141674Smjacob		error = 0;
337242009Smjacob	}
337341674Smjacob
337439213Sgibbs	return (error);
337539213Sgibbs}
337639213Sgibbs
337739213Sgibbsstatic int
337839213Sgibbssaloadunload(struct cam_periph *periph, int load)
337939213Sgibbs{
338039213Sgibbs	union	ccb *ccb;
338139213Sgibbs	struct	sa_softc *softc;
338239213Sgibbs	int	error;
338339213Sgibbs
338439213Sgibbs	softc = (struct sa_softc *)periph->softc;
338539213Sgibbs
338646962Smjacob	ccb = cam_periph_getccb(periph, 1);
338739213Sgibbs
338842716Smjacob	/* It is safe to retry this operation */
338942716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
339054099Smjacob	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);
339139213Sgibbs
339243636Smjacob	softc->dsreg = (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;
3393112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
339443636Smjacob	softc->dsreg = MTIO_DSREG_REST;
339554099Smjacob	QFRLS(ccb);
339639213Sgibbs	xpt_release_ccb(ccb);
339743636Smjacob
339843636Smjacob	if (error || load == 0)
339943636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
340043636Smjacob	else if (error == 0)
340143636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
340239213Sgibbs	return (error);
340339213Sgibbs}
340439213Sgibbs
340539213Sgibbsstatic int
340639213Sgibbssaerase(struct cam_periph *periph, int longerase)
340739213Sgibbs{
340839213Sgibbs
340939213Sgibbs	union	ccb *ccb;
341039213Sgibbs	struct	sa_softc *softc;
341139213Sgibbs	int error;
341239213Sgibbs
341339213Sgibbs	softc = (struct sa_softc *)periph->softc;
3414154360Smjacob	if (softc->open_rdonly)
3415154360Smjacob		return (EBADF);
341639213Sgibbs
341746962Smjacob	ccb = cam_periph_getccb(periph, 1);
341839213Sgibbs
341943636Smjacob	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,
342053259Smjacob	    SSD_FULL_SIZE, ERASE_TIMEOUT);
342139213Sgibbs
342243636Smjacob	softc->dsreg = MTIO_DSREG_ZER;
3423112006Sphk	error = cam_periph_runccb(ccb, saerror, 0, 0, softc->device_stats);
342443636Smjacob	softc->dsreg = MTIO_DSREG_REST;
342539213Sgibbs
342639213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
342743636Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
342839213Sgibbs	xpt_release_ccb(ccb);
342939213Sgibbs	return (error);
343039213Sgibbs}
343139213Sgibbs
343255205Speter#endif /* _KERNEL */
343339213Sgibbs
343439213Sgibbs/*
343539213Sgibbs * Read tape block limits command.
343639213Sgibbs */
343739213Sgibbsvoid
343839213Sgibbsscsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,
343939213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
344039213Sgibbs		   u_int8_t tag_action,
344139213Sgibbs		   struct scsi_read_block_limits_data *rlimit_buf,
344239213Sgibbs		   u_int8_t sense_len, u_int32_t timeout)
344339213Sgibbs{
344439213Sgibbs	struct scsi_read_block_limits *scsi_cmd;
344539213Sgibbs
344646962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
344746962Smjacob	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,
344846962Smjacob	     sizeof(*scsi_cmd), timeout);
344939213Sgibbs
345039213Sgibbs	scsi_cmd = (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;
345139213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
345239213Sgibbs	scsi_cmd->opcode = READ_BLOCK_LIMITS;
345339213Sgibbs}
345439213Sgibbs
345539213Sgibbsvoid
345639213Sgibbsscsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,
345739213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
345839213Sgibbs		   u_int8_t tag_action, int readop, int sli,
345939213Sgibbs		   int fixed, u_int32_t length, u_int8_t *data_ptr,
346039213Sgibbs		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)
346139213Sgibbs{
346239213Sgibbs	struct scsi_sa_rw *scsi_cmd;
346339213Sgibbs
346439213Sgibbs	scsi_cmd = (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;
346539213Sgibbs	scsi_cmd->opcode = readop ? SA_READ : SA_WRITE;
346639213Sgibbs	scsi_cmd->sli_fixed = 0;
346739213Sgibbs	if (sli && readop)
346839213Sgibbs		scsi_cmd->sli_fixed |= SAR_SLI;
346939213Sgibbs	if (fixed)
347039213Sgibbs		scsi_cmd->sli_fixed |= SARW_FIXED;
347139213Sgibbs	scsi_ulto3b(length, scsi_cmd->length);
347239213Sgibbs	scsi_cmd->control = 0;
347339213Sgibbs
347446962Smjacob	cam_fill_csio(csio, retries, cbfcnp, readop ? CAM_DIR_IN : CAM_DIR_OUT,
347546962Smjacob	    tag_action, data_ptr, dxfer_len, sense_len,
347646962Smjacob	    sizeof(*scsi_cmd), timeout);
347739213Sgibbs}
347839213Sgibbs
347939213Sgibbsvoid
348039213Sgibbsscsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,
348139213Sgibbs		 void (*cbfcnp)(struct cam_periph *, union ccb *),
348239213Sgibbs		 u_int8_t tag_action, int immediate, int eot,
348339213Sgibbs		 int reten, int load, u_int8_t sense_len,
348439213Sgibbs		 u_int32_t timeout)
348539213Sgibbs{
348639213Sgibbs	struct scsi_load_unload *scsi_cmd;
348739213Sgibbs
348839213Sgibbs	scsi_cmd = (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;
348939213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
349039213Sgibbs	scsi_cmd->opcode = LOAD_UNLOAD;
349139213Sgibbs	if (immediate)
349239213Sgibbs		scsi_cmd->immediate = SLU_IMMED;
349339213Sgibbs	if (eot)
349439213Sgibbs		scsi_cmd->eot_reten_load |= SLU_EOT;
349539213Sgibbs	if (reten)
349639213Sgibbs		scsi_cmd->eot_reten_load |= SLU_RETEN;
349739213Sgibbs	if (load)
349839213Sgibbs		scsi_cmd->eot_reten_load |= SLU_LOAD;
349939213Sgibbs
350046962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
350146962Smjacob	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);
350239213Sgibbs}
350339213Sgibbs
350439213Sgibbsvoid
350539213Sgibbsscsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,
350639213Sgibbs	    void (*cbfcnp)(struct cam_periph *, union ccb *),
350739213Sgibbs	    u_int8_t tag_action, int immediate, u_int8_t sense_len,
350839213Sgibbs	    u_int32_t timeout)
350939213Sgibbs{
351039213Sgibbs	struct scsi_rewind *scsi_cmd;
351139213Sgibbs
351239213Sgibbs	scsi_cmd = (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;
351339213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
351439213Sgibbs	scsi_cmd->opcode = REWIND;
351539213Sgibbs	if (immediate)
351639213Sgibbs		scsi_cmd->immediate = SREW_IMMED;
351739213Sgibbs
351846962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
351946962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
352039213Sgibbs}
352139213Sgibbs
352239213Sgibbsvoid
352339213Sgibbsscsi_space(struct ccb_scsiio *csio, u_int32_t retries,
352439213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
352539213Sgibbs	   u_int8_t tag_action, scsi_space_code code,
352639213Sgibbs	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)
352739213Sgibbs{
352839213Sgibbs	struct scsi_space *scsi_cmd;
352939213Sgibbs
353039213Sgibbs	scsi_cmd = (struct scsi_space *)&csio->cdb_io.cdb_bytes;
353139213Sgibbs	scsi_cmd->opcode = SPACE;
353239213Sgibbs	scsi_cmd->code = code;
353339213Sgibbs	scsi_ulto3b(count, scsi_cmd->count);
353439213Sgibbs	scsi_cmd->control = 0;
353539213Sgibbs
353646962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
353746962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
353839213Sgibbs}
353939213Sgibbs
354039213Sgibbsvoid
354139213Sgibbsscsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,
354239213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
354339213Sgibbs		     u_int8_t tag_action, int immediate, int setmark,
354439213Sgibbs		     u_int32_t num_marks, u_int8_t sense_len,
354539213Sgibbs		     u_int32_t timeout)
354639213Sgibbs{
354739213Sgibbs	struct scsi_write_filemarks *scsi_cmd;
354839213Sgibbs
354939213Sgibbs	scsi_cmd = (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;
355039213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
355139213Sgibbs	scsi_cmd->opcode = WRITE_FILEMARKS;
355239213Sgibbs	if (immediate)
355339213Sgibbs		scsi_cmd->byte2 |= SWFMRK_IMMED;
355439213Sgibbs	if (setmark)
355539213Sgibbs		scsi_cmd->byte2 |= SWFMRK_WSMK;
355639213Sgibbs
355739213Sgibbs	scsi_ulto3b(num_marks, scsi_cmd->num_marks);
355839213Sgibbs
355946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
356046962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
356139213Sgibbs}
356239213Sgibbs
356339213Sgibbs/*
356439213Sgibbs * The reserve and release unit commands differ only by their opcodes.
356539213Sgibbs */
356639213Sgibbsvoid
356739213Sgibbsscsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,
356839213Sgibbs			  void (*cbfcnp)(struct cam_periph *, union ccb *),
356939213Sgibbs			  u_int8_t tag_action, int third_party,
357039213Sgibbs			  int third_party_id, u_int8_t sense_len,
357139213Sgibbs			  u_int32_t timeout, int reserve)
357239213Sgibbs{
357339213Sgibbs	struct scsi_reserve_release_unit *scsi_cmd;
357439213Sgibbs
357539213Sgibbs	scsi_cmd = (struct scsi_reserve_release_unit *)&csio->cdb_io.cdb_bytes;
357639213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
357739213Sgibbs
357839213Sgibbs	if (reserve)
357939213Sgibbs		scsi_cmd->opcode = RESERVE_UNIT;
358039213Sgibbs	else
358139213Sgibbs		scsi_cmd->opcode = RELEASE_UNIT;
358239213Sgibbs
358339213Sgibbs	if (third_party) {
358439213Sgibbs		scsi_cmd->lun_thirdparty |= SRRU_3RD_PARTY;
358539213Sgibbs		scsi_cmd->lun_thirdparty |=
358639213Sgibbs			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);
358739213Sgibbs	}
358839213Sgibbs
358946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
359046962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
359139213Sgibbs}
359239213Sgibbs
359339213Sgibbsvoid
359439213Sgibbsscsi_erase(struct ccb_scsiio *csio, u_int32_t retries,
359539213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
359639213Sgibbs	   u_int8_t tag_action, int immediate, int long_erase,
359739213Sgibbs	   u_int8_t sense_len, u_int32_t timeout)
359839213Sgibbs{
359939213Sgibbs	struct scsi_erase *scsi_cmd;
360039213Sgibbs
360139213Sgibbs	scsi_cmd = (struct scsi_erase *)&csio->cdb_io.cdb_bytes;
360239213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
360339213Sgibbs
360439213Sgibbs	scsi_cmd->opcode = ERASE;
360539213Sgibbs
360639213Sgibbs	if (immediate)
360739213Sgibbs		scsi_cmd->lun_imm_long |= SE_IMMED;
360839213Sgibbs
360939213Sgibbs	if (long_erase)
361039213Sgibbs		scsi_cmd->lun_imm_long |= SE_LONG;
361139213Sgibbs
361246962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
361346962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
361439213Sgibbs}
361541918Smjacob
361641918Smjacob/*
361741918Smjacob * Read Tape Position command.
361841918Smjacob */
361941918Smjacobvoid
362041918Smjacobscsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,
362141918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
362241918Smjacob		   u_int8_t tag_action, int hardsoft,
362341918Smjacob		   struct scsi_tape_position_data *sbp,
362441918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
362541918Smjacob{
362641918Smjacob	struct scsi_tape_read_position *scmd;
362741918Smjacob
362841918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
362941918Smjacob	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);
363041918Smjacob	scmd = (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;
363141918Smjacob	bzero(scmd, sizeof(*scmd));
363241918Smjacob	scmd->opcode = READ_POSITION;
363341918Smjacob	scmd->byte1 = hardsoft;
363441918Smjacob}
363541918Smjacob
363641918Smjacob/*
363741918Smjacob * Set Tape Position command.
363841918Smjacob */
363941918Smjacobvoid
364041918Smjacobscsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,
364141918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
364241918Smjacob		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,
364341918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
364441918Smjacob{
364541918Smjacob	struct scsi_tape_locate *scmd;
364641918Smjacob
364741918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
364841918Smjacob	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);
364941918Smjacob	scmd = (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;
365041918Smjacob	bzero(scmd, sizeof(*scmd));
365141918Smjacob	scmd->opcode = LOCATE;
365241918Smjacob	if (hardsoft)
365341918Smjacob		scmd->byte1 |= SA_SPOS_BT;
365441918Smjacob	scsi_ulto4b(blkno, scmd->blkaddr);
365541918Smjacob}
3656