scsi_sa.c revision 71268
139213Sgibbs/*
250477Speter * $FreeBSD: head/sys/cam/scsi/scsi_sa.c 71268 2001-01-19 21:08:15Z mjacob $
346962Smjacob *
439213Sgibbs * Implementation of SCSI Sequential Access Peripheral driver for CAM.
539213Sgibbs *
658251Smjacob * Copyright (c) 1999, 2000 Matthew Jacob
739213Sgibbs * All rights reserved.
839213Sgibbs *
939213Sgibbs * Redistribution and use in source and binary forms, with or without
1039213Sgibbs * modification, are permitted provided that the following conditions
1139213Sgibbs * are met:
1239213Sgibbs * 1. Redistributions of source code must retain the above copyright
1339213Sgibbs *    notice, this list of conditions, and the following disclaimer,
1439213Sgibbs *    without modification, immediately at the beginning of the file.
1539213Sgibbs * 2. The name of the author may not be used to endorse or promote products
1639213Sgibbs *    derived from this software without specific prior written permission.
1739213Sgibbs *
1839213Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1939213Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2039213Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2139213Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2239213Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2339213Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2439213Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2539213Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2639213Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2739213Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2839213Sgibbs * SUCH DAMAGE.
2939213Sgibbs *
3039213Sgibbs */
3139213Sgibbs
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>
3960041Sphk#include <sys/bio.h>
4039213Sgibbs#include <sys/malloc.h>
4139213Sgibbs#include <sys/mtio.h>
4265061Speter#ifdef _KERNEL
4339213Sgibbs#include <sys/conf.h>
4465061Speter#endif
4539213Sgibbs#include <sys/devicestat.h>
4639213Sgibbs#include <machine/limits.h>
4739213Sgibbs
4855205Speter#ifndef _KERNEL
4939213Sgibbs#include <stdio.h>
5039213Sgibbs#include <string.h>
5139213Sgibbs#endif
5239213Sgibbs
5339213Sgibbs#include <cam/cam.h>
5439213Sgibbs#include <cam/cam_ccb.h>
5539213Sgibbs#include <cam/cam_extend.h>
5639213Sgibbs#include <cam/cam_periph.h>
5739213Sgibbs#include <cam/cam_xpt_periph.h>
5839213Sgibbs#include <cam/cam_debug.h>
5939213Sgibbs
6039213Sgibbs#include <cam/scsi/scsi_all.h>
6139213Sgibbs#include <cam/scsi/scsi_message.h>
6239213Sgibbs#include <cam/scsi/scsi_sa.h>
6339213Sgibbs
6455205Speter#ifdef _KERNEL
6539213Sgibbs
6639884Sken#include <opt_sa.h>
6739884Sken
6839884Sken#ifndef SA_SPACE_TIMEOUT
6939884Sken#define SA_SPACE_TIMEOUT	1 * 60
7039884Sken#endif
7139884Sken#ifndef SA_REWIND_TIMEOUT
7239884Sken#define SA_REWIND_TIMEOUT	2 * 60
7339884Sken#endif
7439884Sken#ifndef SA_ERASE_TIMEOUT
7539884Sken#define SA_ERASE_TIMEOUT	4 * 60
7639884Sken#endif
7753259Smjacob
7854099Smjacob#define	REWIND_TIMEOUT		(SA_REWIND_TIMEOUT * 60 * 1000)
7954099Smjacob#define	ERASE_TIMEOUT		(SA_ERASE_TIMEOUT * 60 * 1000)
8054099Smjacob#define	SPACE_TIMEOUT		(SA_SPACE_TIMEOUT * 60 * 1000)
8153259Smjacob
8243636Smjacob/*
8351875Smjacob * Additional options that can be set for config: SA_1FM_AT_EOT
8443636Smjacob */
8553259Smjacob
8641906Smjacob#ifndef	UNUSED_PARAMETER
8741906Smjacob#define	UNUSED_PARAMETER(x)	x = x
8841906Smjacob#endif
8941906Smjacob
9053259Smjacob#define	QFRLS(ccb)	\
9153259Smjacob	if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) != 0)	\
9253259Smjacob		cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE)
9353259Smjacob
9453259Smjacob/*
9553259Smjacob * Driver states
9653259Smjacob */
9753259Smjacob
9853259Smjacob
9939213Sgibbstypedef enum {
10046962Smjacob	SA_STATE_NORMAL, SA_STATE_ABNORMAL
10139213Sgibbs} sa_state;
10239213Sgibbs
10371082Smjacob#define ccb_pflags	ppriv_field0
10471082Smjacob#define ccb_bp	 	ppriv_ptr1
10539213Sgibbs
10671082Smjacob#define	SA_CCB_BUFFER_IO	0x0
10771082Smjacob#define	SA_CCB_WAITING		0x1
10871082Smjacob#define	SA_CCB_TYPEMASK		0x1
10971082Smjacob#define	SA_POSITION_UPDATED	0x2
11039213Sgibbs
11171082Smjacob#define	Set_CCB_Type(x, type)				\
11271082Smjacob	x->ccb_h.ccb_pflags &= ~SA_CCB_TYPEMASK;	\
11371082Smjacob	x->ccb_h.ccb_pflags |= type
11471082Smjacob
11571082Smjacob#define	CCB_Type(x)	(x->ccb_h.ccb_pflags & SA_CCB_TYPEMASK)
11671082Smjacob
11771082Smjacob
11871082Smjacob
11939213Sgibbstypedef enum {
12039213Sgibbs	SA_FLAG_OPEN		= 0x0001,
12139213Sgibbs	SA_FLAG_FIXED		= 0x0002,
12239213Sgibbs	SA_FLAG_TAPE_LOCKED	= 0x0004,
12339213Sgibbs	SA_FLAG_TAPE_MOUNTED	= 0x0008,
12439213Sgibbs	SA_FLAG_TAPE_WP		= 0x0010,
12539213Sgibbs	SA_FLAG_TAPE_WRITTEN	= 0x0020,
12641906Smjacob	SA_FLAG_EOM_PENDING	= 0x0040,
12741906Smjacob	SA_FLAG_EIO_PENDING	= 0x0080,
12841906Smjacob	SA_FLAG_EOF_PENDING	= 0x0100,
12939213Sgibbs	SA_FLAG_ERR_PENDING	= (SA_FLAG_EOM_PENDING|SA_FLAG_EIO_PENDING|
13039213Sgibbs				   SA_FLAG_EOF_PENDING),
13141906Smjacob	SA_FLAG_INVALID		= 0x0200,
13241906Smjacob	SA_FLAG_COMP_ENABLED	= 0x0400,
13346962Smjacob	SA_FLAG_COMP_SUPP	= 0x0800,
13446962Smjacob	SA_FLAG_COMP_UNSUPP	= 0x1000,
13546962Smjacob	SA_FLAG_TAPE_FROZEN	= 0x2000
13639213Sgibbs} sa_flags;
13739213Sgibbs
13839213Sgibbstypedef enum {
13939213Sgibbs	SA_MODE_REWIND		= 0x00,
14039213Sgibbs	SA_MODE_NOREWIND	= 0x01,
14139213Sgibbs	SA_MODE_OFFLINE		= 0x02
14239213Sgibbs} sa_mode;
14339213Sgibbs
14439213Sgibbstypedef enum {
14539213Sgibbs	SA_PARAM_NONE		= 0x00,
14639213Sgibbs	SA_PARAM_BLOCKSIZE	= 0x01,
14739213Sgibbs	SA_PARAM_DENSITY	= 0x02,
14839213Sgibbs	SA_PARAM_COMPRESSION	= 0x04,
14939213Sgibbs	SA_PARAM_BUFF_MODE	= 0x08,
15039213Sgibbs	SA_PARAM_NUMBLOCKS	= 0x10,
15139213Sgibbs	SA_PARAM_WP		= 0x20,
15239213Sgibbs	SA_PARAM_SPEED		= 0x40,
15339213Sgibbs	SA_PARAM_ALL		= 0x7f
15439213Sgibbs} sa_params;
15539213Sgibbs
15639213Sgibbstypedef enum {
15739213Sgibbs	SA_QUIRK_NONE		= 0x00,
15860235Smjacob	SA_QUIRK_NOCOMP		= 0x01,	/* Can't deal with compression at all */
15960235Smjacob	SA_QUIRK_FIXED		= 0x02,	/* Force fixed mode */
16060235Smjacob	SA_QUIRK_VARIABLE	= 0x04,	/* Force variable mode */
16143636Smjacob	SA_QUIRK_2FM		= 0x08,	/* Needs Two File Marks at EOD */
16256981Smjacob	SA_QUIRK_1FM		= 0x10,	/* No more than 1 File Mark at EOD */
16360235Smjacob	SA_QUIRK_NODREAD	= 0x20,	/* Don't try and dummy read density */
16471082Smjacob	SA_QUIRK_NO_MODESEL	= 0x40,	/* Don't do mode select at all */
16571082Smjacob	SA_QUIRK_NO_CPAGE	= 0x80	/* Don't use DEVICE COMPRESSION page */
16639213Sgibbs} sa_quirks;
16739213Sgibbs
16853283Smjacob/* units are bits 4-7, 16-21 (1024 units) */
16953283Smjacob#define SAUNIT(DEV) \
17053283Smjacob	(((minor(DEV) & 0xF0) >> 4) |  ((minor(DEV) & 0x3f0000) >> 16))
17153283Smjacob
17253283Smjacob#define SAMODE(z) ((minor(z) & 0x3))
17353283Smjacob#define SADENSITY(z) (((minor(z) >> 2) & 0x3))
17453283Smjacob#define	SA_IS_CTRL(z) (minor(z) & (1 << 29))
17553283Smjacob
17653259Smjacob#define SA_NOT_CTLDEV	0
17753259Smjacob#define SA_CTLDEV	1
17853259Smjacob
17953259Smjacob#define SA_ATYPE_R	0
18053259Smjacob#define SA_ATYPE_NR	1
18153259Smjacob#define SA_ATYPE_ER	2
18253259Smjacob
18353259Smjacob#define SAMINOR(ctl, unit, mode, access) \
18453283Smjacob	((ctl << 29) | ((unit & 0x3f0) << 16) | ((unit & 0xf) << 4) | \
18553283Smjacob	(mode << 0x2) | (access & 0x3))
18653259Smjacob
18753259Smjacob#define SA_NUM_MODES	4
18853259Smjacobstruct sa_devs {
18953259Smjacob	dev_t	ctl_dev;
19053259Smjacob	struct sa_mode_devs {
19153259Smjacob		dev_t	r_dev;
19253259Smjacob		dev_t	nr_dev;
19353259Smjacob		dev_t	er_dev;
19453259Smjacob	} mode_devs[SA_NUM_MODES];
19553259Smjacob};
19653259Smjacob
19739213Sgibbsstruct sa_softc {
19839213Sgibbs	sa_state	state;
19939213Sgibbs	sa_flags	flags;
20039213Sgibbs	sa_quirks	quirks;
20159249Sphk	struct		bio_queue_head bio_queue;
20246962Smjacob	int		queue_count;
20339213Sgibbs	struct		devstat device_stats;
20453259Smjacob	struct sa_devs	devs;
20539213Sgibbs	int		blk_gran;
20639213Sgibbs	int		blk_mask;
20739213Sgibbs	int		blk_shift;
20839213Sgibbs	u_int32_t	max_blk;
20939213Sgibbs	u_int32_t	min_blk;
21041674Smjacob	u_int32_t	comp_algorithm;
21141674Smjacob	u_int32_t	saved_comp_algorithm;
21239213Sgibbs	u_int32_t	media_blksize;
21341906Smjacob	u_int32_t	last_media_blksize;
21439213Sgibbs	u_int32_t	media_numblks;
21541674Smjacob	u_int8_t	media_density;
21639213Sgibbs	u_int8_t	speed;
21741674Smjacob	u_int8_t	scsi_rev;
21843636Smjacob	u_int8_t	dsreg;		/* mtio mt_dsreg, redux */
21939213Sgibbs	int		buffer_mode;
22039213Sgibbs	int		filemarks;
22139213Sgibbs	union		ccb saved_ccb;
22271268Smjacob	int		last_resid_was_io;
22346962Smjacob
22441948Smjacob	/*
22543636Smjacob	 * Relative to BOT Location.
22643636Smjacob	 */
22743636Smjacob	daddr_t		fileno;
22843636Smjacob	daddr_t		blkno;
22943636Smjacob
23043636Smjacob	/*
23141948Smjacob	 * Latched Error Info
23241948Smjacob	 */
23342009Smjacob	struct {
23442009Smjacob		struct scsi_sense_data _last_io_sense;
23542009Smjacob		u_int32_t _last_io_resid;
23642009Smjacob		u_int8_t _last_io_cdb[CAM_MAX_CDBLEN];
23742009Smjacob		struct scsi_sense_data _last_ctl_sense;
23842009Smjacob		u_int32_t _last_ctl_resid;
23942009Smjacob		u_int8_t _last_ctl_cdb[CAM_MAX_CDBLEN];
24042009Smjacob#define	last_io_sense	errinfo._last_io_sense
24142009Smjacob#define	last_io_resid	errinfo._last_io_resid
24242009Smjacob#define	last_io_cdb	errinfo._last_io_cdb
24342009Smjacob#define	last_ctl_sense	errinfo._last_ctl_sense
24442009Smjacob#define	last_ctl_resid	errinfo._last_ctl_resid
24542009Smjacob#define	last_ctl_cdb	errinfo._last_ctl_cdb
24642009Smjacob	} errinfo;
24743636Smjacob	/*
24843636Smjacob	 * Misc other flags/state
24943636Smjacob	 */
25043636Smjacob	u_int32_t
25143636Smjacob				: 31,
25243636Smjacob		ctrl_mode	: 1;	/* control device open */
25339213Sgibbs};
25439213Sgibbs
25539213Sgibbsstruct sa_quirk_entry {
25642563Smjacob	struct scsi_inquiry_pattern inq_pat;	/* matching pattern */
25742563Smjacob	sa_quirks quirks;	/* specific quirk type */
25842563Smjacob	u_int32_t prefblk;	/* preferred blocksize when in fixed mode */
25939213Sgibbs};
26039213Sgibbs
26139213Sgibbsstatic struct sa_quirk_entry sa_quirk_table[] =
26239213Sgibbs{
26339213Sgibbs	{
26460235Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "OnStream",
26560235Smjacob		  "ADR*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_NODREAD |
26660235Smjacob		   SA_QUIRK_1FM|SA_QUIRK_NO_MODESEL, 32768
26760235Smjacob	},
26860235Smjacob	{
26939213Sgibbs		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
27056981Smjacob		  "Python 25601*", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_NODREAD, 0
27141351Sjoerg	},
27241351Sjoerg	{
27342130Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
27456981Smjacob		  "Python*", "*"}, SA_QUIRK_NODREAD, 0
27556981Smjacob	},
27656981Smjacob	{
27756981Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
27843636Smjacob		  "VIPER 150*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
27942130Smjacob	},
28042130Smjacob	{
28142563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28268500Smjacob		  "VIPER 2525 25462", "-011"},
28368500Smjacob		  SA_QUIRK_NOCOMP|SA_QUIRK_1FM|SA_QUIRK_NODREAD, 0
28468500Smjacob	},
28568500Smjacob	{
28668500Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "ARCHIVE",
28746962Smjacob		  "VIPER 2525*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
28842563Smjacob	},
28971082Smjacob#if	0
29042563Smjacob	{
29142533Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
29271082Smjacob		  "C15*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_NO_CPAGE, 0,
29371082Smjacob	},
29471082Smjacob#endif
29571082Smjacob	{
29671082Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
29746962Smjacob		  "T20*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
29846962Smjacob	},
29946962Smjacob	{
30046962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
30144354Smjacob		  "T4000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
30242533Smjacob	},
30342533Smjacob	{
30442716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "HP",
30542716Smjacob		  "HP-88780*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
30642716Smjacob	},
30742716Smjacob	{
30842716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "KENNEDY",
30942716Smjacob		  "*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
31042716Smjacob	},
31142716Smjacob	{
31242716Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "M4 DATA",
31342716Smjacob		  "123107 SCSI*", "*"}, SA_QUIRK_VARIABLE|SA_QUIRK_2FM, 0
31442716Smjacob	},
31551744Smjacob	{	/* jreynold@primenet.com */
31651744Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
31751744Smjacob		"STT8000N*", "*"}, SA_QUIRK_1FM, 0
31851744Smjacob	},
31951875Smjacob	{	/* mike@sentex.net */
32051875Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "Seagate",
32151875Smjacob		"STT20000*", "*"}, SA_QUIRK_1FM, 0
32251875Smjacob	},
32342716Smjacob	{
32441351Sjoerg		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
32543636Smjacob		  " TDC 3600", "U07:"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
32642563Smjacob	},
32742563Smjacob	{
32842563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
32947519Smjacob		  " TDC 3800", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
33047519Smjacob	},
33147519Smjacob	{
33247519Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
33348192Smjacob		  " TDC 4100", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
33448192Smjacob	},
33548192Smjacob	{
33648192Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
33743636Smjacob		  " TDC 4200", "*"}, SA_QUIRK_NOCOMP|SA_QUIRK_1FM, 512
33842563Smjacob	},
33942563Smjacob	{
34046962Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "TANDBERG",
34146962Smjacob		  " SLR*", "*"}, SA_QUIRK_1FM, 0
34246962Smjacob	},
34346962Smjacob	{
34442563Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
34543636Smjacob		  "5525ES*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 512
34645752Smjacob	},
34745752Smjacob	{
34845752Smjacob		{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK",
34945752Smjacob		  "51000*", "*"}, SA_QUIRK_FIXED|SA_QUIRK_1FM, 1024
35039213Sgibbs	}
35139213Sgibbs};
35239213Sgibbs
35339213Sgibbsstatic	d_open_t	saopen;
35439213Sgibbsstatic	d_close_t	saclose;
35539213Sgibbsstatic	d_strategy_t	sastrategy;
35639213Sgibbsstatic	d_ioctl_t	saioctl;
35739213Sgibbsstatic	periph_init_t	sainit;
35839213Sgibbsstatic	periph_ctor_t	saregister;
35940603Skenstatic	periph_oninv_t	saoninvalidate;
36039213Sgibbsstatic	periph_dtor_t	sacleanup;
36139213Sgibbsstatic	periph_start_t	sastart;
36239213Sgibbsstatic	void		saasync(void *callback_arg, u_int32_t code,
36339213Sgibbs				struct cam_path *path, void *arg);
36439213Sgibbsstatic	void		sadone(struct cam_periph *periph,
36539213Sgibbs			       union ccb *start_ccb);
36639213Sgibbsstatic  int		saerror(union ccb *ccb, u_int32_t cam_flags,
36739213Sgibbs				u_int32_t sense_flags);
36868114Smjacobstatic int		samarkswanted(struct cam_periph *);
36939213Sgibbsstatic int		sacheckeod(struct cam_periph *periph);
37039213Sgibbsstatic int		sagetparams(struct cam_periph *periph,
37139213Sgibbs				    sa_params params_to_get,
37239213Sgibbs				    u_int32_t *blocksize, u_int8_t *density,
37339213Sgibbs				    u_int32_t *numblocks, int *buff_mode,
37439213Sgibbs				    u_int8_t *write_protect, u_int8_t *speed,
37539213Sgibbs				    int *comp_supported, int *comp_enabled,
37639213Sgibbs				    u_int32_t *comp_algorithm,
37746962Smjacob				    sa_comp_t *comp_page);
37839213Sgibbsstatic int		sasetparams(struct cam_periph *periph,
37939213Sgibbs				    sa_params params_to_set,
38039213Sgibbs				    u_int32_t blocksize, u_int8_t density,
38142563Smjacob				    u_int32_t comp_algorithm,
38242563Smjacob				    u_int32_t sense_flags);
38339213Sgibbsstatic void		saprevent(struct cam_periph *periph, int action);
38439213Sgibbsstatic int		sarewind(struct cam_periph *periph);
38539213Sgibbsstatic int		saspace(struct cam_periph *periph, int count,
38639213Sgibbs				scsi_space_code code);
38741906Smjacobstatic int		samount(struct cam_periph *, int, dev_t);
38839213Sgibbsstatic int		saretension(struct cam_periph *periph);
38939213Sgibbsstatic int		sareservereleaseunit(struct cam_periph *periph,
39039213Sgibbs					     int reserve);
39139213Sgibbsstatic int		saloadunload(struct cam_periph *periph, int load);
39239213Sgibbsstatic int		saerase(struct cam_periph *periph, int longerase);
39339213Sgibbsstatic int		sawritefilemarks(struct cam_periph *periph,
39439213Sgibbs					 int nmarks, int setmarks);
39541918Smjacobstatic int		sardpos(struct cam_periph *periph, int, u_int32_t *);
39641918Smjacobstatic int		sasetpos(struct cam_periph *periph, int, u_int32_t *);
39739213Sgibbs
39841918Smjacob
39939213Sgibbsstatic struct periph_driver sadriver =
40039213Sgibbs{
40139213Sgibbs	sainit, "sa",
40239213Sgibbs	TAILQ_HEAD_INITIALIZER(sadriver.units), /* generation */ 0
40339213Sgibbs};
40439213Sgibbs
40539213SgibbsDATA_SET(periphdriver_set, sadriver);
40639213Sgibbs
40739213Sgibbs/* For 2.2-stable support */
40839213Sgibbs#ifndef D_TAPE
40939213Sgibbs#define D_TAPE 0
41039213Sgibbs#endif
41139213Sgibbs
41239213Sgibbs#define SA_CDEV_MAJOR 14
41339213Sgibbs
41447625Sphkstatic struct cdevsw sa_cdevsw = {
41547625Sphk	/* open */	saopen,
41647625Sphk	/* close */	saclose,
41747625Sphk	/* read */	physread,
41847625Sphk	/* write */	physwrite,
41947625Sphk	/* ioctl */	saioctl,
42047625Sphk	/* poll */	nopoll,
42147625Sphk	/* mmap */	nommap,
42247625Sphk	/* strategy */	sastrategy,
42347625Sphk	/* name */	"sa",
42447625Sphk	/* maj */	SA_CDEV_MAJOR,
42547625Sphk	/* dump */	nodump,
42647625Sphk	/* psize */	nopsize,
42747625Sphk	/* flags */	D_TAPE,
42851092Sphk	/* bmaj */	-1
42939213Sgibbs};
43039213Sgibbs
43139213Sgibbsstatic struct extend_array *saperiphs;
43239213Sgibbs
43339213Sgibbsstatic int
43439213Sgibbssaopen(dev_t dev, int flags, int fmt, struct proc *p)
43539213Sgibbs{
43639213Sgibbs	struct cam_periph *periph;
43739213Sgibbs	struct sa_softc *softc;
43839213Sgibbs	int unit;
43939213Sgibbs	int mode;
44039213Sgibbs	int density;
44139213Sgibbs	int error;
44240603Sken	int s;
44339213Sgibbs
44439213Sgibbs	unit = SAUNIT(dev);
44539213Sgibbs	mode = SAMODE(dev);
44639213Sgibbs	density = SADENSITY(dev);
44739213Sgibbs
44853259Smjacob	s = splsoftcam();
44939213Sgibbs	periph = cam_extend_get(saperiphs, unit);
45053259Smjacob	if (periph == NULL) {
45153259Smjacob		(void) splx(s);
45239213Sgibbs		return (ENXIO);
45353259Smjacob	}
45439213Sgibbs	softc = (struct sa_softc *)periph->softc;
45553259Smjacob	if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) {
45653259Smjacob		splx(s);
45753259Smjacob		return (error);
45853259Smjacob	}
45953259Smjacob	splx(s);
46039213Sgibbs
46146962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
46246962Smjacob	    ("saopen(%d): dev=0x%x softc=0x%x\n", unit, unit, softc->flags));
46339213Sgibbs
46453259Smjacob	if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
46553259Smjacob		cam_periph_unlock(periph);
46654099Smjacob		return (ENXIO);
46753259Smjacob	}
46843636Smjacob	if (SA_IS_CTRL(dev)) {
46943636Smjacob		softc->ctrl_mode = 1;
47053259Smjacob		cam_periph_unlock(periph);
47143636Smjacob		return (0);
47243636Smjacob	}
47343636Smjacob
47439213Sgibbs
47553259Smjacob	if (softc->flags & SA_FLAG_OPEN) {
47653259Smjacob		error = EBUSY;
47753259Smjacob	} else if (softc->flags & SA_FLAG_INVALID) {
47853259Smjacob		error = ENXIO;
47953259Smjacob	} else {
48053259Smjacob		/*
48153259Smjacob		 * The function samount ensures media is loaded and ready.
48253259Smjacob		 * It also does a device RESERVE if the tape isn't yet mounted.
48353259Smjacob		 */
48453259Smjacob		error = samount(periph, flags, dev);
48539213Sgibbs	}
48639213Sgibbs
48753259Smjacob	if (error) {
48853259Smjacob		cam_periph_release(periph);
48953259Smjacob	} else {
49039213Sgibbs		saprevent(periph, PR_PREVENT);
49139213Sgibbs		softc->flags |= SA_FLAG_OPEN;
49239213Sgibbs	}
49339213Sgibbs	cam_periph_unlock(periph);
49439213Sgibbs	return (error);
49539213Sgibbs}
49639213Sgibbs
49739213Sgibbsstatic int
49839213Sgibbssaclose(dev_t dev, int flag, int fmt, struct proc *p)
49939213Sgibbs{
50039213Sgibbs	struct	cam_periph *periph;
50139213Sgibbs	struct	sa_softc *softc;
50246962Smjacob	int	unit, mode, error, writing, tmp;
50342009Smjacob	int	closedbits = SA_FLAG_OPEN;
50439213Sgibbs
50539213Sgibbs	unit = SAUNIT(dev);
50639213Sgibbs	mode = SAMODE(dev);
50739213Sgibbs	periph = cam_extend_get(saperiphs, unit);
50839213Sgibbs	if (periph == NULL)
50939213Sgibbs		return (ENXIO);
51039213Sgibbs
51139213Sgibbs	softc = (struct sa_softc *)periph->softc;
51239213Sgibbs
51346962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE|CAM_DEBUG_INFO,
51446962Smjacob	    ("saclose(%d): dev=0x%x softc=0x%x\n", unit, unit, softc->flags));
51546962Smjacob
51646962Smjacob
51753259Smjacob	if ((error = cam_periph_lock(periph, PRIBIO)) != 0) {
51853259Smjacob		return (error);
51953259Smjacob	}
52053259Smjacob
52143636Smjacob	if (SA_IS_CTRL(dev)) {
52243636Smjacob		softc->ctrl_mode = 0;
52353259Smjacob		cam_periph_release(periph);
52453259Smjacob		cam_periph_unlock(periph);
52543636Smjacob		return (0);
52643636Smjacob	}
52743636Smjacob
52841906Smjacob	/*
52946962Smjacob	 * Were we writing the tape?
53041906Smjacob	 */
53146962Smjacob	writing = (softc->flags & SA_FLAG_TAPE_WRITTEN) != 0;
53246962Smjacob
53346962Smjacob	/*
53446962Smjacob	 * See whether or not we need to write filemarks. If this
53546962Smjacob	 * fails, we probably have to assume we've lost tape
53646962Smjacob	 * position.
53746962Smjacob	 */
53841906Smjacob	error = sacheckeod(periph);
53941906Smjacob	if (error) {
54041906Smjacob		xpt_print_path(periph->path);
54146962Smjacob		printf("failed to write terminating filemark(s)\n");
54246962Smjacob		softc->flags |= SA_FLAG_TAPE_FROZEN;
54341906Smjacob	}
54439213Sgibbs
54541906Smjacob	/*
54641906Smjacob	 * Whatever we end up doing, allow users to eject tapes from here on.
54741906Smjacob	 */
54839213Sgibbs	saprevent(periph, PR_ALLOW);
54939213Sgibbs
55041906Smjacob	/*
55141906Smjacob	 * Decide how to end...
55241906Smjacob	 */
55353522Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
55453522Smjacob		closedbits |= SA_FLAG_TAPE_FROZEN;
55553522Smjacob	} else switch (mode) {
55639213Sgibbs	case SA_MODE_OFFLINE:
55746962Smjacob		/*
55846962Smjacob		 * An 'offline' close is an unconditional release of
55946962Smjacob		 * frozen && mount conditions, irrespective of whether
56046962Smjacob		 * these operations succeeded. The reason for this is
56146962Smjacob		 * to allow at least some kind of programmatic way
56246962Smjacob		 * around our state getting all fouled up. If somebody
56346962Smjacob		 * issues an 'offline' command, that will be allowed
56446962Smjacob		 * to clear state.
56546962Smjacob		 */
56646962Smjacob		(void) sarewind(periph);
56746962Smjacob		(void) saloadunload(periph, FALSE);
56846962Smjacob		closedbits |= SA_FLAG_TAPE_MOUNTED|SA_FLAG_TAPE_FROZEN;
56939213Sgibbs		break;
57041906Smjacob	case SA_MODE_REWIND:
57146962Smjacob		/*
57246962Smjacob		 * If the rewind fails, return an error- if anyone cares,
57346962Smjacob		 * but not overwriting any previous error.
57446962Smjacob		 *
57546962Smjacob		 * We don't clear the notion of mounted here, but we do
57646962Smjacob		 * clear the notion of frozen if we successfully rewound.
57746962Smjacob		 */
57846962Smjacob		tmp = sarewind(periph);
57946962Smjacob		if (tmp) {
58046962Smjacob			if (error != 0)
58146962Smjacob				error = tmp;
58246962Smjacob		} else {
58346962Smjacob			closedbits |= SA_FLAG_TAPE_FROZEN;
58446962Smjacob		}
58541906Smjacob		break;
58639213Sgibbs	case SA_MODE_NOREWIND:
58741906Smjacob		/*
58841906Smjacob		 * If we're not rewinding/unloading the tape, find out
58941906Smjacob		 * whether we need to back up over one of two filemarks
59041906Smjacob		 * we wrote (if we wrote two filemarks) so that appends
59141906Smjacob		 * from this point on will be sane.
59241906Smjacob		 */
59346962Smjacob		if (error == 0 && writing && (softc->quirks & SA_QUIRK_2FM)) {
59446962Smjacob			tmp = saspace(periph, -1, SS_FILEMARKS);
59546962Smjacob			if (tmp) {
59641906Smjacob				xpt_print_path(periph->path);
59741906Smjacob				printf("unable to backspace over one of double"
59846962Smjacob				   " filemarks at end of tape\n");
59946962Smjacob				xpt_print_path(periph->path);
60051744Smjacob				printf("it is possible that this device"
60146962Smjacob				   " needs a SA_QUIRK_1FM quirk set for it\n");
60246962Smjacob				softc->flags |= SA_FLAG_TAPE_FROZEN;
60341906Smjacob			}
60441906Smjacob		}
60539213Sgibbs		break;
60646962Smjacob	default:
60746962Smjacob		xpt_print_path(periph->path);
60846962Smjacob		panic("unknown mode 0x%x in saclose\n", mode);
60946962Smjacob		/* NOTREACHED */
61046962Smjacob		break;
61139213Sgibbs	}
61239213Sgibbs
61341906Smjacob	/*
61441948Smjacob	 * We wish to note here that there are no more filemarks to be written.
61541906Smjacob	 */
61641906Smjacob	softc->filemarks = 0;
61741948Smjacob	softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
61841906Smjacob
61941906Smjacob	/*
62041906Smjacob	 * And we are no longer open for business.
62141906Smjacob	 */
62242009Smjacob	softc->flags &= ~closedbits;
62346962Smjacob
62446962Smjacob	/*
62546962Smjacob	 * Inform users if tape state if frozen....
62646962Smjacob	 */
62746962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
62846962Smjacob		xpt_print_path(periph->path);
62946962Smjacob		printf("tape is now frozen- use an OFFLINE, REWIND or MTEOM "
63046962Smjacob		    "command to clear this state.\n");
63146962Smjacob	}
63239213Sgibbs
63353259Smjacob	/* release the device if it is no longer mounted */
63453259Smjacob	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0)
63553259Smjacob		sareservereleaseunit(periph, FALSE);
63639213Sgibbs
63739213Sgibbs	cam_periph_unlock(periph);
63839213Sgibbs	cam_periph_release(periph);
63939213Sgibbs
64046962Smjacob	return (error);
64139213Sgibbs}
64239213Sgibbs
64339213Sgibbs/*
64439213Sgibbs * Actually translate the requested transfer into one the physical driver
64539213Sgibbs * can understand.  The transfer is described by a buf and will include
64639213Sgibbs * only one physical transfer.
64739213Sgibbs */
64839213Sgibbsstatic void
64959249Sphksastrategy(struct bio *bp)
65039213Sgibbs{
65139213Sgibbs	struct cam_periph *periph;
65239213Sgibbs	struct sa_softc *softc;
65339213Sgibbs	u_int  unit;
65439213Sgibbs	int    s;
65539213Sgibbs
65659249Sphk	if (SA_IS_CTRL(bp->bio_dev)) {
65759249Sphk		bp->bio_error = EINVAL;
65843636Smjacob		goto bad;
65943636Smjacob	}
66059249Sphk	unit = SAUNIT(bp->bio_dev);
66139213Sgibbs	periph = cam_extend_get(saperiphs, unit);
66239213Sgibbs	if (periph == NULL) {
66359249Sphk		bp->bio_error = ENXIO;
66439213Sgibbs		goto bad;
66539213Sgibbs	}
66639213Sgibbs	softc = (struct sa_softc *)periph->softc;
66739213Sgibbs
66840603Sken	s = splsoftcam();
66940603Sken
67040603Sken	if (softc->flags & SA_FLAG_INVALID) {
67140603Sken		splx(s);
67259249Sphk		bp->bio_error = ENXIO;
67340603Sken		goto bad;
67440603Sken	}
67540603Sken
67646962Smjacob	if (softc->flags & SA_FLAG_TAPE_FROZEN) {
67746962Smjacob		splx(s);
67859249Sphk		bp->bio_error = EPERM;
67946962Smjacob		goto bad;
68046962Smjacob	}
68146962Smjacob
68240603Sken	splx(s);
68340603Sken
68439213Sgibbs	/*
68539213Sgibbs	 * If it's a null transfer, return immediatly
68639213Sgibbs	 */
68759249Sphk	if (bp->bio_bcount == 0)
68839213Sgibbs		goto done;
68939213Sgibbs
69039213Sgibbs	/* valid request?  */
69139213Sgibbs	if (softc->flags & SA_FLAG_FIXED) {
69239213Sgibbs		/*
69339213Sgibbs		 * Fixed block device.  The byte count must
69439213Sgibbs		 * be a multiple of our block size.
69539213Sgibbs		 */
69642716Smjacob		if (((softc->blk_mask != ~0) &&
69759249Sphk		    ((bp->bio_bcount & softc->blk_mask) != 0)) ||
69842716Smjacob		    ((softc->blk_mask == ~0) &&
69959249Sphk		    ((bp->bio_bcount % softc->min_blk) != 0))) {
70039213Sgibbs			xpt_print_path(periph->path);
70139213Sgibbs			printf("Invalid request.  Fixed block device "
70239213Sgibbs			       "requests must be a multiple "
70339213Sgibbs			       "of %d bytes\n", softc->min_blk);
70459249Sphk			bp->bio_error = EINVAL;
70539213Sgibbs			goto bad;
70639213Sgibbs		}
70759249Sphk	} else if ((bp->bio_bcount > softc->max_blk) ||
70859249Sphk		   (bp->bio_bcount < softc->min_blk) ||
70959249Sphk		   (bp->bio_bcount & softc->blk_mask) != 0) {
71039213Sgibbs
71139213Sgibbs		xpt_print_path(periph->path);
71239213Sgibbs		printf("Invalid request.  Variable block device "
71342716Smjacob		    "requests must be ");
71439213Sgibbs		if (softc->blk_mask != 0) {
71542716Smjacob			printf("a multiple of %d ", (0x1 << softc->blk_gran));
71639213Sgibbs		}
71742716Smjacob		printf("between %d and %d bytes\n", softc->min_blk,
71842716Smjacob		    softc->max_blk);
71959249Sphk		bp->bio_error = EINVAL;
72039213Sgibbs		goto bad;
72139213Sgibbs        }
72239213Sgibbs
72339213Sgibbs	/*
72442716Smjacob	 * Mask interrupts so that the device cannot be invalidated until
72539213Sgibbs	 * after we are in the queue.  Otherwise, we might not properly
72639213Sgibbs	 * clean up one of the buffers.
72739213Sgibbs	 */
72839213Sgibbs	s = splbio();
72939213Sgibbs
73039213Sgibbs	/*
73142716Smjacob	 * Place it at the end of the queue.
73239213Sgibbs	 */
73359249Sphk	bioq_insert_tail(&softc->bio_queue, bp);
73439213Sgibbs
73546962Smjacob	softc->queue_count++;
73646962Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("sastrategy: enqueuing a %d "
73759249Sphk	    "%s byte %s queue count now %d\n", (int) bp->bio_bcount,
73846962Smjacob	     (softc->flags & SA_FLAG_FIXED)?  "fixed" : "variable",
73959249Sphk	     (bp->bio_cmd == BIO_READ)? "read" : "write", softc->queue_count));
74046962Smjacob
74139213Sgibbs	splx(s);
74239213Sgibbs
74339213Sgibbs	/*
74439213Sgibbs	 * Schedule ourselves for performing the work.
74539213Sgibbs	 */
74642716Smjacob	xpt_schedule(periph, 1);
74739213Sgibbs
74839213Sgibbs	return;
74939213Sgibbsbad:
75059249Sphk	bp->bio_flags |= BIO_ERROR;
75139213Sgibbsdone:
75239213Sgibbs
75339213Sgibbs	/*
75439213Sgibbs	 * Correctly set the buf to indicate a completed xfer
75539213Sgibbs	 */
75659249Sphk	bp->bio_resid = bp->bio_bcount;
75739213Sgibbs	biodone(bp);
75839213Sgibbs}
75939213Sgibbs
76039213Sgibbsstatic int
76139213Sgibbssaioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
76239213Sgibbs{
76339213Sgibbs	struct cam_periph *periph;
76439213Sgibbs	struct sa_softc *softc;
76548520Speter	scsi_space_code spaceop;
76643636Smjacob	int didlockperiph = 0;
76743636Smjacob	int s;
76839213Sgibbs	int unit;
76939213Sgibbs	int mode;
77039213Sgibbs	int density;
77153259Smjacob	int error = 0;
77239213Sgibbs
77339213Sgibbs	unit = SAUNIT(dev);
77439213Sgibbs	mode = SAMODE(dev);
77539213Sgibbs	density = SADENSITY(dev);
77648520Speter	error = 0;		/* shut up gcc */
77748520Speter	spaceop = 0;		/* shut up gcc */
77839213Sgibbs
77939213Sgibbs	periph = cam_extend_get(saperiphs, unit);
78039213Sgibbs	if (periph == NULL)
78139213Sgibbs		return (ENXIO);
78239213Sgibbs
78339213Sgibbs	softc = (struct sa_softc *)periph->softc;
78439213Sgibbs
78539213Sgibbs	/*
78643636Smjacob	 * Check for control mode accesses. We allow MTIOCGET and
78743636Smjacob	 * MTIOCERRSTAT (but need to be the only one open in order
78843636Smjacob	 * to clear latched status), and MTSETBSIZE, MTSETDNSTY
78943636Smjacob	 * and MTCOMP (but need to be the only one accessing this
79043636Smjacob	 * device to run those).
79143636Smjacob	 */
79243636Smjacob
79343636Smjacob	if (SA_IS_CTRL(dev)) {
79443636Smjacob		switch (cmd) {
79546962Smjacob		case MTIOCGETEOTMODEL:
79643636Smjacob		case MTIOCGET:
79743636Smjacob			break;
79843636Smjacob		case MTIOCERRSTAT:
79943636Smjacob			/*
80043636Smjacob			 * If the periph isn't already locked, lock it
80143636Smjacob			 * so our MTIOCERRSTAT can reset latched error stats.
80243636Smjacob			 *
80343636Smjacob			 * If the periph is already locked, skip it because
80443636Smjacob			 * we're just getting status and it'll be up to the
80543636Smjacob			 * other thread that has this device open to do
80643636Smjacob			 * an MTIOCERRSTAT that would clear latched status.
80743636Smjacob			 */
80843636Smjacob			s = splsoftcam();
80943636Smjacob			if ((periph->flags & CAM_PERIPH_LOCKED) == 0) {
81043636Smjacob				error = cam_periph_lock(periph, PRIBIO|PCATCH);
81143636Smjacob				if (error != 0) {
81243636Smjacob					splx(s);
81343636Smjacob					return (error);
81443636Smjacob				}
81543636Smjacob				didlockperiph = 1;
81643636Smjacob			}
81743636Smjacob			break;
81843636Smjacob
81946962Smjacob		case MTIOCSETEOTMODEL:
82043636Smjacob		case MTSETBSIZ:
82143636Smjacob		case MTSETDNSTY:
82243636Smjacob		case MTCOMP:
82343636Smjacob			/*
82443636Smjacob			 * We need to acquire the peripheral here rather
82543636Smjacob			 * than at open time because we are sharing writable
82643636Smjacob			 * access to data structures.
82743636Smjacob			 */
82843636Smjacob			s = splsoftcam();
82943636Smjacob			error = cam_periph_lock(periph, PRIBIO|PCATCH);
83043636Smjacob			if (error != 0) {
83143636Smjacob				splx(s);
83243636Smjacob				return (error);
83343636Smjacob			}
83443636Smjacob			didlockperiph = 1;
83543636Smjacob			break;
83643636Smjacob
83743636Smjacob		default:
83843636Smjacob			return (EINVAL);
83943636Smjacob		}
84043636Smjacob	}
84143636Smjacob
84243636Smjacob	/*
84339213Sgibbs	 * Find the device that the user is talking about
84439213Sgibbs	 */
84539213Sgibbs	switch (cmd) {
84639213Sgibbs	case MTIOCGET:
84739213Sgibbs	{
84839213Sgibbs		struct mtget *g = (struct mtget *)arg;
84939213Sgibbs
85053259Smjacob		/*
85153259Smjacob		 * If this isn't the control mode device, actually go out
85253259Smjacob		 * and ask the drive again what it's set to.
85353259Smjacob		 */
85453259Smjacob		if (!SA_IS_CTRL(dev)) {
85553259Smjacob			u_int8_t write_protect;
85653259Smjacob			int comp_enabled, comp_supported;
85753259Smjacob			error = sagetparams(periph, SA_PARAM_ALL,
85853259Smjacob			    &softc->media_blksize, &softc->media_density,
85953259Smjacob			    &softc->media_numblks, &softc->buffer_mode,
86053259Smjacob			    &write_protect, &softc->speed, &comp_supported,
86153259Smjacob			    &comp_enabled, &softc->comp_algorithm, NULL);
86253259Smjacob			if (error)
86353259Smjacob				break;
86453259Smjacob			if (write_protect)
86553259Smjacob				softc->flags |= SA_FLAG_TAPE_WP;
86653259Smjacob			else
86753259Smjacob				softc->flags &= ~SA_FLAG_TAPE_WP;
86853259Smjacob			softc->flags &= ~(SA_FLAG_COMP_SUPP|
86953259Smjacob			    SA_FLAG_COMP_ENABLED|SA_FLAG_COMP_UNSUPP);
87053259Smjacob			if (comp_supported) {
87153259Smjacob				if (softc->saved_comp_algorithm == 0)
87253259Smjacob					softc->saved_comp_algorithm =
87353259Smjacob					    softc->comp_algorithm;
87453259Smjacob				softc->flags |= SA_FLAG_COMP_SUPP;
87553259Smjacob				if (comp_enabled)
87653259Smjacob					softc->flags |= SA_FLAG_COMP_ENABLED;
87753259Smjacob			} else
87853259Smjacob				softc->flags |= SA_FLAG_COMP_UNSUPP;
87953259Smjacob		}
88039213Sgibbs		bzero(g, sizeof(struct mtget));
88141948Smjacob		g->mt_type = MT_ISAR;
88239213Sgibbs		if (softc->flags & SA_FLAG_COMP_UNSUPP) {
88339213Sgibbs			g->mt_comp = MT_COMP_UNSUPP;
88439213Sgibbs			g->mt_comp0 = MT_COMP_UNSUPP;
88539213Sgibbs			g->mt_comp1 = MT_COMP_UNSUPP;
88639213Sgibbs			g->mt_comp2 = MT_COMP_UNSUPP;
88739213Sgibbs			g->mt_comp3 = MT_COMP_UNSUPP;
88839213Sgibbs		} else {
88946962Smjacob			if ((softc->flags & SA_FLAG_COMP_ENABLED) == 0) {
89046962Smjacob				g->mt_comp = MT_COMP_DISABLED;
89146962Smjacob			} else {
89246962Smjacob				g->mt_comp = softc->comp_algorithm;
89346962Smjacob			}
89439213Sgibbs			g->mt_comp0 = softc->comp_algorithm;
89539213Sgibbs			g->mt_comp1 = softc->comp_algorithm;
89639213Sgibbs			g->mt_comp2 = softc->comp_algorithm;
89739213Sgibbs			g->mt_comp3 = softc->comp_algorithm;
89839213Sgibbs		}
89946962Smjacob		g->mt_density = softc->media_density;
90039213Sgibbs		g->mt_density0 = softc->media_density;
90139213Sgibbs		g->mt_density1 = softc->media_density;
90239213Sgibbs		g->mt_density2 = softc->media_density;
90339213Sgibbs		g->mt_density3 = softc->media_density;
90446962Smjacob		g->mt_blksiz = softc->media_blksize;
90539213Sgibbs		g->mt_blksiz0 = softc->media_blksize;
90639213Sgibbs		g->mt_blksiz1 = softc->media_blksize;
90739213Sgibbs		g->mt_blksiz2 = softc->media_blksize;
90839213Sgibbs		g->mt_blksiz3 = softc->media_blksize;
90943636Smjacob		g->mt_fileno = softc->fileno;
91043636Smjacob		g->mt_blkno = softc->blkno;
91143636Smjacob		g->mt_dsreg = (short) softc->dsreg;
91271268Smjacob		/*
91371268Smjacob		 * Yes, we know that this is likely to overflow
91471268Smjacob		 */
91571268Smjacob		if (softc->last_resid_was_io) {
91671268Smjacob			if ((g->mt_resid = (short) softc->last_io_resid) != 0) {
91771268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
91871268Smjacob					softc->last_io_resid = 0;
91971268Smjacob				}
92071268Smjacob			}
92171268Smjacob		} else {
92271268Smjacob			if ((g->mt_resid = (short)softc->last_ctl_resid) != 0) {
92371268Smjacob				if (SA_IS_CTRL(dev) == 0 || didlockperiph) {
92471268Smjacob					softc->last_ctl_resid = 0;
92571268Smjacob				}
92671268Smjacob			}
92771268Smjacob		}
92871268Smjacob		if (g->mt_resid) {
92971268Smjacob		}
93039213Sgibbs		error = 0;
93139213Sgibbs		break;
93239213Sgibbs	}
93341948Smjacob	case MTIOCERRSTAT:
93441948Smjacob	{
93541948Smjacob		struct scsi_tape_errors *sep =
93641948Smjacob		    &((union mterrstat *)arg)->scsi_errstat;
93741948Smjacob
93841948Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
93941948Smjacob		    ("saioctl: MTIOCERRSTAT\n"));
94041948Smjacob
94141948Smjacob		bzero(sep, sizeof(*sep));
94241948Smjacob		sep->io_resid = softc->last_io_resid;
94341948Smjacob		bcopy((caddr_t) &softc->last_io_sense, sep->io_sense,
94441948Smjacob		    sizeof (sep->io_sense));
94542009Smjacob		bcopy((caddr_t) &softc->last_io_cdb, sep->io_cdb,
94642009Smjacob		    sizeof (sep->io_cdb));
94742009Smjacob		sep->ctl_resid = softc->last_ctl_resid;
94841948Smjacob		bcopy((caddr_t) &softc->last_ctl_sense, sep->ctl_sense,
94941948Smjacob		    sizeof (sep->ctl_sense));
95042009Smjacob		bcopy((caddr_t) &softc->last_ctl_cdb, sep->ctl_cdb,
95142009Smjacob		    sizeof (sep->ctl_cdb));
95243636Smjacob
95343636Smjacob		if (SA_IS_CTRL(dev) == 0 || didlockperiph)
95443636Smjacob			bzero((caddr_t) &softc->errinfo,
95543636Smjacob			    sizeof (softc->errinfo));
95641948Smjacob		error = 0;
95741948Smjacob		break;
95841948Smjacob	}
95939213Sgibbs	case MTIOCTOP:
96039213Sgibbs	{
96139213Sgibbs		struct mtop *mt;
96239213Sgibbs		int    count;
96339213Sgibbs
96439213Sgibbs		mt = (struct mtop *)arg;
96539213Sgibbs
96639213Sgibbs		CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
96739213Sgibbs			 ("saioctl: op=0x%x count=0x%x\n",
96839213Sgibbs			  mt->mt_op, mt->mt_count));
96939213Sgibbs
97039213Sgibbs		count = mt->mt_count;
97139213Sgibbs		switch (mt->mt_op) {
97241906Smjacob		case MTWEOF:	/* write an end-of-file marker */
97368114Smjacob			/*
97468114Smjacob			 * We don't need to clear the SA_FLAG_TAPE_WRITTEN
97568114Smjacob			 * flag because by keeping track of filemarks
97668114Smjacob			 * we have last written we know ehether or not
97768114Smjacob			 * we need to write more when we close the device.
97868114Smjacob			 */
97941906Smjacob			error = sawritefilemarks(periph, count, FALSE);
98039213Sgibbs			break;
98142009Smjacob		case MTWSS:	/* write a setmark */
98242009Smjacob			error = sawritefilemarks(periph, count, TRUE);
98342009Smjacob			break;
98439213Sgibbs		case MTBSR:	/* backward space record */
98539213Sgibbs		case MTFSR:	/* forward space record */
98639213Sgibbs		case MTBSF:	/* backward space file */
98739213Sgibbs		case MTFSF:	/* forward space file */
98842009Smjacob		case MTBSS:	/* backward space setmark */
98942009Smjacob		case MTFSS:	/* forward space setmark */
99039213Sgibbs		case MTEOD:	/* space to end of recorded medium */
99139213Sgibbs		{
99239213Sgibbs			int nmarks;
99339213Sgibbs
99448520Speter			spaceop = SS_FILEMARKS;
99539213Sgibbs			nmarks = softc->filemarks;
99639213Sgibbs			error = sacheckeod(periph);
99741906Smjacob			if (error) {
99841906Smjacob				xpt_print_path(periph->path);
99941906Smjacob				printf("EOD check prior to spacing failed\n");
100041906Smjacob				softc->flags |= SA_FLAG_EIO_PENDING;
100141906Smjacob				break;
100241906Smjacob			}
100339213Sgibbs			nmarks -= softc->filemarks;
100442009Smjacob			switch(mt->mt_op) {
100542009Smjacob			case MTBSR:
100639213Sgibbs				count = -count;
100742009Smjacob				/* FALLTHROUGH */
100842009Smjacob			case MTFSR:
100939213Sgibbs				spaceop = SS_BLOCKS;
101042009Smjacob				break;
101142009Smjacob			case MTBSF:
101242009Smjacob				count = -count;
101342009Smjacob				/* FALLTHROUGH */
101442009Smjacob			case MTFSF:
101542009Smjacob				break;
101642009Smjacob			case MTBSS:
101742009Smjacob				count = -count;
101842009Smjacob				/* FALLTHROUGH */
101942009Smjacob			case MTFSS:
102042009Smjacob				spaceop = SS_SETMARKS;
102142009Smjacob				break;
102242009Smjacob			case MTEOD:
102339213Sgibbs				spaceop = SS_EOD;
102439213Sgibbs				count = 0;
102539213Sgibbs				nmarks = 0;
102642009Smjacob				break;
102742009Smjacob			default:
102842009Smjacob				error = EINVAL;
102942009Smjacob				break;
103039213Sgibbs			}
103142009Smjacob			if (error)
103242009Smjacob				break;
103339213Sgibbs
103439213Sgibbs			nmarks = softc->filemarks;
103542009Smjacob			/*
103642009Smjacob			 * XXX: Why are we checking again?
103742009Smjacob			 */
103839213Sgibbs			error = sacheckeod(periph);
103942009Smjacob			if (error)
104042009Smjacob				break;
104139213Sgibbs			nmarks -= softc->filemarks;
104242009Smjacob			error = saspace(periph, count - nmarks, spaceop);
104341906Smjacob			/*
104441906Smjacob			 * At this point, clear that we've written the tape
104541906Smjacob			 * and that we've written any filemarks. We really
104641906Smjacob			 * don't know what the applications wishes to do next-
104741906Smjacob			 * the sacheckeod's will make sure we terminated the
104841906Smjacob			 * tape correctly if we'd been writing, but the next
104941906Smjacob			 * action the user application takes will set again
105041906Smjacob			 * whether we need to write filemarks.
105141906Smjacob			 */
105246962Smjacob			softc->flags &=
105346962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
105441906Smjacob			softc->filemarks = 0;
105539213Sgibbs			break;
105639213Sgibbs		}
105739213Sgibbs		case MTREW:	/* rewind */
105841906Smjacob			(void) sacheckeod(periph);
105939213Sgibbs			error = sarewind(periph);
106041906Smjacob			/* see above */
106142009Smjacob			softc->flags &=
106246962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
106341906Smjacob			softc->filemarks = 0;
106439213Sgibbs			break;
106539213Sgibbs		case MTERASE:	/* erase */
106639213Sgibbs			error = saerase(periph, count);
106746962Smjacob			softc->flags &=
106846962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
106939213Sgibbs			break;
107039213Sgibbs		case MTRETENS:	/* re-tension tape */
107139213Sgibbs			error = saretension(periph);
107246962Smjacob			softc->flags &=
107346962Smjacob			    ~(SA_FLAG_TAPE_WRITTEN|SA_FLAG_TAPE_FROZEN);
107439213Sgibbs			break;
107539213Sgibbs		case MTOFFL:	/* rewind and put the drive offline */
107641906Smjacob
107741906Smjacob			(void) sacheckeod(periph);
107841906Smjacob			/* see above */
107941906Smjacob			softc->flags &= ~SA_FLAG_TAPE_WRITTEN;
108041906Smjacob			softc->filemarks = 0;
108141906Smjacob
108246962Smjacob			error = sarewind(periph);
108353522Smjacob			/* clear the frozen flag anyway */
108453522Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
108546962Smjacob
108639213Sgibbs			/*
108753522Smjacob			 * Be sure to allow media removal before ejecting.
108839213Sgibbs			 */
108946962Smjacob
109039213Sgibbs			saprevent(periph, PR_ALLOW);
109153522Smjacob			if (error == 0) {
109243636Smjacob				error = saloadunload(periph, FALSE);
109353522Smjacob				if (error == 0) {
109453522Smjacob					softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
109553522Smjacob				}
109653522Smjacob			}
109746962Smjacob			break;
109839213Sgibbs
109939213Sgibbs		case MTNOP:	/* no operation, sets status only */
110039213Sgibbs		case MTCACHE:	/* enable controller cache */
110139213Sgibbs		case MTNOCACHE:	/* disable controller cache */
110239213Sgibbs			error = 0;
110339213Sgibbs			break;
110446962Smjacob
110539213Sgibbs		case MTSETBSIZ:	/* Set block size for device */
110639213Sgibbs
110739213Sgibbs			error = sasetparams(periph, SA_PARAM_BLOCKSIZE, count,
110842563Smjacob					    0, 0, 0);
110941674Smjacob			if (error == 0) {
111041906Smjacob				softc->last_media_blksize =
111141906Smjacob				    softc->media_blksize;
111241674Smjacob				softc->media_blksize = count;
111341674Smjacob				if (count) {
111441674Smjacob					softc->flags |= SA_FLAG_FIXED;
111541674Smjacob					if (powerof2(count)) {
111641674Smjacob						softc->blk_shift =
111741674Smjacob						    ffs(count) - 1;
111841674Smjacob						softc->blk_mask = count - 1;
111941674Smjacob					} else {
112041674Smjacob						softc->blk_mask = ~0;
112141674Smjacob						softc->blk_shift = 0;
112241674Smjacob					}
112341906Smjacob					/*
112441906Smjacob					 * Make the user's desire 'persistent'.
112541906Smjacob					 */
112641906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
112741906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
112841674Smjacob				} else {
112941674Smjacob					softc->flags &= ~SA_FLAG_FIXED;
113041674Smjacob					if (softc->max_blk == 0) {
113141674Smjacob						softc->max_blk = ~0;
113241674Smjacob					}
113341674Smjacob					softc->blk_shift = 0;
113441674Smjacob					if (softc->blk_gran != 0) {
113541674Smjacob						softc->blk_mask =
113641674Smjacob						    softc->blk_gran - 1;
113741674Smjacob					} else {
113841674Smjacob						softc->blk_mask = 0;
113941674Smjacob					}
114041906Smjacob					/*
114141906Smjacob					 * Make the user's desire 'persistent'.
114241906Smjacob					 */
114341906Smjacob					softc->quirks |= SA_QUIRK_VARIABLE;
114441906Smjacob					softc->quirks &= ~SA_QUIRK_FIXED;
114541674Smjacob				}
114641674Smjacob			}
114739213Sgibbs			break;
114839213Sgibbs		case MTSETDNSTY:	/* Set density for device and mode */
114939213Sgibbs			if (count > UCHAR_MAX) {
115039213Sgibbs				error = EINVAL;
115139213Sgibbs				break;
115239213Sgibbs			} else {
115339213Sgibbs				error = sasetparams(periph, SA_PARAM_DENSITY,
115442563Smjacob						    0, count, 0, 0);
115539213Sgibbs			}
115639213Sgibbs			break;
115739213Sgibbs		case MTCOMP:	/* enable compression */
115839213Sgibbs			/*
115939213Sgibbs			 * Some devices don't support compression, and
116039213Sgibbs			 * don't like it if you ask them for the
116139213Sgibbs			 * compression page.
116239213Sgibbs			 */
116343636Smjacob			if ((softc->quirks & SA_QUIRK_NOCOMP) ||
116443636Smjacob			    (softc->flags & SA_FLAG_COMP_UNSUPP)) {
116539213Sgibbs				error = ENODEV;
116639213Sgibbs				break;
116739213Sgibbs			}
116839213Sgibbs			error = sasetparams(periph, SA_PARAM_COMPRESSION,
116954099Smjacob			    0, 0, count, SF_NO_PRINT);
117039213Sgibbs			break;
117139213Sgibbs		default:
117239213Sgibbs			error = EINVAL;
117339213Sgibbs		}
117439213Sgibbs		break;
117539213Sgibbs	}
117639213Sgibbs	case MTIOCIEOT:
117739213Sgibbs	case MTIOCEEOT:
117839213Sgibbs		error = 0;
117939213Sgibbs		break;
118041918Smjacob	case MTIOCRDSPOS:
118141918Smjacob		error = sardpos(periph, 0, (u_int32_t *) arg);
118241918Smjacob		break;
118341918Smjacob	case MTIOCRDHPOS:
118441918Smjacob		error = sardpos(periph, 1, (u_int32_t *) arg);
118541918Smjacob		break;
118641918Smjacob	case MTIOCSLOCATE:
118741918Smjacob		error = sasetpos(periph, 0, (u_int32_t *) arg);
118841918Smjacob		break;
118941918Smjacob	case MTIOCHLOCATE:
119041918Smjacob		error = sasetpos(periph, 1, (u_int32_t *) arg);
119141918Smjacob		break;
119246962Smjacob	case MTIOCGETEOTMODEL:
119346962Smjacob		error = 0;
119446962Smjacob		if (softc->quirks & SA_QUIRK_1FM)
119546962Smjacob			mode = 1;
119646962Smjacob		else
119746962Smjacob			mode = 2;
119846962Smjacob		*((u_int32_t *) arg) = mode;
119946962Smjacob		break;
120046962Smjacob	case MTIOCSETEOTMODEL:
120146962Smjacob		error = 0;
120246962Smjacob		switch (*((u_int32_t *) arg)) {
120346962Smjacob		case 1:
120446962Smjacob			softc->quirks &= ~SA_QUIRK_2FM;
120546962Smjacob			softc->quirks |= SA_QUIRK_1FM;
120646962Smjacob			break;
120746962Smjacob		case 2:
120846962Smjacob			softc->quirks &= ~SA_QUIRK_1FM;
120946962Smjacob			softc->quirks |= SA_QUIRK_2FM;
121046962Smjacob			break;
121146962Smjacob		default:
121246962Smjacob			error = EINVAL;
121346962Smjacob			break;
121446962Smjacob		}
121546962Smjacob		break;
121639213Sgibbs	default:
121739213Sgibbs		error = cam_periph_ioctl(periph, cmd, arg, saerror);
121839213Sgibbs		break;
121939213Sgibbs	}
122071268Smjacob
122171268Smjacob	/*
122271268Smjacob	 * Check to see if we cleared a frozen state
122371268Smjacob	 */
122471268Smjacob	if (error == 0 && (softc->flags & SA_FLAG_TAPE_FROZEN)) {
122571268Smjacob		switch(cmd) {
122671268Smjacob		case MTIOCRDSPOS:
122771268Smjacob		case MTIOCRDHPOS:
122871268Smjacob		case MTIOCSLOCATE:
122971268Smjacob		case MTIOCHLOCATE:
123071268Smjacob			softc->fileno = (daddr_t) -1;
123171268Smjacob			softc->blkno = (daddr_t) -1;
123271268Smjacob			softc->flags &= ~SA_FLAG_TAPE_FROZEN;
123371268Smjacob			xpt_print_path(periph->path);
123471268Smjacob			printf("tape state now unfrozen.\n");
123571268Smjacob			break;
123671268Smjacob		default:
123771268Smjacob			break;
123871268Smjacob		}
123971268Smjacob	}
124043636Smjacob	if (didlockperiph) {
124143636Smjacob		cam_periph_unlock(periph);
124243636Smjacob	}
124339213Sgibbs	return (error);
124439213Sgibbs}
124539213Sgibbs
124639213Sgibbsstatic void
124739213Sgibbssainit(void)
124839213Sgibbs{
124939213Sgibbs	cam_status status;
125039213Sgibbs	struct cam_path *path;
125139213Sgibbs
125239213Sgibbs	/*
125339213Sgibbs	 * Create our extend array for storing the devices we attach to.
125439213Sgibbs	 */
125539213Sgibbs	saperiphs = cam_extend_new();
125639213Sgibbs	if (saperiphs == NULL) {
125739213Sgibbs		printf("sa: Failed to alloc extend array!\n");
125839213Sgibbs		return;
125939213Sgibbs	}
126039213Sgibbs
126139213Sgibbs	/*
126239213Sgibbs	 * Install a global async callback.
126339213Sgibbs	 */
126439213Sgibbs	status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID,
126539213Sgibbs				 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD);
126639213Sgibbs
126739213Sgibbs	if (status == CAM_REQ_CMP) {
126839213Sgibbs		/* Register the async callbacks of interrest */
126939213Sgibbs		struct ccb_setasync csa; /*
127039213Sgibbs					  * This is an immediate CCB,
127139213Sgibbs					  * so using the stack is OK
127239213Sgibbs					  */
127346962Smjacob		xpt_setup_ccb(&csa.ccb_h, path, 5);
127439213Sgibbs		csa.ccb_h.func_code = XPT_SASYNC_CB;
127539213Sgibbs		csa.event_enable = AC_FOUND_DEVICE;
127639213Sgibbs		csa.callback = saasync;
127739213Sgibbs		csa.callback_arg = NULL;
127839213Sgibbs		xpt_action((union ccb *)&csa);
127939213Sgibbs		status = csa.ccb_h.status;
128039213Sgibbs		xpt_free_path(path);
128139213Sgibbs	}
128239213Sgibbs
128339213Sgibbs	if (status != CAM_REQ_CMP) {
128439213Sgibbs		printf("sa: Failed to attach master async callback "
128539213Sgibbs		       "due to status 0x%x!\n", status);
128639213Sgibbs	}
128739213Sgibbs}
128839213Sgibbs
128939213Sgibbsstatic void
129040603Skensaoninvalidate(struct cam_periph *periph)
129140603Sken{
129240603Sken	struct sa_softc *softc;
129359249Sphk	struct bio *q_bp;
129440603Sken	struct ccb_setasync csa;
129540603Sken	int s;
129640603Sken
129740603Sken	softc = (struct sa_softc *)periph->softc;
129840603Sken
129940603Sken	/*
130040603Sken	 * De-register any async callbacks.
130140603Sken	 */
130240603Sken	xpt_setup_ccb(&csa.ccb_h, periph->path,
130340603Sken		      /* priority */ 5);
130440603Sken	csa.ccb_h.func_code = XPT_SASYNC_CB;
130540603Sken	csa.event_enable = 0;
130640603Sken	csa.callback = saasync;
130740603Sken	csa.callback_arg = periph;
130840603Sken	xpt_action((union ccb *)&csa);
130940603Sken
131040603Sken	softc->flags |= SA_FLAG_INVALID;
131140603Sken
131240603Sken	/*
131340603Sken	 * Although the oninvalidate() routines are always called at
131440603Sken	 * splsoftcam, we need to be at splbio() here to keep the buffer
131540603Sken	 * queue from being modified while we traverse it.
131640603Sken	 */
131740603Sken	s = splbio();
131840603Sken
131940603Sken	/*
132040603Sken	 * Return all queued I/O with ENXIO.
132140603Sken	 * XXX Handle any transactions queued to the card
132240603Sken	 *     with XPT_ABORT_CCB.
132340603Sken	 */
132459249Sphk	while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){
132559249Sphk		bioq_remove(&softc->bio_queue, q_bp);
132659249Sphk		q_bp->bio_resid = q_bp->bio_bcount;
132759249Sphk		q_bp->bio_error = ENXIO;
132859249Sphk		q_bp->bio_flags |= BIO_ERROR;
132940603Sken		biodone(q_bp);
133040603Sken	}
133146962Smjacob	softc->queue_count = 0;
133240603Sken	splx(s);
133340603Sken
133440603Sken	xpt_print_path(periph->path);
133540603Sken	printf("lost device\n");
133640603Sken
133740603Sken}
133840603Sken
133940603Skenstatic void
134039213Sgibbssacleanup(struct cam_periph *periph)
134139213Sgibbs{
134240603Sken	struct sa_softc *softc;
134353259Smjacob	int i;
134440603Sken
134540603Sken	softc = (struct sa_softc *)periph->softc;
134640603Sken
134740603Sken	devstat_remove_entry(&softc->device_stats);
134853259Smjacob
134953259Smjacob	destroy_dev(softc->devs.ctl_dev);
135053259Smjacob
135153259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
135253259Smjacob		destroy_dev(softc->devs.mode_devs[i].r_dev);
135353259Smjacob		destroy_dev(softc->devs.mode_devs[i].nr_dev);
135453259Smjacob		destroy_dev(softc->devs.mode_devs[i].er_dev);
135553259Smjacob	}
135653259Smjacob
135739213Sgibbs	cam_extend_release(saperiphs, periph->unit_number);
135839213Sgibbs	xpt_print_path(periph->path);
135939213Sgibbs	printf("removing device entry\n");
136040603Sken	free(softc, M_DEVBUF);
136139213Sgibbs}
136239213Sgibbs
136339213Sgibbsstatic void
136439213Sgibbssaasync(void *callback_arg, u_int32_t code,
136539213Sgibbs	struct cam_path *path, void *arg)
136639213Sgibbs{
136739213Sgibbs	struct cam_periph *periph;
136839213Sgibbs
136939213Sgibbs	periph = (struct cam_periph *)callback_arg;
137039213Sgibbs	switch (code) {
137139213Sgibbs	case AC_FOUND_DEVICE:
137239213Sgibbs	{
137339213Sgibbs		struct ccb_getdev *cgd;
137439213Sgibbs		cam_status status;
137539213Sgibbs
137639213Sgibbs		cgd = (struct ccb_getdev *)arg;
137739213Sgibbs
137856148Smjacob		if (SID_TYPE(&cgd->inq_data) != T_SEQUENTIAL)
137939213Sgibbs			break;
138039213Sgibbs
138139213Sgibbs		/*
138239213Sgibbs		 * Allocate a peripheral instance for
138339213Sgibbs		 * this device and start the probe
138439213Sgibbs		 * process.
138539213Sgibbs		 */
138640603Sken		status = cam_periph_alloc(saregister, saoninvalidate,
138740603Sken					  sacleanup, sastart,
138839213Sgibbs					  "sa", CAM_PERIPH_BIO, cgd->ccb_h.path,
138939213Sgibbs					  saasync, AC_FOUND_DEVICE, cgd);
139039213Sgibbs
139139213Sgibbs		if (status != CAM_REQ_CMP
139239213Sgibbs		 && status != CAM_REQ_INPROG)
139339213Sgibbs			printf("saasync: Unable to probe new device "
139439213Sgibbs				"due to status 0x%x\n", status);
139539213Sgibbs		break;
139639213Sgibbs	}
139739213Sgibbs	default:
139847413Sgibbs		cam_periph_async(periph, code, path, arg);
139939213Sgibbs		break;
140039213Sgibbs	}
140139213Sgibbs}
140239213Sgibbs
140339213Sgibbsstatic cam_status
140439213Sgibbssaregister(struct cam_periph *periph, void *arg)
140539213Sgibbs{
140639213Sgibbs	struct sa_softc *softc;
140739213Sgibbs	struct ccb_setasync csa;
140839213Sgibbs	struct ccb_getdev *cgd;
140939213Sgibbs	caddr_t match;
141053259Smjacob	int i;
141139213Sgibbs
141239213Sgibbs	cgd = (struct ccb_getdev *)arg;
141339213Sgibbs	if (periph == NULL) {
141439213Sgibbs		printf("saregister: periph was NULL!!\n");
141554099Smjacob		return (CAM_REQ_CMP_ERR);
141639213Sgibbs	}
141739213Sgibbs
141839213Sgibbs	if (cgd == NULL) {
141939213Sgibbs		printf("saregister: no getdev CCB, can't register device\n");
142054099Smjacob		return (CAM_REQ_CMP_ERR);
142139213Sgibbs	}
142239213Sgibbs
142367723Smjacob	softc = (struct sa_softc *)
142467723Smjacob	    malloc(sizeof (*softc), M_DEVBUF, M_NOWAIT | M_ZERO);
142539213Sgibbs	if (softc == NULL) {
142639213Sgibbs		printf("saregister: Unable to probe new device. "
142739213Sgibbs		       "Unable to allocate softc\n");
142854099Smjacob		return (CAM_REQ_CMP_ERR);
142939213Sgibbs	}
143041674Smjacob	softc->scsi_rev = SID_ANSI_REV(&cgd->inq_data);
143139213Sgibbs	softc->state = SA_STATE_NORMAL;
143243636Smjacob	softc->fileno = (daddr_t) -1;
143343636Smjacob	softc->blkno = (daddr_t) -1;
143443636Smjacob
143559249Sphk	bioq_init(&softc->bio_queue);
143639213Sgibbs	periph->softc = softc;
143739213Sgibbs	cam_extend_set(saperiphs, periph->unit_number, periph);
143839213Sgibbs
143939213Sgibbs	/*
144039213Sgibbs	 * See if this device has any quirks.
144139213Sgibbs	 */
144239213Sgibbs	match = cam_quirkmatch((caddr_t)&cgd->inq_data,
144339213Sgibbs			       (caddr_t)sa_quirk_table,
144439213Sgibbs			       sizeof(sa_quirk_table)/sizeof(*sa_quirk_table),
144539213Sgibbs			       sizeof(*sa_quirk_table), scsi_inquiry_match);
144639213Sgibbs
144742563Smjacob	if (match != NULL) {
144839213Sgibbs		softc->quirks = ((struct sa_quirk_entry *)match)->quirks;
144942563Smjacob		softc->last_media_blksize =
145042563Smjacob		    ((struct sa_quirk_entry *)match)->prefblk;
145142563Smjacob#ifdef	CAMDEBUG
145242563Smjacob		xpt_print_path(periph->path);
145343651Smjacob		printf("found quirk entry %d\n", (int)
145443651Smjacob		    (((struct sa_quirk_entry *) match) - sa_quirk_table));
145542563Smjacob#endif
145642563Smjacob	} else
145739213Sgibbs		softc->quirks = SA_QUIRK_NONE;
145839213Sgibbs
145939213Sgibbs	/*
146039213Sgibbs 	 * The SA driver supports a blocksize, but we don't know the
146146962Smjacob	 * blocksize until we media is inserted.  So, set a flag to
146239213Sgibbs	 * indicate that the blocksize is unavailable right now.
146339213Sgibbs	 */
146453259Smjacob	devstat_add_entry(&softc->device_stats, "sa", periph->unit_number, 0,
146556148Smjacob	    DEVSTAT_BS_UNAVAILABLE, SID_TYPE(&cgd->inq_data) |
146656148Smjacob	    DEVSTAT_TYPE_IF_SCSI, DEVSTAT_PRIORITY_TAPE);
146753259Smjacob
146853259Smjacob	softc->devs.ctl_dev = make_dev(&sa_cdevsw, SAMINOR(SA_CTLDEV,
146953259Smjacob	    periph->unit_number, 0, SA_ATYPE_R), UID_ROOT, GID_OPERATOR,
147053259Smjacob	    0660, "r%s%d.ctl", periph->periph_name, periph->unit_number);
147153259Smjacob
147253259Smjacob	for (i = 0; i < SA_NUM_MODES; i++) {
147353283Smjacob
147453259Smjacob		softc->devs.mode_devs[i].r_dev = make_dev(&sa_cdevsw,
147553259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_R),
147653259Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "r%s%d.%d",
147753259Smjacob		    periph->periph_name, periph->unit_number, i);
147853283Smjacob
147953259Smjacob		softc->devs.mode_devs[i].nr_dev = make_dev(&sa_cdevsw,
148053259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_NR),
148153259Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "nr%s%d.%d",
148253259Smjacob		    periph->periph_name, periph->unit_number, i);
148353259Smjacob
148453283Smjacob
148553283Smjacob		softc->devs.mode_devs[i].er_dev = make_dev(&sa_cdevsw,
148653259Smjacob		    SAMINOR(SA_NOT_CTLDEV, periph->unit_number, i, SA_ATYPE_ER),
148753259Smjacob		    UID_ROOT, GID_OPERATOR, 0660, "er%s%d.%d",
148853259Smjacob		    periph->periph_name, periph->unit_number, i);
148965838Smjacob
149065838Smjacob		/*
149165838Smjacob		 * Make the (well known) aliases for the first mode.
149265838Smjacob		 */
149365838Smjacob		if (i == 0) {
149465838Smjacob			make_dev_alias(softc->devs.mode_devs[i].r_dev,
149565838Smjacob			   "r%s%d", periph->periph_name, periph->unit_number);
149665838Smjacob			make_dev_alias(softc->devs.mode_devs[i].nr_dev,
149765838Smjacob			    "nr%s%d", periph->periph_name, periph->unit_number);
149865838Smjacob			make_dev_alias(softc->devs.mode_devs[i].er_dev,
149965838Smjacob			    "er%s%d", periph->periph_name, periph->unit_number);
150065838Smjacob		}
150153259Smjacob	}
150253259Smjacob
150339213Sgibbs	/*
150439213Sgibbs	 * Add an async callback so that we get
150539213Sgibbs	 * notified if this device goes away.
150639213Sgibbs	 */
150739213Sgibbs	xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5);
150839213Sgibbs	csa.ccb_h.func_code = XPT_SASYNC_CB;
150939213Sgibbs	csa.event_enable = AC_LOST_DEVICE;
151039213Sgibbs	csa.callback = saasync;
151139213Sgibbs	csa.callback_arg = periph;
151239213Sgibbs	xpt_action((union ccb *)&csa);
151339213Sgibbs
151439213Sgibbs	xpt_announce_periph(periph, NULL);
151539213Sgibbs
151654099Smjacob	return (CAM_REQ_CMP);
151739213Sgibbs}
151839213Sgibbs
151939213Sgibbsstatic void
152039213Sgibbssastart(struct cam_periph *periph, union ccb *start_ccb)
152139213Sgibbs{
152239213Sgibbs	struct sa_softc *softc;
152339213Sgibbs
152439213Sgibbs	softc = (struct sa_softc *)periph->softc;
152539213Sgibbs
152641948Smjacob	CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("sastart"));
152771082Smjacob
152839213Sgibbs
152939213Sgibbs	switch (softc->state) {
153039213Sgibbs	case SA_STATE_NORMAL:
153139213Sgibbs	{
153239213Sgibbs		/* Pull a buffer from the queue and get going on it */
153359249Sphk		struct bio *bp;
153439213Sgibbs		int s;
153539213Sgibbs
153639213Sgibbs		/*
153739213Sgibbs		 * See if there is a buf with work for us to do..
153839213Sgibbs		 */
153939213Sgibbs		s = splbio();
154059249Sphk		bp = bioq_first(&softc->bio_queue);
154139213Sgibbs		if (periph->immediate_priority <= periph->pinfo.priority) {
154239213Sgibbs			CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE,
154339213Sgibbs					("queuing for immediate ccb\n"));
154471082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_WAITING);
154539213Sgibbs			SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h,
154639213Sgibbs					  periph_links.sle);
154739213Sgibbs			periph->immediate_priority = CAM_PRIORITY_NONE;
154839213Sgibbs			splx(s);
154939213Sgibbs			wakeup(&periph->ccb_list);
155039213Sgibbs		} else if (bp == NULL) {
155139213Sgibbs			splx(s);
155239213Sgibbs			xpt_release_ccb(start_ccb);
155339213Sgibbs		} else if ((softc->flags & SA_FLAG_ERR_PENDING) != 0) {
155459249Sphk			struct bio *done_bp;
155546962Smjacob			softc->queue_count--;
155659249Sphk			bioq_remove(&softc->bio_queue, bp);
155759249Sphk			bp->bio_resid = bp->bio_bcount;
155859249Sphk			bp->bio_flags |= BIO_ERROR;
155939213Sgibbs			if ((softc->flags & SA_FLAG_EOM_PENDING) != 0) {
156059249Sphk				if (bp->bio_cmd == BIO_WRITE)
156159249Sphk					bp->bio_error = ENOSPC;
156244354Smjacob				else
156359249Sphk					bp->bio_error = EIO;
156439213Sgibbs			}
156541948Smjacob			if ((softc->flags & SA_FLAG_EOF_PENDING) != 0) {
156659249Sphk				bp->bio_error = EIO;
156741948Smjacob			}
156839213Sgibbs			if ((softc->flags & SA_FLAG_EIO_PENDING) != 0) {
156959249Sphk				bp->bio_error = EIO;
157039213Sgibbs			}
157141948Smjacob			done_bp = bp;
157259249Sphk			bp = bioq_first(&softc->bio_queue);
157344354Smjacob			/*
157444354Smjacob			 * Only if we have no other buffers queued up
157544354Smjacob			 * do we clear the pending error flag.
157644354Smjacob			 */
157744354Smjacob			if (bp == NULL)
157844354Smjacob				softc->flags &= ~SA_FLAG_ERR_PENDING;
157944354Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
158046962Smjacob			    ("sastart- ERR_PENDING now 0x%x, bp is %sNULL, "
158146962Smjacob			    "%d more buffers queued up\n",
158244354Smjacob			    (softc->flags & SA_FLAG_ERR_PENDING),
158346962Smjacob			    (bp != NULL)? "not " : " ", softc->queue_count));
158439213Sgibbs			splx(s);
158544354Smjacob			xpt_release_ccb(start_ccb);
158641948Smjacob			biodone(done_bp);
158739213Sgibbs		} else {
158839213Sgibbs			u_int32_t length;
158939213Sgibbs
159059249Sphk			bioq_remove(&softc->bio_queue, bp);
159146962Smjacob			softc->queue_count--;
159239213Sgibbs
159339213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0) {
159439213Sgibbs				if (softc->blk_shift != 0) {
159539213Sgibbs					length =
159659249Sphk					    bp->bio_bcount >> softc->blk_shift;
159743636Smjacob				} else if (softc->media_blksize != 0) {
159871082Smjacob					length = bp->bio_bcount /
159971082Smjacob					    softc->media_blksize;
160043636Smjacob				} else {
160159249Sphk					bp->bio_error = EIO;
160243636Smjacob					xpt_print_path(periph->path);
160343636Smjacob					printf("zero blocksize for "
160443636Smjacob					    "FIXED length writes?\n");
160543636Smjacob					splx(s);
160643636Smjacob					biodone(bp);
160743636Smjacob					break;
160839213Sgibbs				}
160941906Smjacob				CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
161041906Smjacob				    ("Fixed Record Count is %d\n", length));
161139213Sgibbs			} else {
161259249Sphk				length = bp->bio_bcount;
161341906Smjacob				CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_INFO,
161441906Smjacob				    ("Variable Record Count is %d\n", length));
161539213Sgibbs			}
161639213Sgibbs			devstat_start_transaction(&softc->device_stats);
161739213Sgibbs			/*
161841906Smjacob			 * Some people have theorized that we should
161939213Sgibbs			 * suppress illegal length indication if we are
162039213Sgibbs			 * running in variable block mode so that we don't
162139213Sgibbs			 * have to request sense every time our requested
162239213Sgibbs			 * block size is larger than the written block.
162339213Sgibbs			 * The residual information from the ccb allows
162439213Sgibbs			 * us to identify this situation anyway.  The only
162539213Sgibbs			 * problem with this is that we will not get
162639213Sgibbs			 * information about blocks that are larger than
162739213Sgibbs			 * our read buffer unless we set the block size
162839213Sgibbs			 * in the mode page to something other than 0.
162941906Smjacob			 *
163041906Smjacob			 * I believe that this is a non-issue. If user apps
163141906Smjacob			 * don't adjust their read size to match our record
163241906Smjacob			 * size, that's just life. Anyway, the typical usage
163341906Smjacob			 * would be to issue, e.g., 64KB reads and occasionally
163441906Smjacob			 * have to do deal with 512 byte or 1KB intermediate
163541906Smjacob			 * records.
163639213Sgibbs			 */
163759249Sphk			softc->dsreg = (bp->bio_cmd == BIO_READ)?
163843636Smjacob			    MTIO_DSREG_RD : MTIO_DSREG_WR;
163946962Smjacob			scsi_sa_read_write(&start_ccb->csio, 0, sadone,
164059249Sphk			    MSG_SIMPLE_Q_TAG, (bp->bio_cmd == BIO_READ),
164146962Smjacob			    FALSE, (softc->flags & SA_FLAG_FIXED) != 0,
164259249Sphk			    length, bp->bio_data, bp->bio_bcount, SSD_FULL_SIZE,
164346962Smjacob			    120 * 60 * 1000);
164471082Smjacob			start_ccb->ccb_h.ccb_pflags &= ~SA_POSITION_UPDATED;
164571082Smjacob			Set_CCB_Type(start_ccb, SA_CCB_BUFFER_IO);
164639213Sgibbs			start_ccb->ccb_h.ccb_bp = bp;
164759249Sphk			bp = bioq_first(&softc->bio_queue);
164839213Sgibbs			splx(s);
164939213Sgibbs			xpt_action(start_ccb);
165039213Sgibbs		}
165139213Sgibbs
165239213Sgibbs		if (bp != NULL) {
165339213Sgibbs			/* Have more work to do, so ensure we stay scheduled */
165446962Smjacob			xpt_schedule(periph, 1);
165539213Sgibbs		}
165639213Sgibbs		break;
165739213Sgibbs	}
165846962Smjacob	case SA_STATE_ABNORMAL:
165946962Smjacob	default:
166046962Smjacob		panic("state 0x%x in sastart", softc->state);
166146962Smjacob		break;
166239213Sgibbs	}
166339213Sgibbs}
166439213Sgibbs
166539213Sgibbs
166639213Sgibbsstatic void
166739213Sgibbssadone(struct cam_periph *periph, union ccb *done_ccb)
166839213Sgibbs{
166939213Sgibbs	struct sa_softc *softc;
167039213Sgibbs	struct ccb_scsiio *csio;
167139213Sgibbs
167239213Sgibbs	softc = (struct sa_softc *)periph->softc;
167339213Sgibbs	csio = &done_ccb->csio;
167471082Smjacob	switch (CCB_Type(csio)) {
167539213Sgibbs	case SA_CCB_BUFFER_IO:
167639213Sgibbs	{
167759249Sphk		struct bio *bp;
167839213Sgibbs		int error;
167939213Sgibbs
168043636Smjacob		softc->dsreg = MTIO_DSREG_REST;
168159249Sphk		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
168239213Sgibbs		error = 0;
168339213Sgibbs		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
168439213Sgibbs			if ((error = saerror(done_ccb, 0, 0)) == ERESTART) {
168539213Sgibbs				/*
168641948Smjacob				 * A retry was scheduled, so just return.
168739213Sgibbs				 */
168839213Sgibbs				return;
168939213Sgibbs			}
169039213Sgibbs		}
169139213Sgibbs
169239213Sgibbs		if (error == EIO) {
169339213Sgibbs			int s;
169459249Sphk			struct bio *q_bp;
169539213Sgibbs
169639213Sgibbs			/*
169753522Smjacob			 * Catastrophic error. Mark the tape as frozen
169853522Smjacob			 * (we no longer know tape position).
169953522Smjacob			 *
170044354Smjacob			 * Return all queued I/O with EIO, and unfreeze
170139213Sgibbs			 * our queue so that future transactions that
170239213Sgibbs			 * attempt to fix this problem can get to the
170339213Sgibbs			 * device.
170439213Sgibbs			 *
170539213Sgibbs			 */
170639213Sgibbs
170739213Sgibbs			s = splbio();
170853522Smjacob			softc->flags |= SA_FLAG_TAPE_FROZEN;
170959249Sphk			while ((q_bp = bioq_first(&softc->bio_queue)) != NULL) {
171059249Sphk				bioq_remove(&softc->bio_queue, q_bp);
171159249Sphk				q_bp->bio_resid = q_bp->bio_bcount;
171259249Sphk				q_bp->bio_error = EIO;
171359249Sphk				q_bp->bio_flags |= BIO_ERROR;
171439213Sgibbs				biodone(q_bp);
171539213Sgibbs			}
171639213Sgibbs			splx(s);
171739213Sgibbs		}
171839213Sgibbs		if (error != 0) {
171959249Sphk			bp->bio_resid = bp->bio_bcount;
172059249Sphk			bp->bio_error = error;
172159249Sphk			bp->bio_flags |= BIO_ERROR;
172243636Smjacob			/*
172343636Smjacob			 * In the error case, position is updated in saerror.
172443636Smjacob			 */
172539213Sgibbs		} else {
172659249Sphk			bp->bio_resid = csio->resid;
172759249Sphk			bp->bio_error = 0;
172839213Sgibbs			if (csio->resid != 0) {
172959249Sphk				bp->bio_flags |= BIO_ERROR;
173039213Sgibbs			}
173159249Sphk			if (bp->bio_cmd == BIO_WRITE) {
173239213Sgibbs				softc->flags |= SA_FLAG_TAPE_WRITTEN;
173339213Sgibbs				softc->filemarks = 0;
173439213Sgibbs			}
173571082Smjacob			if (!(csio->ccb_h.ccb_pflags & SA_POSITION_UPDATED) &&
173671082Smjacob			    (softc->blkno != (daddr_t) -1)) {
173743636Smjacob				if ((softc->flags & SA_FLAG_FIXED) != 0) {
173843636Smjacob					u_int32_t l;
173943636Smjacob					if (softc->blk_shift != 0) {
174059249Sphk						l = bp->bio_bcount >>
174143636Smjacob							softc->blk_shift;
174243636Smjacob					} else {
174359249Sphk						l = bp->bio_bcount /
174443636Smjacob							softc->media_blksize;
174543636Smjacob					}
174643636Smjacob					softc->blkno += (daddr_t) l;
174743636Smjacob				} else {
174843636Smjacob					softc->blkno++;
174943636Smjacob				}
175043636Smjacob			}
175139213Sgibbs		}
175246962Smjacob		/*
175346962Smjacob		 * If we had an error (immediate or pending),
175446962Smjacob		 * release the device queue now.
175546962Smjacob		 */
175646962Smjacob		if (error || (softc->flags & SA_FLAG_ERR_PENDING))
175746962Smjacob			cam_release_devq(done_ccb->ccb_h.path, 0, 0, 0, 0);
175841674Smjacob#ifdef	CAMDEBUG
175959249Sphk		if (error || bp->bio_resid) {
176041674Smjacob			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
176141674Smjacob			    	  ("error %d resid %ld count %ld\n", error,
176259249Sphk				  bp->bio_resid, bp->bio_bcount));
176341674Smjacob		}
176441674Smjacob#endif
176559249Sphk		devstat_end_transaction_bio(&softc->device_stats, bp);
176639213Sgibbs		biodone(bp);
176739213Sgibbs		break;
176839213Sgibbs	}
176939213Sgibbs	case SA_CCB_WAITING:
177039213Sgibbs	{
177139213Sgibbs		/* Caller will release the CCB */
177239213Sgibbs		wakeup(&done_ccb->ccb_h.cbfcnp);
177339213Sgibbs		return;
177439213Sgibbs	}
177539213Sgibbs	}
177639213Sgibbs	xpt_release_ccb(done_ccb);
177739213Sgibbs}
177839213Sgibbs
177941906Smjacob/*
178041906Smjacob * Mount the tape (make sure it's ready for I/O).
178141906Smjacob */
178239213Sgibbsstatic int
178341906Smjacobsamount(struct cam_periph *periph, int oflags, dev_t dev)
178439213Sgibbs{
178539213Sgibbs	struct	sa_softc *softc;
178639213Sgibbs	union	ccb *ccb;
178739213Sgibbs	int	error;
178839213Sgibbs
178941906Smjacob	/*
179041906Smjacob	 * oflags can be checked for 'kind' of open (read-only check) - later
179141906Smjacob	 * dev can be checked for a control-mode or compression open - later
179241906Smjacob	 */
179341906Smjacob	UNUSED_PARAMETER(oflags);
179441906Smjacob	UNUSED_PARAMETER(dev);
179541906Smjacob
179641906Smjacob
179739213Sgibbs	softc = (struct sa_softc *)periph->softc;
179839213Sgibbs
179939213Sgibbs	/*
180053259Smjacob	 * This should determine if something has happend since the last
180153259Smjacob	 * open/mount that would invalidate the mount. We do *not* want
180253259Smjacob	 * to retry this command- we just want the status. But we only
180353259Smjacob	 * do this if we're mounted already- if we're not mounted,
180453259Smjacob	 * we don't care about the unit read state and can instead use
180553259Smjacob	 * this opportunity to attempt to reserve the tape unit.
180639213Sgibbs	 */
180753259Smjacob
180853259Smjacob	if (softc->flags & SA_FLAG_TAPE_MOUNTED) {
180953259Smjacob		ccb = cam_periph_getccb(periph, 1);
181053259Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
181153259Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5 * 60 * 1000);
181254099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
181353259Smjacob		    &softc->device_stats);
181453259Smjacob		QFRLS(ccb);
181553259Smjacob		if (error == ENXIO) {
181653259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
181753259Smjacob			scsi_test_unit_ready(&ccb->csio, 0, sadone,
181853259Smjacob			    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5 * 60 * 1000);
181954099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
182053259Smjacob			    &softc->device_stats);
182153259Smjacob			QFRLS(ccb);
182253259Smjacob		} else if (error) {
182354099Smjacob			/*
182454099Smjacob			 * We don't need to freeze the tape because we
182554099Smjacob			 * will now attempt to rewind/load it.
182654099Smjacob			 */
182753259Smjacob			softc->flags &= ~SA_FLAG_TAPE_MOUNTED;
182854099Smjacob			if (CAM_DEBUGGED(ccb->ccb_h.path, CAM_DEBUG_INFO)) {
182954099Smjacob				xpt_print_path(ccb->ccb_h.path);
183054099Smjacob				printf("error %d on TUR in samount\n", error);
183154099Smjacob			}
183253259Smjacob		}
183353259Smjacob	} else {
183453259Smjacob		error = sareservereleaseunit(periph, TRUE);
183553259Smjacob		if (error) {
183653259Smjacob			return (error);
183753259Smjacob		}
183853259Smjacob		ccb = cam_periph_getccb(periph, 1);
183954105Smjacob		scsi_test_unit_ready(&ccb->csio, 0, sadone,
184054105Smjacob		    MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5 * 60 * 1000);
184154105Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
184254105Smjacob		    &softc->device_stats);
184354105Smjacob		QFRLS(ccb);
184453259Smjacob	}
184539213Sgibbs
184639213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_MOUNTED) == 0) {
184753259Smjacob		struct scsi_read_block_limits_data *rblim = NULL;
184853259Smjacob		int comp_enabled, comp_supported;
184941906Smjacob		u_int8_t write_protect, guessing = 0;
185039213Sgibbs
185139213Sgibbs		/*
185239213Sgibbs		 * Clear out old state.
185339213Sgibbs		 */
185439213Sgibbs		softc->flags &= ~(SA_FLAG_TAPE_WP|SA_FLAG_TAPE_WRITTEN|
185539213Sgibbs				  SA_FLAG_ERR_PENDING|SA_FLAG_COMP_ENABLED|
185646962Smjacob				  SA_FLAG_COMP_SUPP|SA_FLAG_COMP_UNSUPP);
185739213Sgibbs		softc->filemarks = 0;
185839213Sgibbs
185939213Sgibbs		/*
186043636Smjacob		 * *Very* first off, make sure we're loaded to BOT.
186139213Sgibbs		 */
186243636Smjacob		scsi_load_unload(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
186354099Smjacob		    FALSE, FALSE, 1, SSD_FULL_SIZE, REWIND_TIMEOUT);
186454099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
186543636Smjacob		    &softc->device_stats);
186653259Smjacob		QFRLS(ccb);
186753259Smjacob
186843636Smjacob		/*
186944354Smjacob		 * In case this doesn't work, do a REWIND instead
187043636Smjacob		 */
187144354Smjacob		if (error) {
187253259Smjacob			scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
187353259Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
187454099Smjacob			error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
187543636Smjacob				&softc->device_stats);
187653259Smjacob			QFRLS(ccb);
187743636Smjacob		}
187843636Smjacob		if (error) {
187943636Smjacob			xpt_release_ccb(ccb);
188043636Smjacob			goto exit;
188143636Smjacob		}
188243636Smjacob
188343636Smjacob		/*
188453259Smjacob		 * Do a dummy test read to force access to the
188553259Smjacob		 * media so that the drive will really know what's
188654099Smjacob		 * there. We actually don't really care what the
188754099Smjacob		 * blocksize on tape is and don't expect to really
188854099Smjacob		 * read a full record.
188943636Smjacob		 */
189039213Sgibbs		rblim = (struct  scsi_read_block_limits_data *)
189154099Smjacob		    malloc(8192, M_TEMP, M_WAITOK);
189253259Smjacob		if (rblim == NULL) {
189353259Smjacob			xpt_print_path(ccb->ccb_h.path);
189453259Smjacob			printf("no memory for test read\n");
189553259Smjacob			xpt_release_ccb(ccb);
189653259Smjacob			error = ENOMEM;
189753259Smjacob			goto exit;
189853259Smjacob		}
189956981Smjacob
190056981Smjacob		if ((softc->quirks & SA_QUIRK_NODREAD) == 0) {
190156981Smjacob			scsi_sa_read_write(&ccb->csio, 0, sadone,
190256981Smjacob			    MSG_SIMPLE_Q_TAG, 1, FALSE, 0, 8192,
190356981Smjacob			    (void *) rblim, 8192, SSD_FULL_SIZE,
190456981Smjacob			    120 * 60 * 1000);
190556981Smjacob			(void) cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
190656981Smjacob			    &softc->device_stats);
190756981Smjacob			QFRLS(ccb);
190856981Smjacob			scsi_rewind(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
190956981Smjacob			    FALSE, SSD_FULL_SIZE, REWIND_TIMEOUT);
191056981Smjacob			error = cam_periph_runccb(ccb, saerror, 0,
191156981Smjacob			    SF_NO_PRINT | SF_RETRY_SELTO | SF_RETRY_UA,
191256981Smjacob			    &softc->device_stats);
191356981Smjacob			QFRLS(ccb);
191456981Smjacob			if (error) {
191556981Smjacob				xpt_print_path(ccb->ccb_h.path);
191656981Smjacob				printf("unable to rewind after test read\n");
191756981Smjacob				xpt_release_ccb(ccb);
191856981Smjacob				goto exit;
191956981Smjacob			}
192053259Smjacob		}
192139213Sgibbs
192253259Smjacob		/*
192353259Smjacob		 * Next off, determine block limits.
192453259Smjacob		 */
192553259Smjacob		scsi_read_block_limits(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
192642563Smjacob		    rblim, SSD_FULL_SIZE, 5000);
192739213Sgibbs
192842563Smjacob		error = cam_periph_runccb(ccb, saerror, 0,
192954099Smjacob		    SF_NO_PRINT | SF_RETRY_UA | SF_RETRY_SELTO,
193054099Smjacob		    &softc->device_stats);
193153259Smjacob		QFRLS(ccb);
193239213Sgibbs		xpt_release_ccb(ccb);
193339213Sgibbs
193441674Smjacob		if (error != 0) {
193541674Smjacob			/*
193641674Smjacob			 * If it's less than SCSI-2, READ BLOCK LIMITS is not
193741674Smjacob			 * a MANDATORY command. Anyway- it doesn't matter-
193841674Smjacob			 * we can proceed anyway.
193941674Smjacob			 */
194041674Smjacob			softc->blk_gran = 0;
194141674Smjacob			softc->max_blk = ~0;
194241674Smjacob			softc->min_blk = 0;
194341674Smjacob		} else {
194441674Smjacob			if (softc->scsi_rev >= SCSI_REV_3) {
194541674Smjacob				softc->blk_gran = RBL_GRAN(rblim);
194641674Smjacob			} else {
194741674Smjacob				softc->blk_gran = 0;
194841674Smjacob			}
194941674Smjacob			/*
195041674Smjacob			 * We take max_blk == min_blk to mean a default to
195141674Smjacob			 * fixed mode- but note that whatever we get out of
195241674Smjacob			 * sagetparams below will actually determine whether
195341674Smjacob			 * we are actually *in* fixed mode.
195441674Smjacob			 */
195541674Smjacob			softc->max_blk = scsi_3btoul(rblim->maximum);
195641674Smjacob			softc->min_blk = scsi_2btoul(rblim->minimum);
195741674Smjacob
195841674Smjacob
195941674Smjacob		}
196041674Smjacob		/*
196141674Smjacob		 * Next, perform a mode sense to determine
196241674Smjacob		 * current density, blocksize, compression etc.
196341674Smjacob		 */
196441674Smjacob		error = sagetparams(periph, SA_PARAM_ALL,
196541674Smjacob				    &softc->media_blksize,
196641674Smjacob				    &softc->media_density,
196741674Smjacob				    &softc->media_numblks,
196841674Smjacob				    &softc->buffer_mode, &write_protect,
196941674Smjacob				    &softc->speed, &comp_supported,
197041674Smjacob				    &comp_enabled, &softc->comp_algorithm,
197141674Smjacob				    NULL);
197241674Smjacob
197341674Smjacob		if (error != 0) {
197441674Smjacob			/*
197541674Smjacob			 * We could work a little harder here. We could
197641674Smjacob			 * adjust our attempts to get information. It
197741674Smjacob			 * might be an ancient tape drive. If someone
197841674Smjacob			 * nudges us, we'll do that.
197941674Smjacob			 */
198039213Sgibbs			goto exit;
198141674Smjacob		}
198239213Sgibbs
198341906Smjacob		/*
198441906Smjacob		 * If no quirk has determined that this is a device that is
198541906Smjacob		 * preferred to be in fixed or variable mode, now is the time
198641906Smjacob		 * to find out.
198741906Smjacob	 	 */
198841906Smjacob		if ((softc->quirks & (SA_QUIRK_FIXED|SA_QUIRK_VARIABLE)) == 0) {
198941906Smjacob			guessing = 1;
199043636Smjacob			/*
199143636Smjacob			 * This could be expensive to find out. Luckily we
199243636Smjacob			 * only need to do this once. If we start out in
199343636Smjacob			 * 'default' mode, try and set ourselves to one
199443636Smjacob			 * of the densities that would determine a wad
199543636Smjacob			 * of other stuff. Go from highest to lowest.
199643636Smjacob			 */
199743636Smjacob			if (softc->media_density == SCSI_DEFAULT_DENSITY) {
199843636Smjacob				int i;
199943636Smjacob				static u_int8_t ctry[] = {
200043636Smjacob					SCSI_DENSITY_HALFINCH_PE,
200143636Smjacob					SCSI_DENSITY_HALFINCH_6250C,
200243636Smjacob					SCSI_DENSITY_HALFINCH_6250,
200343636Smjacob					SCSI_DENSITY_HALFINCH_1600,
200443636Smjacob					SCSI_DENSITY_HALFINCH_800,
200546962Smjacob					SCSI_DENSITY_QIC_4GB,
200646962Smjacob					SCSI_DENSITY_QIC_2GB,
200743636Smjacob					SCSI_DENSITY_QIC_525_320,
200843636Smjacob					SCSI_DENSITY_QIC_150,
200943636Smjacob					SCSI_DENSITY_QIC_120,
201043636Smjacob					SCSI_DENSITY_QIC_24,
201143636Smjacob					SCSI_DENSITY_QIC_11_9TRK,
201243636Smjacob					SCSI_DENSITY_QIC_11_4TRK,
201346962Smjacob					SCSI_DENSITY_QIC_1320,
201446962Smjacob					SCSI_DENSITY_QIC_3080,
201543636Smjacob					0
201643636Smjacob				};
201743636Smjacob				for (i = 0; ctry[i]; i++) {
201843636Smjacob					error = sasetparams(periph,
201943636Smjacob					    SA_PARAM_DENSITY, 0, ctry[i],
202043636Smjacob					    0, SF_NO_PRINT);
202143636Smjacob					if (error == 0) {
202243636Smjacob						softc->media_density = ctry[i];
202343636Smjacob						break;
202443636Smjacob					}
202543636Smjacob				}
202643636Smjacob			}
202741906Smjacob			switch (softc->media_density) {
202841906Smjacob			case SCSI_DENSITY_QIC_11_4TRK:
202941906Smjacob			case SCSI_DENSITY_QIC_11_9TRK:
203041906Smjacob			case SCSI_DENSITY_QIC_24:
203141906Smjacob			case SCSI_DENSITY_QIC_120:
203241906Smjacob			case SCSI_DENSITY_QIC_150:
203365861Smjacob			case SCSI_DENSITY_QIC_525_320:
203443636Smjacob			case SCSI_DENSITY_QIC_1320:
203543636Smjacob			case SCSI_DENSITY_QIC_3080:
203646962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
203743636Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
203841906Smjacob				softc->last_media_blksize = 512;
203941906Smjacob				break;
204046962Smjacob			case SCSI_DENSITY_QIC_4GB:
204146962Smjacob			case SCSI_DENSITY_QIC_2GB:
204246962Smjacob				softc->quirks &= ~SA_QUIRK_2FM;
204346962Smjacob				softc->quirks |= SA_QUIRK_FIXED|SA_QUIRK_1FM;
204446962Smjacob				softc->last_media_blksize = 1024;
204546962Smjacob				break;
204641906Smjacob			default:
204741906Smjacob				softc->last_media_blksize =
204841906Smjacob				    softc->media_blksize;
204941906Smjacob				softc->quirks |= SA_QUIRK_VARIABLE;
205041906Smjacob				break;
205141906Smjacob			}
205241906Smjacob		}
205342563Smjacob
205441906Smjacob		/*
205541906Smjacob		 * If no quirk has determined that this is a device that needs
205641906Smjacob		 * to have 2 Filemarks at EOD, now is the time to find out.
205741906Smjacob		 */
205842563Smjacob
205942735Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0) {
206041906Smjacob			switch (softc->media_density) {
206141906Smjacob			case SCSI_DENSITY_HALFINCH_800:
206241906Smjacob			case SCSI_DENSITY_HALFINCH_1600:
206341906Smjacob			case SCSI_DENSITY_HALFINCH_6250:
206441906Smjacob			case SCSI_DENSITY_HALFINCH_6250C:
206541906Smjacob			case SCSI_DENSITY_HALFINCH_PE:
206646962Smjacob				softc->quirks &= ~SA_QUIRK_1FM;
206741906Smjacob				softc->quirks |= SA_QUIRK_2FM;
206841906Smjacob				break;
206941906Smjacob			default:
207041906Smjacob				break;
207141906Smjacob			}
207241906Smjacob		}
207341906Smjacob
207441906Smjacob		/*
207541906Smjacob		 * Now validate that some info we got makes sense.
207641906Smjacob		 */
207741674Smjacob		if ((softc->max_blk < softc->media_blksize) ||
207841674Smjacob		    (softc->min_blk > softc->media_blksize &&
207941674Smjacob		    softc->media_blksize)) {
208041674Smjacob			xpt_print_path(ccb->ccb_h.path);
208141674Smjacob			printf("BLOCK LIMITS (%d..%d) could not match current "
208241674Smjacob			    "block settings (%d)- adjusting\n", softc->min_blk,
208341674Smjacob			    softc->max_blk, softc->media_blksize);
208441674Smjacob			softc->max_blk = softc->min_blk =
208541674Smjacob			    softc->media_blksize;
208641674Smjacob		}
208741906Smjacob
208841674Smjacob		/*
208941906Smjacob		 * Now put ourselves into the right frame of mind based
209041906Smjacob		 * upon quirks...
209141906Smjacob		 */
209241906Smjacobtryagain:
209342563Smjacob		/*
209442563Smjacob		 * If we want to be in FIXED mode and our current blocksize
209542563Smjacob		 * is not equal to our last blocksize (if nonzero), try and
209642563Smjacob		 * set ourselves to this last blocksize (as the 'preferred'
209742563Smjacob		 * block size).  The initial quirkmatch at registry sets the
209842563Smjacob		 * initial 'last' blocksize. If, for whatever reason, this
209942563Smjacob		 * 'last' blocksize is zero, set the blocksize to 512,
210042563Smjacob		 * or min_blk if that's larger.
210142563Smjacob		 */
210241906Smjacob		if ((softc->quirks & SA_QUIRK_FIXED) &&
210360235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0 &&
210442563Smjacob		    (softc->media_blksize != softc->last_media_blksize)) {
210541906Smjacob			softc->media_blksize = softc->last_media_blksize;
210641906Smjacob			if (softc->media_blksize == 0) {
210742563Smjacob				softc->media_blksize = 512;
210841906Smjacob				if (softc->media_blksize < softc->min_blk) {
210941906Smjacob					softc->media_blksize = softc->min_blk;
211041906Smjacob				}
211141906Smjacob			}
211241906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
211342563Smjacob			    softc->media_blksize, 0, 0, SF_NO_PRINT);
211441906Smjacob			if (error) {
211541906Smjacob				xpt_print_path(ccb->ccb_h.path);
211641906Smjacob				printf("unable to set fixed blocksize to %d\n",
211741906Smjacob				     softc->media_blksize);
211841906Smjacob				goto exit;
211941906Smjacob			}
212041906Smjacob		}
212141906Smjacob
212241906Smjacob		if ((softc->quirks & SA_QUIRK_VARIABLE) &&
212341906Smjacob		    (softc->media_blksize != 0)) {
212441906Smjacob			softc->last_media_blksize = softc->media_blksize;
212541906Smjacob			softc->media_blksize = 0;
212641906Smjacob			error = sasetparams(periph, SA_PARAM_BLOCKSIZE,
212742563Smjacob			    0, 0, 0, SF_NO_PRINT);
212841906Smjacob			if (error) {
212941906Smjacob				/*
213041906Smjacob				 * If this fails and we were guessing, just
213141906Smjacob				 * assume that we got it wrong and go try
213242563Smjacob				 * fixed block mode. Don't even check against
213342563Smjacob				 * density code at this point.
213441906Smjacob				 */
213542563Smjacob				if (guessing) {
213641906Smjacob					softc->quirks &= ~SA_QUIRK_VARIABLE;
213741906Smjacob					softc->quirks |= SA_QUIRK_FIXED;
213841906Smjacob					if (softc->last_media_blksize == 0)
213941906Smjacob						softc->last_media_blksize = 512;
214041906Smjacob					goto tryagain;
214141906Smjacob				}
214242563Smjacob				xpt_print_path(ccb->ccb_h.path);
214341906Smjacob				printf("unable to set variable blocksize\n");
214441906Smjacob				goto exit;
214541906Smjacob			}
214641906Smjacob		}
214741906Smjacob
214841906Smjacob		/*
214941674Smjacob		 * Now that we have the current block size,
215041674Smjacob		 * set up some parameters for sastart's usage.
215141674Smjacob		 */
215241674Smjacob		if (softc->media_blksize) {
215339213Sgibbs			softc->flags |= SA_FLAG_FIXED;
215441674Smjacob			if (powerof2(softc->media_blksize)) {
215541674Smjacob				softc->blk_shift =
215641674Smjacob				    ffs(softc->media_blksize) - 1;
215741674Smjacob				softc->blk_mask = softc->media_blksize - 1;
215839213Sgibbs			} else {
215939213Sgibbs				softc->blk_mask = ~0;
216039213Sgibbs				softc->blk_shift = 0;
216139213Sgibbs			}
216239213Sgibbs		} else {
216339213Sgibbs			/*
216441674Smjacob			 * The SCSI-3 spec allows 0 to mean "unspecified".
216541674Smjacob			 * The SCSI-1 spec allows 0 to mean 'infinite'.
216641674Smjacob			 *
216741674Smjacob			 * Either works here.
216839213Sgibbs			 */
216939213Sgibbs			if (softc->max_blk == 0) {
217039213Sgibbs				softc->max_blk = ~0;
217139213Sgibbs			}
217239213Sgibbs			softc->blk_shift = 0;
217339213Sgibbs			if (softc->blk_gran != 0) {
217439213Sgibbs				softc->blk_mask = softc->blk_gran - 1;
217539213Sgibbs			} else {
217639213Sgibbs				softc->blk_mask = 0;
217739213Sgibbs			}
217839213Sgibbs		}
217939213Sgibbs
218039213Sgibbs		if (write_protect)
218139213Sgibbs			softc->flags |= SA_FLAG_TAPE_WP;
218239213Sgibbs
218339213Sgibbs		if (comp_supported) {
218443636Smjacob			if (softc->saved_comp_algorithm == 0)
218543636Smjacob				softc->saved_comp_algorithm =
218643636Smjacob				    softc->comp_algorithm;
218746962Smjacob			softc->flags |= SA_FLAG_COMP_SUPP;
218846962Smjacob			if (comp_enabled)
218946962Smjacob				softc->flags |= SA_FLAG_COMP_ENABLED;
219039213Sgibbs		} else
219139213Sgibbs			softc->flags |= SA_FLAG_COMP_UNSUPP;
219239213Sgibbs
219360235Smjacob		if ((softc->buffer_mode == SMH_SA_BUF_MODE_NOBUF) &&
219460235Smjacob		    (softc->quirks & SA_QUIRK_NO_MODESEL) == 0) {
219541906Smjacob			error = sasetparams(periph, SA_PARAM_BUFF_MODE, 0,
219642563Smjacob			    0, 0, SF_NO_PRINT);
219741906Smjacob			if (error == 0)
219841906Smjacob				softc->buffer_mode = SMH_SA_BUF_MODE_SIBUF;
219960235Smjacob			xpt_print_path(ccb->ccb_h.path);
220060235Smjacob			printf("unable to set buffered mode\n");
220160235Smjacob			error = 0;	/* not an error */
220241906Smjacob		}
220339213Sgibbs
220439213Sgibbs
220544354Smjacob		if (error == 0) {
220641906Smjacob			softc->flags |= SA_FLAG_TAPE_MOUNTED;
220744354Smjacob		}
220839213Sgibbsexit:
220939213Sgibbs		if (rblim != NULL)
221039213Sgibbs			free(rblim, M_TEMP);
221139213Sgibbs
221243636Smjacob		if (error != 0) {
221343636Smjacob			softc->dsreg = MTIO_DSREG_NIL;
221444354Smjacob		} else {
221544354Smjacob			softc->fileno = softc->blkno = 0;
221643636Smjacob			softc->dsreg = MTIO_DSREG_REST;
221744354Smjacob		}
221851875Smjacob#ifdef	SA_1FM_AT_EOD
221951875Smjacob		if ((softc->quirks & SA_QUIRK_2FM) == 0)
222051875Smjacob			softc->quirks |= SA_QUIRK_1FM;
222151875Smjacob#else
222246962Smjacob		if ((softc->quirks & SA_QUIRK_1FM) == 0)
222343636Smjacob			softc->quirks |= SA_QUIRK_2FM;
222443636Smjacob#endif
222539213Sgibbs	} else
222639213Sgibbs		xpt_release_ccb(ccb);
222739213Sgibbs
222853259Smjacob	/*
222953259Smjacob	 * If we return an error, we're not mounted any more,
223053259Smjacob	 * so release any device reservation.
223153259Smjacob	 */
223253259Smjacob	if (error != 0) {
223353259Smjacob		(void) sareservereleaseunit(periph, FALSE);
223453259Smjacob	}
223554099Smjacob	return (error);
223639213Sgibbs}
223739213Sgibbs
223868114Smjacob/*
223968114Smjacob * How many filemarks do we need to write if we were to terminate the
224068114Smjacob * tape session right now? Note that this can be a negative number
224168114Smjacob */
224268114Smjacob
224339213Sgibbsstatic int
224468114Smjacobsamarkswanted(struct cam_periph *periph)
224539213Sgibbs{
224639213Sgibbs	int	markswanted;
224739213Sgibbs	struct	sa_softc *softc;
224839213Sgibbs
224939213Sgibbs	softc = (struct sa_softc *)periph->softc;
225039213Sgibbs	markswanted = 0;
225139213Sgibbs	if ((softc->flags & SA_FLAG_TAPE_WRITTEN) != 0) {
225239213Sgibbs		markswanted++;
225346962Smjacob		if (softc->quirks & SA_QUIRK_2FM)
225439213Sgibbs			markswanted++;
225539213Sgibbs	}
225668114Smjacob	markswanted -= softc->filemarks;
225768114Smjacob	return (markswanted);
225868114Smjacob}
225939213Sgibbs
226068114Smjacobstatic int
226168114Smjacobsacheckeod(struct cam_periph *periph)
226268114Smjacob{
226368114Smjacob	int	error;
226468114Smjacob	int	markswanted;
226568114Smjacob	struct	sa_softc *softc;
226668114Smjacob
226768114Smjacob	softc = (struct sa_softc *)periph->softc;
226868114Smjacob	markswanted = samarkswanted(periph);
226968114Smjacob
227068114Smjacob	if (markswanted > 0) {
227141906Smjacob		error = sawritefilemarks(periph, markswanted, FALSE);
227239213Sgibbs	} else {
227339213Sgibbs		error = 0;
227439213Sgibbs	}
227539213Sgibbs	return (error);
227639213Sgibbs}
227739213Sgibbs
227839213Sgibbsstatic int
227946962Smjacobsaerror(union ccb *ccb, u_int32_t cflgs, u_int32_t sflgs)
228039213Sgibbs{
228146962Smjacob	static const char *toobig =
228246962Smjacob	    "%d-byte tape record bigger than suplied buffer\n";
228339213Sgibbs	struct	cam_periph *periph;
228439213Sgibbs	struct	sa_softc *softc;
228539213Sgibbs	struct	ccb_scsiio *csio;
228639213Sgibbs	struct	scsi_sense_data *sense;
228753259Smjacob	u_int32_t resid = 0;
228853259Smjacob	int32_t	info = 0;
228939213Sgibbs	int	error_code, sense_key, asc, ascq;
229046962Smjacob	int	error, defer_action;
229139213Sgibbs
229239213Sgibbs	periph = xpt_path_periph(ccb->ccb_h.path);
229339213Sgibbs	softc = (struct sa_softc *)periph->softc;
229439213Sgibbs	csio = &ccb->csio;
229539213Sgibbs	sense = &csio->sense_data;
229639213Sgibbs	scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq);
229739213Sgibbs	error = 0;
229846962Smjacob
229946962Smjacob	/*
230046962Smjacob	 * Calculate/latch up, any residuals...
230146962Smjacob	 */
230241948Smjacob	if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) {
230339213Sgibbs		if ((sense->error_code & SSD_ERRCODE_VALID) != 0) {
230446962Smjacob			info = (int32_t) scsi_4btoul(sense->info);
230539213Sgibbs			resid = info;
230639213Sgibbs			if ((softc->flags & SA_FLAG_FIXED) != 0)
230739213Sgibbs				resid *= softc->media_blksize;
230839213Sgibbs		} else {
230939213Sgibbs			resid = csio->dxfer_len;
231039213Sgibbs			info = resid;
231141674Smjacob			if ((softc->flags & SA_FLAG_FIXED) != 0) {
231241674Smjacob				if (softc->media_blksize)
231341674Smjacob					info /= softc->media_blksize;
231441674Smjacob			}
231539213Sgibbs		}
231671082Smjacob		if (CCB_Type(csio) == SA_CCB_BUFFER_IO) {
231741948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_io_sense,
231841948Smjacob			    sizeof (struct scsi_sense_data));
231942009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_io_cdb,
232042009Smjacob			    (int) csio->cdb_len);
232141948Smjacob			softc->last_io_resid = resid;
232271268Smjacob			softc->last_resid_was_io = 1;
232341948Smjacob		} else {
232441948Smjacob			bcopy((caddr_t) sense, (caddr_t) &softc->last_ctl_sense,
232541948Smjacob			    sizeof (struct scsi_sense_data));
232642009Smjacob			bcopy(csio->cdb_io.cdb_bytes, softc->last_ctl_cdb,
232742009Smjacob			    (int) csio->cdb_len);
232841948Smjacob			softc->last_ctl_resid = resid;
232971268Smjacob			softc->last_resid_was_io = 0;
233041948Smjacob		}
233171082Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Key 0x%x ASC/ASCQ "
233271082Smjacob		    "0x%x 0x%x flags 0x%x resid %d dxfer_len %d\n", sense_key,
233353259Smjacob		    asc, ascq, sense->flags & ~SSD_KEY_RESERVED, resid,
233453259Smjacob		    csio->dxfer_len));
233553259Smjacob	} else {
233653259Smjacob		CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Cam Status 0x%x\n",
233753259Smjacob		    csio->ccb_h.status & CAM_STATUS_MASK));
233841948Smjacob	}
233941948Smjacob
234046962Smjacob	/*
234146962Smjacob	 * If it's neither a SCSI Check Condition Error nor a non-read/write
234246962Smjacob	 * command, let the common code deal with it the error setting.
234346962Smjacob	 */
234446962Smjacob	if ((csio->ccb_h.status & CAM_STATUS_MASK) != CAM_SCSI_STATUS_ERROR ||
234571082Smjacob	    (CCB_Type(csio) == SA_CCB_WAITING)) {
234646962Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
234746962Smjacob	}
234841948Smjacob
234946962Smjacob	/*
235046962Smjacob	 * Calculate whether we'll defer action.
235146962Smjacob	 */
235246962Smjacob
235346962Smjacob	if (resid > 0 && resid < csio->dxfer_len &&
235446962Smjacob	    (softc->flags & SA_FLAG_FIXED) != 0) {
235546962Smjacob		defer_action = TRUE;
235646962Smjacob	} else {
235746962Smjacob		defer_action = FALSE;
235846962Smjacob	}
235946962Smjacob
236046962Smjacob	/*
236146962Smjacob	 * Handle filemark, end of tape, mismatched record sizes....
236246962Smjacob	 * From this point out, we're only handling read/write cases.
236346962Smjacob	 * Handle writes && reads differently.
236446962Smjacob	 */
236546962Smjacob
236646962Smjacob	if (csio->cdb_io.cdb_bytes[0] == SA_WRITE) {
236746962Smjacob		if (sense->flags & SSD_FILEMARK) {
236846962Smjacob			xpt_print_path(csio->ccb_h.path);
236946962Smjacob			printf("filemark detected on write?\n");
237046962Smjacob			if (softc->fileno != (daddr_t) -1) {
237146962Smjacob				softc->fileno++;
237246962Smjacob				softc->blkno = 0;
237371082Smjacob				csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED;
237446962Smjacob			}
237546962Smjacob		}
237646962Smjacob		if (sense->flags & SSD_EOM) {
237739213Sgibbs			csio->resid = resid;
237839213Sgibbs			if (defer_action) {
237946962Smjacob				error = -1;
238039213Sgibbs				softc->flags |= SA_FLAG_EOM_PENDING;
238139213Sgibbs			} else {
238246962Smjacob				error = ENOSPC;
238339213Sgibbs			}
238439213Sgibbs		}
238546962Smjacob	} else {
238646962Smjacob		if (sense_key == SSD_KEY_BLANK_CHECK) {
238739213Sgibbs			csio->resid = resid;
238846962Smjacob			if (defer_action) {
238946962Smjacob				error = -1;
239046962Smjacob				softc->flags |= SA_FLAG_EOM_PENDING;
239146962Smjacob			} else {
239246962Smjacob				error = EIO;
239346962Smjacob			}
239446962Smjacob		}
239546962Smjacob		if (sense->flags & SSD_FILEMARK) {
239646962Smjacob			csio->resid = resid;
239746962Smjacob			if (defer_action) {
239846962Smjacob				error = -1;
239939213Sgibbs				softc->flags |= SA_FLAG_EOF_PENDING;
240046962Smjacob			}
240146962Smjacob			/*
240246962Smjacob			 * Unconditionally, if we detected a filemark on a read,
240346962Smjacob			 * mark that we've run moved a file ahead.
240446962Smjacob			 */
240543636Smjacob			if (softc->fileno != (daddr_t) -1) {
240643636Smjacob				softc->fileno++;
240743636Smjacob				softc->blkno = 0;
240871082Smjacob				csio->ccb_h.ccb_pflags |= SA_POSITION_UPDATED;
240943636Smjacob			}
241039213Sgibbs		}
241146962Smjacob	}
241246962Smjacob	/*
241346962Smjacob	 * Incorrect Length usually applies to read, but can apply to writes.
241446962Smjacob	 */
241546962Smjacob	if (error == 0 && (sense->flags & SSD_ILI)) {
241646962Smjacob		if (info < 0) {
241746962Smjacob			xpt_print_path(csio->ccb_h.path);
241846962Smjacob			printf(toobig, csio->dxfer_len - info);
241946962Smjacob			csio->resid = csio->dxfer_len;
242046962Smjacob			error = EIO;
242146962Smjacob		} else {
242246962Smjacob			csio->resid = resid;
242346962Smjacob			if ((softc->flags & SA_FLAG_FIXED) != 0) {
242446962Smjacob				if (defer_action)
242546962Smjacob					softc->flags |= SA_FLAG_EIO_PENDING;
242646962Smjacob				else
242746962Smjacob					error = EIO;
242846962Smjacob			}
242946962Smjacob			/*
243046962Smjacob			 * Bump the block number if we hadn't seen a filemark.
243146962Smjacob			 * Do this independent of errors (we've moved anyway).
243246962Smjacob			 */
243346962Smjacob			if ((sense->flags & SSD_FILEMARK) == 0) {
243446962Smjacob				if (softc->blkno != (daddr_t) -1) {
243546962Smjacob					softc->blkno++;
243671082Smjacob					csio->ccb_h.ccb_pflags |=
243771082Smjacob					   SA_POSITION_UPDATED;
243839213Sgibbs				}
243939213Sgibbs			}
244039213Sgibbs		}
244139213Sgibbs	}
244239213Sgibbs	if (error == 0)
244346962Smjacob		return (cam_periph_error(ccb, cflgs, sflgs, &softc->saved_ccb));
244439213Sgibbs
244546962Smjacob	if (error == -1)
244646962Smjacob		return (0);
244746962Smjacob	else
244846962Smjacob		return (error);
244939213Sgibbs}
245039213Sgibbs
245139213Sgibbsstatic int
245239213Sgibbssagetparams(struct cam_periph *periph, sa_params params_to_get,
245339213Sgibbs	    u_int32_t *blocksize, u_int8_t *density, u_int32_t *numblocks,
245439213Sgibbs	    int *buff_mode, u_int8_t *write_protect, u_int8_t *speed,
245539213Sgibbs	    int *comp_supported, int *comp_enabled, u_int32_t *comp_algorithm,
245646962Smjacob	    sa_comp_t *tcs)
245739213Sgibbs{
245839213Sgibbs	union ccb *ccb;
245939213Sgibbs	void *mode_buffer;
246039213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
246139213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
246239213Sgibbs	int mode_buffer_len;
246339213Sgibbs	struct sa_softc *softc;
246446962Smjacob	u_int8_t cpage;
246539213Sgibbs	int error;
246639213Sgibbs	cam_status status;
246739213Sgibbs
246839213Sgibbs	softc = (struct sa_softc *)periph->softc;
246946962Smjacob	ccb = cam_periph_getccb(periph, 1);
247071082Smjacob	if (softc->quirks & SA_QUIRK_NO_CPAGE)
247171082Smjacob		cpage = SA_DEVICE_CONFIGURATION_PAGE;
247271082Smjacob	else
247371082Smjacob		cpage = SA_DATA_COMPRESSION_PAGE;
247439213Sgibbs
247539213Sgibbsretry:
247639213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
247739213Sgibbs
247839213Sgibbs	if (params_to_get & SA_PARAM_COMPRESSION) {
247939213Sgibbs		if (softc->quirks & SA_QUIRK_NOCOMP) {
248039213Sgibbs			*comp_supported = FALSE;
248139213Sgibbs			params_to_get &= ~SA_PARAM_COMPRESSION;
248239213Sgibbs		} else
248346962Smjacob			mode_buffer_len += sizeof (sa_comp_t);
248439213Sgibbs	}
248554099Smjacob
248667723Smjacob	mode_buffer = malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);
248739213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
248839213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
248939213Sgibbs
249042716Smjacob	/* it is safe to retry this */
249142716Smjacob	scsi_mode_sense(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
249242716Smjacob	    SMS_PAGE_CTRL_CURRENT, (params_to_get & SA_PARAM_COMPRESSION) ?
249346962Smjacob	    cpage : SMS_VENDOR_SPECIFIC_PAGE, mode_buffer, mode_buffer_len,
249446962Smjacob	    SSD_FULL_SIZE, 5000);
249539213Sgibbs
249654099Smjacob	error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
249754099Smjacob	    &softc->device_stats);
249854099Smjacob	QFRLS(ccb);
249939213Sgibbs
250039213Sgibbs	status = ccb->ccb_h.status & CAM_STATUS_MASK;
250139213Sgibbs
250242716Smjacob	if (error == EINVAL && (params_to_get & SA_PARAM_COMPRESSION) != 0) {
250339213Sgibbs		/*
250446962Smjacob		 * Hmm. Let's see if we can try another page...
250546962Smjacob		 * If we've already done that, give up on compression
250646962Smjacob		 * for this device and remember this for the future
250746962Smjacob		 * and attempt the request without asking for compression
250846962Smjacob		 * info.
250939213Sgibbs		 */
251046962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
251146962Smjacob			cpage = SA_DEVICE_CONFIGURATION_PAGE;
251246962Smjacob			goto retry;
251346962Smjacob		}
251439213Sgibbs		softc->quirks |= SA_QUIRK_NOCOMP;
251539213Sgibbs		free(mode_buffer, M_TEMP);
251639213Sgibbs		goto retry;
251746962Smjacob	} else if (status == CAM_SCSI_STATUS_ERROR) {
251846962Smjacob		/* Tell the user about the fatal error. */
251946962Smjacob		scsi_sense_print(&ccb->csio);
252046962Smjacob		goto sagetparamsexit;
252146962Smjacob	}
252239213Sgibbs
252346962Smjacob	/*
252446962Smjacob	 * If the user only wants the compression information, and
252546962Smjacob	 * the device doesn't send back the block descriptor, it's
252646962Smjacob	 * no big deal.  If the user wants more than just
252746962Smjacob	 * compression, though, and the device doesn't pass back the
252846962Smjacob	 * block descriptor, we need to send another mode sense to
252946962Smjacob	 * get the block descriptor.
253046962Smjacob	 */
253146962Smjacob	if ((mode_hdr->blk_desc_len == 0) &&
253246962Smjacob	    (params_to_get & SA_PARAM_COMPRESSION) &&
253346962Smjacob	    (params_to_get & ~(SA_PARAM_COMPRESSION))) {
253439213Sgibbs
253539213Sgibbs		/*
253646962Smjacob		 * Decrease the mode buffer length by the size of
253746962Smjacob		 * the compression page, to make sure the data
253846962Smjacob		 * there doesn't get overwritten.
253939213Sgibbs		 */
254046962Smjacob		mode_buffer_len -= sizeof (sa_comp_t);
254139213Sgibbs
254246962Smjacob		/*
254346962Smjacob		 * Now move the compression page that we presumably
254446962Smjacob		 * got back down the memory chunk a little bit so
254546962Smjacob		 * it doesn't get spammed.
254646962Smjacob		 */
254754099Smjacob		bcopy(&mode_hdr[0], &mode_hdr[1], sizeof (sa_comp_t));
254854099Smjacob		bzero(&mode_hdr[0], sizeof (mode_hdr[0]));
254939213Sgibbs
255046962Smjacob		/*
255146962Smjacob		 * Now, we issue another mode sense and just ask
255246962Smjacob		 * for the block descriptor, etc.
255346962Smjacob		 */
255439213Sgibbs
255546962Smjacob		scsi_mode_sense(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
255646962Smjacob		    SMS_PAGE_CTRL_CURRENT, SMS_VENDOR_SPECIFIC_PAGE,
255746962Smjacob		    mode_buffer, mode_buffer_len, SSD_FULL_SIZE, 5000);
255839213Sgibbs
255954099Smjacob		error = cam_periph_runccb(ccb, saerror, 0, SF_NO_PRINT,
256046962Smjacob		    &softc->device_stats);
256154099Smjacob		QFRLS(ccb);
256239213Sgibbs
256346962Smjacob		if (error != 0)
256446962Smjacob			goto sagetparamsexit;
256546962Smjacob	}
256639213Sgibbs
256746962Smjacob	if (params_to_get & SA_PARAM_BLOCKSIZE)
256846962Smjacob		*blocksize = scsi_3btoul(mode_blk->blklen);
256939213Sgibbs
257046962Smjacob	if (params_to_get & SA_PARAM_NUMBLOCKS)
257146962Smjacob		*numblocks = scsi_3btoul(mode_blk->nblocks);
257239213Sgibbs
257346962Smjacob	if (params_to_get & SA_PARAM_BUFF_MODE)
257446962Smjacob		*buff_mode = mode_hdr->dev_spec & SMH_SA_BUF_MODE_MASK;
257539213Sgibbs
257646962Smjacob	if (params_to_get & SA_PARAM_DENSITY)
257746962Smjacob		*density = mode_blk->density;
257839213Sgibbs
257946962Smjacob	if (params_to_get & SA_PARAM_WP)
258046962Smjacob		*write_protect = (mode_hdr->dev_spec & SMH_SA_WP)? TRUE : FALSE;
258139213Sgibbs
258246962Smjacob	if (params_to_get & SA_PARAM_SPEED)
258346962Smjacob		*speed = mode_hdr->dev_spec & SMH_SA_SPEED_MASK;
258439213Sgibbs
258546962Smjacob	if (params_to_get & SA_PARAM_COMPRESSION) {
258654099Smjacob		sa_comp_t *ntcs = (sa_comp_t *) &mode_blk[1];
258746962Smjacob		if (cpage == SA_DATA_COMPRESSION_PAGE) {
258846962Smjacob			struct scsi_data_compression_page *cp = &ntcs->dcomp;
258946962Smjacob			*comp_supported =
259046962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCC)? TRUE : FALSE;
259146962Smjacob			*comp_enabled =
259246962Smjacob			    (cp->dce_and_dcc & SA_DCP_DCE)? TRUE : FALSE;
259346962Smjacob			*comp_algorithm = scsi_4btoul(cp->comp_algorithm);
259446962Smjacob		} else {
259546962Smjacob			struct scsi_dev_conf_page *cp = &ntcs->dconf;
259646962Smjacob			/*
259746962Smjacob			 * We don't really know whether this device supports
259846962Smjacob			 * Data Compression if the the algorithm field is
259946962Smjacob			 * zero. Just say we do.
260046962Smjacob			 */
260146962Smjacob			*comp_supported = TRUE;
260246962Smjacob			*comp_enabled =
260346962Smjacob			    (cp->sel_comp_alg != SA_COMP_NONE)? TRUE : FALSE;
260446962Smjacob			*comp_algorithm = cp->sel_comp_alg;
260541906Smjacob		}
260646962Smjacob		if (tcs != NULL)
260754099Smjacob			bcopy(ntcs, tcs, sizeof (sa_comp_t));
260839213Sgibbs	}
260939213Sgibbs
261046962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
261146962Smjacob		int idx;
261246962Smjacob		char *xyz = mode_buffer;
261346962Smjacob		xpt_print_path(periph->path);
261446962Smjacob		printf("Mode Sense Data=");
261546962Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
261646962Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
261746962Smjacob		printf("\n");
261846962Smjacob	}
261946962Smjacob
262039213Sgibbssagetparamsexit:
262139213Sgibbs
262239213Sgibbs	xpt_release_ccb(ccb);
262339213Sgibbs	free(mode_buffer, M_TEMP);
262454099Smjacob	return (error);
262539213Sgibbs}
262639213Sgibbs
262739213Sgibbs/*
262839213Sgibbs * The purpose of this function is to set one of four different parameters
262939213Sgibbs * for a tape drive:
263039213Sgibbs *	- blocksize
263139213Sgibbs *	- density
263239213Sgibbs *	- compression / compression algorithm
263339213Sgibbs *	- buffering mode
263439213Sgibbs *
263539213Sgibbs * The assumption is that this will be called from saioctl(), and therefore
263639213Sgibbs * from a process context.  Thus the waiting malloc calls below.  If that
263739213Sgibbs * assumption ever changes, the malloc calls should be changed to be
263839213Sgibbs * NOWAIT mallocs.
263939213Sgibbs *
264039213Sgibbs * Any or all of the four parameters may be set when this function is
264139213Sgibbs * called.  It should handle setting more than one parameter at once.
264239213Sgibbs */
264339213Sgibbsstatic int
264439213Sgibbssasetparams(struct cam_periph *periph, sa_params params_to_set,
264546962Smjacob	    u_int32_t blocksize, u_int8_t density, u_int32_t calg,
264642563Smjacob	    u_int32_t sense_flags)
264739213Sgibbs{
264839213Sgibbs	struct sa_softc *softc;
264939213Sgibbs	u_int32_t current_blocksize;
265046962Smjacob	u_int32_t current_calg;
265139213Sgibbs	u_int8_t current_density;
265239213Sgibbs	u_int8_t current_speed;
265339213Sgibbs	int comp_enabled, comp_supported;
265439213Sgibbs	void *mode_buffer;
265539213Sgibbs	int mode_buffer_len;
265639213Sgibbs	struct scsi_mode_header_6 *mode_hdr;
265739213Sgibbs	struct scsi_mode_blk_desc *mode_blk;
265846962Smjacob	sa_comp_t *ccomp, *cpage;
265939213Sgibbs	int buff_mode;
266046962Smjacob	union ccb *ccb = NULL;
266139213Sgibbs	int error;
266239213Sgibbs
266339213Sgibbs	softc = (struct sa_softc *)periph->softc;
266439213Sgibbs
266546962Smjacob	ccomp = malloc(sizeof (sa_comp_t), M_TEMP, M_WAITOK);
266639213Sgibbs
266739213Sgibbs	/*
266839213Sgibbs	 * Since it doesn't make sense to set the number of blocks, or
266939213Sgibbs	 * write protection, we won't try to get the current value.  We
267039213Sgibbs	 * always want to get the blocksize, so we can set it back to the
267139213Sgibbs	 * proper value.
267239213Sgibbs	 */
267346962Smjacob	error = sagetparams(periph,
267446962Smjacob	    params_to_set | SA_PARAM_BLOCKSIZE | SA_PARAM_SPEED,
267546962Smjacob	    &current_blocksize, &current_density, NULL, &buff_mode, NULL,
267646962Smjacob	    &current_speed, &comp_supported, &comp_enabled,
267746962Smjacob	    &current_calg, ccomp);
267839213Sgibbs
267939213Sgibbs	if (error != 0) {
268046962Smjacob		free(ccomp, M_TEMP);
268154099Smjacob		return (error);
268239213Sgibbs	}
268339213Sgibbs
268439213Sgibbs	mode_buffer_len = sizeof(*mode_hdr) + sizeof(*mode_blk);
268539213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION)
268646962Smjacob		mode_buffer_len += sizeof (sa_comp_t);
268739213Sgibbs
268867723Smjacob	mode_buffer = malloc(mode_buffer_len, M_TEMP, M_WAITOK | M_ZERO);
268939213Sgibbs
269039213Sgibbs	mode_hdr = (struct scsi_mode_header_6 *)mode_buffer;
269139213Sgibbs	mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
269239213Sgibbs
269353259Smjacob	ccb = cam_periph_getccb(periph, 1);
269453259Smjacob
269553259Smjacobretry:
269653259Smjacob
269739213Sgibbs	if (params_to_set & SA_PARAM_COMPRESSION) {
269853259Smjacob		if (mode_blk) {
269953259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
270053259Smjacob		} else {
270153259Smjacob			cpage = (sa_comp_t *)&mode_hdr[1];
270253259Smjacob		}
270346962Smjacob		bcopy(ccomp, cpage, sizeof (sa_comp_t));
270454099Smjacob		cpage->hdr.pagecode &= ~0x80;
270539213Sgibbs	} else
270646962Smjacob		cpage = NULL;
270739213Sgibbs
270839213Sgibbs	/*
270939213Sgibbs	 * If the caller wants us to set the blocksize, use the one they
271039213Sgibbs	 * pass in.  Otherwise, use the blocksize we got back from the
271139213Sgibbs	 * mode select above.
271239213Sgibbs	 */
271353259Smjacob	if (mode_blk) {
271453259Smjacob		if (params_to_set & SA_PARAM_BLOCKSIZE)
271553259Smjacob			scsi_ulto3b(blocksize, mode_blk->blklen);
271653259Smjacob		else
271753259Smjacob			scsi_ulto3b(current_blocksize, mode_blk->blklen);
271839213Sgibbs
271953259Smjacob		/*
272053259Smjacob		 * Set density if requested, else preserve old density.
272153259Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
272253259Smjacob		 * devices, else density we've latched up in our softc.
272353259Smjacob		 */
272453259Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
272553259Smjacob			mode_blk->density = density;
272653259Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
272753259Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
272853259Smjacob		} else {
272953259Smjacob			mode_blk->density = softc->media_density;
273053259Smjacob		}
273141674Smjacob	}
273239213Sgibbs
273339213Sgibbs	/*
273439213Sgibbs	 * For mode selects, these two fields must be zero.
273539213Sgibbs	 */
273639213Sgibbs	mode_hdr->data_length = 0;
273739213Sgibbs	mode_hdr->medium_type = 0;
273839213Sgibbs
273939213Sgibbs	/* set the speed to the current value */
274039213Sgibbs	mode_hdr->dev_spec = current_speed;
274139213Sgibbs
274239213Sgibbs	/* set single-initiator buffering mode */
274339213Sgibbs	mode_hdr->dev_spec |= SMH_SA_BUF_MODE_SIBUF;
274439213Sgibbs
274553259Smjacob	if (mode_blk)
274653259Smjacob		mode_hdr->blk_desc_len = sizeof(struct scsi_mode_blk_desc);
274753259Smjacob	else
274853259Smjacob		mode_hdr->blk_desc_len = 0;
274939213Sgibbs
275039213Sgibbs	/*
275139213Sgibbs	 * First, if the user wants us to set the compression algorithm or
275239213Sgibbs	 * just turn compression on, check to make sure that this drive
275339213Sgibbs	 * supports compression.
275439213Sgibbs	 */
275546962Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
275639213Sgibbs		/*
275739213Sgibbs		 * If the compression algorithm is 0, disable compression.
275839213Sgibbs		 * If the compression algorithm is non-zero, enable
275939213Sgibbs		 * compression and set the compression type to the
276039213Sgibbs		 * specified compression algorithm, unless the algorithm is
276139213Sgibbs		 * MT_COMP_ENABLE.  In that case, we look at the
276239213Sgibbs		 * compression algorithm that is currently set and if it is
276339213Sgibbs		 * non-zero, we leave it as-is.  If it is zero, and we have
276439213Sgibbs		 * saved a compression algorithm from a time when
276539213Sgibbs		 * compression was enabled before, set the compression to
276639213Sgibbs		 * the saved value.
276739213Sgibbs		 */
276854099Smjacob		switch (ccomp->hdr.pagecode & ~0x80) {
276946962Smjacob		case SA_DATA_COMPRESSION_PAGE:
277046962Smjacob		if (ccomp->dcomp.dce_and_dcc & SA_DCP_DCC) {
277146962Smjacob			struct scsi_data_compression_page *dcp = &cpage->dcomp;
277246962Smjacob			if (calg == 0) {
277354099Smjacob				/*
277454099Smjacob				 * Disable compression, but leave the
277554099Smjacob				 * decompression and the capability bit
277654099Smjacob				 * alone.
277754099Smjacob				 */
277854099Smjacob				dcp->dce_and_dcc = SA_DCP_DCC;
277954099Smjacob				dcp->dde_and_red |= SA_DCP_DDE;
278046962Smjacob				break;
278146962Smjacob			}
278253259Smjacob			/* enable compression && decompression */
278354099Smjacob			dcp->dce_and_dcc = SA_DCP_DCE | SA_DCP_DCC;
278454099Smjacob			dcp->dde_and_red |= SA_DCP_DDE;
278553259Smjacob			/*
278653259Smjacob			 * If there, use compression algorithm from caller.
278753259Smjacob			 * Otherwise, if there's a saved compression algorithm
278853259Smjacob			 * and there is no current algorithm, use the saved
278953259Smjacob			 * algorithm. Else parrot back what we got and hope
279053259Smjacob			 * for the best.
279153259Smjacob			 */
279246962Smjacob			if (calg != MT_COMP_ENABLE) {
279346962Smjacob				scsi_ulto4b(calg, dcp->comp_algorithm);
279453259Smjacob				scsi_ulto4b(calg, dcp->decomp_algorithm);
279546962Smjacob			} else if (scsi_4btoul(dcp->comp_algorithm) == 0 &&
279646962Smjacob			    softc->saved_comp_algorithm != 0) {
279739213Sgibbs				scsi_ulto4b(softc->saved_comp_algorithm,
279846962Smjacob				    dcp->comp_algorithm);
279953259Smjacob				scsi_ulto4b(softc->saved_comp_algorithm,
280053259Smjacob				    dcp->decomp_algorithm);
280139213Sgibbs			}
280246962Smjacob			break;
280339213Sgibbs		}
280453259Smjacob		case SA_DEVICE_CONFIGURATION_PAGE:
280546962Smjacob		{
280646962Smjacob			struct scsi_dev_conf_page *dcp = &cpage->dconf;
280746962Smjacob			if (calg == 0) {
280846962Smjacob				dcp->sel_comp_alg = SA_COMP_NONE;
280946962Smjacob				break;
281046962Smjacob			}
281146962Smjacob			if (calg != MT_COMP_ENABLE) {
281246962Smjacob				dcp->sel_comp_alg = calg;
281346962Smjacob			} else if (dcp->sel_comp_alg == SA_COMP_NONE &&
281446962Smjacob			    softc->saved_comp_algorithm != 0) {
281546962Smjacob				dcp->sel_comp_alg = softc->saved_comp_algorithm;
281646962Smjacob			}
281746962Smjacob			break;
281839213Sgibbs		}
281946962Smjacob		default:
282046962Smjacob			/*
282154099Smjacob			 * The drive doesn't seem to support compression,
282246962Smjacob			 * so turn off the set compression bit.
282346962Smjacob			 */
282446962Smjacob			params_to_set &= ~SA_PARAM_COMPRESSION;
282546962Smjacob			xpt_print_path(periph->path);
282654099Smjacob			printf("device does not seem to support compression\n");
282754099Smjacob
282846962Smjacob			/*
282946962Smjacob			 * If that was the only thing the user wanted us to set,
283046962Smjacob			 * clean up allocated resources and return with
283146962Smjacob			 * 'operation not supported'.
283246962Smjacob			 */
283346962Smjacob			if (params_to_set == SA_PARAM_NONE) {
283446962Smjacob				free(mode_buffer, M_TEMP);
283554099Smjacob				xpt_release_ccb(ccb);
283654099Smjacob				return (ENODEV);
283746962Smjacob			}
283846962Smjacob
283946962Smjacob			/*
284046962Smjacob			 * That wasn't the only thing the user wanted us to set.
284146962Smjacob			 * So, decrease the stated mode buffer length by the
284246962Smjacob			 * size of the compression mode page.
284346962Smjacob			 */
284446962Smjacob			mode_buffer_len -= sizeof(sa_comp_t);
284546962Smjacob		}
284639213Sgibbs	}
284739213Sgibbs
284846962Smjacob	/* It is safe to retry this operation */
284946962Smjacob	scsi_mode_select(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG,
285046962Smjacob	    (params_to_set & SA_PARAM_COMPRESSION)? TRUE : FALSE,
285146962Smjacob	    FALSE, mode_buffer, mode_buffer_len, SSD_FULL_SIZE, 5000);
285241674Smjacob
285346962Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
285446962Smjacob	    sense_flags, &softc->device_stats);
285554099Smjacob	QFRLS(ccb);
285639213Sgibbs
285746962Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
285841906Smjacob		int idx;
285941674Smjacob		char *xyz = mode_buffer;
286041674Smjacob		xpt_print_path(periph->path);
286142009Smjacob		printf("Err%d, Mode Select Data=", error);
286241906Smjacob		for (idx = 0; idx < mode_buffer_len; idx++)
286342009Smjacob			printf(" 0x%02x", xyz[idx] & 0xff);
286441674Smjacob		printf("\n");
286541674Smjacob	}
286641674Smjacob
286739213Sgibbs
286853259Smjacob	if (error) {
286939213Sgibbs		/*
287053259Smjacob		 * If we can, try without setting density/blocksize.
287153259Smjacob		 */
287253259Smjacob		if (mode_blk) {
287353259Smjacob			if ((params_to_set &
287453259Smjacob			    (SA_PARAM_DENSITY|SA_PARAM_BLOCKSIZE)) == 0) {
287553259Smjacob				mode_blk = NULL;
287653259Smjacob				goto retry;
287753259Smjacob			}
287853259Smjacob		} else {
287953259Smjacob			mode_blk = (struct scsi_mode_blk_desc *)&mode_hdr[1];
288053259Smjacob			cpage = (sa_comp_t *)&mode_blk[1];
288153259Smjacob		}
288253259Smjacob
288353259Smjacob		/*
288439213Sgibbs		 * If we were setting the blocksize, and that failed, we
288539213Sgibbs		 * want to set it to its original value.  If we weren't
288639213Sgibbs		 * setting the blocksize, we don't want to change it.
288739213Sgibbs		 */
288839213Sgibbs		scsi_ulto3b(current_blocksize, mode_blk->blklen);
288939213Sgibbs
289039213Sgibbs		/*
289141674Smjacob		 * Set density if requested, else preserve old density.
289241674Smjacob		 * SCSI_SAME_DENSITY only applies to SCSI-2 or better
289341674Smjacob		 * devices, else density we've latched up in our softc.
289439213Sgibbs		 */
289541674Smjacob		if (params_to_set & SA_PARAM_DENSITY) {
289639213Sgibbs			mode_blk->density = current_density;
289741674Smjacob		} else if (softc->scsi_rev > SCSI_REV_CCS) {
289841674Smjacob			mode_blk->density = SCSI_SAME_DENSITY;
289941674Smjacob		} else {
290041674Smjacob			mode_blk->density = softc->media_density;
290141674Smjacob		}
290239213Sgibbs
290339213Sgibbs		if (params_to_set & SA_PARAM_COMPRESSION)
290446962Smjacob			bcopy(ccomp, cpage, sizeof (sa_comp_t));
290539213Sgibbs
290639213Sgibbs		/*
290739213Sgibbs		 * The retry count is the only CCB field that might have been
290839213Sgibbs		 * changed that we care about, so reset it back to 1.
290939213Sgibbs		 */
291039213Sgibbs		ccb->ccb_h.retry_count = 1;
291154099Smjacob		cam_periph_runccb(ccb, saerror, 0, sense_flags,
291254099Smjacob		    &softc->device_stats);
291354099Smjacob		QFRLS(ccb);
291439213Sgibbs	}
291539213Sgibbs
291653259Smjacob	xpt_release_ccb(ccb);
291753259Smjacob
291846962Smjacob	if (ccomp != NULL)
291946962Smjacob		free(ccomp, M_TEMP);
292039213Sgibbs
292141948Smjacob	if (params_to_set & SA_PARAM_COMPRESSION) {
292241948Smjacob		if (error) {
292341948Smjacob			softc->flags &= ~SA_FLAG_COMP_ENABLED;
292446962Smjacob			/*
292546962Smjacob			 * Even if we get an error setting compression,
292646962Smjacob			 * do not say that we don't support it. We could
292746962Smjacob			 * have been wrong, or it may be media specific.
292846962Smjacob			 *	softc->flags &= ~SA_FLAG_COMP_SUPP;
292946962Smjacob			 */
293041948Smjacob			softc->saved_comp_algorithm = softc->comp_algorithm;
293141948Smjacob			softc->comp_algorithm = 0;
293241948Smjacob		} else {
293341948Smjacob			softc->flags |= SA_FLAG_COMP_ENABLED;
293446962Smjacob			softc->comp_algorithm = calg;
293541948Smjacob		}
293641948Smjacob	}
293741948Smjacob
293839213Sgibbs	free(mode_buffer, M_TEMP);
293954099Smjacob	return (error);
294039213Sgibbs}
294139213Sgibbs
294239213Sgibbsstatic void
294339213Sgibbssaprevent(struct cam_periph *periph, int action)
294439213Sgibbs{
294539213Sgibbs	struct	sa_softc *softc;
294639213Sgibbs	union	ccb *ccb;
294742735Smjacob	int	error, sf;
294839213Sgibbs
294939213Sgibbs	softc = (struct sa_softc *)periph->softc;
295039213Sgibbs
295142735Smjacob	if ((action == PR_ALLOW) && (softc->flags & SA_FLAG_TAPE_LOCKED) == 0)
295239213Sgibbs		return;
295342735Smjacob	if ((action == PR_PREVENT) && (softc->flags & SA_FLAG_TAPE_LOCKED) != 0)
295442735Smjacob		return;
295539213Sgibbs
295656981Smjacob	/*
295756981Smjacob	 * We can be quiet about illegal requests.
295856981Smjacob	 */
295956981Smjacob	if (CAM_DEBUGGED(periph->path, CAM_DEBUG_INFO)) {
296042735Smjacob		sf = 0;
296156981Smjacob	} else
296242735Smjacob		sf = SF_QUIET_IR;
296342735Smjacob
296442716Smjacob	ccb = cam_periph_getccb(periph, 1);
296539213Sgibbs
296642716Smjacob	/* It is safe to retry this operation */
296742716Smjacob	scsi_prevent(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, action,
296853259Smjacob	    SSD_FULL_SIZE, 100000);
296939213Sgibbs
297043636Smjacob	error = cam_periph_runccb(ccb, saerror, 0, sf, &softc->device_stats);
297153522Smjacob	QFRLS(ccb);
297239213Sgibbs	if (error == 0) {
297339213Sgibbs		if (action == PR_ALLOW)
297439213Sgibbs			softc->flags &= ~SA_FLAG_TAPE_LOCKED;
297539213Sgibbs		else
297639213Sgibbs			softc->flags |= SA_FLAG_TAPE_LOCKED;
297739213Sgibbs	}
297839213Sgibbs
297939213Sgibbs	xpt_release_ccb(ccb);
298039213Sgibbs}
298139213Sgibbs
298239213Sgibbsstatic int
298339213Sgibbssarewind(struct cam_periph *periph)
298439213Sgibbs{
298539213Sgibbs	union	ccb *ccb;
298639213Sgibbs	struct	sa_softc *softc;
298739213Sgibbs	int	error;
298839213Sgibbs
298939213Sgibbs	softc = (struct sa_softc *)periph->softc;
299039213Sgibbs
299146962Smjacob	ccb = cam_periph_getccb(periph, 1);
299239213Sgibbs
299342716Smjacob	/* It is safe to retry this operation */
299446962Smjacob	scsi_rewind(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG, FALSE,
299553259Smjacob	    SSD_FULL_SIZE, REWIND_TIMEOUT);
299639213Sgibbs
299743636Smjacob	softc->dsreg = MTIO_DSREG_REW;
299842716Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
299943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
300039213Sgibbs
300139213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
300242716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
300342716Smjacob
300439213Sgibbs	xpt_release_ccb(ccb);
300543636Smjacob	if (error == 0)
300643636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
300743636Smjacob	else
300843636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
300939213Sgibbs	return (error);
301039213Sgibbs}
301139213Sgibbs
301239213Sgibbsstatic int
301339213Sgibbssaspace(struct cam_periph *periph, int count, scsi_space_code code)
301439213Sgibbs{
301539213Sgibbs	union	ccb *ccb;
301639213Sgibbs	struct	sa_softc *softc;
301739213Sgibbs	int	error;
301839213Sgibbs
301939213Sgibbs	softc = (struct sa_softc *)periph->softc;
302039213Sgibbs
302146962Smjacob	ccb = cam_periph_getccb(periph, 1);
302239213Sgibbs
302342716Smjacob	/* This cannot be retried */
302439213Sgibbs
302542716Smjacob	scsi_space(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG, code, count,
302653259Smjacob	    SSD_FULL_SIZE, SPACE_TIMEOUT);
302739213Sgibbs
302871087Smjacob	/*
302971087Smjacob	 * Clear residual because we will be using it.
303071087Smjacob	 */
303171087Smjacob	softc->last_ctl_resid = 0;
303271087Smjacob
303343636Smjacob	softc->dsreg = (count < 0)? MTIO_DSREG_REV : MTIO_DSREG_FWD;
303442716Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
303543636Smjacob	softc->dsreg = MTIO_DSREG_REST;
303642716Smjacob
303739213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
303842716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
303942716Smjacob
304039213Sgibbs	xpt_release_ccb(ccb);
304143636Smjacob
304242716Smjacob	/*
304343636Smjacob	 * If a spacing operation has failed, we need to invalidate
304443636Smjacob	 * this mount.
304543636Smjacob	 *
304643636Smjacob	 * If the spacing operation was setmarks or to end of recorded data,
304743636Smjacob	 * we no longer know our relative position.
304843636Smjacob	 *
304971087Smjacob	 * If the spacing operations was spacing files in reverse, we
305071087Smjacob	 * take account of the residual, but still check against less
305171087Smjacob	 * than zero- if we've gone negative, we must have hit BOT.
305271087Smjacob	 *
305371087Smjacob	 * If the spacing operations was spacing records in reverse and
305471087Smjacob	 * we have a residual, we've either hit BOT or hit a filemark.
305571087Smjacob	 * In the former case, we know our new record number (0). In
305671087Smjacob	 * the latter case, we have absolutely no idea what the real
305771087Smjacob	 * record number is- we've stopped between the end of the last
305871087Smjacob	 * record in the previous file and the filemark that stopped
305971087Smjacob	 * our spacing backwards.
306042716Smjacob	 */
306143636Smjacob	if (error) {
306243636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
306343636Smjacob	} else if (code == SS_SETMARKS || code == SS_EOD) {
306443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
306543636Smjacob	} else if (code == SS_FILEMARKS && softc->fileno != (daddr_t) -1) {
306671087Smjacob		softc->fileno += (count - softc->last_ctl_resid);
306771087Smjacob		if (softc->fileno < 0)	/* we must of hit BOT */
306871087Smjacob			softc->fileno = 0;
306943636Smjacob		softc->blkno = 0;
307043636Smjacob	} else if (code == SS_BLOCKS && softc->blkno != (daddr_t) -1) {
307171087Smjacob		softc->blkno += (count - softc->last_ctl_resid);
307271087Smjacob		if (count < 0) {
307371087Smjacob			if (softc->last_ctl_resid || softc->blkno < 0) {
307471087Smjacob				if (softc->fileno == 0) {
307571087Smjacob					softc->blkno = 0;
307671087Smjacob				} else {
307771087Smjacob					softc->blkno = (daddr_t) -1;
307871087Smjacob				}
307971087Smjacob			}
308071087Smjacob		}
308143636Smjacob	}
308239213Sgibbs	return (error);
308339213Sgibbs}
308439213Sgibbs
308539213Sgibbsstatic int
308639213Sgibbssawritefilemarks(struct cam_periph *periph, int nmarks, int setmarks)
308739213Sgibbs{
308839213Sgibbs	union	ccb *ccb;
308939213Sgibbs	struct	sa_softc *softc;
309071087Smjacob	int	error, nwm = 0;
309139213Sgibbs
309239213Sgibbs	softc = (struct sa_softc *)periph->softc;
309339213Sgibbs
309446962Smjacob	ccb = cam_periph_getccb(periph, 1);
309571087Smjacob	/*
309671087Smjacob	 * Clear residual because we will be using it.
309771087Smjacob	 */
309871087Smjacob	softc->last_ctl_resid = 0;
309939213Sgibbs
310043636Smjacob	softc->dsreg = MTIO_DSREG_FMK;
310142716Smjacob	/* this *must* not be retried */
310242716Smjacob	scsi_write_filemarks(&ccb->csio, 0, sadone, MSG_SIMPLE_Q_TAG,
310341918Smjacob	    FALSE, setmarks, nmarks, SSD_FULL_SIZE, 60000);
310443636Smjacob	softc->dsreg = MTIO_DSREG_REST;
310539213Sgibbs
310643636Smjacob
310741918Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
310839213Sgibbs
310939213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
311042716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
311139213Sgibbs
311241918Smjacob	if (error == 0 && nmarks) {
311341918Smjacob		struct sa_softc *softc = (struct sa_softc *)periph->softc;
311471087Smjacob		nwm = nmarks - softc->last_ctl_resid;
311571087Smjacob		softc->filemarks += nwm;
311639213Sgibbs	}
311771087Smjacob
311841918Smjacob	xpt_release_ccb(ccb);
311943636Smjacob
312043636Smjacob	/*
312143636Smjacob	 * Update relative positions (if we're doing that).
312243636Smjacob	 */
312343636Smjacob	if (error) {
312443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
312543636Smjacob	} else if (softc->fileno != (daddr_t) -1) {
312671087Smjacob		softc->fileno += nwm;
312743636Smjacob		softc->blkno = 0;
312843636Smjacob	}
312941918Smjacob	return (error);
313041918Smjacob}
313139213Sgibbs
313241918Smjacobstatic int
313341918Smjacobsardpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
313441918Smjacob{
313541918Smjacob	struct scsi_tape_position_data loc;
313641918Smjacob	union ccb *ccb;
313746962Smjacob	struct sa_softc *softc = (struct sa_softc *)periph->softc;
313841918Smjacob	int error;
313941918Smjacob
314041918Smjacob	/*
314171082Smjacob	 * We try and flush any buffered writes here if we were writing
314271082Smjacob	 * and we're trying to get hardware block position. It eats
314371082Smjacob	 * up performance substantially, but I'm wary of drive firmware.
314446962Smjacob	 *
314571082Smjacob	 * I think that *logical* block position is probably okay-
314671082Smjacob	 * but hardware block position might have to wait for data
314771082Smjacob	 * to hit media to be valid. Caveat Emptor.
314841918Smjacob	 */
314941918Smjacob
315071082Smjacob	if (hard && (softc->flags & SA_FLAG_TAPE_WRITTEN)) {
315146962Smjacob		error = sawritefilemarks(periph, 0, 0);
315246962Smjacob		if (error && error != EACCES)
315346962Smjacob			return (error);
315446962Smjacob	}
315541918Smjacob
315666678Smjacob	ccb = cam_periph_getccb(periph, 1);
315741918Smjacob	scsi_read_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
315841918Smjacob	    hard, &loc, SSD_FULL_SIZE, 5000);
315943636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
316041918Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
316143636Smjacob	softc->dsreg = MTIO_DSREG_REST;
316241918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
316341918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
316441918Smjacob
316541918Smjacob	if (error == 0) {
316641918Smjacob		if (loc.flags & SA_RPOS_UNCERTAIN) {
316741918Smjacob			error = EINVAL;		/* nothing is certain */
316841918Smjacob		} else {
316941918Smjacob			*blkptr = scsi_4btoul(loc.firstblk);
317041918Smjacob		}
317141918Smjacob	}
317241918Smjacob
317339213Sgibbs	xpt_release_ccb(ccb);
317439213Sgibbs	return (error);
317539213Sgibbs}
317639213Sgibbs
317739213Sgibbsstatic int
317841918Smjacobsasetpos(struct cam_periph *periph, int hard, u_int32_t *blkptr)
317941918Smjacob{
318041918Smjacob	union ccb *ccb;
318141918Smjacob	struct sa_softc *softc;
318241918Smjacob	int error;
318341918Smjacob
318441918Smjacob	/*
318546962Smjacob	 * We used to try and flush any buffered writes here.
318646962Smjacob	 * Now we push this onto user applications to either
318746962Smjacob	 * flush the pending writes themselves (via a zero count
318846962Smjacob	 * WRITE FILEMARKS command) or they can trust their tape
318946962Smjacob	 * drive to do this correctly for them.
319046962Smjacob 	 */
319141918Smjacob
319241918Smjacob	softc = (struct sa_softc *)periph->softc;
319346962Smjacob	ccb = cam_periph_getccb(periph, 1);
319441918Smjacob
319543636Smjacob
319641918Smjacob	scsi_set_position(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG,
319741918Smjacob	    hard, *blkptr, SSD_FULL_SIZE, 60 * 60 * 1000);
319843636Smjacob
319943636Smjacob	softc->dsreg = MTIO_DSREG_POS;
320041918Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
320143636Smjacob	softc->dsreg = MTIO_DSREG_REST;
320241918Smjacob	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
320341918Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, 0);
320441918Smjacob	xpt_release_ccb(ccb);
320541918Smjacob	/*
320646962Smjacob	 * Note relative file && block number position as now unknown.
320741918Smjacob	 */
320843636Smjacob	softc->fileno = softc->blkno = (daddr_t) -1;
320941918Smjacob	return (error);
321041918Smjacob}
321141918Smjacob
321241918Smjacobstatic int
321339213Sgibbssaretension(struct cam_periph *periph)
321439213Sgibbs{
321539213Sgibbs	union ccb *ccb;
321639213Sgibbs	struct sa_softc *softc;
321739213Sgibbs	int error;
321839213Sgibbs
321939213Sgibbs	softc = (struct sa_softc *)periph->softc;
322039213Sgibbs
322146962Smjacob	ccb = cam_periph_getccb(periph, 1);
322239213Sgibbs
322342716Smjacob	/* It is safe to retry this operation */
322442716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
322553259Smjacob	    FALSE, TRUE,  TRUE, SSD_FULL_SIZE, ERASE_TIMEOUT);
322639213Sgibbs
322743636Smjacob	softc->dsreg = MTIO_DSREG_TEN;
322842716Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
322943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
323039213Sgibbs
323139213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
323242716Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
323339213Sgibbs	xpt_release_ccb(ccb);
323443636Smjacob	if (error == 0)
323543636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
323643636Smjacob	else
323743636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
323854099Smjacob	return (error);
323939213Sgibbs}
324039213Sgibbs
324139213Sgibbsstatic int
324239213Sgibbssareservereleaseunit(struct cam_periph *periph, int reserve)
324339213Sgibbs{
324439213Sgibbs	union ccb *ccb;
324539213Sgibbs	struct sa_softc *softc;
324654099Smjacob	int error;
324739213Sgibbs
324842009Smjacob	softc = (struct sa_softc *)periph->softc;
324942716Smjacob	ccb = cam_periph_getccb(periph,  1);
325039213Sgibbs
325142716Smjacob	/* It is safe to retry this operation */
325254099Smjacob	scsi_reserve_release_unit(&ccb->csio, 2, sadone, MSG_SIMPLE_Q_TAG,
325342716Smjacob	    FALSE,  0, SSD_FULL_SIZE,  5000, reserve);
325443636Smjacob	softc->dsreg = MTIO_DSREG_RBSY;
325554099Smjacob	error = cam_periph_runccb(ccb, saerror, 0,
325654099Smjacob	    SF_RETRY_UA | SF_NO_PRINT, &softc->device_stats);
325743636Smjacob	softc->dsreg = MTIO_DSREG_REST;
325854099Smjacob	QFRLS(ccb);
325939213Sgibbs	xpt_release_ccb(ccb);
326039213Sgibbs
326141674Smjacob	/*
326241674Smjacob	 * If the error was Illegal Request, then the device doesn't support
326341674Smjacob	 * RESERVE/RELEASE. This is not an error.
326441674Smjacob	 */
326542009Smjacob	if (error == EINVAL) {
326641674Smjacob		error = 0;
326742009Smjacob	}
326841674Smjacob
326939213Sgibbs	return (error);
327039213Sgibbs}
327139213Sgibbs
327239213Sgibbsstatic int
327339213Sgibbssaloadunload(struct cam_periph *periph, int load)
327439213Sgibbs{
327539213Sgibbs	union	ccb *ccb;
327639213Sgibbs	struct	sa_softc *softc;
327739213Sgibbs	int	error;
327839213Sgibbs
327939213Sgibbs	softc = (struct sa_softc *)periph->softc;
328039213Sgibbs
328146962Smjacob	ccb = cam_periph_getccb(periph, 1);
328239213Sgibbs
328342716Smjacob	/* It is safe to retry this operation */
328442716Smjacob	scsi_load_unload(&ccb->csio, 5, sadone, MSG_SIMPLE_Q_TAG, FALSE,
328554099Smjacob	    FALSE, FALSE, load, SSD_FULL_SIZE, REWIND_TIMEOUT);
328639213Sgibbs
328743636Smjacob	softc->dsreg = (load)? MTIO_DSREG_LD : MTIO_DSREG_UNL;
328842716Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
328943636Smjacob	softc->dsreg = MTIO_DSREG_REST;
329054099Smjacob	QFRLS(ccb);
329139213Sgibbs	xpt_release_ccb(ccb);
329243636Smjacob
329343636Smjacob	if (error || load == 0)
329443636Smjacob		softc->fileno = softc->blkno = (daddr_t) -1;
329543636Smjacob	else if (error == 0)
329643636Smjacob		softc->fileno = softc->blkno = (daddr_t) 0;
329739213Sgibbs	return (error);
329839213Sgibbs}
329939213Sgibbs
330039213Sgibbsstatic int
330139213Sgibbssaerase(struct cam_periph *periph, int longerase)
330239213Sgibbs{
330339213Sgibbs
330439213Sgibbs	union	ccb *ccb;
330539213Sgibbs	struct	sa_softc *softc;
330639213Sgibbs	int error;
330739213Sgibbs
330839213Sgibbs	softc = (struct sa_softc *)periph->softc;
330939213Sgibbs
331046962Smjacob	ccb = cam_periph_getccb(periph, 1);
331139213Sgibbs
331243636Smjacob	scsi_erase(&ccb->csio, 1, sadone, MSG_SIMPLE_Q_TAG, FALSE, longerase,
331353259Smjacob	    SSD_FULL_SIZE, ERASE_TIMEOUT);
331439213Sgibbs
331543636Smjacob	softc->dsreg = MTIO_DSREG_ZER;
331643636Smjacob	error = cam_periph_runccb(ccb, saerror, 0, 0, &softc->device_stats);
331743636Smjacob	softc->dsreg = MTIO_DSREG_REST;
331839213Sgibbs
331939213Sgibbs	if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
332043636Smjacob		cam_release_devq(ccb->ccb_h.path, 0, 0, 0, FALSE);
332139213Sgibbs	xpt_release_ccb(ccb);
332239213Sgibbs	return (error);
332339213Sgibbs}
332439213Sgibbs
332555205Speter#endif /* _KERNEL */
332639213Sgibbs
332739213Sgibbs/*
332839213Sgibbs * Read tape block limits command.
332939213Sgibbs */
333039213Sgibbsvoid
333139213Sgibbsscsi_read_block_limits(struct ccb_scsiio *csio, u_int32_t retries,
333239213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
333339213Sgibbs		   u_int8_t tag_action,
333439213Sgibbs		   struct scsi_read_block_limits_data *rlimit_buf,
333539213Sgibbs		   u_int8_t sense_len, u_int32_t timeout)
333639213Sgibbs{
333739213Sgibbs	struct scsi_read_block_limits *scsi_cmd;
333839213Sgibbs
333946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
334046962Smjacob	     (u_int8_t *)rlimit_buf, sizeof(*rlimit_buf), sense_len,
334146962Smjacob	     sizeof(*scsi_cmd), timeout);
334239213Sgibbs
334339213Sgibbs	scsi_cmd = (struct scsi_read_block_limits *)&csio->cdb_io.cdb_bytes;
334439213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
334539213Sgibbs	scsi_cmd->opcode = READ_BLOCK_LIMITS;
334639213Sgibbs}
334739213Sgibbs
334839213Sgibbsvoid
334939213Sgibbsscsi_sa_read_write(struct ccb_scsiio *csio, u_int32_t retries,
335039213Sgibbs		   void (*cbfcnp)(struct cam_periph *, union ccb *),
335139213Sgibbs		   u_int8_t tag_action, int readop, int sli,
335239213Sgibbs		   int fixed, u_int32_t length, u_int8_t *data_ptr,
335339213Sgibbs		   u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout)
335439213Sgibbs{
335539213Sgibbs	struct scsi_sa_rw *scsi_cmd;
335639213Sgibbs
335739213Sgibbs	scsi_cmd = (struct scsi_sa_rw *)&csio->cdb_io.cdb_bytes;
335839213Sgibbs	scsi_cmd->opcode = readop ? SA_READ : SA_WRITE;
335939213Sgibbs	scsi_cmd->sli_fixed = 0;
336039213Sgibbs	if (sli && readop)
336139213Sgibbs		scsi_cmd->sli_fixed |= SAR_SLI;
336239213Sgibbs	if (fixed)
336339213Sgibbs		scsi_cmd->sli_fixed |= SARW_FIXED;
336439213Sgibbs	scsi_ulto3b(length, scsi_cmd->length);
336539213Sgibbs	scsi_cmd->control = 0;
336639213Sgibbs
336746962Smjacob	cam_fill_csio(csio, retries, cbfcnp, readop ? CAM_DIR_IN : CAM_DIR_OUT,
336846962Smjacob	    tag_action, data_ptr, dxfer_len, sense_len,
336946962Smjacob	    sizeof(*scsi_cmd), timeout);
337039213Sgibbs}
337139213Sgibbs
337239213Sgibbsvoid
337339213Sgibbsscsi_load_unload(struct ccb_scsiio *csio, u_int32_t retries,
337439213Sgibbs		 void (*cbfcnp)(struct cam_periph *, union ccb *),
337539213Sgibbs		 u_int8_t tag_action, int immediate, int eot,
337639213Sgibbs		 int reten, int load, u_int8_t sense_len,
337739213Sgibbs		 u_int32_t timeout)
337839213Sgibbs{
337939213Sgibbs	struct scsi_load_unload *scsi_cmd;
338039213Sgibbs
338139213Sgibbs	scsi_cmd = (struct scsi_load_unload *)&csio->cdb_io.cdb_bytes;
338239213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
338339213Sgibbs	scsi_cmd->opcode = LOAD_UNLOAD;
338439213Sgibbs	if (immediate)
338539213Sgibbs		scsi_cmd->immediate = SLU_IMMED;
338639213Sgibbs	if (eot)
338739213Sgibbs		scsi_cmd->eot_reten_load |= SLU_EOT;
338839213Sgibbs	if (reten)
338939213Sgibbs		scsi_cmd->eot_reten_load |= SLU_RETEN;
339039213Sgibbs	if (load)
339139213Sgibbs		scsi_cmd->eot_reten_load |= SLU_LOAD;
339239213Sgibbs
339346962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
339446962Smjacob	    NULL, 0, sense_len, sizeof(*scsi_cmd), timeout);
339539213Sgibbs}
339639213Sgibbs
339739213Sgibbsvoid
339839213Sgibbsscsi_rewind(struct ccb_scsiio *csio, u_int32_t retries,
339939213Sgibbs	    void (*cbfcnp)(struct cam_periph *, union ccb *),
340039213Sgibbs	    u_int8_t tag_action, int immediate, u_int8_t sense_len,
340139213Sgibbs	    u_int32_t timeout)
340239213Sgibbs{
340339213Sgibbs	struct scsi_rewind *scsi_cmd;
340439213Sgibbs
340539213Sgibbs	scsi_cmd = (struct scsi_rewind *)&csio->cdb_io.cdb_bytes;
340639213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
340739213Sgibbs	scsi_cmd->opcode = REWIND;
340839213Sgibbs	if (immediate)
340939213Sgibbs		scsi_cmd->immediate = SREW_IMMED;
341039213Sgibbs
341146962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
341246962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
341339213Sgibbs}
341439213Sgibbs
341539213Sgibbsvoid
341639213Sgibbsscsi_space(struct ccb_scsiio *csio, u_int32_t retries,
341739213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
341839213Sgibbs	   u_int8_t tag_action, scsi_space_code code,
341939213Sgibbs	   u_int32_t count, u_int8_t sense_len, u_int32_t timeout)
342039213Sgibbs{
342139213Sgibbs	struct scsi_space *scsi_cmd;
342239213Sgibbs
342339213Sgibbs	scsi_cmd = (struct scsi_space *)&csio->cdb_io.cdb_bytes;
342439213Sgibbs	scsi_cmd->opcode = SPACE;
342539213Sgibbs	scsi_cmd->code = code;
342639213Sgibbs	scsi_ulto3b(count, scsi_cmd->count);
342739213Sgibbs	scsi_cmd->control = 0;
342839213Sgibbs
342946962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
343046962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
343139213Sgibbs}
343239213Sgibbs
343339213Sgibbsvoid
343439213Sgibbsscsi_write_filemarks(struct ccb_scsiio *csio, u_int32_t retries,
343539213Sgibbs		     void (*cbfcnp)(struct cam_periph *, union ccb *),
343639213Sgibbs		     u_int8_t tag_action, int immediate, int setmark,
343739213Sgibbs		     u_int32_t num_marks, u_int8_t sense_len,
343839213Sgibbs		     u_int32_t timeout)
343939213Sgibbs{
344039213Sgibbs	struct scsi_write_filemarks *scsi_cmd;
344139213Sgibbs
344239213Sgibbs	scsi_cmd = (struct scsi_write_filemarks *)&csio->cdb_io.cdb_bytes;
344339213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
344439213Sgibbs	scsi_cmd->opcode = WRITE_FILEMARKS;
344539213Sgibbs	if (immediate)
344639213Sgibbs		scsi_cmd->byte2 |= SWFMRK_IMMED;
344739213Sgibbs	if (setmark)
344839213Sgibbs		scsi_cmd->byte2 |= SWFMRK_WSMK;
344939213Sgibbs
345039213Sgibbs	scsi_ulto3b(num_marks, scsi_cmd->num_marks);
345139213Sgibbs
345246962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
345346962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
345439213Sgibbs}
345539213Sgibbs
345639213Sgibbs/*
345739213Sgibbs * The reserve and release unit commands differ only by their opcodes.
345839213Sgibbs */
345939213Sgibbsvoid
346039213Sgibbsscsi_reserve_release_unit(struct ccb_scsiio *csio, u_int32_t retries,
346139213Sgibbs			  void (*cbfcnp)(struct cam_periph *, union ccb *),
346239213Sgibbs			  u_int8_t tag_action, int third_party,
346339213Sgibbs			  int third_party_id, u_int8_t sense_len,
346439213Sgibbs			  u_int32_t timeout, int reserve)
346539213Sgibbs{
346639213Sgibbs	struct scsi_reserve_release_unit *scsi_cmd;
346739213Sgibbs
346839213Sgibbs	scsi_cmd = (struct scsi_reserve_release_unit *)&csio->cdb_io.cdb_bytes;
346939213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
347039213Sgibbs
347139213Sgibbs	if (reserve)
347239213Sgibbs		scsi_cmd->opcode = RESERVE_UNIT;
347339213Sgibbs	else
347439213Sgibbs		scsi_cmd->opcode = RELEASE_UNIT;
347539213Sgibbs
347639213Sgibbs	if (third_party) {
347739213Sgibbs		scsi_cmd->lun_thirdparty |= SRRU_3RD_PARTY;
347839213Sgibbs		scsi_cmd->lun_thirdparty |=
347939213Sgibbs			((third_party_id << SRRU_3RD_SHAMT) & SRRU_3RD_MASK);
348039213Sgibbs	}
348139213Sgibbs
348246962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
348346962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
348439213Sgibbs}
348539213Sgibbs
348639213Sgibbsvoid
348739213Sgibbsscsi_erase(struct ccb_scsiio *csio, u_int32_t retries,
348839213Sgibbs	   void (*cbfcnp)(struct cam_periph *, union ccb *),
348939213Sgibbs	   u_int8_t tag_action, int immediate, int long_erase,
349039213Sgibbs	   u_int8_t sense_len, u_int32_t timeout)
349139213Sgibbs{
349239213Sgibbs	struct scsi_erase *scsi_cmd;
349339213Sgibbs
349439213Sgibbs	scsi_cmd = (struct scsi_erase *)&csio->cdb_io.cdb_bytes;
349539213Sgibbs	bzero(scsi_cmd, sizeof(*scsi_cmd));
349639213Sgibbs
349739213Sgibbs	scsi_cmd->opcode = ERASE;
349839213Sgibbs
349939213Sgibbs	if (immediate)
350039213Sgibbs		scsi_cmd->lun_imm_long |= SE_IMMED;
350139213Sgibbs
350239213Sgibbs	if (long_erase)
350339213Sgibbs		scsi_cmd->lun_imm_long |= SE_LONG;
350439213Sgibbs
350546962Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, NULL,
350646962Smjacob	    0, sense_len, sizeof(*scsi_cmd), timeout);
350739213Sgibbs}
350841918Smjacob
350941918Smjacob/*
351041918Smjacob * Read Tape Position command.
351141918Smjacob */
351241918Smjacobvoid
351341918Smjacobscsi_read_position(struct ccb_scsiio *csio, u_int32_t retries,
351441918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
351541918Smjacob		   u_int8_t tag_action, int hardsoft,
351641918Smjacob		   struct scsi_tape_position_data *sbp,
351741918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
351841918Smjacob{
351941918Smjacob	struct scsi_tape_read_position *scmd;
352041918Smjacob
352141918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action,
352241918Smjacob	    (u_int8_t *)sbp, sizeof (*sbp), sense_len, sizeof(*scmd), timeout);
352341918Smjacob	scmd = (struct scsi_tape_read_position *)&csio->cdb_io.cdb_bytes;
352441918Smjacob	bzero(scmd, sizeof(*scmd));
352541918Smjacob	scmd->opcode = READ_POSITION;
352641918Smjacob	scmd->byte1 = hardsoft;
352741918Smjacob}
352841918Smjacob
352941918Smjacob/*
353041918Smjacob * Set Tape Position command.
353141918Smjacob */
353241918Smjacobvoid
353341918Smjacobscsi_set_position(struct ccb_scsiio *csio, u_int32_t retries,
353441918Smjacob		   void (*cbfcnp)(struct cam_periph *, union ccb *),
353541918Smjacob		   u_int8_t tag_action, int hardsoft, u_int32_t blkno,
353641918Smjacob		   u_int8_t sense_len, u_int32_t timeout)
353741918Smjacob{
353841918Smjacob	struct scsi_tape_locate *scmd;
353941918Smjacob
354041918Smjacob	cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action,
354141918Smjacob	    (u_int8_t *)NULL, 0, sense_len, sizeof(*scmd), timeout);
354241918Smjacob	scmd = (struct scsi_tape_locate *)&csio->cdb_io.cdb_bytes;
354341918Smjacob	bzero(scmd, sizeof(*scmd));
354441918Smjacob	scmd->opcode = LOCATE;
354541918Smjacob	if (hardsoft)
354641918Smjacob		scmd->byte1 |= SA_SPOS_BT;
354741918Smjacob	scsi_ulto4b(blkno, scmd->blkaddr);
354841918Smjacob}
3549