162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci   SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
462306a36Sopenharmony_ci   file Documentation/scsi/st.rst for more information.
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci   History:
762306a36Sopenharmony_ci   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
862306a36Sopenharmony_ci   Contribution and ideas from several people including (in alphabetical
962306a36Sopenharmony_ci   order) Klaus Ehrenfried, Eugene Exarevsky, Eric Lee Green, Wolfgang Denk,
1062306a36Sopenharmony_ci   Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
1162306a36Sopenharmony_ci   Michael Schaefer, J"org Weule, and Eric Youngdale.
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci   Copyright 1992 - 2016 Kai Makisara
1462306a36Sopenharmony_ci   email Kai.Makisara@kolumbus.fi
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci   Some small formal changes - aeb, 950809
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci   Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const char *verstr = "20160209";
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/module.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <linux/compat.h>
2662306a36Sopenharmony_ci#include <linux/fs.h>
2762306a36Sopenharmony_ci#include <linux/kernel.h>
2862306a36Sopenharmony_ci#include <linux/sched/signal.h>
2962306a36Sopenharmony_ci#include <linux/mm.h>
3062306a36Sopenharmony_ci#include <linux/init.h>
3162306a36Sopenharmony_ci#include <linux/string.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <linux/errno.h>
3462306a36Sopenharmony_ci#include <linux/mtio.h>
3562306a36Sopenharmony_ci#include <linux/major.h>
3662306a36Sopenharmony_ci#include <linux/cdrom.h>
3762306a36Sopenharmony_ci#include <linux/ioctl.h>
3862306a36Sopenharmony_ci#include <linux/fcntl.h>
3962306a36Sopenharmony_ci#include <linux/spinlock.h>
4062306a36Sopenharmony_ci#include <linux/blkdev.h>
4162306a36Sopenharmony_ci#include <linux/moduleparam.h>
4262306a36Sopenharmony_ci#include <linux/cdev.h>
4362306a36Sopenharmony_ci#include <linux/idr.h>
4462306a36Sopenharmony_ci#include <linux/delay.h>
4562306a36Sopenharmony_ci#include <linux/mutex.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#include <linux/uaccess.h>
4862306a36Sopenharmony_ci#include <asm/dma.h>
4962306a36Sopenharmony_ci#include <asm/unaligned.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <scsi/scsi.h>
5262306a36Sopenharmony_ci#include <scsi/scsi_dbg.h>
5362306a36Sopenharmony_ci#include <scsi/scsi_device.h>
5462306a36Sopenharmony_ci#include <scsi/scsi_driver.h>
5562306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
5662306a36Sopenharmony_ci#include <scsi/scsi_host.h>
5762306a36Sopenharmony_ci#include <scsi/scsi_ioctl.h>
5862306a36Sopenharmony_ci#include <scsi/sg.h>
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* The driver prints some debugging information on the console if DEBUG
6262306a36Sopenharmony_ci   is defined and non-zero. */
6362306a36Sopenharmony_ci#define DEBUG 1
6462306a36Sopenharmony_ci#define NO_DEBUG 0
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define ST_DEB_MSG  KERN_NOTICE
6762306a36Sopenharmony_ci#if DEBUG
6862306a36Sopenharmony_ci/* The message level for the debug messages is currently set to KERN_NOTICE
6962306a36Sopenharmony_ci   so that people can easily see the messages. Later when the debugging messages
7062306a36Sopenharmony_ci   in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
7162306a36Sopenharmony_ci#define DEB(a) a
7262306a36Sopenharmony_ci#define DEBC(a) if (debugging) { a ; }
7362306a36Sopenharmony_ci#else
7462306a36Sopenharmony_ci#define DEB(a)
7562306a36Sopenharmony_ci#define DEBC(a)
7662306a36Sopenharmony_ci#endif
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci#define ST_KILOBYTE 1024
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci#include "st_options.h"
8162306a36Sopenharmony_ci#include "st.h"
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int buffer_kbs;
8462306a36Sopenharmony_cistatic int max_sg_segs;
8562306a36Sopenharmony_cistatic int try_direct_io = TRY_DIRECT_IO;
8662306a36Sopenharmony_cistatic int try_rdio = 1;
8762306a36Sopenharmony_cistatic int try_wdio = 1;
8862306a36Sopenharmony_cistatic int debug_flag;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct class st_sysfs_class;
9162306a36Sopenharmony_cistatic const struct attribute_group *st_dev_groups[];
9262306a36Sopenharmony_cistatic const struct attribute_group *st_drv_groups[];
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ciMODULE_AUTHOR("Kai Makisara");
9562306a36Sopenharmony_ciMODULE_DESCRIPTION("SCSI tape (st) driver");
9662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
9762306a36Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(SCSI_TAPE_MAJOR);
9862306a36Sopenharmony_ciMODULE_ALIAS_SCSI_DEVICE(TYPE_TAPE);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/* Set 'perm' (4th argument) to 0 to disable module_param's definition
10162306a36Sopenharmony_ci * of sysfs parameters (which module_param doesn't yet support).
10262306a36Sopenharmony_ci * Sysfs parameters defined explicitly later.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cimodule_param_named(buffer_kbs, buffer_kbs, int, 0);
10562306a36Sopenharmony_ciMODULE_PARM_DESC(buffer_kbs, "Default driver buffer size for fixed block mode (KB; 32)");
10662306a36Sopenharmony_cimodule_param_named(max_sg_segs, max_sg_segs, int, 0);
10762306a36Sopenharmony_ciMODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)");
10862306a36Sopenharmony_cimodule_param_named(try_direct_io, try_direct_io, int, 0);
10962306a36Sopenharmony_ciMODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)");
11062306a36Sopenharmony_cimodule_param_named(debug_flag, debug_flag, int, 0);
11162306a36Sopenharmony_ciMODULE_PARM_DESC(debug_flag, "Enable DEBUG, same as setting debugging=1");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci/* Extra parameters for testing */
11562306a36Sopenharmony_cimodule_param_named(try_rdio, try_rdio, int, 0);
11662306a36Sopenharmony_ciMODULE_PARM_DESC(try_rdio, "Try direct read i/o when possible");
11762306a36Sopenharmony_cimodule_param_named(try_wdio, try_wdio, int, 0);
11862306a36Sopenharmony_ciMODULE_PARM_DESC(try_wdio, "Try direct write i/o when possible");
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#ifndef MODULE
12162306a36Sopenharmony_cistatic int write_threshold_kbs;  /* retained for compatibility */
12262306a36Sopenharmony_cistatic struct st_dev_parm {
12362306a36Sopenharmony_ci	char *name;
12462306a36Sopenharmony_ci	int *val;
12562306a36Sopenharmony_ci} parms[] __initdata = {
12662306a36Sopenharmony_ci	{
12762306a36Sopenharmony_ci		"buffer_kbs", &buffer_kbs
12862306a36Sopenharmony_ci	},
12962306a36Sopenharmony_ci	{       /* Retained for compatibility with 2.4 */
13062306a36Sopenharmony_ci		"write_threshold_kbs", &write_threshold_kbs
13162306a36Sopenharmony_ci	},
13262306a36Sopenharmony_ci	{
13362306a36Sopenharmony_ci		"max_sg_segs", NULL
13462306a36Sopenharmony_ci	},
13562306a36Sopenharmony_ci	{
13662306a36Sopenharmony_ci		"try_direct_io", &try_direct_io
13762306a36Sopenharmony_ci	},
13862306a36Sopenharmony_ci	{
13962306a36Sopenharmony_ci		"debug_flag", &debug_flag
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci#endif
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* Restrict the number of modes so that names for all are assigned */
14562306a36Sopenharmony_ci#if ST_NBR_MODES > 16
14662306a36Sopenharmony_ci#error "Maximum number of modes is 16"
14762306a36Sopenharmony_ci#endif
14862306a36Sopenharmony_ci/* Bit reversed order to get same names for same minors with all
14962306a36Sopenharmony_ci   mode counts */
15062306a36Sopenharmony_cistatic const char *st_formats[] = {
15162306a36Sopenharmony_ci	"",  "r", "k", "s", "l", "t", "o", "u",
15262306a36Sopenharmony_ci	"m", "v", "p", "x", "a", "y", "q", "z"};
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/* The default definitions have been moved to st_options.h */
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#define ST_FIXED_BUFFER_SIZE (ST_FIXED_BUFFER_BLOCKS * ST_KILOBYTE)
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/* The buffer size should fit into the 24 bits for length in the
15962306a36Sopenharmony_ci   6-byte SCSI read and write commands. */
16062306a36Sopenharmony_ci#if ST_FIXED_BUFFER_SIZE >= (2 << 24 - 1)
16162306a36Sopenharmony_ci#error "Buffer size should not exceed (2 << 24 - 1) bytes!"
16262306a36Sopenharmony_ci#endif
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int debugging = DEBUG;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci#define MAX_RETRIES 0
16762306a36Sopenharmony_ci#define MAX_WRITE_RETRIES 0
16862306a36Sopenharmony_ci#define MAX_READY_RETRIES 0
16962306a36Sopenharmony_ci#define NO_TAPE  NOT_READY
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci#define ST_TIMEOUT (900 * HZ)
17262306a36Sopenharmony_ci#define ST_LONG_TIMEOUT (14000 * HZ)
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci/* Remove mode bits and auto-rewind bit (7) */
17562306a36Sopenharmony_ci#define TAPE_NR(x) ( ((iminor(x) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
17662306a36Sopenharmony_ci	(iminor(x) & ((1 << ST_MODE_SHIFT)-1)))
17762306a36Sopenharmony_ci#define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci/* Construct the minor number from the device (d), mode (m), and non-rewind (n) data */
18062306a36Sopenharmony_ci#define TAPE_MINOR(d, m, n) (((d & ~(255 >> (ST_NBR_MODE_BITS + 1))) << (ST_NBR_MODE_BITS + 1)) | \
18162306a36Sopenharmony_ci  (d & (255 >> (ST_NBR_MODE_BITS + 1))) | (m << ST_MODE_SHIFT) | ((n != 0) << 7) )
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci/* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
18462306a36Sopenharmony_ci   24 bits) */
18562306a36Sopenharmony_ci#define SET_DENS_AND_BLK 0x10001
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int st_fixed_buffer_size = ST_FIXED_BUFFER_SIZE;
18862306a36Sopenharmony_cistatic int st_max_sg_segs = ST_MAX_SG;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int modes_defined;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic int enlarge_buffer(struct st_buffer *, int);
19362306a36Sopenharmony_cistatic void clear_buffer(struct st_buffer *);
19462306a36Sopenharmony_cistatic void normalize_buffer(struct st_buffer *);
19562306a36Sopenharmony_cistatic int append_to_buffer(const char __user *, struct st_buffer *, int);
19662306a36Sopenharmony_cistatic int from_buffer(struct st_buffer *, char __user *, int);
19762306a36Sopenharmony_cistatic void move_buffer_data(struct st_buffer *, int);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int sgl_map_user_pages(struct st_buffer *, const unsigned int,
20062306a36Sopenharmony_ci			      unsigned long, size_t, int);
20162306a36Sopenharmony_cistatic int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int st_probe(struct device *);
20462306a36Sopenharmony_cistatic int st_remove(struct device *);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic struct scsi_driver st_template = {
20762306a36Sopenharmony_ci	.gendrv = {
20862306a36Sopenharmony_ci		.name		= "st",
20962306a36Sopenharmony_ci		.owner		= THIS_MODULE,
21062306a36Sopenharmony_ci		.probe		= st_probe,
21162306a36Sopenharmony_ci		.remove		= st_remove,
21262306a36Sopenharmony_ci		.groups		= st_drv_groups,
21362306a36Sopenharmony_ci	},
21462306a36Sopenharmony_ci};
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int st_compression(struct scsi_tape *, int);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int find_partition(struct scsi_tape *);
21962306a36Sopenharmony_cistatic int switch_partition(struct scsi_tape *);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void scsi_tape_release(struct kref *);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci#define to_scsi_tape(obj) container_of(obj, struct scsi_tape, kref)
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic DEFINE_MUTEX(st_ref_mutex);
22862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(st_index_lock);
22962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(st_use_lock);
23062306a36Sopenharmony_cistatic DEFINE_IDR(st_index_idr);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci#ifndef SIGS_FROM_OSST
23562306a36Sopenharmony_ci#define SIGS_FROM_OSST \
23662306a36Sopenharmony_ci	{"OnStream", "SC-", "", "osst"}, \
23762306a36Sopenharmony_ci	{"OnStream", "DI-", "", "osst"}, \
23862306a36Sopenharmony_ci	{"OnStream", "DP-", "", "osst"}, \
23962306a36Sopenharmony_ci	{"OnStream", "USB", "", "osst"}, \
24062306a36Sopenharmony_ci	{"OnStream", "FW-", "", "osst"}
24162306a36Sopenharmony_ci#endif
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic struct scsi_tape *scsi_tape_get(int dev)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct scsi_tape *STp = NULL;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	mutex_lock(&st_ref_mutex);
24862306a36Sopenharmony_ci	spin_lock(&st_index_lock);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	STp = idr_find(&st_index_idr, dev);
25162306a36Sopenharmony_ci	if (!STp) goto out;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	kref_get(&STp->kref);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (!STp->device)
25662306a36Sopenharmony_ci		goto out_put;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (scsi_device_get(STp->device))
25962306a36Sopenharmony_ci		goto out_put;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	goto out;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ciout_put:
26462306a36Sopenharmony_ci	kref_put(&STp->kref, scsi_tape_release);
26562306a36Sopenharmony_ci	STp = NULL;
26662306a36Sopenharmony_ciout:
26762306a36Sopenharmony_ci	spin_unlock(&st_index_lock);
26862306a36Sopenharmony_ci	mutex_unlock(&st_ref_mutex);
26962306a36Sopenharmony_ci	return STp;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void scsi_tape_put(struct scsi_tape *STp)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct scsi_device *sdev = STp->device;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	mutex_lock(&st_ref_mutex);
27762306a36Sopenharmony_ci	kref_put(&STp->kref, scsi_tape_release);
27862306a36Sopenharmony_ci	scsi_device_put(sdev);
27962306a36Sopenharmony_ci	mutex_unlock(&st_ref_mutex);
28062306a36Sopenharmony_ci}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistruct st_reject_data {
28362306a36Sopenharmony_ci	char *vendor;
28462306a36Sopenharmony_ci	char *model;
28562306a36Sopenharmony_ci	char *rev;
28662306a36Sopenharmony_ci	char *driver_hint; /* Name of the correct driver, NULL if unknown */
28762306a36Sopenharmony_ci};
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic struct st_reject_data reject_list[] = {
29062306a36Sopenharmony_ci	/* {"XXX", "Yy-", "", NULL},  example */
29162306a36Sopenharmony_ci	SIGS_FROM_OSST,
29262306a36Sopenharmony_ci	{NULL, }};
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci/* If the device signature is on the list of incompatible drives, the
29562306a36Sopenharmony_ci   function returns a pointer to the name of the correct driver (if known) */
29662306a36Sopenharmony_cistatic char * st_incompatible(struct scsi_device* SDp)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct st_reject_data *rp;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
30162306a36Sopenharmony_ci		if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
30262306a36Sopenharmony_ci		    !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
30362306a36Sopenharmony_ci		    !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
30462306a36Sopenharmony_ci			if (rp->driver_hint)
30562306a36Sopenharmony_ci				return rp->driver_hint;
30662306a36Sopenharmony_ci			else
30762306a36Sopenharmony_ci				return "unknown";
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	return NULL;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci#define st_printk(prefix, t, fmt, a...) \
31462306a36Sopenharmony_ci	sdev_prefix_printk(prefix, (t)->device, (t)->name, fmt, ##a)
31562306a36Sopenharmony_ci#ifdef DEBUG
31662306a36Sopenharmony_ci#define DEBC_printk(t, fmt, a...) \
31762306a36Sopenharmony_ci	if (debugging) { st_printk(ST_DEB_MSG, t, fmt, ##a ); }
31862306a36Sopenharmony_ci#else
31962306a36Sopenharmony_ci#define DEBC_printk(t, fmt, a...)
32062306a36Sopenharmony_ci#endif
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void st_analyze_sense(struct st_request *SRpnt, struct st_cmdstatus *s)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	const u8 *ucp;
32562306a36Sopenharmony_ci	const u8 *sense = SRpnt->sense;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	s->have_sense = scsi_normalize_sense(SRpnt->sense,
32862306a36Sopenharmony_ci				SCSI_SENSE_BUFFERSIZE, &s->sense_hdr);
32962306a36Sopenharmony_ci	s->flags = 0;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (s->have_sense) {
33262306a36Sopenharmony_ci		s->deferred = 0;
33362306a36Sopenharmony_ci		s->remainder_valid =
33462306a36Sopenharmony_ci			scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64);
33562306a36Sopenharmony_ci		switch (sense[0] & 0x7f) {
33662306a36Sopenharmony_ci		case 0x71:
33762306a36Sopenharmony_ci			s->deferred = 1;
33862306a36Sopenharmony_ci			fallthrough;
33962306a36Sopenharmony_ci		case 0x70:
34062306a36Sopenharmony_ci			s->fixed_format = 1;
34162306a36Sopenharmony_ci			s->flags = sense[2] & 0xe0;
34262306a36Sopenharmony_ci			break;
34362306a36Sopenharmony_ci		case 0x73:
34462306a36Sopenharmony_ci			s->deferred = 1;
34562306a36Sopenharmony_ci			fallthrough;
34662306a36Sopenharmony_ci		case 0x72:
34762306a36Sopenharmony_ci			s->fixed_format = 0;
34862306a36Sopenharmony_ci			ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
34962306a36Sopenharmony_ci			s->flags = ucp ? (ucp[3] & 0xe0) : 0;
35062306a36Sopenharmony_ci			break;
35162306a36Sopenharmony_ci		}
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/* Convert the result to success code */
35762306a36Sopenharmony_cistatic int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	int result = SRpnt->result;
36062306a36Sopenharmony_ci	u8 scode;
36162306a36Sopenharmony_ci	DEB(const char *stp;)
36262306a36Sopenharmony_ci	char *name = STp->name;
36362306a36Sopenharmony_ci	struct st_cmdstatus *cmdstatp;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!result)
36662306a36Sopenharmony_ci		return 0;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	cmdstatp = &STp->buffer->cmdstat;
36962306a36Sopenharmony_ci	st_analyze_sense(SRpnt, cmdstatp);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (cmdstatp->have_sense)
37262306a36Sopenharmony_ci		scode = STp->buffer->cmdstat.sense_hdr.sense_key;
37362306a36Sopenharmony_ci	else
37462306a36Sopenharmony_ci		scode = 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	DEB(
37762306a36Sopenharmony_ci	if (debugging) {
37862306a36Sopenharmony_ci		st_printk(ST_DEB_MSG, STp,
37962306a36Sopenharmony_ci			    "Error: %x, cmd: %x %x %x %x %x %x\n", result,
38062306a36Sopenharmony_ci			    SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2],
38162306a36Sopenharmony_ci			    SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]);
38262306a36Sopenharmony_ci		if (cmdstatp->have_sense)
38362306a36Sopenharmony_ci			__scsi_print_sense(STp->device, name,
38462306a36Sopenharmony_ci					   SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
38562306a36Sopenharmony_ci	} ) /* end DEB */
38662306a36Sopenharmony_ci	if (!debugging) { /* Abnormal conditions for tape */
38762306a36Sopenharmony_ci		if (!cmdstatp->have_sense)
38862306a36Sopenharmony_ci			st_printk(KERN_WARNING, STp,
38962306a36Sopenharmony_ci			       "Error %x (driver bt 0, host bt 0x%x).\n",
39062306a36Sopenharmony_ci			       result, host_byte(result));
39162306a36Sopenharmony_ci		else if (cmdstatp->have_sense &&
39262306a36Sopenharmony_ci			 scode != NO_SENSE &&
39362306a36Sopenharmony_ci			 scode != RECOVERED_ERROR &&
39462306a36Sopenharmony_ci			 /* scode != UNIT_ATTENTION && */
39562306a36Sopenharmony_ci			 scode != BLANK_CHECK &&
39662306a36Sopenharmony_ci			 scode != VOLUME_OVERFLOW &&
39762306a36Sopenharmony_ci			 SRpnt->cmd[0] != MODE_SENSE &&
39862306a36Sopenharmony_ci			 SRpnt->cmd[0] != TEST_UNIT_READY) {
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci			__scsi_print_sense(STp->device, name,
40162306a36Sopenharmony_ci					   SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (cmdstatp->fixed_format &&
40662306a36Sopenharmony_ci	    STp->cln_mode >= EXTENDED_SENSE_START) {  /* Only fixed format sense */
40762306a36Sopenharmony_ci		if (STp->cln_sense_value)
40862306a36Sopenharmony_ci			STp->cleaning_req |= ((SRpnt->sense[STp->cln_mode] &
40962306a36Sopenharmony_ci					       STp->cln_sense_mask) == STp->cln_sense_value);
41062306a36Sopenharmony_ci		else
41162306a36Sopenharmony_ci			STp->cleaning_req |= ((SRpnt->sense[STp->cln_mode] &
41262306a36Sopenharmony_ci					       STp->cln_sense_mask) != 0);
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	if (cmdstatp->have_sense &&
41562306a36Sopenharmony_ci	    cmdstatp->sense_hdr.asc == 0 && cmdstatp->sense_hdr.ascq == 0x17)
41662306a36Sopenharmony_ci		STp->cleaning_req = 1; /* ASC and ASCQ => cleaning requested */
41762306a36Sopenharmony_ci	if (cmdstatp->have_sense && scode == UNIT_ATTENTION && cmdstatp->sense_hdr.asc == 0x29)
41862306a36Sopenharmony_ci		STp->pos_unknown = 1; /* ASC => power on / reset */
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	STp->pos_unknown |= STp->device->was_reset;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (cmdstatp->have_sense &&
42362306a36Sopenharmony_ci	    scode == RECOVERED_ERROR
42462306a36Sopenharmony_ci#if ST_RECOVERED_WRITE_FATAL
42562306a36Sopenharmony_ci	    && SRpnt->cmd[0] != WRITE_6
42662306a36Sopenharmony_ci	    && SRpnt->cmd[0] != WRITE_FILEMARKS
42762306a36Sopenharmony_ci#endif
42862306a36Sopenharmony_ci	    ) {
42962306a36Sopenharmony_ci		STp->recover_count++;
43062306a36Sopenharmony_ci		STp->recover_reg++;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		DEB(
43362306a36Sopenharmony_ci		if (debugging) {
43462306a36Sopenharmony_ci			if (SRpnt->cmd[0] == READ_6)
43562306a36Sopenharmony_ci				stp = "read";
43662306a36Sopenharmony_ci			else if (SRpnt->cmd[0] == WRITE_6)
43762306a36Sopenharmony_ci				stp = "write";
43862306a36Sopenharmony_ci			else
43962306a36Sopenharmony_ci				stp = "ioctl";
44062306a36Sopenharmony_ci			st_printk(ST_DEB_MSG, STp,
44162306a36Sopenharmony_ci				  "Recovered %s error (%d).\n",
44262306a36Sopenharmony_ci				  stp, STp->recover_count);
44362306a36Sopenharmony_ci		} ) /* end DEB */
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		if (cmdstatp->flags == 0)
44662306a36Sopenharmony_ci			return 0;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci	return (-EIO);
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic struct st_request *st_allocate_request(struct scsi_tape *stp)
45262306a36Sopenharmony_ci{
45362306a36Sopenharmony_ci	struct st_request *streq;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	streq = kzalloc(sizeof(*streq), GFP_KERNEL);
45662306a36Sopenharmony_ci	if (streq)
45762306a36Sopenharmony_ci		streq->stp = stp;
45862306a36Sopenharmony_ci	else {
45962306a36Sopenharmony_ci		st_printk(KERN_ERR, stp,
46062306a36Sopenharmony_ci			  "Can't get SCSI request.\n");
46162306a36Sopenharmony_ci		if (signal_pending(current))
46262306a36Sopenharmony_ci			stp->buffer->syscall_result = -EINTR;
46362306a36Sopenharmony_ci		else
46462306a36Sopenharmony_ci			stp->buffer->syscall_result = -EBUSY;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return streq;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic void st_release_request(struct st_request *streq)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	kfree(streq);
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void st_do_stats(struct scsi_tape *STp, struct request *req)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
47862306a36Sopenharmony_ci	ktime_t now;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	now = ktime_get();
48162306a36Sopenharmony_ci	if (scmd->cmnd[0] == WRITE_6) {
48262306a36Sopenharmony_ci		now = ktime_sub(now, STp->stats->write_time);
48362306a36Sopenharmony_ci		atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time);
48462306a36Sopenharmony_ci		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
48562306a36Sopenharmony_ci		atomic64_inc(&STp->stats->write_cnt);
48662306a36Sopenharmony_ci		if (scmd->result) {
48762306a36Sopenharmony_ci			atomic64_add(atomic_read(&STp->stats->last_write_size)
48862306a36Sopenharmony_ci				- STp->buffer->cmdstat.residual,
48962306a36Sopenharmony_ci				&STp->stats->write_byte_cnt);
49062306a36Sopenharmony_ci			if (STp->buffer->cmdstat.residual > 0)
49162306a36Sopenharmony_ci				atomic64_inc(&STp->stats->resid_cnt);
49262306a36Sopenharmony_ci		} else
49362306a36Sopenharmony_ci			atomic64_add(atomic_read(&STp->stats->last_write_size),
49462306a36Sopenharmony_ci				&STp->stats->write_byte_cnt);
49562306a36Sopenharmony_ci	} else if (scmd->cmnd[0] == READ_6) {
49662306a36Sopenharmony_ci		now = ktime_sub(now, STp->stats->read_time);
49762306a36Sopenharmony_ci		atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time);
49862306a36Sopenharmony_ci		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
49962306a36Sopenharmony_ci		atomic64_inc(&STp->stats->read_cnt);
50062306a36Sopenharmony_ci		if (scmd->result) {
50162306a36Sopenharmony_ci			atomic64_add(atomic_read(&STp->stats->last_read_size)
50262306a36Sopenharmony_ci				- STp->buffer->cmdstat.residual,
50362306a36Sopenharmony_ci				&STp->stats->read_byte_cnt);
50462306a36Sopenharmony_ci			if (STp->buffer->cmdstat.residual > 0)
50562306a36Sopenharmony_ci				atomic64_inc(&STp->stats->resid_cnt);
50662306a36Sopenharmony_ci		} else
50762306a36Sopenharmony_ci			atomic64_add(atomic_read(&STp->stats->last_read_size),
50862306a36Sopenharmony_ci				&STp->stats->read_byte_cnt);
50962306a36Sopenharmony_ci	} else {
51062306a36Sopenharmony_ci		now = ktime_sub(now, STp->stats->other_time);
51162306a36Sopenharmony_ci		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
51262306a36Sopenharmony_ci		atomic64_inc(&STp->stats->other_cnt);
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	atomic64_dec(&STp->stats->in_flight);
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic enum rq_end_io_ret st_scsi_execute_end(struct request *req,
51862306a36Sopenharmony_ci					      blk_status_t status)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
52162306a36Sopenharmony_ci	struct st_request *SRpnt = req->end_io_data;
52262306a36Sopenharmony_ci	struct scsi_tape *STp = SRpnt->stp;
52362306a36Sopenharmony_ci	struct bio *tmp;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	STp->buffer->cmdstat.midlevel_result = SRpnt->result = scmd->result;
52662306a36Sopenharmony_ci	STp->buffer->cmdstat.residual = scmd->resid_len;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	st_do_stats(STp, req);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	tmp = SRpnt->bio;
53162306a36Sopenharmony_ci	if (scmd->sense_len)
53262306a36Sopenharmony_ci		memcpy(SRpnt->sense, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
53362306a36Sopenharmony_ci	if (SRpnt->waiting)
53462306a36Sopenharmony_ci		complete(SRpnt->waiting);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	blk_rq_unmap_user(tmp);
53762306a36Sopenharmony_ci	blk_mq_free_request(req);
53862306a36Sopenharmony_ci	return RQ_END_IO_NONE;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
54262306a36Sopenharmony_ci			   int data_direction, void *buffer, unsigned bufflen,
54362306a36Sopenharmony_ci			   int timeout, int retries)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct request *req;
54662306a36Sopenharmony_ci	struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
54762306a36Sopenharmony_ci	int err = 0;
54862306a36Sopenharmony_ci	struct scsi_tape *STp = SRpnt->stp;
54962306a36Sopenharmony_ci	struct scsi_cmnd *scmd;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	req = scsi_alloc_request(SRpnt->stp->device->request_queue,
55262306a36Sopenharmony_ci			data_direction == DMA_TO_DEVICE ?
55362306a36Sopenharmony_ci			REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);
55462306a36Sopenharmony_ci	if (IS_ERR(req))
55562306a36Sopenharmony_ci		return PTR_ERR(req);
55662306a36Sopenharmony_ci	scmd = blk_mq_rq_to_pdu(req);
55762306a36Sopenharmony_ci	req->rq_flags |= RQF_QUIET;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	mdata->null_mapped = 1;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (bufflen) {
56262306a36Sopenharmony_ci		err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen,
56362306a36Sopenharmony_ci				      GFP_KERNEL);
56462306a36Sopenharmony_ci		if (err) {
56562306a36Sopenharmony_ci			blk_mq_free_request(req);
56662306a36Sopenharmony_ci			return err;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	atomic64_inc(&STp->stats->in_flight);
57162306a36Sopenharmony_ci	if (cmd[0] == WRITE_6) {
57262306a36Sopenharmony_ci		atomic_set(&STp->stats->last_write_size, bufflen);
57362306a36Sopenharmony_ci		STp->stats->write_time = ktime_get();
57462306a36Sopenharmony_ci	} else if (cmd[0] == READ_6) {
57562306a36Sopenharmony_ci		atomic_set(&STp->stats->last_read_size, bufflen);
57662306a36Sopenharmony_ci		STp->stats->read_time = ktime_get();
57762306a36Sopenharmony_ci	} else {
57862306a36Sopenharmony_ci		STp->stats->other_time = ktime_get();
57962306a36Sopenharmony_ci	}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	SRpnt->bio = req->bio;
58262306a36Sopenharmony_ci	scmd->cmd_len = COMMAND_SIZE(cmd[0]);
58362306a36Sopenharmony_ci	memcpy(scmd->cmnd, cmd, scmd->cmd_len);
58462306a36Sopenharmony_ci	req->timeout = timeout;
58562306a36Sopenharmony_ci	scmd->allowed = retries;
58662306a36Sopenharmony_ci	req->end_io = st_scsi_execute_end;
58762306a36Sopenharmony_ci	req->end_io_data = SRpnt;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	blk_execute_rq_nowait(req, true);
59062306a36Sopenharmony_ci	return 0;
59162306a36Sopenharmony_ci}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci/* Do the scsi command. Waits until command performed if do_wait is true.
59462306a36Sopenharmony_ci   Otherwise write_behind_check() is used to check that the command
59562306a36Sopenharmony_ci   has finished. */
59662306a36Sopenharmony_cistatic struct st_request *
59762306a36Sopenharmony_cist_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd,
59862306a36Sopenharmony_ci	   int bytes, int direction, int timeout, int retries, int do_wait)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct completion *waiting;
60162306a36Sopenharmony_ci	struct rq_map_data *mdata = &STp->buffer->map_data;
60262306a36Sopenharmony_ci	int ret;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* if async, make sure there's no command outstanding */
60562306a36Sopenharmony_ci	if (!do_wait && ((STp->buffer)->last_SRpnt)) {
60662306a36Sopenharmony_ci		st_printk(KERN_ERR, STp,
60762306a36Sopenharmony_ci			  "Async command already active.\n");
60862306a36Sopenharmony_ci		if (signal_pending(current))
60962306a36Sopenharmony_ci			(STp->buffer)->syscall_result = (-EINTR);
61062306a36Sopenharmony_ci		else
61162306a36Sopenharmony_ci			(STp->buffer)->syscall_result = (-EBUSY);
61262306a36Sopenharmony_ci		return NULL;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!SRpnt) {
61662306a36Sopenharmony_ci		SRpnt = st_allocate_request(STp);
61762306a36Sopenharmony_ci		if (!SRpnt)
61862306a36Sopenharmony_ci			return NULL;
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	/* If async IO, set last_SRpnt. This ptr tells write_behind_check
62262306a36Sopenharmony_ci	   which IO is outstanding. It's nulled out when the IO completes. */
62362306a36Sopenharmony_ci	if (!do_wait)
62462306a36Sopenharmony_ci		(STp->buffer)->last_SRpnt = SRpnt;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	waiting = &STp->wait;
62762306a36Sopenharmony_ci	init_completion(waiting);
62862306a36Sopenharmony_ci	SRpnt->waiting = waiting;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (STp->buffer->do_dio) {
63162306a36Sopenharmony_ci		mdata->page_order = 0;
63262306a36Sopenharmony_ci		mdata->nr_entries = STp->buffer->sg_segs;
63362306a36Sopenharmony_ci		mdata->pages = STp->buffer->mapped_pages;
63462306a36Sopenharmony_ci	} else {
63562306a36Sopenharmony_ci		mdata->page_order = STp->buffer->reserved_page_order;
63662306a36Sopenharmony_ci		mdata->nr_entries =
63762306a36Sopenharmony_ci			DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order);
63862306a36Sopenharmony_ci		mdata->pages = STp->buffer->reserved_pages;
63962306a36Sopenharmony_ci		mdata->offset = 0;
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd));
64362306a36Sopenharmony_ci	STp->buffer->cmdstat.have_sense = 0;
64462306a36Sopenharmony_ci	STp->buffer->syscall_result = 0;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	ret = st_scsi_execute(SRpnt, cmd, direction, NULL, bytes, timeout,
64762306a36Sopenharmony_ci			      retries);
64862306a36Sopenharmony_ci	if (ret) {
64962306a36Sopenharmony_ci		/* could not allocate the buffer or request was too large */
65062306a36Sopenharmony_ci		(STp->buffer)->syscall_result = (-EBUSY);
65162306a36Sopenharmony_ci		(STp->buffer)->last_SRpnt = NULL;
65262306a36Sopenharmony_ci	} else if (do_wait) {
65362306a36Sopenharmony_ci		wait_for_completion(waiting);
65462306a36Sopenharmony_ci		SRpnt->waiting = NULL;
65562306a36Sopenharmony_ci		(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
65662306a36Sopenharmony_ci	}
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	return SRpnt;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci/* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
66362306a36Sopenharmony_ci   write has been correct but EOM early warning reached, -EIO if write ended in
66462306a36Sopenharmony_ci   error or zero if write successful. Asynchronous writes are used only in
66562306a36Sopenharmony_ci   variable block mode. */
66662306a36Sopenharmony_cistatic int write_behind_check(struct scsi_tape * STp)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	int retval = 0;
66962306a36Sopenharmony_ci	struct st_buffer *STbuffer;
67062306a36Sopenharmony_ci	struct st_partstat *STps;
67162306a36Sopenharmony_ci	struct st_cmdstatus *cmdstatp;
67262306a36Sopenharmony_ci	struct st_request *SRpnt;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	STbuffer = STp->buffer;
67562306a36Sopenharmony_ci	if (!STbuffer->writing)
67662306a36Sopenharmony_ci		return 0;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	DEB(
67962306a36Sopenharmony_ci	if (STp->write_pending)
68062306a36Sopenharmony_ci		STp->nbr_waits++;
68162306a36Sopenharmony_ci	else
68262306a36Sopenharmony_ci		STp->nbr_finished++;
68362306a36Sopenharmony_ci	) /* end DEB */
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	wait_for_completion(&(STp->wait));
68662306a36Sopenharmony_ci	SRpnt = STbuffer->last_SRpnt;
68762306a36Sopenharmony_ci	STbuffer->last_SRpnt = NULL;
68862306a36Sopenharmony_ci	SRpnt->waiting = NULL;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
69162306a36Sopenharmony_ci	st_release_request(SRpnt);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	STbuffer->buffer_bytes -= STbuffer->writing;
69462306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
69562306a36Sopenharmony_ci	if (STps->drv_block >= 0) {
69662306a36Sopenharmony_ci		if (STp->block_size == 0)
69762306a36Sopenharmony_ci			STps->drv_block++;
69862306a36Sopenharmony_ci		else
69962306a36Sopenharmony_ci			STps->drv_block += STbuffer->writing / STp->block_size;
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	cmdstatp = &STbuffer->cmdstat;
70362306a36Sopenharmony_ci	if (STbuffer->syscall_result) {
70462306a36Sopenharmony_ci		retval = -EIO;
70562306a36Sopenharmony_ci		if (cmdstatp->have_sense && !cmdstatp->deferred &&
70662306a36Sopenharmony_ci		    (cmdstatp->flags & SENSE_EOM) &&
70762306a36Sopenharmony_ci		    (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
70862306a36Sopenharmony_ci		     cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) {
70962306a36Sopenharmony_ci			/* EOM at write-behind, has all data been written? */
71062306a36Sopenharmony_ci			if (!cmdstatp->remainder_valid ||
71162306a36Sopenharmony_ci			    cmdstatp->uremainder64 == 0)
71262306a36Sopenharmony_ci				retval = -ENOSPC;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci		if (retval == -EIO)
71562306a36Sopenharmony_ci			STps->drv_block = -1;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	STbuffer->writing = 0;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	DEB(if (debugging && retval)
72062306a36Sopenharmony_ci		    st_printk(ST_DEB_MSG, STp,
72162306a36Sopenharmony_ci				"Async write error %x, return value %d.\n",
72262306a36Sopenharmony_ci				STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return retval;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci/* Step over EOF if it has been inadvertently crossed (ioctl not used because
72962306a36Sopenharmony_ci   it messes up the block number). */
73062306a36Sopenharmony_cistatic int cross_eof(struct scsi_tape * STp, int forward)
73162306a36Sopenharmony_ci{
73262306a36Sopenharmony_ci	struct st_request *SRpnt;
73362306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	cmd[0] = SPACE;
73662306a36Sopenharmony_ci	cmd[1] = 0x01;		/* Space FileMarks */
73762306a36Sopenharmony_ci	if (forward) {
73862306a36Sopenharmony_ci		cmd[2] = cmd[3] = 0;
73962306a36Sopenharmony_ci		cmd[4] = 1;
74062306a36Sopenharmony_ci	} else
74162306a36Sopenharmony_ci		cmd[2] = cmd[3] = cmd[4] = 0xff;	/* -1 filemarks */
74262306a36Sopenharmony_ci	cmd[5] = 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	DEBC_printk(STp, "Stepping over filemark %s.\n",
74562306a36Sopenharmony_ci		    forward ? "forward" : "backward");
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
74862306a36Sopenharmony_ci			   STp->device->request_queue->rq_timeout,
74962306a36Sopenharmony_ci			   MAX_RETRIES, 1);
75062306a36Sopenharmony_ci	if (!SRpnt)
75162306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	st_release_request(SRpnt);
75462306a36Sopenharmony_ci	SRpnt = NULL;
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	if ((STp->buffer)->cmdstat.midlevel_result != 0)
75762306a36Sopenharmony_ci		st_printk(KERN_ERR, STp,
75862306a36Sopenharmony_ci			  "Stepping over filemark %s failed.\n",
75962306a36Sopenharmony_ci			  forward ? "forward" : "backward");
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	return (STp->buffer)->syscall_result;
76262306a36Sopenharmony_ci}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci/* Flush the write buffer (never need to write if variable blocksize). */
76662306a36Sopenharmony_cistatic int st_flush_write_buffer(struct scsi_tape * STp)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	int transfer, blks;
76962306a36Sopenharmony_ci	int result;
77062306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
77162306a36Sopenharmony_ci	struct st_request *SRpnt;
77262306a36Sopenharmony_ci	struct st_partstat *STps;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	result = write_behind_check(STp);
77562306a36Sopenharmony_ci	if (result)
77662306a36Sopenharmony_ci		return result;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	result = 0;
77962306a36Sopenharmony_ci	if (STp->dirty == 1) {
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		transfer = STp->buffer->buffer_bytes;
78262306a36Sopenharmony_ci		DEBC_printk(STp, "Flushing %d bytes.\n", transfer);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		memset(cmd, 0, MAX_COMMAND_SIZE);
78562306a36Sopenharmony_ci		cmd[0] = WRITE_6;
78662306a36Sopenharmony_ci		cmd[1] = 1;
78762306a36Sopenharmony_ci		blks = transfer / STp->block_size;
78862306a36Sopenharmony_ci		cmd[2] = blks >> 16;
78962306a36Sopenharmony_ci		cmd[3] = blks >> 8;
79062306a36Sopenharmony_ci		cmd[4] = blks;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci		SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
79362306a36Sopenharmony_ci				   STp->device->request_queue->rq_timeout,
79462306a36Sopenharmony_ci				   MAX_WRITE_RETRIES, 1);
79562306a36Sopenharmony_ci		if (!SRpnt)
79662306a36Sopenharmony_ci			return (STp->buffer)->syscall_result;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci		STps = &(STp->ps[STp->partition]);
79962306a36Sopenharmony_ci		if ((STp->buffer)->syscall_result != 0) {
80062306a36Sopenharmony_ci			struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci			if (cmdstatp->have_sense && !cmdstatp->deferred &&
80362306a36Sopenharmony_ci			    (cmdstatp->flags & SENSE_EOM) &&
80462306a36Sopenharmony_ci			    (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
80562306a36Sopenharmony_ci			     cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
80662306a36Sopenharmony_ci			    (!cmdstatp->remainder_valid ||
80762306a36Sopenharmony_ci			     cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */
80862306a36Sopenharmony_ci				STp->dirty = 0;
80962306a36Sopenharmony_ci				(STp->buffer)->buffer_bytes = 0;
81062306a36Sopenharmony_ci				if (STps->drv_block >= 0)
81162306a36Sopenharmony_ci					STps->drv_block += blks;
81262306a36Sopenharmony_ci				result = (-ENOSPC);
81362306a36Sopenharmony_ci			} else {
81462306a36Sopenharmony_ci				st_printk(KERN_ERR, STp, "Error on flush.\n");
81562306a36Sopenharmony_ci				STps->drv_block = (-1);
81662306a36Sopenharmony_ci				result = (-EIO);
81762306a36Sopenharmony_ci			}
81862306a36Sopenharmony_ci		} else {
81962306a36Sopenharmony_ci			if (STps->drv_block >= 0)
82062306a36Sopenharmony_ci				STps->drv_block += blks;
82162306a36Sopenharmony_ci			STp->dirty = 0;
82262306a36Sopenharmony_ci			(STp->buffer)->buffer_bytes = 0;
82362306a36Sopenharmony_ci		}
82462306a36Sopenharmony_ci		st_release_request(SRpnt);
82562306a36Sopenharmony_ci		SRpnt = NULL;
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci	return result;
82862306a36Sopenharmony_ci}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci/* Flush the tape buffer. The tape will be positioned correctly unless
83262306a36Sopenharmony_ci   seek_next is true. */
83362306a36Sopenharmony_cistatic int flush_buffer(struct scsi_tape *STp, int seek_next)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	int backspace, result;
83662306a36Sopenharmony_ci	struct st_partstat *STps;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/*
83962306a36Sopenharmony_ci	 * If there was a bus reset, block further access
84062306a36Sopenharmony_ci	 * to this device.
84162306a36Sopenharmony_ci	 */
84262306a36Sopenharmony_ci	if (STp->pos_unknown)
84362306a36Sopenharmony_ci		return (-EIO);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	if (STp->ready != ST_READY)
84662306a36Sopenharmony_ci		return 0;
84762306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
84862306a36Sopenharmony_ci	if (STps->rw == ST_WRITING)	/* Writing */
84962306a36Sopenharmony_ci		return st_flush_write_buffer(STp);
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	if (STp->block_size == 0)
85262306a36Sopenharmony_ci		return 0;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	backspace = ((STp->buffer)->buffer_bytes +
85562306a36Sopenharmony_ci		     (STp->buffer)->read_pointer) / STp->block_size -
85662306a36Sopenharmony_ci	    ((STp->buffer)->read_pointer + STp->block_size - 1) /
85762306a36Sopenharmony_ci	    STp->block_size;
85862306a36Sopenharmony_ci	(STp->buffer)->buffer_bytes = 0;
85962306a36Sopenharmony_ci	(STp->buffer)->read_pointer = 0;
86062306a36Sopenharmony_ci	result = 0;
86162306a36Sopenharmony_ci	if (!seek_next) {
86262306a36Sopenharmony_ci		if (STps->eof == ST_FM_HIT) {
86362306a36Sopenharmony_ci			result = cross_eof(STp, 0);	/* Back over the EOF hit */
86462306a36Sopenharmony_ci			if (!result)
86562306a36Sopenharmony_ci				STps->eof = ST_NOEOF;
86662306a36Sopenharmony_ci			else {
86762306a36Sopenharmony_ci				if (STps->drv_file >= 0)
86862306a36Sopenharmony_ci					STps->drv_file++;
86962306a36Sopenharmony_ci				STps->drv_block = 0;
87062306a36Sopenharmony_ci			}
87162306a36Sopenharmony_ci		}
87262306a36Sopenharmony_ci		if (!result && backspace > 0)
87362306a36Sopenharmony_ci			result = st_int_ioctl(STp, MTBSR, backspace);
87462306a36Sopenharmony_ci	} else if (STps->eof == ST_FM_HIT) {
87562306a36Sopenharmony_ci		if (STps->drv_file >= 0)
87662306a36Sopenharmony_ci			STps->drv_file++;
87762306a36Sopenharmony_ci		STps->drv_block = 0;
87862306a36Sopenharmony_ci		STps->eof = ST_NOEOF;
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci	return result;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci/* Set the mode parameters */
88562306a36Sopenharmony_cistatic int set_mode_densblk(struct scsi_tape * STp, struct st_modedef * STm)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci	int set_it = 0;
88862306a36Sopenharmony_ci	unsigned long arg;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	if (!STp->density_changed &&
89162306a36Sopenharmony_ci	    STm->default_density >= 0 &&
89262306a36Sopenharmony_ci	    STm->default_density != STp->density) {
89362306a36Sopenharmony_ci		arg = STm->default_density;
89462306a36Sopenharmony_ci		set_it = 1;
89562306a36Sopenharmony_ci	} else
89662306a36Sopenharmony_ci		arg = STp->density;
89762306a36Sopenharmony_ci	arg <<= MT_ST_DENSITY_SHIFT;
89862306a36Sopenharmony_ci	if (!STp->blksize_changed &&
89962306a36Sopenharmony_ci	    STm->default_blksize >= 0 &&
90062306a36Sopenharmony_ci	    STm->default_blksize != STp->block_size) {
90162306a36Sopenharmony_ci		arg |= STm->default_blksize;
90262306a36Sopenharmony_ci		set_it = 1;
90362306a36Sopenharmony_ci	} else
90462306a36Sopenharmony_ci		arg |= STp->block_size;
90562306a36Sopenharmony_ci	if (set_it &&
90662306a36Sopenharmony_ci	    st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
90762306a36Sopenharmony_ci		st_printk(KERN_WARNING, STp,
90862306a36Sopenharmony_ci			  "Can't set default block size to %d bytes "
90962306a36Sopenharmony_ci			  "and density %x.\n",
91062306a36Sopenharmony_ci			  STm->default_blksize, STm->default_density);
91162306a36Sopenharmony_ci		if (modes_defined)
91262306a36Sopenharmony_ci			return (-EINVAL);
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci	return 0;
91562306a36Sopenharmony_ci}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci/* Lock or unlock the drive door. Don't use when st_request allocated. */
91962306a36Sopenharmony_cistatic int do_door_lock(struct scsi_tape * STp, int do_lock)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	int retval;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	DEBC_printk(STp, "%socking drive door.\n", do_lock ? "L" : "Unl");
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	retval = scsi_set_medium_removal(STp->device,
92662306a36Sopenharmony_ci			do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
92762306a36Sopenharmony_ci	if (!retval)
92862306a36Sopenharmony_ci		STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
92962306a36Sopenharmony_ci	else
93062306a36Sopenharmony_ci		STp->door_locked = ST_LOCK_FAILS;
93162306a36Sopenharmony_ci	return retval;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci/* Set the internal state after reset */
93662306a36Sopenharmony_cistatic void reset_state(struct scsi_tape *STp)
93762306a36Sopenharmony_ci{
93862306a36Sopenharmony_ci	int i;
93962306a36Sopenharmony_ci	struct st_partstat *STps;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	STp->pos_unknown = 0;
94262306a36Sopenharmony_ci	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
94362306a36Sopenharmony_ci		STps = &(STp->ps[i]);
94462306a36Sopenharmony_ci		STps->rw = ST_IDLE;
94562306a36Sopenharmony_ci		STps->eof = ST_NOEOF;
94662306a36Sopenharmony_ci		STps->at_sm = 0;
94762306a36Sopenharmony_ci		STps->last_block_valid = 0;
94862306a36Sopenharmony_ci		STps->drv_block = -1;
94962306a36Sopenharmony_ci		STps->drv_file = -1;
95062306a36Sopenharmony_ci	}
95162306a36Sopenharmony_ci	if (STp->can_partitions) {
95262306a36Sopenharmony_ci		STp->partition = find_partition(STp);
95362306a36Sopenharmony_ci		if (STp->partition < 0)
95462306a36Sopenharmony_ci			STp->partition = 0;
95562306a36Sopenharmony_ci		STp->new_partition = STp->partition;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/* Test if the drive is ready. Returns either one of the codes below or a negative system
96062306a36Sopenharmony_ci   error code. */
96162306a36Sopenharmony_ci#define CHKRES_READY       0
96262306a36Sopenharmony_ci#define CHKRES_NEW_SESSION 1
96362306a36Sopenharmony_ci#define CHKRES_NOT_READY   2
96462306a36Sopenharmony_ci#define CHKRES_NO_TAPE     3
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci#define MAX_ATTENTIONS    10
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_cistatic int test_ready(struct scsi_tape *STp, int do_wait)
96962306a36Sopenharmony_ci{
97062306a36Sopenharmony_ci	int attentions, waits, max_wait, scode;
97162306a36Sopenharmony_ci	int retval = CHKRES_READY, new_session = 0;
97262306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
97362306a36Sopenharmony_ci	struct st_request *SRpnt = NULL;
97462306a36Sopenharmony_ci	struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	for (attentions=waits=0; ; ) {
97962306a36Sopenharmony_ci		memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
98062306a36Sopenharmony_ci		cmd[0] = TEST_UNIT_READY;
98162306a36Sopenharmony_ci		SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
98262306a36Sopenharmony_ci				   STp->long_timeout, MAX_READY_RETRIES, 1);
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci		if (!SRpnt) {
98562306a36Sopenharmony_ci			retval = (STp->buffer)->syscall_result;
98662306a36Sopenharmony_ci			break;
98762306a36Sopenharmony_ci		}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		if (cmdstatp->have_sense) {
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci			scode = cmdstatp->sense_hdr.sense_key;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci			if (scode == UNIT_ATTENTION) { /* New media? */
99462306a36Sopenharmony_ci				new_session = 1;
99562306a36Sopenharmony_ci				if (attentions < MAX_ATTENTIONS) {
99662306a36Sopenharmony_ci					attentions++;
99762306a36Sopenharmony_ci					continue;
99862306a36Sopenharmony_ci				}
99962306a36Sopenharmony_ci				else {
100062306a36Sopenharmony_ci					retval = (-EIO);
100162306a36Sopenharmony_ci					break;
100262306a36Sopenharmony_ci				}
100362306a36Sopenharmony_ci			}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci			if (scode == NOT_READY) {
100662306a36Sopenharmony_ci				if (waits < max_wait) {
100762306a36Sopenharmony_ci					if (msleep_interruptible(1000)) {
100862306a36Sopenharmony_ci						retval = (-EINTR);
100962306a36Sopenharmony_ci						break;
101062306a36Sopenharmony_ci					}
101162306a36Sopenharmony_ci					waits++;
101262306a36Sopenharmony_ci					continue;
101362306a36Sopenharmony_ci				}
101462306a36Sopenharmony_ci				else {
101562306a36Sopenharmony_ci					if ((STp->device)->scsi_level >= SCSI_2 &&
101662306a36Sopenharmony_ci					    cmdstatp->sense_hdr.asc == 0x3a)	/* Check ASC */
101762306a36Sopenharmony_ci						retval = CHKRES_NO_TAPE;
101862306a36Sopenharmony_ci					else
101962306a36Sopenharmony_ci						retval = CHKRES_NOT_READY;
102062306a36Sopenharmony_ci					break;
102162306a36Sopenharmony_ci				}
102262306a36Sopenharmony_ci			}
102362306a36Sopenharmony_ci		}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci		retval = (STp->buffer)->syscall_result;
102662306a36Sopenharmony_ci		if (!retval)
102762306a36Sopenharmony_ci			retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY;
102862306a36Sopenharmony_ci		break;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	if (SRpnt != NULL)
103262306a36Sopenharmony_ci		st_release_request(SRpnt);
103362306a36Sopenharmony_ci	return retval;
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/* See if the drive is ready and gather information about the tape. Return values:
103862306a36Sopenharmony_ci   < 0   negative error code from errno.h
103962306a36Sopenharmony_ci   0     drive ready
104062306a36Sopenharmony_ci   1     drive not ready (possibly no tape)
104162306a36Sopenharmony_ci*/
104262306a36Sopenharmony_cistatic int check_tape(struct scsi_tape *STp, struct file *filp)
104362306a36Sopenharmony_ci{
104462306a36Sopenharmony_ci	int i, retval, new_session = 0, do_wait;
104562306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning;
104662306a36Sopenharmony_ci	unsigned short st_flags = filp->f_flags;
104762306a36Sopenharmony_ci	struct st_request *SRpnt = NULL;
104862306a36Sopenharmony_ci	struct st_modedef *STm;
104962306a36Sopenharmony_ci	struct st_partstat *STps;
105062306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
105162306a36Sopenharmony_ci	int mode = TAPE_MODE(inode);
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	STp->ready = ST_READY;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci	if (mode != STp->current_mode) {
105662306a36Sopenharmony_ci		DEBC_printk(STp, "Mode change from %d to %d.\n",
105762306a36Sopenharmony_ci			    STp->current_mode, mode);
105862306a36Sopenharmony_ci		new_session = 1;
105962306a36Sopenharmony_ci		STp->current_mode = mode;
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	saved_cleaning = STp->cleaning_req;
106462306a36Sopenharmony_ci	STp->cleaning_req = 0;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	do_wait = ((filp->f_flags & O_NONBLOCK) == 0);
106762306a36Sopenharmony_ci	retval = test_ready(STp, do_wait);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	if (retval < 0)
107062306a36Sopenharmony_ci	    goto err_out;
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	if (retval == CHKRES_NEW_SESSION) {
107362306a36Sopenharmony_ci		STp->pos_unknown = 0;
107462306a36Sopenharmony_ci		STp->partition = STp->new_partition = 0;
107562306a36Sopenharmony_ci		if (STp->can_partitions)
107662306a36Sopenharmony_ci			STp->nbr_partitions = 1; /* This guess will be updated later
107762306a36Sopenharmony_ci                                                    if necessary */
107862306a36Sopenharmony_ci		for (i = 0; i < ST_NBR_PARTITIONS; i++) {
107962306a36Sopenharmony_ci			STps = &(STp->ps[i]);
108062306a36Sopenharmony_ci			STps->rw = ST_IDLE;
108162306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
108262306a36Sopenharmony_ci			STps->at_sm = 0;
108362306a36Sopenharmony_ci			STps->last_block_valid = 0;
108462306a36Sopenharmony_ci			STps->drv_block = 0;
108562306a36Sopenharmony_ci			STps->drv_file = 0;
108662306a36Sopenharmony_ci		}
108762306a36Sopenharmony_ci		new_session = 1;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci	else {
109062306a36Sopenharmony_ci		STp->cleaning_req |= saved_cleaning;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci		if (retval == CHKRES_NOT_READY || retval == CHKRES_NO_TAPE) {
109362306a36Sopenharmony_ci			if (retval == CHKRES_NO_TAPE)
109462306a36Sopenharmony_ci				STp->ready = ST_NO_TAPE;
109562306a36Sopenharmony_ci			else
109662306a36Sopenharmony_ci				STp->ready = ST_NOT_READY;
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci			STp->density = 0;	/* Clear the erroneous "residue" */
109962306a36Sopenharmony_ci			STp->write_prot = 0;
110062306a36Sopenharmony_ci			STp->block_size = 0;
110162306a36Sopenharmony_ci			STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
110262306a36Sopenharmony_ci			STp->partition = STp->new_partition = 0;
110362306a36Sopenharmony_ci			STp->door_locked = ST_UNLOCKED;
110462306a36Sopenharmony_ci			return CHKRES_NOT_READY;
110562306a36Sopenharmony_ci		}
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	if (STp->omit_blklims)
110962306a36Sopenharmony_ci		STp->min_block = STp->max_block = (-1);
111062306a36Sopenharmony_ci	else {
111162306a36Sopenharmony_ci		memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
111262306a36Sopenharmony_ci		cmd[0] = READ_BLOCK_LIMITS;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
111562306a36Sopenharmony_ci				   STp->device->request_queue->rq_timeout,
111662306a36Sopenharmony_ci				   MAX_READY_RETRIES, 1);
111762306a36Sopenharmony_ci		if (!SRpnt) {
111862306a36Sopenharmony_ci			retval = (STp->buffer)->syscall_result;
111962306a36Sopenharmony_ci			goto err_out;
112062306a36Sopenharmony_ci		}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		if (!SRpnt->result && !STp->buffer->cmdstat.have_sense) {
112362306a36Sopenharmony_ci			STp->max_block = ((STp->buffer)->b_data[1] << 16) |
112462306a36Sopenharmony_ci			    ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
112562306a36Sopenharmony_ci			STp->min_block = ((STp->buffer)->b_data[4] << 8) |
112662306a36Sopenharmony_ci			    (STp->buffer)->b_data[5];
112762306a36Sopenharmony_ci			if ( DEB( debugging || ) !STp->inited)
112862306a36Sopenharmony_ci				st_printk(KERN_INFO, STp,
112962306a36Sopenharmony_ci					  "Block limits %d - %d bytes.\n",
113062306a36Sopenharmony_ci					  STp->min_block, STp->max_block);
113162306a36Sopenharmony_ci		} else {
113262306a36Sopenharmony_ci			STp->min_block = STp->max_block = (-1);
113362306a36Sopenharmony_ci			DEBC_printk(STp, "Can't read block limits.\n");
113462306a36Sopenharmony_ci		}
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
113862306a36Sopenharmony_ci	cmd[0] = MODE_SENSE;
113962306a36Sopenharmony_ci	cmd[4] = 12;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
114262306a36Sopenharmony_ci			   STp->device->request_queue->rq_timeout,
114362306a36Sopenharmony_ci			   MAX_READY_RETRIES, 1);
114462306a36Sopenharmony_ci	if (!SRpnt) {
114562306a36Sopenharmony_ci		retval = (STp->buffer)->syscall_result;
114662306a36Sopenharmony_ci		goto err_out;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if ((STp->buffer)->syscall_result != 0) {
115062306a36Sopenharmony_ci		DEBC_printk(STp, "No Mode Sense.\n");
115162306a36Sopenharmony_ci		STp->block_size = ST_DEFAULT_BLOCK;	/* Educated guess (?) */
115262306a36Sopenharmony_ci		(STp->buffer)->syscall_result = 0;	/* Prevent error propagation */
115362306a36Sopenharmony_ci		STp->drv_write_prot = 0;
115462306a36Sopenharmony_ci	} else {
115562306a36Sopenharmony_ci		DEBC_printk(STp,"Mode sense. Length %d, "
115662306a36Sopenharmony_ci			    "medium %x, WBS %x, BLL %d\n",
115762306a36Sopenharmony_ci			    (STp->buffer)->b_data[0],
115862306a36Sopenharmony_ci			    (STp->buffer)->b_data[1],
115962306a36Sopenharmony_ci			    (STp->buffer)->b_data[2],
116062306a36Sopenharmony_ci			    (STp->buffer)->b_data[3]);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		if ((STp->buffer)->b_data[3] >= 8) {
116362306a36Sopenharmony_ci			STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
116462306a36Sopenharmony_ci			STp->density = (STp->buffer)->b_data[4];
116562306a36Sopenharmony_ci			STp->block_size = (STp->buffer)->b_data[9] * 65536 +
116662306a36Sopenharmony_ci			    (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
116762306a36Sopenharmony_ci			DEBC_printk(STp, "Density %x, tape length: %x, "
116862306a36Sopenharmony_ci				    "drv buffer: %d\n",
116962306a36Sopenharmony_ci				    STp->density,
117062306a36Sopenharmony_ci				    (STp->buffer)->b_data[5] * 65536 +
117162306a36Sopenharmony_ci				    (STp->buffer)->b_data[6] * 256 +
117262306a36Sopenharmony_ci				    (STp->buffer)->b_data[7],
117362306a36Sopenharmony_ci				    STp->drv_buffer);
117462306a36Sopenharmony_ci		}
117562306a36Sopenharmony_ci		STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
117662306a36Sopenharmony_ci		if (!STp->drv_buffer && STp->immediate_filemark) {
117762306a36Sopenharmony_ci			st_printk(KERN_WARNING, STp,
117862306a36Sopenharmony_ci				  "non-buffered tape: disabling "
117962306a36Sopenharmony_ci				  "writing immediate filemarks\n");
118062306a36Sopenharmony_ci			STp->immediate_filemark = 0;
118162306a36Sopenharmony_ci		}
118262306a36Sopenharmony_ci	}
118362306a36Sopenharmony_ci	st_release_request(SRpnt);
118462306a36Sopenharmony_ci	SRpnt = NULL;
118562306a36Sopenharmony_ci	STp->inited = 1;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (STp->block_size > 0)
118862306a36Sopenharmony_ci		(STp->buffer)->buffer_blocks =
118962306a36Sopenharmony_ci			(STp->buffer)->buffer_size / STp->block_size;
119062306a36Sopenharmony_ci	else
119162306a36Sopenharmony_ci		(STp->buffer)->buffer_blocks = 1;
119262306a36Sopenharmony_ci	(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	DEBC_printk(STp, "Block size: %d, buffer size: %d (%d blocks).\n",
119562306a36Sopenharmony_ci		    STp->block_size, (STp->buffer)->buffer_size,
119662306a36Sopenharmony_ci		    (STp->buffer)->buffer_blocks);
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (STp->drv_write_prot) {
119962306a36Sopenharmony_ci		STp->write_prot = 1;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		DEBC_printk(STp, "Write protected\n");
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci		if (do_wait &&
120462306a36Sopenharmony_ci		    ((st_flags & O_ACCMODE) == O_WRONLY ||
120562306a36Sopenharmony_ci		     (st_flags & O_ACCMODE) == O_RDWR)) {
120662306a36Sopenharmony_ci			retval = (-EROFS);
120762306a36Sopenharmony_ci			goto err_out;
120862306a36Sopenharmony_ci		}
120962306a36Sopenharmony_ci	}
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (STp->can_partitions && STp->nbr_partitions < 1) {
121262306a36Sopenharmony_ci		/* This code is reached when the device is opened for the first time
121362306a36Sopenharmony_ci		   after the driver has been initialized with tape in the drive and the
121462306a36Sopenharmony_ci		   partition support has been enabled. */
121562306a36Sopenharmony_ci		DEBC_printk(STp, "Updating partition number in status.\n");
121662306a36Sopenharmony_ci		if ((STp->partition = find_partition(STp)) < 0) {
121762306a36Sopenharmony_ci			retval = STp->partition;
121862306a36Sopenharmony_ci			goto err_out;
121962306a36Sopenharmony_ci		}
122062306a36Sopenharmony_ci		STp->new_partition = STp->partition;
122162306a36Sopenharmony_ci		STp->nbr_partitions = 1; /* This guess will be updated when necessary */
122262306a36Sopenharmony_ci	}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	if (new_session) {	/* Change the drive parameters for the new mode */
122562306a36Sopenharmony_ci		STp->density_changed = STp->blksize_changed = 0;
122662306a36Sopenharmony_ci		STp->compression_changed = 0;
122762306a36Sopenharmony_ci		if (!(STm->defaults_for_writes) &&
122862306a36Sopenharmony_ci		    (retval = set_mode_densblk(STp, STm)) < 0)
122962306a36Sopenharmony_ci		    goto err_out;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		if (STp->default_drvbuffer != 0xff) {
123262306a36Sopenharmony_ci			if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
123362306a36Sopenharmony_ci				st_printk(KERN_WARNING, STp,
123462306a36Sopenharmony_ci					  "Can't set default drive "
123562306a36Sopenharmony_ci					  "buffering to %d.\n",
123662306a36Sopenharmony_ci					  STp->default_drvbuffer);
123762306a36Sopenharmony_ci		}
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	return CHKRES_READY;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci err_out:
124362306a36Sopenharmony_ci	return retval;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci/* Open the device. Needs to take the BKL only because of incrementing the SCSI host
124862306a36Sopenharmony_ci   module count. */
124962306a36Sopenharmony_cistatic int st_open(struct inode *inode, struct file *filp)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	int i, retval = (-EIO);
125262306a36Sopenharmony_ci	int resumed = 0;
125362306a36Sopenharmony_ci	struct scsi_tape *STp;
125462306a36Sopenharmony_ci	struct st_partstat *STps;
125562306a36Sopenharmony_ci	int dev = TAPE_NR(inode);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/*
125862306a36Sopenharmony_ci	 * We really want to do nonseekable_open(inode, filp); here, but some
125962306a36Sopenharmony_ci	 * versions of tar incorrectly call lseek on tapes and bail out if that
126062306a36Sopenharmony_ci	 * fails.  So we disallow pread() and pwrite(), but permit lseeks.
126162306a36Sopenharmony_ci	 */
126262306a36Sopenharmony_ci	filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (!(STp = scsi_tape_get(dev))) {
126562306a36Sopenharmony_ci		return -ENXIO;
126662306a36Sopenharmony_ci	}
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	filp->private_data = STp;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	spin_lock(&st_use_lock);
127162306a36Sopenharmony_ci	if (STp->in_use) {
127262306a36Sopenharmony_ci		spin_unlock(&st_use_lock);
127362306a36Sopenharmony_ci		DEBC_printk(STp, "Device already in use.\n");
127462306a36Sopenharmony_ci		scsi_tape_put(STp);
127562306a36Sopenharmony_ci		return (-EBUSY);
127662306a36Sopenharmony_ci	}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	STp->in_use = 1;
127962306a36Sopenharmony_ci	spin_unlock(&st_use_lock);
128062306a36Sopenharmony_ci	STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	if (scsi_autopm_get_device(STp->device) < 0) {
128362306a36Sopenharmony_ci		retval = -EIO;
128462306a36Sopenharmony_ci		goto err_out;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ci	resumed = 1;
128762306a36Sopenharmony_ci	if (!scsi_block_when_processing_errors(STp->device)) {
128862306a36Sopenharmony_ci		retval = (-ENXIO);
128962306a36Sopenharmony_ci		goto err_out;
129062306a36Sopenharmony_ci	}
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	/* See that we have at least a one page buffer available */
129362306a36Sopenharmony_ci	if (!enlarge_buffer(STp->buffer, PAGE_SIZE)) {
129462306a36Sopenharmony_ci		st_printk(KERN_WARNING, STp,
129562306a36Sopenharmony_ci			  "Can't allocate one page tape buffer.\n");
129662306a36Sopenharmony_ci		retval = (-EOVERFLOW);
129762306a36Sopenharmony_ci		goto err_out;
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	(STp->buffer)->cleared = 0;
130162306a36Sopenharmony_ci	(STp->buffer)->writing = 0;
130262306a36Sopenharmony_ci	(STp->buffer)->syscall_result = 0;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	STp->write_prot = ((filp->f_flags & O_ACCMODE) == O_RDONLY);
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	STp->dirty = 0;
130762306a36Sopenharmony_ci	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
130862306a36Sopenharmony_ci		STps = &(STp->ps[i]);
130962306a36Sopenharmony_ci		STps->rw = ST_IDLE;
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci	STp->try_dio_now = STp->try_dio;
131262306a36Sopenharmony_ci	STp->recover_count = 0;
131362306a36Sopenharmony_ci	DEB( STp->nbr_waits = STp->nbr_finished = 0;
131462306a36Sopenharmony_ci	     STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = 0; )
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	retval = check_tape(STp, filp);
131762306a36Sopenharmony_ci	if (retval < 0)
131862306a36Sopenharmony_ci		goto err_out;
131962306a36Sopenharmony_ci	if ((filp->f_flags & O_NONBLOCK) == 0 &&
132062306a36Sopenharmony_ci	    retval != CHKRES_READY) {
132162306a36Sopenharmony_ci		if (STp->ready == NO_TAPE)
132262306a36Sopenharmony_ci			retval = (-ENOMEDIUM);
132362306a36Sopenharmony_ci		else
132462306a36Sopenharmony_ci			retval = (-EIO);
132562306a36Sopenharmony_ci		goto err_out;
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci	return 0;
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci err_out:
133062306a36Sopenharmony_ci	normalize_buffer(STp->buffer);
133162306a36Sopenharmony_ci	spin_lock(&st_use_lock);
133262306a36Sopenharmony_ci	STp->in_use = 0;
133362306a36Sopenharmony_ci	spin_unlock(&st_use_lock);
133462306a36Sopenharmony_ci	if (resumed)
133562306a36Sopenharmony_ci		scsi_autopm_put_device(STp->device);
133662306a36Sopenharmony_ci	scsi_tape_put(STp);
133762306a36Sopenharmony_ci	return retval;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/* Flush the tape buffer before close */
134362306a36Sopenharmony_cistatic int st_flush(struct file *filp, fl_owner_t id)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	int result = 0, result2;
134662306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
134762306a36Sopenharmony_ci	struct st_request *SRpnt;
134862306a36Sopenharmony_ci	struct scsi_tape *STp = filp->private_data;
134962306a36Sopenharmony_ci	struct st_modedef *STm = &(STp->modes[STp->current_mode]);
135062306a36Sopenharmony_ci	struct st_partstat *STps = &(STp->ps[STp->partition]);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (file_count(filp) > 1)
135362306a36Sopenharmony_ci		return 0;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	if (STps->rw == ST_WRITING && !STp->pos_unknown) {
135662306a36Sopenharmony_ci		result = st_flush_write_buffer(STp);
135762306a36Sopenharmony_ci		if (result != 0 && result != (-ENOSPC))
135862306a36Sopenharmony_ci			goto out;
135962306a36Sopenharmony_ci	}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	if (STp->can_partitions &&
136262306a36Sopenharmony_ci	    (result2 = switch_partition(STp)) < 0) {
136362306a36Sopenharmony_ci		DEBC_printk(STp, "switch_partition at close failed.\n");
136462306a36Sopenharmony_ci		if (result == 0)
136562306a36Sopenharmony_ci			result = result2;
136662306a36Sopenharmony_ci		goto out;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	DEBC( if (STp->nbr_requests)
137062306a36Sopenharmony_ci		st_printk(KERN_DEBUG, STp,
137162306a36Sopenharmony_ci			  "Number of r/w requests %d, dio used in %d, "
137262306a36Sopenharmony_ci			  "pages %d.\n", STp->nbr_requests, STp->nbr_dio,
137362306a36Sopenharmony_ci			  STp->nbr_pages));
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	if (STps->rw == ST_WRITING && !STp->pos_unknown) {
137662306a36Sopenharmony_ci		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci#if DEBUG
137962306a36Sopenharmony_ci		DEBC_printk(STp, "Async write waits %d, finished %d.\n",
138062306a36Sopenharmony_ci			    STp->nbr_waits, STp->nbr_finished);
138162306a36Sopenharmony_ci#endif
138262306a36Sopenharmony_ci		memset(cmd, 0, MAX_COMMAND_SIZE);
138362306a36Sopenharmony_ci		cmd[0] = WRITE_FILEMARKS;
138462306a36Sopenharmony_ci		if (STp->immediate_filemark)
138562306a36Sopenharmony_ci			cmd[1] = 1;
138662306a36Sopenharmony_ci		cmd[4] = 1 + STp->two_fm;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci		SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
138962306a36Sopenharmony_ci				   STp->device->request_queue->rq_timeout,
139062306a36Sopenharmony_ci				   MAX_WRITE_RETRIES, 1);
139162306a36Sopenharmony_ci		if (!SRpnt) {
139262306a36Sopenharmony_ci			result = (STp->buffer)->syscall_result;
139362306a36Sopenharmony_ci			goto out;
139462306a36Sopenharmony_ci		}
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci		if (STp->buffer->syscall_result == 0 ||
139762306a36Sopenharmony_ci		    (cmdstatp->have_sense && !cmdstatp->deferred &&
139862306a36Sopenharmony_ci		     (cmdstatp->flags & SENSE_EOM) &&
139962306a36Sopenharmony_ci		     (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
140062306a36Sopenharmony_ci		      cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
140162306a36Sopenharmony_ci		     (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0))) {
140262306a36Sopenharmony_ci			/* Write successful at EOM */
140362306a36Sopenharmony_ci			st_release_request(SRpnt);
140462306a36Sopenharmony_ci			SRpnt = NULL;
140562306a36Sopenharmony_ci			if (STps->drv_file >= 0)
140662306a36Sopenharmony_ci				STps->drv_file++;
140762306a36Sopenharmony_ci			STps->drv_block = 0;
140862306a36Sopenharmony_ci			if (STp->two_fm)
140962306a36Sopenharmony_ci				cross_eof(STp, 0);
141062306a36Sopenharmony_ci			STps->eof = ST_FM;
141162306a36Sopenharmony_ci		}
141262306a36Sopenharmony_ci		else { /* Write error */
141362306a36Sopenharmony_ci			st_release_request(SRpnt);
141462306a36Sopenharmony_ci			SRpnt = NULL;
141562306a36Sopenharmony_ci			st_printk(KERN_ERR, STp,
141662306a36Sopenharmony_ci				  "Error on write filemark.\n");
141762306a36Sopenharmony_ci			if (result == 0)
141862306a36Sopenharmony_ci				result = (-EIO);
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		DEBC_printk(STp, "Buffer flushed, %d EOF(s) written\n", cmd[4]);
142262306a36Sopenharmony_ci	} else if (!STp->rew_at_close) {
142362306a36Sopenharmony_ci		STps = &(STp->ps[STp->partition]);
142462306a36Sopenharmony_ci		if (!STm->sysv || STps->rw != ST_READING) {
142562306a36Sopenharmony_ci			if (STp->can_bsr)
142662306a36Sopenharmony_ci				result = flush_buffer(STp, 0);
142762306a36Sopenharmony_ci			else if (STps->eof == ST_FM_HIT) {
142862306a36Sopenharmony_ci				result = cross_eof(STp, 0);
142962306a36Sopenharmony_ci				if (result) {
143062306a36Sopenharmony_ci					if (STps->drv_file >= 0)
143162306a36Sopenharmony_ci						STps->drv_file++;
143262306a36Sopenharmony_ci					STps->drv_block = 0;
143362306a36Sopenharmony_ci					STps->eof = ST_FM;
143462306a36Sopenharmony_ci				} else
143562306a36Sopenharmony_ci					STps->eof = ST_NOEOF;
143662306a36Sopenharmony_ci			}
143762306a36Sopenharmony_ci		} else if ((STps->eof == ST_NOEOF &&
143862306a36Sopenharmony_ci			    !(result = cross_eof(STp, 1))) ||
143962306a36Sopenharmony_ci			   STps->eof == ST_FM_HIT) {
144062306a36Sopenharmony_ci			if (STps->drv_file >= 0)
144162306a36Sopenharmony_ci				STps->drv_file++;
144262306a36Sopenharmony_ci			STps->drv_block = 0;
144362306a36Sopenharmony_ci			STps->eof = ST_FM;
144462306a36Sopenharmony_ci		}
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci      out:
144862306a36Sopenharmony_ci	if (STp->rew_at_close) {
144962306a36Sopenharmony_ci		result2 = st_int_ioctl(STp, MTREW, 1);
145062306a36Sopenharmony_ci		if (result == 0)
145162306a36Sopenharmony_ci			result = result2;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci	return result;
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci/* Close the device and release it. BKL is not needed: this is the only thread
145862306a36Sopenharmony_ci   accessing this tape. */
145962306a36Sopenharmony_cistatic int st_release(struct inode *inode, struct file *filp)
146062306a36Sopenharmony_ci{
146162306a36Sopenharmony_ci	struct scsi_tape *STp = filp->private_data;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (STp->door_locked == ST_LOCKED_AUTO)
146462306a36Sopenharmony_ci		do_door_lock(STp, 0);
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	normalize_buffer(STp->buffer);
146762306a36Sopenharmony_ci	spin_lock(&st_use_lock);
146862306a36Sopenharmony_ci	STp->in_use = 0;
146962306a36Sopenharmony_ci	spin_unlock(&st_use_lock);
147062306a36Sopenharmony_ci	scsi_autopm_put_device(STp->device);
147162306a36Sopenharmony_ci	scsi_tape_put(STp);
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	return 0;
147462306a36Sopenharmony_ci}
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci/* The checks common to both reading and writing */
147762306a36Sopenharmony_cistatic ssize_t rw_checks(struct scsi_tape *STp, struct file *filp, size_t count)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	ssize_t retval = 0;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	/*
148262306a36Sopenharmony_ci	 * If we are in the middle of error recovery, don't let anyone
148362306a36Sopenharmony_ci	 * else try and use this device.  Also, if error recovery fails, it
148462306a36Sopenharmony_ci	 * may try and take the device offline, in which case all further
148562306a36Sopenharmony_ci	 * access to the device is prohibited.
148662306a36Sopenharmony_ci	 */
148762306a36Sopenharmony_ci	if (!scsi_block_when_processing_errors(STp->device)) {
148862306a36Sopenharmony_ci		retval = (-ENXIO);
148962306a36Sopenharmony_ci		goto out;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	if (STp->ready != ST_READY) {
149362306a36Sopenharmony_ci		if (STp->ready == ST_NO_TAPE)
149462306a36Sopenharmony_ci			retval = (-ENOMEDIUM);
149562306a36Sopenharmony_ci		else
149662306a36Sopenharmony_ci			retval = (-EIO);
149762306a36Sopenharmony_ci		goto out;
149862306a36Sopenharmony_ci	}
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (! STp->modes[STp->current_mode].defined) {
150162306a36Sopenharmony_ci		retval = (-ENXIO);
150262306a36Sopenharmony_ci		goto out;
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/*
150762306a36Sopenharmony_ci	 * If there was a bus reset, block further access
150862306a36Sopenharmony_ci	 * to this device.
150962306a36Sopenharmony_ci	 */
151062306a36Sopenharmony_ci	if (STp->pos_unknown) {
151162306a36Sopenharmony_ci		retval = (-EIO);
151262306a36Sopenharmony_ci		goto out;
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	if (count == 0)
151662306a36Sopenharmony_ci		goto out;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	DEB(
151962306a36Sopenharmony_ci	if (!STp->in_use) {
152062306a36Sopenharmony_ci		st_printk(ST_DEB_MSG, STp,
152162306a36Sopenharmony_ci			  "Incorrect device.\n");
152262306a36Sopenharmony_ci		retval = (-EIO);
152362306a36Sopenharmony_ci		goto out;
152462306a36Sopenharmony_ci	} ) /* end DEB */
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (STp->can_partitions &&
152762306a36Sopenharmony_ci	    (retval = switch_partition(STp)) < 0)
152862306a36Sopenharmony_ci		goto out;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	if (STp->block_size == 0 && STp->max_block > 0 &&
153162306a36Sopenharmony_ci	    (count < STp->min_block || count > STp->max_block)) {
153262306a36Sopenharmony_ci		retval = (-EINVAL);
153362306a36Sopenharmony_ci		goto out;
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
153762306a36Sopenharmony_ci	    !do_door_lock(STp, 1))
153862306a36Sopenharmony_ci		STp->door_locked = ST_LOCKED_AUTO;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci out:
154162306a36Sopenharmony_ci	return retval;
154262306a36Sopenharmony_ci}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci
154562306a36Sopenharmony_cistatic int setup_buffering(struct scsi_tape *STp, const char __user *buf,
154662306a36Sopenharmony_ci			   size_t count, int is_read)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	int i, bufsize, retval = 0;
154962306a36Sopenharmony_ci	struct st_buffer *STbp = STp->buffer;
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	if (is_read)
155262306a36Sopenharmony_ci		i = STp->try_dio_now && try_rdio;
155362306a36Sopenharmony_ci	else
155462306a36Sopenharmony_ci		i = STp->try_dio_now && try_wdio;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	if (i && ((unsigned long)buf & queue_dma_alignment(
155762306a36Sopenharmony_ci					STp->device->request_queue)) == 0) {
155862306a36Sopenharmony_ci		i = sgl_map_user_pages(STbp, STbp->use_sg, (unsigned long)buf,
155962306a36Sopenharmony_ci				       count, (is_read ? READ : WRITE));
156062306a36Sopenharmony_ci		if (i > 0) {
156162306a36Sopenharmony_ci			STbp->do_dio = i;
156262306a36Sopenharmony_ci			STbp->buffer_bytes = 0;   /* can be used as transfer counter */
156362306a36Sopenharmony_ci		}
156462306a36Sopenharmony_ci		else
156562306a36Sopenharmony_ci			STbp->do_dio = 0;  /* fall back to buffering with any error */
156662306a36Sopenharmony_ci		STbp->sg_segs = STbp->do_dio;
156762306a36Sopenharmony_ci		DEB(
156862306a36Sopenharmony_ci		     if (STbp->do_dio) {
156962306a36Sopenharmony_ci			STp->nbr_dio++;
157062306a36Sopenharmony_ci			STp->nbr_pages += STbp->do_dio;
157162306a36Sopenharmony_ci		     }
157262306a36Sopenharmony_ci		)
157362306a36Sopenharmony_ci	} else
157462306a36Sopenharmony_ci		STbp->do_dio = 0;
157562306a36Sopenharmony_ci	DEB( STp->nbr_requests++; )
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	if (!STbp->do_dio) {
157862306a36Sopenharmony_ci		if (STp->block_size)
157962306a36Sopenharmony_ci			bufsize = STp->block_size > st_fixed_buffer_size ?
158062306a36Sopenharmony_ci				STp->block_size : st_fixed_buffer_size;
158162306a36Sopenharmony_ci		else {
158262306a36Sopenharmony_ci			bufsize = count;
158362306a36Sopenharmony_ci			/* Make sure that data from previous user is not leaked even if
158462306a36Sopenharmony_ci			   HBA does not return correct residual */
158562306a36Sopenharmony_ci			if (is_read && STp->sili && !STbp->cleared)
158662306a36Sopenharmony_ci				clear_buffer(STbp);
158762306a36Sopenharmony_ci		}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci		if (bufsize > STbp->buffer_size &&
159062306a36Sopenharmony_ci		    !enlarge_buffer(STbp, bufsize)) {
159162306a36Sopenharmony_ci			st_printk(KERN_WARNING, STp,
159262306a36Sopenharmony_ci				  "Can't allocate %d byte tape buffer.\n",
159362306a36Sopenharmony_ci				  bufsize);
159462306a36Sopenharmony_ci			retval = (-EOVERFLOW);
159562306a36Sopenharmony_ci			goto out;
159662306a36Sopenharmony_ci		}
159762306a36Sopenharmony_ci		if (STp->block_size)
159862306a36Sopenharmony_ci			STbp->buffer_blocks = bufsize / STp->block_size;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci out:
160262306a36Sopenharmony_ci	return retval;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci/* Can be called more than once after each setup_buffer() */
160762306a36Sopenharmony_cistatic void release_buffering(struct scsi_tape *STp, int is_read)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct st_buffer *STbp;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	STbp = STp->buffer;
161262306a36Sopenharmony_ci	if (STbp->do_dio) {
161362306a36Sopenharmony_ci		sgl_unmap_user_pages(STbp, STbp->do_dio, is_read);
161462306a36Sopenharmony_ci		STbp->do_dio = 0;
161562306a36Sopenharmony_ci		STbp->sg_segs = 0;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci}
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci/* Write command */
162162306a36Sopenharmony_cistatic ssize_t
162262306a36Sopenharmony_cist_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
162362306a36Sopenharmony_ci{
162462306a36Sopenharmony_ci	ssize_t total;
162562306a36Sopenharmony_ci	ssize_t i, do_count, blks, transfer;
162662306a36Sopenharmony_ci	ssize_t retval;
162762306a36Sopenharmony_ci	int undone, retry_eot = 0, scode;
162862306a36Sopenharmony_ci	int async_write;
162962306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
163062306a36Sopenharmony_ci	const char __user *b_point;
163162306a36Sopenharmony_ci	struct st_request *SRpnt = NULL;
163262306a36Sopenharmony_ci	struct scsi_tape *STp = filp->private_data;
163362306a36Sopenharmony_ci	struct st_modedef *STm;
163462306a36Sopenharmony_ci	struct st_partstat *STps;
163562306a36Sopenharmony_ci	struct st_buffer *STbp;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	if (mutex_lock_interruptible(&STp->lock))
163862306a36Sopenharmony_ci		return -ERESTARTSYS;
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	retval = rw_checks(STp, filp, count);
164162306a36Sopenharmony_ci	if (retval || count == 0)
164262306a36Sopenharmony_ci		goto out;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	/* Write must be integral number of blocks */
164562306a36Sopenharmony_ci	if (STp->block_size != 0 && (count % STp->block_size) != 0) {
164662306a36Sopenharmony_ci		st_printk(KERN_WARNING, STp,
164762306a36Sopenharmony_ci			  "Write not multiple of tape block size.\n");
164862306a36Sopenharmony_ci		retval = (-EINVAL);
164962306a36Sopenharmony_ci		goto out;
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
165362306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	if (STp->write_prot) {
165662306a36Sopenharmony_ci		retval = (-EACCES);
165762306a36Sopenharmony_ci		goto out;
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	if (STps->rw == ST_READING) {
166262306a36Sopenharmony_ci		retval = flush_buffer(STp, 0);
166362306a36Sopenharmony_ci		if (retval)
166462306a36Sopenharmony_ci			goto out;
166562306a36Sopenharmony_ci		STps->rw = ST_WRITING;
166662306a36Sopenharmony_ci	} else if (STps->rw != ST_WRITING &&
166762306a36Sopenharmony_ci		   STps->drv_file == 0 && STps->drv_block == 0) {
166862306a36Sopenharmony_ci		if ((retval = set_mode_densblk(STp, STm)) < 0)
166962306a36Sopenharmony_ci			goto out;
167062306a36Sopenharmony_ci		if (STm->default_compression != ST_DONT_TOUCH &&
167162306a36Sopenharmony_ci		    !(STp->compression_changed)) {
167262306a36Sopenharmony_ci			if (st_compression(STp, (STm->default_compression == ST_YES))) {
167362306a36Sopenharmony_ci				st_printk(KERN_WARNING, STp,
167462306a36Sopenharmony_ci					  "Can't set default compression.\n");
167562306a36Sopenharmony_ci				if (modes_defined) {
167662306a36Sopenharmony_ci					retval = (-EINVAL);
167762306a36Sopenharmony_ci					goto out;
167862306a36Sopenharmony_ci				}
167962306a36Sopenharmony_ci			}
168062306a36Sopenharmony_ci		}
168162306a36Sopenharmony_ci	}
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	STbp = STp->buffer;
168462306a36Sopenharmony_ci	i = write_behind_check(STp);
168562306a36Sopenharmony_ci	if (i) {
168662306a36Sopenharmony_ci		if (i == -ENOSPC)
168762306a36Sopenharmony_ci			STps->eof = ST_EOM_OK;
168862306a36Sopenharmony_ci		else
168962306a36Sopenharmony_ci			STps->eof = ST_EOM_ERROR;
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	if (STps->eof == ST_EOM_OK) {
169362306a36Sopenharmony_ci		STps->eof = ST_EOD_1;  /* allow next write */
169462306a36Sopenharmony_ci		retval = (-ENOSPC);
169562306a36Sopenharmony_ci		goto out;
169662306a36Sopenharmony_ci	}
169762306a36Sopenharmony_ci	else if (STps->eof == ST_EOM_ERROR) {
169862306a36Sopenharmony_ci		retval = (-EIO);
169962306a36Sopenharmony_ci		goto out;
170062306a36Sopenharmony_ci	}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/* Check the buffer readability in cases where copy_user might catch
170362306a36Sopenharmony_ci	   the problems after some tape movement. */
170462306a36Sopenharmony_ci	if (STp->block_size != 0 &&
170562306a36Sopenharmony_ci	    !STbp->do_dio &&
170662306a36Sopenharmony_ci	    (copy_from_user(&i, buf, 1) != 0 ||
170762306a36Sopenharmony_ci	     copy_from_user(&i, buf + count - 1, 1) != 0)) {
170862306a36Sopenharmony_ci		retval = (-EFAULT);
170962306a36Sopenharmony_ci		goto out;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci	retval = setup_buffering(STp, buf, count, 0);
171362306a36Sopenharmony_ci	if (retval)
171462306a36Sopenharmony_ci		goto out;
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	total = count;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
171962306a36Sopenharmony_ci	cmd[0] = WRITE_6;
172062306a36Sopenharmony_ci	cmd[1] = (STp->block_size != 0);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	STps->rw = ST_WRITING;
172362306a36Sopenharmony_ci
172462306a36Sopenharmony_ci	b_point = buf;
172562306a36Sopenharmony_ci	while (count > 0 && !retry_eot) {
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci		if (STbp->do_dio) {
172862306a36Sopenharmony_ci			do_count = count;
172962306a36Sopenharmony_ci		}
173062306a36Sopenharmony_ci		else {
173162306a36Sopenharmony_ci			if (STp->block_size == 0)
173262306a36Sopenharmony_ci				do_count = count;
173362306a36Sopenharmony_ci			else {
173462306a36Sopenharmony_ci				do_count = STbp->buffer_blocks * STp->block_size -
173562306a36Sopenharmony_ci					STbp->buffer_bytes;
173662306a36Sopenharmony_ci				if (do_count > count)
173762306a36Sopenharmony_ci					do_count = count;
173862306a36Sopenharmony_ci			}
173962306a36Sopenharmony_ci
174062306a36Sopenharmony_ci			i = append_to_buffer(b_point, STbp, do_count);
174162306a36Sopenharmony_ci			if (i) {
174262306a36Sopenharmony_ci				retval = i;
174362306a36Sopenharmony_ci				goto out;
174462306a36Sopenharmony_ci			}
174562306a36Sopenharmony_ci		}
174662306a36Sopenharmony_ci		count -= do_count;
174762306a36Sopenharmony_ci		b_point += do_count;
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci		async_write = STp->block_size == 0 && !STbp->do_dio &&
175062306a36Sopenharmony_ci			STm->do_async_writes && STps->eof < ST_EOM_OK;
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci		if (STp->block_size != 0 && STm->do_buffer_writes &&
175362306a36Sopenharmony_ci		    !(STp->try_dio_now && try_wdio) && STps->eof < ST_EOM_OK &&
175462306a36Sopenharmony_ci		    STbp->buffer_bytes < STbp->buffer_size) {
175562306a36Sopenharmony_ci			STp->dirty = 1;
175662306a36Sopenharmony_ci			/* Don't write a buffer that is not full enough. */
175762306a36Sopenharmony_ci			if (!async_write && count == 0)
175862306a36Sopenharmony_ci				break;
175962306a36Sopenharmony_ci		}
176062306a36Sopenharmony_ci
176162306a36Sopenharmony_ci	retry_write:
176262306a36Sopenharmony_ci		if (STp->block_size == 0)
176362306a36Sopenharmony_ci			blks = transfer = do_count;
176462306a36Sopenharmony_ci		else {
176562306a36Sopenharmony_ci			if (!STbp->do_dio)
176662306a36Sopenharmony_ci				blks = STbp->buffer_bytes;
176762306a36Sopenharmony_ci			else
176862306a36Sopenharmony_ci				blks = do_count;
176962306a36Sopenharmony_ci			blks /= STp->block_size;
177062306a36Sopenharmony_ci			transfer = blks * STp->block_size;
177162306a36Sopenharmony_ci		}
177262306a36Sopenharmony_ci		cmd[2] = blks >> 16;
177362306a36Sopenharmony_ci		cmd[3] = blks >> 8;
177462306a36Sopenharmony_ci		cmd[4] = blks;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
177762306a36Sopenharmony_ci				   STp->device->request_queue->rq_timeout,
177862306a36Sopenharmony_ci				   MAX_WRITE_RETRIES, !async_write);
177962306a36Sopenharmony_ci		if (!SRpnt) {
178062306a36Sopenharmony_ci			retval = STbp->syscall_result;
178162306a36Sopenharmony_ci			goto out;
178262306a36Sopenharmony_ci		}
178362306a36Sopenharmony_ci		if (async_write && !STbp->syscall_result) {
178462306a36Sopenharmony_ci			STbp->writing = transfer;
178562306a36Sopenharmony_ci			STp->dirty = !(STbp->writing ==
178662306a36Sopenharmony_ci				       STbp->buffer_bytes);
178762306a36Sopenharmony_ci			SRpnt = NULL;  /* Prevent releasing this request! */
178862306a36Sopenharmony_ci			DEB( STp->write_pending = 1; )
178962306a36Sopenharmony_ci			break;
179062306a36Sopenharmony_ci		}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci		if (STbp->syscall_result != 0) {
179362306a36Sopenharmony_ci			struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci			DEBC_printk(STp, "Error on write:\n");
179662306a36Sopenharmony_ci			if (cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) {
179762306a36Sopenharmony_ci				scode = cmdstatp->sense_hdr.sense_key;
179862306a36Sopenharmony_ci				if (cmdstatp->remainder_valid)
179962306a36Sopenharmony_ci					undone = (int)cmdstatp->uremainder64;
180062306a36Sopenharmony_ci				else if (STp->block_size == 0 &&
180162306a36Sopenharmony_ci					 scode == VOLUME_OVERFLOW)
180262306a36Sopenharmony_ci					undone = transfer;
180362306a36Sopenharmony_ci				else
180462306a36Sopenharmony_ci					undone = 0;
180562306a36Sopenharmony_ci				if (STp->block_size != 0)
180662306a36Sopenharmony_ci					undone *= STp->block_size;
180762306a36Sopenharmony_ci				if (undone <= do_count) {
180862306a36Sopenharmony_ci					/* Only data from this write is not written */
180962306a36Sopenharmony_ci					count += undone;
181062306a36Sopenharmony_ci					b_point -= undone;
181162306a36Sopenharmony_ci					do_count -= undone;
181262306a36Sopenharmony_ci					if (STp->block_size)
181362306a36Sopenharmony_ci						blks = (transfer - undone) / STp->block_size;
181462306a36Sopenharmony_ci					STps->eof = ST_EOM_OK;
181562306a36Sopenharmony_ci					/* Continue in fixed block mode if all written
181662306a36Sopenharmony_ci					   in this request but still something left to write
181762306a36Sopenharmony_ci					   (retval left to zero)
181862306a36Sopenharmony_ci					*/
181962306a36Sopenharmony_ci					if (STp->block_size == 0 ||
182062306a36Sopenharmony_ci					    undone > 0 || count == 0)
182162306a36Sopenharmony_ci						retval = (-ENOSPC); /* EOM within current request */
182262306a36Sopenharmony_ci					DEBC_printk(STp, "EOM with %d "
182362306a36Sopenharmony_ci						    "bytes unwritten.\n",
182462306a36Sopenharmony_ci						    (int)count);
182562306a36Sopenharmony_ci				} else {
182662306a36Sopenharmony_ci					/* EOT within data buffered earlier (possible only
182762306a36Sopenharmony_ci					   in fixed block mode without direct i/o) */
182862306a36Sopenharmony_ci					if (!retry_eot && !cmdstatp->deferred &&
182962306a36Sopenharmony_ci					    (scode == NO_SENSE || scode == RECOVERED_ERROR)) {
183062306a36Sopenharmony_ci						move_buffer_data(STp->buffer, transfer - undone);
183162306a36Sopenharmony_ci						retry_eot = 1;
183262306a36Sopenharmony_ci						if (STps->drv_block >= 0) {
183362306a36Sopenharmony_ci							STps->drv_block += (transfer - undone) /
183462306a36Sopenharmony_ci								STp->block_size;
183562306a36Sopenharmony_ci						}
183662306a36Sopenharmony_ci						STps->eof = ST_EOM_OK;
183762306a36Sopenharmony_ci						DEBC_printk(STp, "Retry "
183862306a36Sopenharmony_ci							    "write of %d "
183962306a36Sopenharmony_ci							    "bytes at EOM.\n",
184062306a36Sopenharmony_ci							    STp->buffer->buffer_bytes);
184162306a36Sopenharmony_ci						goto retry_write;
184262306a36Sopenharmony_ci					}
184362306a36Sopenharmony_ci					else {
184462306a36Sopenharmony_ci						/* Either error within data buffered by driver or
184562306a36Sopenharmony_ci						   failed retry */
184662306a36Sopenharmony_ci						count -= do_count;
184762306a36Sopenharmony_ci						blks = do_count = 0;
184862306a36Sopenharmony_ci						STps->eof = ST_EOM_ERROR;
184962306a36Sopenharmony_ci						STps->drv_block = (-1); /* Too cautious? */
185062306a36Sopenharmony_ci						retval = (-EIO);	/* EOM for old data */
185162306a36Sopenharmony_ci						DEBC_printk(STp, "EOM with "
185262306a36Sopenharmony_ci							    "lost data.\n");
185362306a36Sopenharmony_ci					}
185462306a36Sopenharmony_ci				}
185562306a36Sopenharmony_ci			} else {
185662306a36Sopenharmony_ci				count += do_count;
185762306a36Sopenharmony_ci				STps->drv_block = (-1);		/* Too cautious? */
185862306a36Sopenharmony_ci				retval = STbp->syscall_result;
185962306a36Sopenharmony_ci			}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci		}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci		if (STps->drv_block >= 0) {
186462306a36Sopenharmony_ci			if (STp->block_size == 0)
186562306a36Sopenharmony_ci				STps->drv_block += (do_count > 0);
186662306a36Sopenharmony_ci			else
186762306a36Sopenharmony_ci				STps->drv_block += blks;
186862306a36Sopenharmony_ci		}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci		STbp->buffer_bytes = 0;
187162306a36Sopenharmony_ci		STp->dirty = 0;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci		if (retval || retry_eot) {
187462306a36Sopenharmony_ci			if (count < total)
187562306a36Sopenharmony_ci				retval = total - count;
187662306a36Sopenharmony_ci			goto out;
187762306a36Sopenharmony_ci		}
187862306a36Sopenharmony_ci	}
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	if (STps->eof == ST_EOD_1)
188162306a36Sopenharmony_ci		STps->eof = ST_EOM_OK;
188262306a36Sopenharmony_ci	else if (STps->eof != ST_EOM_OK)
188362306a36Sopenharmony_ci		STps->eof = ST_NOEOF;
188462306a36Sopenharmony_ci	retval = total - count;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci out:
188762306a36Sopenharmony_ci	if (SRpnt != NULL)
188862306a36Sopenharmony_ci		st_release_request(SRpnt);
188962306a36Sopenharmony_ci	release_buffering(STp, 0);
189062306a36Sopenharmony_ci	mutex_unlock(&STp->lock);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	return retval;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci/* Read data from the tape. Returns zero in the normal case, one if the
189662306a36Sopenharmony_ci   eof status has changed, and the negative error code in case of a
189762306a36Sopenharmony_ci   fatal error. Otherwise updates the buffer and the eof state.
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci   Does release user buffer mapping if it is set.
190062306a36Sopenharmony_ci*/
190162306a36Sopenharmony_cistatic long read_tape(struct scsi_tape *STp, long count,
190262306a36Sopenharmony_ci		      struct st_request ** aSRpnt)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	int transfer, blks, bytes;
190562306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
190662306a36Sopenharmony_ci	struct st_request *SRpnt;
190762306a36Sopenharmony_ci	struct st_modedef *STm;
190862306a36Sopenharmony_ci	struct st_partstat *STps;
190962306a36Sopenharmony_ci	struct st_buffer *STbp;
191062306a36Sopenharmony_ci	int retval = 0;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	if (count == 0)
191362306a36Sopenharmony_ci		return 0;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
191662306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
191762306a36Sopenharmony_ci	if (STps->eof == ST_FM_HIT)
191862306a36Sopenharmony_ci		return 1;
191962306a36Sopenharmony_ci	STbp = STp->buffer;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	if (STp->block_size == 0)
192262306a36Sopenharmony_ci		blks = bytes = count;
192362306a36Sopenharmony_ci	else {
192462306a36Sopenharmony_ci		if (!(STp->try_dio_now && try_rdio) && STm->do_read_ahead) {
192562306a36Sopenharmony_ci			blks = (STp->buffer)->buffer_blocks;
192662306a36Sopenharmony_ci			bytes = blks * STp->block_size;
192762306a36Sopenharmony_ci		} else {
192862306a36Sopenharmony_ci			bytes = count;
192962306a36Sopenharmony_ci			if (!STbp->do_dio && bytes > (STp->buffer)->buffer_size)
193062306a36Sopenharmony_ci				bytes = (STp->buffer)->buffer_size;
193162306a36Sopenharmony_ci			blks = bytes / STp->block_size;
193262306a36Sopenharmony_ci			bytes = blks * STp->block_size;
193362306a36Sopenharmony_ci		}
193462306a36Sopenharmony_ci	}
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
193762306a36Sopenharmony_ci	cmd[0] = READ_6;
193862306a36Sopenharmony_ci	cmd[1] = (STp->block_size != 0);
193962306a36Sopenharmony_ci	if (!cmd[1] && STp->sili)
194062306a36Sopenharmony_ci		cmd[1] |= 2;
194162306a36Sopenharmony_ci	cmd[2] = blks >> 16;
194262306a36Sopenharmony_ci	cmd[3] = blks >> 8;
194362306a36Sopenharmony_ci	cmd[4] = blks;
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	SRpnt = *aSRpnt;
194662306a36Sopenharmony_ci	SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
194762306a36Sopenharmony_ci			   STp->device->request_queue->rq_timeout,
194862306a36Sopenharmony_ci			   MAX_RETRIES, 1);
194962306a36Sopenharmony_ci	release_buffering(STp, 1);
195062306a36Sopenharmony_ci	*aSRpnt = SRpnt;
195162306a36Sopenharmony_ci	if (!SRpnt)
195262306a36Sopenharmony_ci		return STbp->syscall_result;
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	STbp->read_pointer = 0;
195562306a36Sopenharmony_ci	STps->at_sm = 0;
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	/* Something to check */
195862306a36Sopenharmony_ci	if (STbp->syscall_result) {
195962306a36Sopenharmony_ci		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci		retval = 1;
196262306a36Sopenharmony_ci		DEBC_printk(STp,
196362306a36Sopenharmony_ci			    "Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
196462306a36Sopenharmony_ci			    SRpnt->sense[0], SRpnt->sense[1],
196562306a36Sopenharmony_ci			    SRpnt->sense[2], SRpnt->sense[3],
196662306a36Sopenharmony_ci			    SRpnt->sense[4], SRpnt->sense[5],
196762306a36Sopenharmony_ci			    SRpnt->sense[6], SRpnt->sense[7]);
196862306a36Sopenharmony_ci		if (cmdstatp->have_sense) {
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci			if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
197162306a36Sopenharmony_ci				cmdstatp->flags &= 0xcf;	/* No need for EOM in this case */
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci			if (cmdstatp->flags != 0) { /* EOF, EOM, or ILI */
197462306a36Sopenharmony_ci				/* Compute the residual count */
197562306a36Sopenharmony_ci				if (cmdstatp->remainder_valid)
197662306a36Sopenharmony_ci					transfer = (int)cmdstatp->uremainder64;
197762306a36Sopenharmony_ci				else
197862306a36Sopenharmony_ci					transfer = 0;
197962306a36Sopenharmony_ci				if (cmdstatp->sense_hdr.sense_key == MEDIUM_ERROR) {
198062306a36Sopenharmony_ci					if (STp->block_size == 0)
198162306a36Sopenharmony_ci						transfer = bytes;
198262306a36Sopenharmony_ci					/* Some drives set ILI with MEDIUM ERROR */
198362306a36Sopenharmony_ci					cmdstatp->flags &= ~SENSE_ILI;
198462306a36Sopenharmony_ci				}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci				if (cmdstatp->flags & SENSE_ILI) {	/* ILI */
198762306a36Sopenharmony_ci					if (STp->block_size == 0 &&
198862306a36Sopenharmony_ci					    transfer < 0) {
198962306a36Sopenharmony_ci						st_printk(KERN_NOTICE, STp,
199062306a36Sopenharmony_ci							  "Failed to read %d "
199162306a36Sopenharmony_ci							  "byte block with %d "
199262306a36Sopenharmony_ci							  "byte transfer.\n",
199362306a36Sopenharmony_ci							  bytes - transfer,
199462306a36Sopenharmony_ci							  bytes);
199562306a36Sopenharmony_ci						if (STps->drv_block >= 0)
199662306a36Sopenharmony_ci							STps->drv_block += 1;
199762306a36Sopenharmony_ci						STbp->buffer_bytes = 0;
199862306a36Sopenharmony_ci						return (-ENOMEM);
199962306a36Sopenharmony_ci					} else if (STp->block_size == 0) {
200062306a36Sopenharmony_ci						STbp->buffer_bytes = bytes - transfer;
200162306a36Sopenharmony_ci					} else {
200262306a36Sopenharmony_ci						st_release_request(SRpnt);
200362306a36Sopenharmony_ci						SRpnt = *aSRpnt = NULL;
200462306a36Sopenharmony_ci						if (transfer == blks) {	/* We did not get anything, error */
200562306a36Sopenharmony_ci							st_printk(KERN_NOTICE, STp,
200662306a36Sopenharmony_ci								  "Incorrect "
200762306a36Sopenharmony_ci								  "block size.\n");
200862306a36Sopenharmony_ci							if (STps->drv_block >= 0)
200962306a36Sopenharmony_ci								STps->drv_block += blks - transfer + 1;
201062306a36Sopenharmony_ci							st_int_ioctl(STp, MTBSR, 1);
201162306a36Sopenharmony_ci							return (-EIO);
201262306a36Sopenharmony_ci						}
201362306a36Sopenharmony_ci						/* We have some data, deliver it */
201462306a36Sopenharmony_ci						STbp->buffer_bytes = (blks - transfer) *
201562306a36Sopenharmony_ci						    STp->block_size;
201662306a36Sopenharmony_ci						DEBC_printk(STp, "ILI but "
201762306a36Sopenharmony_ci							    "enough data "
201862306a36Sopenharmony_ci							    "received %ld "
201962306a36Sopenharmony_ci							    "%d.\n", count,
202062306a36Sopenharmony_ci							    STbp->buffer_bytes);
202162306a36Sopenharmony_ci						if (STps->drv_block >= 0)
202262306a36Sopenharmony_ci							STps->drv_block += 1;
202362306a36Sopenharmony_ci						if (st_int_ioctl(STp, MTBSR, 1))
202462306a36Sopenharmony_ci							return (-EIO);
202562306a36Sopenharmony_ci					}
202662306a36Sopenharmony_ci				} else if (cmdstatp->flags & SENSE_FMK) {	/* FM overrides EOM */
202762306a36Sopenharmony_ci					if (STps->eof != ST_FM_HIT)
202862306a36Sopenharmony_ci						STps->eof = ST_FM_HIT;
202962306a36Sopenharmony_ci					else
203062306a36Sopenharmony_ci						STps->eof = ST_EOD_2;
203162306a36Sopenharmony_ci					if (STp->block_size == 0)
203262306a36Sopenharmony_ci						STbp->buffer_bytes = 0;
203362306a36Sopenharmony_ci					else
203462306a36Sopenharmony_ci						STbp->buffer_bytes =
203562306a36Sopenharmony_ci						    bytes - transfer * STp->block_size;
203662306a36Sopenharmony_ci					DEBC_printk(STp, "EOF detected (%d "
203762306a36Sopenharmony_ci						    "bytes read).\n",
203862306a36Sopenharmony_ci						    STbp->buffer_bytes);
203962306a36Sopenharmony_ci				} else if (cmdstatp->flags & SENSE_EOM) {
204062306a36Sopenharmony_ci					if (STps->eof == ST_FM)
204162306a36Sopenharmony_ci						STps->eof = ST_EOD_1;
204262306a36Sopenharmony_ci					else
204362306a36Sopenharmony_ci						STps->eof = ST_EOM_OK;
204462306a36Sopenharmony_ci					if (STp->block_size == 0)
204562306a36Sopenharmony_ci						STbp->buffer_bytes = bytes - transfer;
204662306a36Sopenharmony_ci					else
204762306a36Sopenharmony_ci						STbp->buffer_bytes =
204862306a36Sopenharmony_ci						    bytes - transfer * STp->block_size;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci					DEBC_printk(STp, "EOM detected (%d "
205162306a36Sopenharmony_ci						    "bytes read).\n",
205262306a36Sopenharmony_ci						    STbp->buffer_bytes);
205362306a36Sopenharmony_ci				}
205462306a36Sopenharmony_ci			}
205562306a36Sopenharmony_ci			/* end of EOF, EOM, ILI test */
205662306a36Sopenharmony_ci			else {	/* nonzero sense key */
205762306a36Sopenharmony_ci				DEBC_printk(STp, "Tape error while reading.\n");
205862306a36Sopenharmony_ci				STps->drv_block = (-1);
205962306a36Sopenharmony_ci				if (STps->eof == ST_FM &&
206062306a36Sopenharmony_ci				    cmdstatp->sense_hdr.sense_key == BLANK_CHECK) {
206162306a36Sopenharmony_ci					DEBC_printk(STp, "Zero returned for "
206262306a36Sopenharmony_ci						    "first BLANK CHECK "
206362306a36Sopenharmony_ci						    "after EOF.\n");
206462306a36Sopenharmony_ci					STps->eof = ST_EOD_2;	/* First BLANK_CHECK after FM */
206562306a36Sopenharmony_ci				} else	/* Some other extended sense code */
206662306a36Sopenharmony_ci					retval = (-EIO);
206762306a36Sopenharmony_ci			}
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci			if (STbp->buffer_bytes < 0)  /* Caused by bogus sense data */
207062306a36Sopenharmony_ci				STbp->buffer_bytes = 0;
207162306a36Sopenharmony_ci		}
207262306a36Sopenharmony_ci		/* End of extended sense test */
207362306a36Sopenharmony_ci		else {		/* Non-extended sense */
207462306a36Sopenharmony_ci			retval = STbp->syscall_result;
207562306a36Sopenharmony_ci		}
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci	/* End of error handling */
207962306a36Sopenharmony_ci	else {			/* Read successful */
208062306a36Sopenharmony_ci		STbp->buffer_bytes = bytes;
208162306a36Sopenharmony_ci		if (STp->sili) /* In fixed block mode residual is always zero here */
208262306a36Sopenharmony_ci			STbp->buffer_bytes -= STp->buffer->cmdstat.residual;
208362306a36Sopenharmony_ci	}
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci	if (STps->drv_block >= 0) {
208662306a36Sopenharmony_ci		if (STp->block_size == 0)
208762306a36Sopenharmony_ci			STps->drv_block++;
208862306a36Sopenharmony_ci		else
208962306a36Sopenharmony_ci			STps->drv_block += STbp->buffer_bytes / STp->block_size;
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci	return retval;
209262306a36Sopenharmony_ci}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci/* Read command */
209662306a36Sopenharmony_cistatic ssize_t
209762306a36Sopenharmony_cist_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
209862306a36Sopenharmony_ci{
209962306a36Sopenharmony_ci	ssize_t total;
210062306a36Sopenharmony_ci	ssize_t retval = 0;
210162306a36Sopenharmony_ci	ssize_t i, transfer;
210262306a36Sopenharmony_ci	int special, do_dio = 0;
210362306a36Sopenharmony_ci	struct st_request *SRpnt = NULL;
210462306a36Sopenharmony_ci	struct scsi_tape *STp = filp->private_data;
210562306a36Sopenharmony_ci	struct st_modedef *STm;
210662306a36Sopenharmony_ci	struct st_partstat *STps;
210762306a36Sopenharmony_ci	struct st_buffer *STbp = STp->buffer;
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	if (mutex_lock_interruptible(&STp->lock))
211062306a36Sopenharmony_ci		return -ERESTARTSYS;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	retval = rw_checks(STp, filp, count);
211362306a36Sopenharmony_ci	if (retval || count == 0)
211462306a36Sopenharmony_ci		goto out;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
211762306a36Sopenharmony_ci	if (STp->block_size != 0 && (count % STp->block_size) != 0) {
211862306a36Sopenharmony_ci		if (!STm->do_read_ahead) {
211962306a36Sopenharmony_ci			retval = (-EINVAL);	/* Read must be integral number of blocks */
212062306a36Sopenharmony_ci			goto out;
212162306a36Sopenharmony_ci		}
212262306a36Sopenharmony_ci		STp->try_dio_now = 0;  /* Direct i/o can't handle split blocks */
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
212662306a36Sopenharmony_ci	if (STps->rw == ST_WRITING) {
212762306a36Sopenharmony_ci		retval = flush_buffer(STp, 0);
212862306a36Sopenharmony_ci		if (retval)
212962306a36Sopenharmony_ci			goto out;
213062306a36Sopenharmony_ci		STps->rw = ST_READING;
213162306a36Sopenharmony_ci	}
213262306a36Sopenharmony_ci	DEB(
213362306a36Sopenharmony_ci	if (debugging && STps->eof != ST_NOEOF)
213462306a36Sopenharmony_ci		st_printk(ST_DEB_MSG, STp,
213562306a36Sopenharmony_ci			  "EOF/EOM flag up (%d). Bytes %d\n",
213662306a36Sopenharmony_ci			  STps->eof, STbp->buffer_bytes);
213762306a36Sopenharmony_ci	) /* end DEB */
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	retval = setup_buffering(STp, buf, count, 1);
214062306a36Sopenharmony_ci	if (retval)
214162306a36Sopenharmony_ci		goto out;
214262306a36Sopenharmony_ci	do_dio = STbp->do_dio;
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	if (STbp->buffer_bytes == 0 &&
214562306a36Sopenharmony_ci	    STps->eof >= ST_EOD_1) {
214662306a36Sopenharmony_ci		if (STps->eof < ST_EOD) {
214762306a36Sopenharmony_ci			STps->eof += 1;
214862306a36Sopenharmony_ci			retval = 0;
214962306a36Sopenharmony_ci			goto out;
215062306a36Sopenharmony_ci		}
215162306a36Sopenharmony_ci		retval = (-EIO);	/* EOM or Blank Check */
215262306a36Sopenharmony_ci		goto out;
215362306a36Sopenharmony_ci	}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	if (do_dio) {
215662306a36Sopenharmony_ci		/* Check the buffer writability before any tape movement. Don't alter
215762306a36Sopenharmony_ci		   buffer data. */
215862306a36Sopenharmony_ci		if (copy_from_user(&i, buf, 1) != 0 ||
215962306a36Sopenharmony_ci		    copy_to_user(buf, &i, 1) != 0 ||
216062306a36Sopenharmony_ci		    copy_from_user(&i, buf + count - 1, 1) != 0 ||
216162306a36Sopenharmony_ci		    copy_to_user(buf + count - 1, &i, 1) != 0) {
216262306a36Sopenharmony_ci			retval = (-EFAULT);
216362306a36Sopenharmony_ci			goto out;
216462306a36Sopenharmony_ci		}
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	STps->rw = ST_READING;
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci	/* Loop until enough data in buffer or a special condition found */
217162306a36Sopenharmony_ci	for (total = 0, special = 0; total < count && !special;) {
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci		/* Get new data if the buffer is empty */
217462306a36Sopenharmony_ci		if (STbp->buffer_bytes == 0) {
217562306a36Sopenharmony_ci			special = read_tape(STp, count - total, &SRpnt);
217662306a36Sopenharmony_ci			if (special < 0) {	/* No need to continue read */
217762306a36Sopenharmony_ci				retval = special;
217862306a36Sopenharmony_ci				goto out;
217962306a36Sopenharmony_ci			}
218062306a36Sopenharmony_ci		}
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci		/* Move the data from driver buffer to user buffer */
218362306a36Sopenharmony_ci		if (STbp->buffer_bytes > 0) {
218462306a36Sopenharmony_ci			DEB(
218562306a36Sopenharmony_ci			if (debugging && STps->eof != ST_NOEOF)
218662306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
218762306a36Sopenharmony_ci					  "EOF up (%d). Left %d, needed %d.\n",
218862306a36Sopenharmony_ci					  STps->eof, STbp->buffer_bytes,
218962306a36Sopenharmony_ci					  (int)(count - total));
219062306a36Sopenharmony_ci			) /* end DEB */
219162306a36Sopenharmony_ci			transfer = STbp->buffer_bytes < count - total ?
219262306a36Sopenharmony_ci			    STbp->buffer_bytes : count - total;
219362306a36Sopenharmony_ci			if (!do_dio) {
219462306a36Sopenharmony_ci				i = from_buffer(STbp, buf, transfer);
219562306a36Sopenharmony_ci				if (i) {
219662306a36Sopenharmony_ci					retval = i;
219762306a36Sopenharmony_ci					goto out;
219862306a36Sopenharmony_ci				}
219962306a36Sopenharmony_ci			}
220062306a36Sopenharmony_ci			buf += transfer;
220162306a36Sopenharmony_ci			total += transfer;
220262306a36Sopenharmony_ci		}
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci		if (STp->block_size == 0)
220562306a36Sopenharmony_ci			break;	/* Read only one variable length block */
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci	}			/* for (total = 0, special = 0;
220862306a36Sopenharmony_ci                                   total < count && !special; ) */
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	/* Change the eof state if no data from tape or buffer */
221162306a36Sopenharmony_ci	if (total == 0) {
221262306a36Sopenharmony_ci		if (STps->eof == ST_FM_HIT) {
221362306a36Sopenharmony_ci			STps->eof = ST_FM;
221462306a36Sopenharmony_ci			STps->drv_block = 0;
221562306a36Sopenharmony_ci			if (STps->drv_file >= 0)
221662306a36Sopenharmony_ci				STps->drv_file++;
221762306a36Sopenharmony_ci		} else if (STps->eof == ST_EOD_1) {
221862306a36Sopenharmony_ci			STps->eof = ST_EOD_2;
221962306a36Sopenharmony_ci			STps->drv_block = 0;
222062306a36Sopenharmony_ci			if (STps->drv_file >= 0)
222162306a36Sopenharmony_ci				STps->drv_file++;
222262306a36Sopenharmony_ci		} else if (STps->eof == ST_EOD_2)
222362306a36Sopenharmony_ci			STps->eof = ST_EOD;
222462306a36Sopenharmony_ci	} else if (STps->eof == ST_FM)
222562306a36Sopenharmony_ci		STps->eof = ST_NOEOF;
222662306a36Sopenharmony_ci	retval = total;
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci out:
222962306a36Sopenharmony_ci	if (SRpnt != NULL) {
223062306a36Sopenharmony_ci		st_release_request(SRpnt);
223162306a36Sopenharmony_ci		SRpnt = NULL;
223262306a36Sopenharmony_ci	}
223362306a36Sopenharmony_ci	if (do_dio) {
223462306a36Sopenharmony_ci		release_buffering(STp, 1);
223562306a36Sopenharmony_ci		STbp->buffer_bytes = 0;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci	mutex_unlock(&STp->lock);
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	return retval;
224062306a36Sopenharmony_ci}
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ciDEB(
224562306a36Sopenharmony_ci/* Set the driver options */
224662306a36Sopenharmony_cistatic void st_log_options(struct scsi_tape * STp, struct st_modedef * STm)
224762306a36Sopenharmony_ci{
224862306a36Sopenharmony_ci	if (debugging) {
224962306a36Sopenharmony_ci		st_printk(KERN_INFO, STp,
225062306a36Sopenharmony_ci			  "Mode %d options: buffer writes: %d, "
225162306a36Sopenharmony_ci			  "async writes: %d, read ahead: %d\n",
225262306a36Sopenharmony_ci			  STp->current_mode, STm->do_buffer_writes,
225362306a36Sopenharmony_ci			  STm->do_async_writes, STm->do_read_ahead);
225462306a36Sopenharmony_ci		st_printk(KERN_INFO, STp,
225562306a36Sopenharmony_ci			  "    can bsr: %d, two FMs: %d, "
225662306a36Sopenharmony_ci			  "fast mteom: %d, auto lock: %d,\n",
225762306a36Sopenharmony_ci			  STp->can_bsr, STp->two_fm, STp->fast_mteom,
225862306a36Sopenharmony_ci			  STp->do_auto_lock);
225962306a36Sopenharmony_ci		st_printk(KERN_INFO, STp,
226062306a36Sopenharmony_ci			  "    defs for wr: %d, no block limits: %d, "
226162306a36Sopenharmony_ci			  "partitions: %d, s2 log: %d\n",
226262306a36Sopenharmony_ci			  STm->defaults_for_writes, STp->omit_blklims,
226362306a36Sopenharmony_ci			  STp->can_partitions, STp->scsi2_logical);
226462306a36Sopenharmony_ci		st_printk(KERN_INFO, STp,
226562306a36Sopenharmony_ci			  "    sysv: %d nowait: %d sili: %d "
226662306a36Sopenharmony_ci			  "nowait_filemark: %d\n",
226762306a36Sopenharmony_ci			  STm->sysv, STp->immediate, STp->sili,
226862306a36Sopenharmony_ci			  STp->immediate_filemark);
226962306a36Sopenharmony_ci		st_printk(KERN_INFO, STp, "    debugging: %d\n", debugging);
227062306a36Sopenharmony_ci	}
227162306a36Sopenharmony_ci}
227262306a36Sopenharmony_ci	)
227362306a36Sopenharmony_ci
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_cistatic int st_set_options(struct scsi_tape *STp, long options)
227662306a36Sopenharmony_ci{
227762306a36Sopenharmony_ci	int value;
227862306a36Sopenharmony_ci	long code;
227962306a36Sopenharmony_ci	struct st_modedef *STm;
228062306a36Sopenharmony_ci	struct cdev *cd0, *cd1;
228162306a36Sopenharmony_ci	struct device *d0, *d1;
228262306a36Sopenharmony_ci
228362306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
228462306a36Sopenharmony_ci	if (!STm->defined) {
228562306a36Sopenharmony_ci		cd0 = STm->cdevs[0];
228662306a36Sopenharmony_ci		cd1 = STm->cdevs[1];
228762306a36Sopenharmony_ci		d0  = STm->devs[0];
228862306a36Sopenharmony_ci		d1  = STm->devs[1];
228962306a36Sopenharmony_ci		memcpy(STm, &(STp->modes[0]), sizeof(struct st_modedef));
229062306a36Sopenharmony_ci		STm->cdevs[0] = cd0;
229162306a36Sopenharmony_ci		STm->cdevs[1] = cd1;
229262306a36Sopenharmony_ci		STm->devs[0]  = d0;
229362306a36Sopenharmony_ci		STm->devs[1]  = d1;
229462306a36Sopenharmony_ci		modes_defined = 1;
229562306a36Sopenharmony_ci		DEBC_printk(STp, "Initialized mode %d definition from mode 0\n",
229662306a36Sopenharmony_ci			    STp->current_mode);
229762306a36Sopenharmony_ci	}
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	code = options & MT_ST_OPTIONS;
230062306a36Sopenharmony_ci	if (code == MT_ST_BOOLEANS) {
230162306a36Sopenharmony_ci		STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
230262306a36Sopenharmony_ci		STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
230362306a36Sopenharmony_ci		STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
230462306a36Sopenharmony_ci		STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
230562306a36Sopenharmony_ci		STp->two_fm = (options & MT_ST_TWO_FM) != 0;
230662306a36Sopenharmony_ci		STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
230762306a36Sopenharmony_ci		STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
230862306a36Sopenharmony_ci		STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
230962306a36Sopenharmony_ci		STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
231062306a36Sopenharmony_ci		if ((STp->device)->scsi_level >= SCSI_2)
231162306a36Sopenharmony_ci			STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
231262306a36Sopenharmony_ci		STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
231362306a36Sopenharmony_ci		STp->immediate = (options & MT_ST_NOWAIT) != 0;
231462306a36Sopenharmony_ci		STp->immediate_filemark = (options & MT_ST_NOWAIT_EOF) != 0;
231562306a36Sopenharmony_ci		STm->sysv = (options & MT_ST_SYSV) != 0;
231662306a36Sopenharmony_ci		STp->sili = (options & MT_ST_SILI) != 0;
231762306a36Sopenharmony_ci		DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
231862306a36Sopenharmony_ci		     st_log_options(STp, STm); )
231962306a36Sopenharmony_ci	} else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
232062306a36Sopenharmony_ci		value = (code == MT_ST_SETBOOLEANS);
232162306a36Sopenharmony_ci		if ((options & MT_ST_BUFFER_WRITES) != 0)
232262306a36Sopenharmony_ci			STm->do_buffer_writes = value;
232362306a36Sopenharmony_ci		if ((options & MT_ST_ASYNC_WRITES) != 0)
232462306a36Sopenharmony_ci			STm->do_async_writes = value;
232562306a36Sopenharmony_ci		if ((options & MT_ST_DEF_WRITES) != 0)
232662306a36Sopenharmony_ci			STm->defaults_for_writes = value;
232762306a36Sopenharmony_ci		if ((options & MT_ST_READ_AHEAD) != 0)
232862306a36Sopenharmony_ci			STm->do_read_ahead = value;
232962306a36Sopenharmony_ci		if ((options & MT_ST_TWO_FM) != 0)
233062306a36Sopenharmony_ci			STp->two_fm = value;
233162306a36Sopenharmony_ci		if ((options & MT_ST_FAST_MTEOM) != 0)
233262306a36Sopenharmony_ci			STp->fast_mteom = value;
233362306a36Sopenharmony_ci		if ((options & MT_ST_AUTO_LOCK) != 0)
233462306a36Sopenharmony_ci			STp->do_auto_lock = value;
233562306a36Sopenharmony_ci		if ((options & MT_ST_CAN_BSR) != 0)
233662306a36Sopenharmony_ci			STp->can_bsr = value;
233762306a36Sopenharmony_ci		if ((options & MT_ST_NO_BLKLIMS) != 0)
233862306a36Sopenharmony_ci			STp->omit_blklims = value;
233962306a36Sopenharmony_ci		if ((STp->device)->scsi_level >= SCSI_2 &&
234062306a36Sopenharmony_ci		    (options & MT_ST_CAN_PARTITIONS) != 0)
234162306a36Sopenharmony_ci			STp->can_partitions = value;
234262306a36Sopenharmony_ci		if ((options & MT_ST_SCSI2LOGICAL) != 0)
234362306a36Sopenharmony_ci			STp->scsi2_logical = value;
234462306a36Sopenharmony_ci		if ((options & MT_ST_NOWAIT) != 0)
234562306a36Sopenharmony_ci			STp->immediate = value;
234662306a36Sopenharmony_ci		if ((options & MT_ST_NOWAIT_EOF) != 0)
234762306a36Sopenharmony_ci			STp->immediate_filemark = value;
234862306a36Sopenharmony_ci		if ((options & MT_ST_SYSV) != 0)
234962306a36Sopenharmony_ci			STm->sysv = value;
235062306a36Sopenharmony_ci		if ((options & MT_ST_SILI) != 0)
235162306a36Sopenharmony_ci			STp->sili = value;
235262306a36Sopenharmony_ci		DEB(
235362306a36Sopenharmony_ci		if ((options & MT_ST_DEBUGGING) != 0)
235462306a36Sopenharmony_ci			debugging = value;
235562306a36Sopenharmony_ci			st_log_options(STp, STm); )
235662306a36Sopenharmony_ci	} else if (code == MT_ST_WRITE_THRESHOLD) {
235762306a36Sopenharmony_ci		/* Retained for compatibility */
235862306a36Sopenharmony_ci	} else if (code == MT_ST_DEF_BLKSIZE) {
235962306a36Sopenharmony_ci		value = (options & ~MT_ST_OPTIONS);
236062306a36Sopenharmony_ci		if (value == ~MT_ST_OPTIONS) {
236162306a36Sopenharmony_ci			STm->default_blksize = (-1);
236262306a36Sopenharmony_ci			DEBC_printk(STp, "Default block size disabled.\n");
236362306a36Sopenharmony_ci		} else {
236462306a36Sopenharmony_ci			STm->default_blksize = value;
236562306a36Sopenharmony_ci			DEBC_printk(STp,"Default block size set to "
236662306a36Sopenharmony_ci				    "%d bytes.\n", STm->default_blksize);
236762306a36Sopenharmony_ci			if (STp->ready == ST_READY) {
236862306a36Sopenharmony_ci				STp->blksize_changed = 0;
236962306a36Sopenharmony_ci				set_mode_densblk(STp, STm);
237062306a36Sopenharmony_ci			}
237162306a36Sopenharmony_ci		}
237262306a36Sopenharmony_ci	} else if (code == MT_ST_TIMEOUTS) {
237362306a36Sopenharmony_ci		value = (options & ~MT_ST_OPTIONS);
237462306a36Sopenharmony_ci		if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
237562306a36Sopenharmony_ci			STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
237662306a36Sopenharmony_ci			DEBC_printk(STp, "Long timeout set to %d seconds.\n",
237762306a36Sopenharmony_ci				    (value & ~MT_ST_SET_LONG_TIMEOUT));
237862306a36Sopenharmony_ci		} else {
237962306a36Sopenharmony_ci			blk_queue_rq_timeout(STp->device->request_queue,
238062306a36Sopenharmony_ci					     value * HZ);
238162306a36Sopenharmony_ci			DEBC_printk(STp, "Normal timeout set to %d seconds.\n",
238262306a36Sopenharmony_ci				    value);
238362306a36Sopenharmony_ci		}
238462306a36Sopenharmony_ci	} else if (code == MT_ST_SET_CLN) {
238562306a36Sopenharmony_ci		value = (options & ~MT_ST_OPTIONS) & 0xff;
238662306a36Sopenharmony_ci		if (value != 0 &&
238762306a36Sopenharmony_ci			(value < EXTENDED_SENSE_START ||
238862306a36Sopenharmony_ci				value >= SCSI_SENSE_BUFFERSIZE))
238962306a36Sopenharmony_ci			return (-EINVAL);
239062306a36Sopenharmony_ci		STp->cln_mode = value;
239162306a36Sopenharmony_ci		STp->cln_sense_mask = (options >> 8) & 0xff;
239262306a36Sopenharmony_ci		STp->cln_sense_value = (options >> 16) & 0xff;
239362306a36Sopenharmony_ci		st_printk(KERN_INFO, STp,
239462306a36Sopenharmony_ci			  "Cleaning request mode %d, mask %02x, value %02x\n",
239562306a36Sopenharmony_ci			  value, STp->cln_sense_mask, STp->cln_sense_value);
239662306a36Sopenharmony_ci	} else if (code == MT_ST_DEF_OPTIONS) {
239762306a36Sopenharmony_ci		code = (options & ~MT_ST_CLEAR_DEFAULT);
239862306a36Sopenharmony_ci		value = (options & MT_ST_CLEAR_DEFAULT);
239962306a36Sopenharmony_ci		if (code == MT_ST_DEF_DENSITY) {
240062306a36Sopenharmony_ci			if (value == MT_ST_CLEAR_DEFAULT) {
240162306a36Sopenharmony_ci				STm->default_density = (-1);
240262306a36Sopenharmony_ci				DEBC_printk(STp,
240362306a36Sopenharmony_ci					    "Density default disabled.\n");
240462306a36Sopenharmony_ci			} else {
240562306a36Sopenharmony_ci				STm->default_density = value & 0xff;
240662306a36Sopenharmony_ci				DEBC_printk(STp, "Density default set to %x\n",
240762306a36Sopenharmony_ci					    STm->default_density);
240862306a36Sopenharmony_ci				if (STp->ready == ST_READY) {
240962306a36Sopenharmony_ci					STp->density_changed = 0;
241062306a36Sopenharmony_ci					set_mode_densblk(STp, STm);
241162306a36Sopenharmony_ci				}
241262306a36Sopenharmony_ci			}
241362306a36Sopenharmony_ci		} else if (code == MT_ST_DEF_DRVBUFFER) {
241462306a36Sopenharmony_ci			if (value == MT_ST_CLEAR_DEFAULT) {
241562306a36Sopenharmony_ci				STp->default_drvbuffer = 0xff;
241662306a36Sopenharmony_ci				DEBC_printk(STp,
241762306a36Sopenharmony_ci					    "Drive buffer default disabled.\n");
241862306a36Sopenharmony_ci			} else {
241962306a36Sopenharmony_ci				STp->default_drvbuffer = value & 7;
242062306a36Sopenharmony_ci				DEBC_printk(STp,
242162306a36Sopenharmony_ci					    "Drive buffer default set to %x\n",
242262306a36Sopenharmony_ci					    STp->default_drvbuffer);
242362306a36Sopenharmony_ci				if (STp->ready == ST_READY)
242462306a36Sopenharmony_ci					st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer);
242562306a36Sopenharmony_ci			}
242662306a36Sopenharmony_ci		} else if (code == MT_ST_DEF_COMPRESSION) {
242762306a36Sopenharmony_ci			if (value == MT_ST_CLEAR_DEFAULT) {
242862306a36Sopenharmony_ci				STm->default_compression = ST_DONT_TOUCH;
242962306a36Sopenharmony_ci				DEBC_printk(STp,
243062306a36Sopenharmony_ci					    "Compression default disabled.\n");
243162306a36Sopenharmony_ci			} else {
243262306a36Sopenharmony_ci				if ((value & 0xff00) != 0) {
243362306a36Sopenharmony_ci					STp->c_algo = (value & 0xff00) >> 8;
243462306a36Sopenharmony_ci					DEBC_printk(STp, "Compression "
243562306a36Sopenharmony_ci						    "algorithm set to 0x%x.\n",
243662306a36Sopenharmony_ci						    STp->c_algo);
243762306a36Sopenharmony_ci				}
243862306a36Sopenharmony_ci				if ((value & 0xff) != 0xff) {
243962306a36Sopenharmony_ci					STm->default_compression = (value & 1 ? ST_YES : ST_NO);
244062306a36Sopenharmony_ci					DEBC_printk(STp, "Compression default "
244162306a36Sopenharmony_ci						    "set to %x\n",
244262306a36Sopenharmony_ci						    (value & 1));
244362306a36Sopenharmony_ci					if (STp->ready == ST_READY) {
244462306a36Sopenharmony_ci						STp->compression_changed = 0;
244562306a36Sopenharmony_ci						st_compression(STp, (STm->default_compression == ST_YES));
244662306a36Sopenharmony_ci					}
244762306a36Sopenharmony_ci				}
244862306a36Sopenharmony_ci			}
244962306a36Sopenharmony_ci		}
245062306a36Sopenharmony_ci	} else
245162306a36Sopenharmony_ci		return (-EIO);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	return 0;
245462306a36Sopenharmony_ci}
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci#define MODE_HEADER_LENGTH  4
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci/* Mode header and page byte offsets */
245962306a36Sopenharmony_ci#define MH_OFF_DATA_LENGTH     0
246062306a36Sopenharmony_ci#define MH_OFF_MEDIUM_TYPE     1
246162306a36Sopenharmony_ci#define MH_OFF_DEV_SPECIFIC    2
246262306a36Sopenharmony_ci#define MH_OFF_BDESCS_LENGTH   3
246362306a36Sopenharmony_ci#define MP_OFF_PAGE_NBR        0
246462306a36Sopenharmony_ci#define MP_OFF_PAGE_LENGTH     1
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci/* Mode header and page bit masks */
246762306a36Sopenharmony_ci#define MH_BIT_WP              0x80
246862306a36Sopenharmony_ci#define MP_MSK_PAGE_NBR        0x3f
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci/* Don't return block descriptors */
247162306a36Sopenharmony_ci#define MODE_SENSE_OMIT_BDESCS 0x08
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci#define MODE_SELECT_PAGE_FORMAT 0x10
247462306a36Sopenharmony_ci
247562306a36Sopenharmony_ci/* Read a mode page into the tape buffer. The block descriptors are included
247662306a36Sopenharmony_ci   if incl_block_descs is true. The page control is ored to the page number
247762306a36Sopenharmony_ci   parameter, if necessary. */
247862306a36Sopenharmony_cistatic int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
247962306a36Sopenharmony_ci{
248062306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
248162306a36Sopenharmony_ci	struct st_request *SRpnt;
248262306a36Sopenharmony_ci
248362306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
248462306a36Sopenharmony_ci	cmd[0] = MODE_SENSE;
248562306a36Sopenharmony_ci	if (omit_block_descs)
248662306a36Sopenharmony_ci		cmd[1] = MODE_SENSE_OMIT_BDESCS;
248762306a36Sopenharmony_ci	cmd[2] = page;
248862306a36Sopenharmony_ci	cmd[4] = 255;
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_FROM_DEVICE,
249162306a36Sopenharmony_ci			   STp->device->request_queue->rq_timeout, 0, 1);
249262306a36Sopenharmony_ci	if (SRpnt == NULL)
249362306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	st_release_request(SRpnt);
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	return STp->buffer->syscall_result;
249862306a36Sopenharmony_ci}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci/* Send the mode page in the tape buffer to the drive. Assumes that the mode data
250262306a36Sopenharmony_ci   in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
250362306a36Sopenharmony_cistatic int write_mode_page(struct scsi_tape *STp, int page, int slow)
250462306a36Sopenharmony_ci{
250562306a36Sopenharmony_ci	int pgo;
250662306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
250762306a36Sopenharmony_ci	struct st_request *SRpnt;
250862306a36Sopenharmony_ci	int timeout;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
251162306a36Sopenharmony_ci	cmd[0] = MODE_SELECT;
251262306a36Sopenharmony_ci	cmd[1] = MODE_SELECT_PAGE_FORMAT;
251362306a36Sopenharmony_ci	pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
251462306a36Sopenharmony_ci	cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci	/* Clear reserved fields */
251762306a36Sopenharmony_ci	(STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
251862306a36Sopenharmony_ci	(STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
251962306a36Sopenharmony_ci	(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
252062306a36Sopenharmony_ci	(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	timeout = slow ?
252362306a36Sopenharmony_ci		STp->long_timeout : STp->device->request_queue->rq_timeout;
252462306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_TO_DEVICE,
252562306a36Sopenharmony_ci			   timeout, 0, 1);
252662306a36Sopenharmony_ci	if (SRpnt == NULL)
252762306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_ci	st_release_request(SRpnt);
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	return STp->buffer->syscall_result;
253262306a36Sopenharmony_ci}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci#define COMPRESSION_PAGE        0x0f
253662306a36Sopenharmony_ci#define COMPRESSION_PAGE_LENGTH 16
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci#define CP_OFF_DCE_DCC          2
253962306a36Sopenharmony_ci#define CP_OFF_C_ALGO           7
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci#define DCE_MASK  0x80
254262306a36Sopenharmony_ci#define DCC_MASK  0x40
254362306a36Sopenharmony_ci#define RED_MASK  0x60
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci/* Control the compression with mode page 15. Algorithm not changed if zero.
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_ci   The block descriptors are read and written because Sony SDT-7000 does not
254962306a36Sopenharmony_ci   work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
255062306a36Sopenharmony_ci   Including block descriptors should not cause any harm to other drives. */
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_cistatic int st_compression(struct scsi_tape * STp, int state)
255362306a36Sopenharmony_ci{
255462306a36Sopenharmony_ci	int retval;
255562306a36Sopenharmony_ci	int mpoffs;  /* Offset to mode page start */
255662306a36Sopenharmony_ci	unsigned char *b_data = (STp->buffer)->b_data;
255762306a36Sopenharmony_ci
255862306a36Sopenharmony_ci	if (STp->ready != ST_READY)
255962306a36Sopenharmony_ci		return (-EIO);
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	/* Read the current page contents */
256262306a36Sopenharmony_ci	retval = read_mode_page(STp, COMPRESSION_PAGE, 0);
256362306a36Sopenharmony_ci	if (retval) {
256462306a36Sopenharmony_ci		DEBC_printk(STp, "Compression mode page not supported.\n");
256562306a36Sopenharmony_ci		return (-EIO);
256662306a36Sopenharmony_ci	}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
256962306a36Sopenharmony_ci	DEBC_printk(STp, "Compression state is %d.\n",
257062306a36Sopenharmony_ci		    (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0));
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	/* Check if compression can be changed */
257362306a36Sopenharmony_ci	if ((b_data[mpoffs + CP_OFF_DCE_DCC] & DCC_MASK) == 0) {
257462306a36Sopenharmony_ci		DEBC_printk(STp, "Compression not supported.\n");
257562306a36Sopenharmony_ci		return (-EIO);
257662306a36Sopenharmony_ci	}
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	/* Do the change */
257962306a36Sopenharmony_ci	if (state) {
258062306a36Sopenharmony_ci		b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
258162306a36Sopenharmony_ci		if (STp->c_algo != 0)
258262306a36Sopenharmony_ci			b_data[mpoffs + CP_OFF_C_ALGO] = STp->c_algo;
258362306a36Sopenharmony_ci	}
258462306a36Sopenharmony_ci	else {
258562306a36Sopenharmony_ci		b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
258662306a36Sopenharmony_ci		if (STp->c_algo != 0)
258762306a36Sopenharmony_ci			b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */
258862306a36Sopenharmony_ci	}
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	retval = write_mode_page(STp, COMPRESSION_PAGE, 0);
259162306a36Sopenharmony_ci	if (retval) {
259262306a36Sopenharmony_ci		DEBC_printk(STp, "Compression change failed.\n");
259362306a36Sopenharmony_ci		return (-EIO);
259462306a36Sopenharmony_ci	}
259562306a36Sopenharmony_ci	DEBC_printk(STp, "Compression state changed to %d.\n", state);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	STp->compression_changed = 1;
259862306a36Sopenharmony_ci	return 0;
259962306a36Sopenharmony_ci}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci/* Process the load and unload commands (does unload if the load code is zero) */
260362306a36Sopenharmony_cistatic int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_code)
260462306a36Sopenharmony_ci{
260562306a36Sopenharmony_ci	int retval = (-EIO), timeout;
260662306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
260762306a36Sopenharmony_ci	struct st_partstat *STps;
260862306a36Sopenharmony_ci	struct st_request *SRpnt;
260962306a36Sopenharmony_ci
261062306a36Sopenharmony_ci	if (STp->ready != ST_READY && !load_code) {
261162306a36Sopenharmony_ci		if (STp->ready == ST_NO_TAPE)
261262306a36Sopenharmony_ci			return (-ENOMEDIUM);
261362306a36Sopenharmony_ci		else
261462306a36Sopenharmony_ci			return (-EIO);
261562306a36Sopenharmony_ci	}
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
261862306a36Sopenharmony_ci	cmd[0] = START_STOP;
261962306a36Sopenharmony_ci	if (load_code)
262062306a36Sopenharmony_ci		cmd[4] |= 1;
262162306a36Sopenharmony_ci	/*
262262306a36Sopenharmony_ci	 * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
262362306a36Sopenharmony_ci	 */
262462306a36Sopenharmony_ci	if (load_code >= 1 + MT_ST_HPLOADER_OFFSET
262562306a36Sopenharmony_ci	    && load_code <= 6 + MT_ST_HPLOADER_OFFSET) {
262662306a36Sopenharmony_ci		DEBC_printk(STp, " Enhanced %sload slot %2d.\n",
262762306a36Sopenharmony_ci			    (cmd[4]) ? "" : "un",
262862306a36Sopenharmony_ci			    load_code - MT_ST_HPLOADER_OFFSET);
262962306a36Sopenharmony_ci		cmd[3] = load_code - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
263062306a36Sopenharmony_ci	}
263162306a36Sopenharmony_ci	if (STp->immediate) {
263262306a36Sopenharmony_ci		cmd[1] = 1;	/* Don't wait for completion */
263362306a36Sopenharmony_ci		timeout = STp->device->request_queue->rq_timeout;
263462306a36Sopenharmony_ci	}
263562306a36Sopenharmony_ci	else
263662306a36Sopenharmony_ci		timeout = STp->long_timeout;
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	DEBC(
263962306a36Sopenharmony_ci		if (!load_code)
264062306a36Sopenharmony_ci			st_printk(ST_DEB_MSG, STp, "Unloading tape.\n");
264162306a36Sopenharmony_ci		else
264262306a36Sopenharmony_ci			st_printk(ST_DEB_MSG, STp, "Loading tape.\n");
264362306a36Sopenharmony_ci		);
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
264662306a36Sopenharmony_ci			   timeout, MAX_RETRIES, 1);
264762306a36Sopenharmony_ci	if (!SRpnt)
264862306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	retval = (STp->buffer)->syscall_result;
265162306a36Sopenharmony_ci	st_release_request(SRpnt);
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	if (!retval) {	/* SCSI command successful */
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci		if (!load_code) {
265662306a36Sopenharmony_ci			STp->rew_at_close = 0;
265762306a36Sopenharmony_ci			STp->ready = ST_NO_TAPE;
265862306a36Sopenharmony_ci		}
265962306a36Sopenharmony_ci		else {
266062306a36Sopenharmony_ci			STp->rew_at_close = STp->autorew_dev;
266162306a36Sopenharmony_ci			retval = check_tape(STp, filp);
266262306a36Sopenharmony_ci			if (retval > 0)
266362306a36Sopenharmony_ci				retval = 0;
266462306a36Sopenharmony_ci		}
266562306a36Sopenharmony_ci	}
266662306a36Sopenharmony_ci	else {
266762306a36Sopenharmony_ci		STps = &(STp->ps[STp->partition]);
266862306a36Sopenharmony_ci		STps->drv_file = STps->drv_block = (-1);
266962306a36Sopenharmony_ci	}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	return retval;
267262306a36Sopenharmony_ci}
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci#if DEBUG
267562306a36Sopenharmony_ci#define ST_DEB_FORWARD  0
267662306a36Sopenharmony_ci#define ST_DEB_BACKWARD 1
267762306a36Sopenharmony_cistatic void deb_space_print(struct scsi_tape *STp, int direction, char *units, unsigned char *cmd)
267862306a36Sopenharmony_ci{
267962306a36Sopenharmony_ci	s32 sc;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci	if (!debugging)
268262306a36Sopenharmony_ci		return;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	sc = sign_extend32(get_unaligned_be24(&cmd[2]), 23);
268562306a36Sopenharmony_ci	if (direction)
268662306a36Sopenharmony_ci		sc = -sc;
268762306a36Sopenharmony_ci	st_printk(ST_DEB_MSG, STp, "Spacing tape %s over %d %s.\n",
268862306a36Sopenharmony_ci		  direction ? "backward" : "forward", sc, units);
268962306a36Sopenharmony_ci}
269062306a36Sopenharmony_ci#else
269162306a36Sopenharmony_ci#define ST_DEB_FORWARD  0
269262306a36Sopenharmony_ci#define ST_DEB_BACKWARD 1
269362306a36Sopenharmony_cistatic void deb_space_print(struct scsi_tape *STp, int direction, char *units, unsigned char *cmd) {}
269462306a36Sopenharmony_ci#endif
269562306a36Sopenharmony_ci
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci/* Internal ioctl function */
269862306a36Sopenharmony_cistatic int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned long arg)
269962306a36Sopenharmony_ci{
270062306a36Sopenharmony_ci	int timeout;
270162306a36Sopenharmony_ci	long ltmp;
270262306a36Sopenharmony_ci	int ioctl_result;
270362306a36Sopenharmony_ci	int chg_eof = 1;
270462306a36Sopenharmony_ci	unsigned char cmd[MAX_COMMAND_SIZE];
270562306a36Sopenharmony_ci	struct st_request *SRpnt;
270662306a36Sopenharmony_ci	struct st_partstat *STps;
270762306a36Sopenharmony_ci	int fileno, blkno, at_sm, undone;
270862306a36Sopenharmony_ci	int datalen = 0, direction = DMA_NONE;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	WARN_ON(STp->buffer->do_dio != 0);
271162306a36Sopenharmony_ci	if (STp->ready != ST_READY) {
271262306a36Sopenharmony_ci		if (STp->ready == ST_NO_TAPE)
271362306a36Sopenharmony_ci			return (-ENOMEDIUM);
271462306a36Sopenharmony_ci		else
271562306a36Sopenharmony_ci			return (-EIO);
271662306a36Sopenharmony_ci	}
271762306a36Sopenharmony_ci	timeout = STp->long_timeout;
271862306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
271962306a36Sopenharmony_ci	fileno = STps->drv_file;
272062306a36Sopenharmony_ci	blkno = STps->drv_block;
272162306a36Sopenharmony_ci	at_sm = STps->at_sm;
272262306a36Sopenharmony_ci
272362306a36Sopenharmony_ci	memset(cmd, 0, MAX_COMMAND_SIZE);
272462306a36Sopenharmony_ci	switch (cmd_in) {
272562306a36Sopenharmony_ci	case MTFSFM:
272662306a36Sopenharmony_ci		chg_eof = 0;	/* Changed from the FSF after this */
272762306a36Sopenharmony_ci		fallthrough;
272862306a36Sopenharmony_ci	case MTFSF:
272962306a36Sopenharmony_ci		cmd[0] = SPACE;
273062306a36Sopenharmony_ci		cmd[1] = 0x01;	/* Space FileMarks */
273162306a36Sopenharmony_ci		cmd[2] = (arg >> 16);
273262306a36Sopenharmony_ci		cmd[3] = (arg >> 8);
273362306a36Sopenharmony_ci		cmd[4] = arg;
273462306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_FORWARD, "filemarks", cmd);
273562306a36Sopenharmony_ci		if (fileno >= 0)
273662306a36Sopenharmony_ci			fileno += arg;
273762306a36Sopenharmony_ci		blkno = 0;
273862306a36Sopenharmony_ci		at_sm &= (arg == 0);
273962306a36Sopenharmony_ci		break;
274062306a36Sopenharmony_ci	case MTBSFM:
274162306a36Sopenharmony_ci		chg_eof = 0;	/* Changed from the FSF after this */
274262306a36Sopenharmony_ci		fallthrough;
274362306a36Sopenharmony_ci	case MTBSF:
274462306a36Sopenharmony_ci		cmd[0] = SPACE;
274562306a36Sopenharmony_ci		cmd[1] = 0x01;	/* Space FileMarks */
274662306a36Sopenharmony_ci		ltmp = (-arg);
274762306a36Sopenharmony_ci		cmd[2] = (ltmp >> 16);
274862306a36Sopenharmony_ci		cmd[3] = (ltmp >> 8);
274962306a36Sopenharmony_ci		cmd[4] = ltmp;
275062306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_BACKWARD, "filemarks", cmd);
275162306a36Sopenharmony_ci		if (fileno >= 0)
275262306a36Sopenharmony_ci			fileno -= arg;
275362306a36Sopenharmony_ci		blkno = (-1);	/* We can't know the block number */
275462306a36Sopenharmony_ci		at_sm &= (arg == 0);
275562306a36Sopenharmony_ci		break;
275662306a36Sopenharmony_ci	case MTFSR:
275762306a36Sopenharmony_ci		cmd[0] = SPACE;
275862306a36Sopenharmony_ci		cmd[1] = 0x00;	/* Space Blocks */
275962306a36Sopenharmony_ci		cmd[2] = (arg >> 16);
276062306a36Sopenharmony_ci		cmd[3] = (arg >> 8);
276162306a36Sopenharmony_ci		cmd[4] = arg;
276262306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_FORWARD, "blocks", cmd);
276362306a36Sopenharmony_ci		if (blkno >= 0)
276462306a36Sopenharmony_ci			blkno += arg;
276562306a36Sopenharmony_ci		at_sm &= (arg == 0);
276662306a36Sopenharmony_ci		break;
276762306a36Sopenharmony_ci	case MTBSR:
276862306a36Sopenharmony_ci		cmd[0] = SPACE;
276962306a36Sopenharmony_ci		cmd[1] = 0x00;	/* Space Blocks */
277062306a36Sopenharmony_ci		ltmp = (-arg);
277162306a36Sopenharmony_ci		cmd[2] = (ltmp >> 16);
277262306a36Sopenharmony_ci		cmd[3] = (ltmp >> 8);
277362306a36Sopenharmony_ci		cmd[4] = ltmp;
277462306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_BACKWARD, "blocks", cmd);
277562306a36Sopenharmony_ci		if (blkno >= 0)
277662306a36Sopenharmony_ci			blkno -= arg;
277762306a36Sopenharmony_ci		at_sm &= (arg == 0);
277862306a36Sopenharmony_ci		break;
277962306a36Sopenharmony_ci	case MTFSS:
278062306a36Sopenharmony_ci		cmd[0] = SPACE;
278162306a36Sopenharmony_ci		cmd[1] = 0x04;	/* Space Setmarks */
278262306a36Sopenharmony_ci		cmd[2] = (arg >> 16);
278362306a36Sopenharmony_ci		cmd[3] = (arg >> 8);
278462306a36Sopenharmony_ci		cmd[4] = arg;
278562306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_FORWARD, "setmarks", cmd);
278662306a36Sopenharmony_ci		if (arg != 0) {
278762306a36Sopenharmony_ci			blkno = fileno = (-1);
278862306a36Sopenharmony_ci			at_sm = 1;
278962306a36Sopenharmony_ci		}
279062306a36Sopenharmony_ci		break;
279162306a36Sopenharmony_ci	case MTBSS:
279262306a36Sopenharmony_ci		cmd[0] = SPACE;
279362306a36Sopenharmony_ci		cmd[1] = 0x04;	/* Space Setmarks */
279462306a36Sopenharmony_ci		ltmp = (-arg);
279562306a36Sopenharmony_ci		cmd[2] = (ltmp >> 16);
279662306a36Sopenharmony_ci		cmd[3] = (ltmp >> 8);
279762306a36Sopenharmony_ci		cmd[4] = ltmp;
279862306a36Sopenharmony_ci		deb_space_print(STp, ST_DEB_BACKWARD, "setmarks", cmd);
279962306a36Sopenharmony_ci		if (arg != 0) {
280062306a36Sopenharmony_ci			blkno = fileno = (-1);
280162306a36Sopenharmony_ci			at_sm = 1;
280262306a36Sopenharmony_ci		}
280362306a36Sopenharmony_ci		break;
280462306a36Sopenharmony_ci	case MTWEOF:
280562306a36Sopenharmony_ci	case MTWEOFI:
280662306a36Sopenharmony_ci	case MTWSM:
280762306a36Sopenharmony_ci		if (STp->write_prot)
280862306a36Sopenharmony_ci			return (-EACCES);
280962306a36Sopenharmony_ci		cmd[0] = WRITE_FILEMARKS;
281062306a36Sopenharmony_ci		if (cmd_in == MTWSM)
281162306a36Sopenharmony_ci			cmd[1] = 2;
281262306a36Sopenharmony_ci		if (cmd_in == MTWEOFI ||
281362306a36Sopenharmony_ci		    (cmd_in == MTWEOF && STp->immediate_filemark))
281462306a36Sopenharmony_ci			cmd[1] |= 1;
281562306a36Sopenharmony_ci		cmd[2] = (arg >> 16);
281662306a36Sopenharmony_ci		cmd[3] = (arg >> 8);
281762306a36Sopenharmony_ci		cmd[4] = arg;
281862306a36Sopenharmony_ci		timeout = STp->device->request_queue->rq_timeout;
281962306a36Sopenharmony_ci		DEBC(
282062306a36Sopenharmony_ci			if (cmd_in != MTWSM)
282162306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
282262306a36Sopenharmony_ci					  "Writing %d filemarks.\n",
282362306a36Sopenharmony_ci					  cmd[2] * 65536 +
282462306a36Sopenharmony_ci					  cmd[3] * 256 +
282562306a36Sopenharmony_ci					  cmd[4]);
282662306a36Sopenharmony_ci			else
282762306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
282862306a36Sopenharmony_ci					  "Writing %d setmarks.\n",
282962306a36Sopenharmony_ci					  cmd[2] * 65536 +
283062306a36Sopenharmony_ci					  cmd[3] * 256 +
283162306a36Sopenharmony_ci					  cmd[4]);
283262306a36Sopenharmony_ci		)
283362306a36Sopenharmony_ci		if (fileno >= 0)
283462306a36Sopenharmony_ci			fileno += arg;
283562306a36Sopenharmony_ci		blkno = 0;
283662306a36Sopenharmony_ci		at_sm = (cmd_in == MTWSM);
283762306a36Sopenharmony_ci		break;
283862306a36Sopenharmony_ci	case MTREW:
283962306a36Sopenharmony_ci		cmd[0] = REZERO_UNIT;
284062306a36Sopenharmony_ci		if (STp->immediate) {
284162306a36Sopenharmony_ci			cmd[1] = 1;	/* Don't wait for completion */
284262306a36Sopenharmony_ci			timeout = STp->device->request_queue->rq_timeout;
284362306a36Sopenharmony_ci		}
284462306a36Sopenharmony_ci		DEBC_printk(STp, "Rewinding tape.\n");
284562306a36Sopenharmony_ci		fileno = blkno = at_sm = 0;
284662306a36Sopenharmony_ci		break;
284762306a36Sopenharmony_ci	case MTNOP:
284862306a36Sopenharmony_ci		DEBC_printk(STp, "No op on tape.\n");
284962306a36Sopenharmony_ci		return 0;	/* Should do something ? */
285062306a36Sopenharmony_ci	case MTRETEN:
285162306a36Sopenharmony_ci		cmd[0] = START_STOP;
285262306a36Sopenharmony_ci		if (STp->immediate) {
285362306a36Sopenharmony_ci			cmd[1] = 1;	/* Don't wait for completion */
285462306a36Sopenharmony_ci			timeout = STp->device->request_queue->rq_timeout;
285562306a36Sopenharmony_ci		}
285662306a36Sopenharmony_ci		cmd[4] = 3;
285762306a36Sopenharmony_ci		DEBC_printk(STp, "Retensioning tape.\n");
285862306a36Sopenharmony_ci		fileno = blkno = at_sm = 0;
285962306a36Sopenharmony_ci		break;
286062306a36Sopenharmony_ci	case MTEOM:
286162306a36Sopenharmony_ci		if (!STp->fast_mteom) {
286262306a36Sopenharmony_ci			/* space to the end of tape */
286362306a36Sopenharmony_ci			ioctl_result = st_int_ioctl(STp, MTFSF, 0x7fffff);
286462306a36Sopenharmony_ci			fileno = STps->drv_file;
286562306a36Sopenharmony_ci			if (STps->eof >= ST_EOD_1)
286662306a36Sopenharmony_ci				return 0;
286762306a36Sopenharmony_ci			/* The next lines would hide the number of spaced FileMarks
286862306a36Sopenharmony_ci			   That's why I inserted the previous lines. I had no luck
286962306a36Sopenharmony_ci			   with detecting EOM with FSF, so we go now to EOM.
287062306a36Sopenharmony_ci			   Joerg Weule */
287162306a36Sopenharmony_ci		} else
287262306a36Sopenharmony_ci			fileno = (-1);
287362306a36Sopenharmony_ci		cmd[0] = SPACE;
287462306a36Sopenharmony_ci		cmd[1] = 3;
287562306a36Sopenharmony_ci		DEBC_printk(STp, "Spacing to end of recorded medium.\n");
287662306a36Sopenharmony_ci		blkno = -1;
287762306a36Sopenharmony_ci		at_sm = 0;
287862306a36Sopenharmony_ci		break;
287962306a36Sopenharmony_ci	case MTERASE:
288062306a36Sopenharmony_ci		if (STp->write_prot)
288162306a36Sopenharmony_ci			return (-EACCES);
288262306a36Sopenharmony_ci		cmd[0] = ERASE;
288362306a36Sopenharmony_ci		cmd[1] = (arg ? 1 : 0);	/* Long erase with non-zero argument */
288462306a36Sopenharmony_ci		if (STp->immediate) {
288562306a36Sopenharmony_ci			cmd[1] |= 2;	/* Don't wait for completion */
288662306a36Sopenharmony_ci			timeout = STp->device->request_queue->rq_timeout;
288762306a36Sopenharmony_ci		}
288862306a36Sopenharmony_ci		else
288962306a36Sopenharmony_ci			timeout = STp->long_timeout * 8;
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_ci		DEBC_printk(STp, "Erasing tape.\n");
289262306a36Sopenharmony_ci		fileno = blkno = at_sm = 0;
289362306a36Sopenharmony_ci		break;
289462306a36Sopenharmony_ci	case MTSETBLK:		/* Set block length */
289562306a36Sopenharmony_ci	case MTSETDENSITY:	/* Set tape density */
289662306a36Sopenharmony_ci	case MTSETDRVBUFFER:	/* Set drive buffering */
289762306a36Sopenharmony_ci	case SET_DENS_AND_BLK:	/* Set density and block size */
289862306a36Sopenharmony_ci		chg_eof = 0;
289962306a36Sopenharmony_ci		if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
290062306a36Sopenharmony_ci			return (-EIO);	/* Not allowed if data in buffer */
290162306a36Sopenharmony_ci		if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
290262306a36Sopenharmony_ci		    (arg & MT_ST_BLKSIZE_MASK) != 0 &&
290362306a36Sopenharmony_ci		    STp->max_block > 0 &&
290462306a36Sopenharmony_ci		    ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
290562306a36Sopenharmony_ci		     (arg & MT_ST_BLKSIZE_MASK) > STp->max_block)) {
290662306a36Sopenharmony_ci			st_printk(KERN_WARNING, STp, "Illegal block size.\n");
290762306a36Sopenharmony_ci			return (-EINVAL);
290862306a36Sopenharmony_ci		}
290962306a36Sopenharmony_ci		cmd[0] = MODE_SELECT;
291062306a36Sopenharmony_ci		if ((STp->use_pf & USE_PF))
291162306a36Sopenharmony_ci			cmd[1] = MODE_SELECT_PAGE_FORMAT;
291262306a36Sopenharmony_ci		cmd[4] = datalen = 12;
291362306a36Sopenharmony_ci		direction = DMA_TO_DEVICE;
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci		memset((STp->buffer)->b_data, 0, 12);
291662306a36Sopenharmony_ci		if (cmd_in == MTSETDRVBUFFER)
291762306a36Sopenharmony_ci			(STp->buffer)->b_data[2] = (arg & 7) << 4;
291862306a36Sopenharmony_ci		else
291962306a36Sopenharmony_ci			(STp->buffer)->b_data[2] =
292062306a36Sopenharmony_ci			    STp->drv_buffer << 4;
292162306a36Sopenharmony_ci		(STp->buffer)->b_data[3] = 8;	/* block descriptor length */
292262306a36Sopenharmony_ci		if (cmd_in == MTSETDENSITY) {
292362306a36Sopenharmony_ci			(STp->buffer)->b_data[4] = arg;
292462306a36Sopenharmony_ci			STp->density_changed = 1;	/* At least we tried ;-) */
292562306a36Sopenharmony_ci		} else if (cmd_in == SET_DENS_AND_BLK)
292662306a36Sopenharmony_ci			(STp->buffer)->b_data[4] = arg >> 24;
292762306a36Sopenharmony_ci		else
292862306a36Sopenharmony_ci			(STp->buffer)->b_data[4] = STp->density;
292962306a36Sopenharmony_ci		if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
293062306a36Sopenharmony_ci			ltmp = arg & MT_ST_BLKSIZE_MASK;
293162306a36Sopenharmony_ci			if (cmd_in == MTSETBLK)
293262306a36Sopenharmony_ci				STp->blksize_changed = 1; /* At least we tried ;-) */
293362306a36Sopenharmony_ci		} else
293462306a36Sopenharmony_ci			ltmp = STp->block_size;
293562306a36Sopenharmony_ci		(STp->buffer)->b_data[9] = (ltmp >> 16);
293662306a36Sopenharmony_ci		(STp->buffer)->b_data[10] = (ltmp >> 8);
293762306a36Sopenharmony_ci		(STp->buffer)->b_data[11] = ltmp;
293862306a36Sopenharmony_ci		timeout = STp->device->request_queue->rq_timeout;
293962306a36Sopenharmony_ci		DEBC(
294062306a36Sopenharmony_ci			if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
294162306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
294262306a36Sopenharmony_ci					  "Setting block size to %d bytes.\n",
294362306a36Sopenharmony_ci					  (STp->buffer)->b_data[9] * 65536 +
294462306a36Sopenharmony_ci					  (STp->buffer)->b_data[10] * 256 +
294562306a36Sopenharmony_ci					  (STp->buffer)->b_data[11]);
294662306a36Sopenharmony_ci			if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
294762306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
294862306a36Sopenharmony_ci					  "Setting density code to %x.\n",
294962306a36Sopenharmony_ci					  (STp->buffer)->b_data[4]);
295062306a36Sopenharmony_ci			if (cmd_in == MTSETDRVBUFFER)
295162306a36Sopenharmony_ci				st_printk(ST_DEB_MSG, STp,
295262306a36Sopenharmony_ci					  "Setting drive buffer code to %d.\n",
295362306a36Sopenharmony_ci					  ((STp->buffer)->b_data[2] >> 4) & 7);
295462306a36Sopenharmony_ci		)
295562306a36Sopenharmony_ci		break;
295662306a36Sopenharmony_ci	default:
295762306a36Sopenharmony_ci		return (-ENOSYS);
295862306a36Sopenharmony_ci	}
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
296162306a36Sopenharmony_ci			   timeout, MAX_RETRIES, 1);
296262306a36Sopenharmony_ci	if (!SRpnt)
296362306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
296462306a36Sopenharmony_ci
296562306a36Sopenharmony_ci	ioctl_result = (STp->buffer)->syscall_result;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	if (!ioctl_result) {	/* SCSI command successful */
296862306a36Sopenharmony_ci		st_release_request(SRpnt);
296962306a36Sopenharmony_ci		SRpnt = NULL;
297062306a36Sopenharmony_ci		STps->drv_block = blkno;
297162306a36Sopenharmony_ci		STps->drv_file = fileno;
297262306a36Sopenharmony_ci		STps->at_sm = at_sm;
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci		if (cmd_in == MTBSFM)
297562306a36Sopenharmony_ci			ioctl_result = st_int_ioctl(STp, MTFSF, 1);
297662306a36Sopenharmony_ci		else if (cmd_in == MTFSFM)
297762306a36Sopenharmony_ci			ioctl_result = st_int_ioctl(STp, MTBSF, 1);
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci		if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
298062306a36Sopenharmony_ci			STp->block_size = arg & MT_ST_BLKSIZE_MASK;
298162306a36Sopenharmony_ci			if (STp->block_size != 0) {
298262306a36Sopenharmony_ci				(STp->buffer)->buffer_blocks =
298362306a36Sopenharmony_ci				    (STp->buffer)->buffer_size / STp->block_size;
298462306a36Sopenharmony_ci			}
298562306a36Sopenharmony_ci			(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
298662306a36Sopenharmony_ci			if (cmd_in == SET_DENS_AND_BLK)
298762306a36Sopenharmony_ci				STp->density = arg >> MT_ST_DENSITY_SHIFT;
298862306a36Sopenharmony_ci		} else if (cmd_in == MTSETDRVBUFFER)
298962306a36Sopenharmony_ci			STp->drv_buffer = (arg & 7);
299062306a36Sopenharmony_ci		else if (cmd_in == MTSETDENSITY)
299162306a36Sopenharmony_ci			STp->density = arg;
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci		if (cmd_in == MTEOM)
299462306a36Sopenharmony_ci			STps->eof = ST_EOD;
299562306a36Sopenharmony_ci		else if (cmd_in == MTFSF)
299662306a36Sopenharmony_ci			STps->eof = ST_FM;
299762306a36Sopenharmony_ci		else if (chg_eof)
299862306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
299962306a36Sopenharmony_ci
300062306a36Sopenharmony_ci		if (cmd_in == MTWEOF || cmd_in == MTWEOFI)
300162306a36Sopenharmony_ci			STps->rw = ST_IDLE;  /* prevent automatic WEOF at close */
300262306a36Sopenharmony_ci	} else { /* SCSI command was not completely successful. Don't return
300362306a36Sopenharmony_ci                    from this block without releasing the SCSI command block! */
300462306a36Sopenharmony_ci		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci		if (cmdstatp->flags & SENSE_EOM) {
300762306a36Sopenharmony_ci			if (cmd_in != MTBSF && cmd_in != MTBSFM &&
300862306a36Sopenharmony_ci			    cmd_in != MTBSR && cmd_in != MTBSS)
300962306a36Sopenharmony_ci				STps->eof = ST_EOM_OK;
301062306a36Sopenharmony_ci			STps->drv_block = 0;
301162306a36Sopenharmony_ci		}
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci		if (cmdstatp->remainder_valid)
301462306a36Sopenharmony_ci			undone = (int)cmdstatp->uremainder64;
301562306a36Sopenharmony_ci		else
301662306a36Sopenharmony_ci			undone = 0;
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_ci		if ((cmd_in == MTWEOF || cmd_in == MTWEOFI) &&
301962306a36Sopenharmony_ci		    cmdstatp->have_sense &&
302062306a36Sopenharmony_ci		    (cmdstatp->flags & SENSE_EOM)) {
302162306a36Sopenharmony_ci			if (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
302262306a36Sopenharmony_ci			    cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) {
302362306a36Sopenharmony_ci				ioctl_result = 0;	/* EOF(s) written successfully at EOM */
302462306a36Sopenharmony_ci				STps->eof = ST_NOEOF;
302562306a36Sopenharmony_ci			} else {  /* Writing EOF(s) failed */
302662306a36Sopenharmony_ci				if (fileno >= 0)
302762306a36Sopenharmony_ci					fileno -= undone;
302862306a36Sopenharmony_ci				if (undone < arg)
302962306a36Sopenharmony_ci					STps->eof = ST_NOEOF;
303062306a36Sopenharmony_ci			}
303162306a36Sopenharmony_ci			STps->drv_file = fileno;
303262306a36Sopenharmony_ci		} else if ((cmd_in == MTFSF) || (cmd_in == MTFSFM)) {
303362306a36Sopenharmony_ci			if (fileno >= 0)
303462306a36Sopenharmony_ci				STps->drv_file = fileno - undone;
303562306a36Sopenharmony_ci			else
303662306a36Sopenharmony_ci				STps->drv_file = fileno;
303762306a36Sopenharmony_ci			STps->drv_block = -1;
303862306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
303962306a36Sopenharmony_ci		} else if ((cmd_in == MTBSF) || (cmd_in == MTBSFM)) {
304062306a36Sopenharmony_ci			if (arg > 0 && undone < 0)  /* Some drives get this wrong */
304162306a36Sopenharmony_ci				undone = (-undone);
304262306a36Sopenharmony_ci			if (STps->drv_file >= 0)
304362306a36Sopenharmony_ci				STps->drv_file = fileno + undone;
304462306a36Sopenharmony_ci			STps->drv_block = 0;
304562306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
304662306a36Sopenharmony_ci		} else if (cmd_in == MTFSR) {
304762306a36Sopenharmony_ci			if (cmdstatp->flags & SENSE_FMK) {	/* Hit filemark */
304862306a36Sopenharmony_ci				if (STps->drv_file >= 0)
304962306a36Sopenharmony_ci					STps->drv_file++;
305062306a36Sopenharmony_ci				STps->drv_block = 0;
305162306a36Sopenharmony_ci				STps->eof = ST_FM;
305262306a36Sopenharmony_ci			} else {
305362306a36Sopenharmony_ci				if (blkno >= undone)
305462306a36Sopenharmony_ci					STps->drv_block = blkno - undone;
305562306a36Sopenharmony_ci				else
305662306a36Sopenharmony_ci					STps->drv_block = (-1);
305762306a36Sopenharmony_ci				STps->eof = ST_NOEOF;
305862306a36Sopenharmony_ci			}
305962306a36Sopenharmony_ci		} else if (cmd_in == MTBSR) {
306062306a36Sopenharmony_ci			if (cmdstatp->flags & SENSE_FMK) {	/* Hit filemark */
306162306a36Sopenharmony_ci				STps->drv_file--;
306262306a36Sopenharmony_ci				STps->drv_block = (-1);
306362306a36Sopenharmony_ci			} else {
306462306a36Sopenharmony_ci				if (arg > 0 && undone < 0)  /* Some drives get this wrong */
306562306a36Sopenharmony_ci					undone = (-undone);
306662306a36Sopenharmony_ci				if (STps->drv_block >= 0)
306762306a36Sopenharmony_ci					STps->drv_block = blkno + undone;
306862306a36Sopenharmony_ci			}
306962306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
307062306a36Sopenharmony_ci		} else if (cmd_in == MTEOM) {
307162306a36Sopenharmony_ci			STps->drv_file = (-1);
307262306a36Sopenharmony_ci			STps->drv_block = (-1);
307362306a36Sopenharmony_ci			STps->eof = ST_EOD;
307462306a36Sopenharmony_ci		} else if (cmd_in == MTSETBLK ||
307562306a36Sopenharmony_ci			   cmd_in == MTSETDENSITY ||
307662306a36Sopenharmony_ci			   cmd_in == MTSETDRVBUFFER ||
307762306a36Sopenharmony_ci			   cmd_in == SET_DENS_AND_BLK) {
307862306a36Sopenharmony_ci			if (cmdstatp->sense_hdr.sense_key == ILLEGAL_REQUEST &&
307962306a36Sopenharmony_ci			    !(STp->use_pf & PF_TESTED)) {
308062306a36Sopenharmony_ci				/* Try the other possible state of Page Format if not
308162306a36Sopenharmony_ci				   already tried */
308262306a36Sopenharmony_ci				STp->use_pf = (STp->use_pf ^ USE_PF) | PF_TESTED;
308362306a36Sopenharmony_ci				st_release_request(SRpnt);
308462306a36Sopenharmony_ci				SRpnt = NULL;
308562306a36Sopenharmony_ci				return st_int_ioctl(STp, cmd_in, arg);
308662306a36Sopenharmony_ci			}
308762306a36Sopenharmony_ci		} else if (chg_eof)
308862306a36Sopenharmony_ci			STps->eof = ST_NOEOF;
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci		if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
309162306a36Sopenharmony_ci			STps->eof = ST_EOD;
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_ci		st_release_request(SRpnt);
309462306a36Sopenharmony_ci		SRpnt = NULL;
309562306a36Sopenharmony_ci	}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci	return ioctl_result;
309862306a36Sopenharmony_ci}
309962306a36Sopenharmony_ci
310062306a36Sopenharmony_ci
310162306a36Sopenharmony_ci/* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
310262306a36Sopenharmony_ci   structure. */
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_cistatic int get_location(struct scsi_tape *STp, unsigned int *block, int *partition,
310562306a36Sopenharmony_ci			int logical)
310662306a36Sopenharmony_ci{
310762306a36Sopenharmony_ci	int result;
310862306a36Sopenharmony_ci	unsigned char scmd[MAX_COMMAND_SIZE];
310962306a36Sopenharmony_ci	struct st_request *SRpnt;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	if (STp->ready != ST_READY)
311262306a36Sopenharmony_ci		return (-EIO);
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ci	memset(scmd, 0, MAX_COMMAND_SIZE);
311562306a36Sopenharmony_ci	if ((STp->device)->scsi_level < SCSI_2) {
311662306a36Sopenharmony_ci		scmd[0] = QFA_REQUEST_BLOCK;
311762306a36Sopenharmony_ci		scmd[4] = 3;
311862306a36Sopenharmony_ci	} else {
311962306a36Sopenharmony_ci		scmd[0] = READ_POSITION;
312062306a36Sopenharmony_ci		if (!logical && !STp->scsi2_logical)
312162306a36Sopenharmony_ci			scmd[1] = 1;
312262306a36Sopenharmony_ci	}
312362306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
312462306a36Sopenharmony_ci			   STp->device->request_queue->rq_timeout,
312562306a36Sopenharmony_ci			   MAX_READY_RETRIES, 1);
312662306a36Sopenharmony_ci	if (!SRpnt)
312762306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci	if ((STp->buffer)->syscall_result != 0 ||
313062306a36Sopenharmony_ci	    (STp->device->scsi_level >= SCSI_2 &&
313162306a36Sopenharmony_ci	     ((STp->buffer)->b_data[0] & 4) != 0)) {
313262306a36Sopenharmony_ci		*block = *partition = 0;
313362306a36Sopenharmony_ci		DEBC_printk(STp, " Can't read tape position.\n");
313462306a36Sopenharmony_ci		result = (-EIO);
313562306a36Sopenharmony_ci	} else {
313662306a36Sopenharmony_ci		result = 0;
313762306a36Sopenharmony_ci		if ((STp->device)->scsi_level < SCSI_2) {
313862306a36Sopenharmony_ci			*block = ((STp->buffer)->b_data[0] << 16)
313962306a36Sopenharmony_ci			    + ((STp->buffer)->b_data[1] << 8)
314062306a36Sopenharmony_ci			    + (STp->buffer)->b_data[2];
314162306a36Sopenharmony_ci			*partition = 0;
314262306a36Sopenharmony_ci		} else {
314362306a36Sopenharmony_ci			*block = ((STp->buffer)->b_data[4] << 24)
314462306a36Sopenharmony_ci			    + ((STp->buffer)->b_data[5] << 16)
314562306a36Sopenharmony_ci			    + ((STp->buffer)->b_data[6] << 8)
314662306a36Sopenharmony_ci			    + (STp->buffer)->b_data[7];
314762306a36Sopenharmony_ci			*partition = (STp->buffer)->b_data[1];
314862306a36Sopenharmony_ci			if (((STp->buffer)->b_data[0] & 0x80) &&
314962306a36Sopenharmony_ci			    (STp->buffer)->b_data[1] == 0)	/* BOP of partition 0 */
315062306a36Sopenharmony_ci				STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
315162306a36Sopenharmony_ci		}
315262306a36Sopenharmony_ci		DEBC_printk(STp, "Got tape pos. blk %d part %d.\n",
315362306a36Sopenharmony_ci			    *block, *partition);
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci	st_release_request(SRpnt);
315662306a36Sopenharmony_ci	SRpnt = NULL;
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_ci	return result;
315962306a36Sopenharmony_ci}
316062306a36Sopenharmony_ci
316162306a36Sopenharmony_ci
316262306a36Sopenharmony_ci/* Set the tape block and partition. Negative partition means that only the
316362306a36Sopenharmony_ci   block should be set in vendor specific way. */
316462306a36Sopenharmony_cistatic int set_location(struct scsi_tape *STp, unsigned int block, int partition,
316562306a36Sopenharmony_ci			int logical)
316662306a36Sopenharmony_ci{
316762306a36Sopenharmony_ci	struct st_partstat *STps;
316862306a36Sopenharmony_ci	int result, p;
316962306a36Sopenharmony_ci	unsigned int blk;
317062306a36Sopenharmony_ci	int timeout;
317162306a36Sopenharmony_ci	unsigned char scmd[MAX_COMMAND_SIZE];
317262306a36Sopenharmony_ci	struct st_request *SRpnt;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	if (STp->ready != ST_READY)
317562306a36Sopenharmony_ci		return (-EIO);
317662306a36Sopenharmony_ci	timeout = STp->long_timeout;
317762306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	DEBC_printk(STp, "Setting block to %d and partition to %d.\n",
318062306a36Sopenharmony_ci		    block, partition);
318162306a36Sopenharmony_ci	DEB(if (partition < 0)
318262306a36Sopenharmony_ci		return (-EIO); )
318362306a36Sopenharmony_ci
318462306a36Sopenharmony_ci	/* Update the location at the partition we are leaving */
318562306a36Sopenharmony_ci	if ((!STp->can_partitions && partition != 0) ||
318662306a36Sopenharmony_ci	    partition >= ST_NBR_PARTITIONS)
318762306a36Sopenharmony_ci		return (-EINVAL);
318862306a36Sopenharmony_ci	if (partition != STp->partition) {
318962306a36Sopenharmony_ci		if (get_location(STp, &blk, &p, 1))
319062306a36Sopenharmony_ci			STps->last_block_valid = 0;
319162306a36Sopenharmony_ci		else {
319262306a36Sopenharmony_ci			STps->last_block_valid = 1;
319362306a36Sopenharmony_ci			STps->last_block_visited = blk;
319462306a36Sopenharmony_ci			DEBC_printk(STp, "Visited block %d for "
319562306a36Sopenharmony_ci				    "partition %d saved.\n",
319662306a36Sopenharmony_ci				    blk, STp->partition);
319762306a36Sopenharmony_ci		}
319862306a36Sopenharmony_ci	}
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci	memset(scmd, 0, MAX_COMMAND_SIZE);
320162306a36Sopenharmony_ci	if ((STp->device)->scsi_level < SCSI_2) {
320262306a36Sopenharmony_ci		scmd[0] = QFA_SEEK_BLOCK;
320362306a36Sopenharmony_ci		scmd[2] = (block >> 16);
320462306a36Sopenharmony_ci		scmd[3] = (block >> 8);
320562306a36Sopenharmony_ci		scmd[4] = block;
320662306a36Sopenharmony_ci		scmd[5] = 0;
320762306a36Sopenharmony_ci	} else {
320862306a36Sopenharmony_ci		scmd[0] = SEEK_10;
320962306a36Sopenharmony_ci		scmd[3] = (block >> 24);
321062306a36Sopenharmony_ci		scmd[4] = (block >> 16);
321162306a36Sopenharmony_ci		scmd[5] = (block >> 8);
321262306a36Sopenharmony_ci		scmd[6] = block;
321362306a36Sopenharmony_ci		if (!logical && !STp->scsi2_logical)
321462306a36Sopenharmony_ci			scmd[1] = 4;
321562306a36Sopenharmony_ci		if (STp->partition != partition) {
321662306a36Sopenharmony_ci			scmd[1] |= 2;
321762306a36Sopenharmony_ci			scmd[8] = partition;
321862306a36Sopenharmony_ci			DEBC_printk(STp, "Trying to change partition "
321962306a36Sopenharmony_ci				    "from %d to %d\n", STp->partition,
322062306a36Sopenharmony_ci				    partition);
322162306a36Sopenharmony_ci		}
322262306a36Sopenharmony_ci	}
322362306a36Sopenharmony_ci	if (STp->immediate) {
322462306a36Sopenharmony_ci		scmd[1] |= 1;		/* Don't wait for completion */
322562306a36Sopenharmony_ci		timeout = STp->device->request_queue->rq_timeout;
322662306a36Sopenharmony_ci	}
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
322962306a36Sopenharmony_ci			   timeout, MAX_READY_RETRIES, 1);
323062306a36Sopenharmony_ci	if (!SRpnt)
323162306a36Sopenharmony_ci		return (STp->buffer)->syscall_result;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	STps->drv_block = STps->drv_file = (-1);
323462306a36Sopenharmony_ci	STps->eof = ST_NOEOF;
323562306a36Sopenharmony_ci	if ((STp->buffer)->syscall_result != 0) {
323662306a36Sopenharmony_ci		result = (-EIO);
323762306a36Sopenharmony_ci		if (STp->can_partitions &&
323862306a36Sopenharmony_ci		    (STp->device)->scsi_level >= SCSI_2 &&
323962306a36Sopenharmony_ci		    (p = find_partition(STp)) >= 0)
324062306a36Sopenharmony_ci			STp->partition = p;
324162306a36Sopenharmony_ci	} else {
324262306a36Sopenharmony_ci		if (STp->can_partitions) {
324362306a36Sopenharmony_ci			STp->partition = partition;
324462306a36Sopenharmony_ci			STps = &(STp->ps[partition]);
324562306a36Sopenharmony_ci			if (!STps->last_block_valid ||
324662306a36Sopenharmony_ci			    STps->last_block_visited != block) {
324762306a36Sopenharmony_ci				STps->at_sm = 0;
324862306a36Sopenharmony_ci				STps->rw = ST_IDLE;
324962306a36Sopenharmony_ci			}
325062306a36Sopenharmony_ci		} else
325162306a36Sopenharmony_ci			STps->at_sm = 0;
325262306a36Sopenharmony_ci		if (block == 0)
325362306a36Sopenharmony_ci			STps->drv_block = STps->drv_file = 0;
325462306a36Sopenharmony_ci		result = 0;
325562306a36Sopenharmony_ci	}
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	st_release_request(SRpnt);
325862306a36Sopenharmony_ci	SRpnt = NULL;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	return result;
326162306a36Sopenharmony_ci}
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci/* Find the current partition number for the drive status. Called from open and
326562306a36Sopenharmony_ci   returns either partition number of negative error code. */
326662306a36Sopenharmony_cistatic int find_partition(struct scsi_tape *STp)
326762306a36Sopenharmony_ci{
326862306a36Sopenharmony_ci	int i, partition;
326962306a36Sopenharmony_ci	unsigned int block;
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	if ((i = get_location(STp, &block, &partition, 1)) < 0)
327262306a36Sopenharmony_ci		return i;
327362306a36Sopenharmony_ci	if (partition >= ST_NBR_PARTITIONS)
327462306a36Sopenharmony_ci		return (-EIO);
327562306a36Sopenharmony_ci	return partition;
327662306a36Sopenharmony_ci}
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci/* Change the partition if necessary */
328062306a36Sopenharmony_cistatic int switch_partition(struct scsi_tape *STp)
328162306a36Sopenharmony_ci{
328262306a36Sopenharmony_ci	struct st_partstat *STps;
328362306a36Sopenharmony_ci
328462306a36Sopenharmony_ci	if (STp->partition == STp->new_partition)
328562306a36Sopenharmony_ci		return 0;
328662306a36Sopenharmony_ci	STps = &(STp->ps[STp->new_partition]);
328762306a36Sopenharmony_ci	if (!STps->last_block_valid)
328862306a36Sopenharmony_ci		STps->last_block_visited = 0;
328962306a36Sopenharmony_ci	return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
329062306a36Sopenharmony_ci}
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci/* Functions for reading and writing the medium partition mode page. */
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci#define PART_PAGE   0x11
329562306a36Sopenharmony_ci#define PART_PAGE_FIXED_LENGTH 8
329662306a36Sopenharmony_ci
329762306a36Sopenharmony_ci#define PP_OFF_MAX_ADD_PARTS   2
329862306a36Sopenharmony_ci#define PP_OFF_NBR_ADD_PARTS   3
329962306a36Sopenharmony_ci#define PP_OFF_FLAGS           4
330062306a36Sopenharmony_ci#define PP_OFF_PART_UNITS      6
330162306a36Sopenharmony_ci#define PP_OFF_RESERVED        7
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci#define PP_BIT_IDP             0x20
330462306a36Sopenharmony_ci#define PP_BIT_FDP             0x80
330562306a36Sopenharmony_ci#define PP_MSK_PSUM_MB         0x10
330662306a36Sopenharmony_ci#define PP_MSK_PSUM_UNITS      0x18
330762306a36Sopenharmony_ci#define PP_MSK_POFM            0x04
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci/* Get the number of partitions on the tape. As a side effect reads the
331062306a36Sopenharmony_ci   mode page into the tape buffer. */
331162306a36Sopenharmony_cistatic int nbr_partitions(struct scsi_tape *STp)
331262306a36Sopenharmony_ci{
331362306a36Sopenharmony_ci	int result;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	if (STp->ready != ST_READY)
331662306a36Sopenharmony_ci		return (-EIO);
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ci	result = read_mode_page(STp, PART_PAGE, 1);
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci	if (result) {
332162306a36Sopenharmony_ci		DEBC_printk(STp, "Can't read medium partition page.\n");
332262306a36Sopenharmony_ci		result = (-EIO);
332362306a36Sopenharmony_ci	} else {
332462306a36Sopenharmony_ci		result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
332562306a36Sopenharmony_ci					      PP_OFF_NBR_ADD_PARTS] + 1;
332662306a36Sopenharmony_ci		DEBC_printk(STp, "Number of partitions %d.\n", result);
332762306a36Sopenharmony_ci	}
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	return result;
333062306a36Sopenharmony_ci}
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_cistatic int format_medium(struct scsi_tape *STp, int format)
333462306a36Sopenharmony_ci{
333562306a36Sopenharmony_ci	int result = 0;
333662306a36Sopenharmony_ci	int timeout = STp->long_timeout;
333762306a36Sopenharmony_ci	unsigned char scmd[MAX_COMMAND_SIZE];
333862306a36Sopenharmony_ci	struct st_request *SRpnt;
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	memset(scmd, 0, MAX_COMMAND_SIZE);
334162306a36Sopenharmony_ci	scmd[0] = FORMAT_UNIT;
334262306a36Sopenharmony_ci	scmd[2] = format;
334362306a36Sopenharmony_ci	if (STp->immediate) {
334462306a36Sopenharmony_ci		scmd[1] |= 1;		/* Don't wait for completion */
334562306a36Sopenharmony_ci		timeout = STp->device->request_queue->rq_timeout;
334662306a36Sopenharmony_ci	}
334762306a36Sopenharmony_ci	DEBC_printk(STp, "Sending FORMAT MEDIUM\n");
334862306a36Sopenharmony_ci	SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
334962306a36Sopenharmony_ci			   timeout, MAX_RETRIES, 1);
335062306a36Sopenharmony_ci	if (!SRpnt)
335162306a36Sopenharmony_ci		result = STp->buffer->syscall_result;
335262306a36Sopenharmony_ci	return result;
335362306a36Sopenharmony_ci}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ci/* Partition the tape into two partitions if size > 0 or one partition if
335762306a36Sopenharmony_ci   size == 0.
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci   The block descriptors are read and written because Sony SDT-7000 does not
336062306a36Sopenharmony_ci   work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci   My HP C1533A drive returns only one partition size field. This is used to
336362306a36Sopenharmony_ci   set the size of partition 1. There is no size field for the default partition.
336462306a36Sopenharmony_ci   Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
336562306a36Sopenharmony_ci   used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
336662306a36Sopenharmony_ci   The following algorithm is used to accommodate both drives: if the number of
336762306a36Sopenharmony_ci   partition size fields is greater than the maximum number of additional partitions
336862306a36Sopenharmony_ci   in the mode page, the second field is used. Otherwise the first field is used.
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci   For Seagate DDS drives the page length must be 8 when no partitions is defined
337162306a36Sopenharmony_ci   and 10 when 1 partition is defined (information from Eric Lee Green). This is
337262306a36Sopenharmony_ci   is acceptable also to some other old drives and enforced if the first partition
337362306a36Sopenharmony_ci   size field is used for the first additional partition size.
337462306a36Sopenharmony_ci
337562306a36Sopenharmony_ci   For drives that advertize SCSI-3 or newer, use the SSC-3 methods.
337662306a36Sopenharmony_ci */
337762306a36Sopenharmony_cistatic int partition_tape(struct scsi_tape *STp, int size)
337862306a36Sopenharmony_ci{
337962306a36Sopenharmony_ci	int result;
338062306a36Sopenharmony_ci	int target_partition;
338162306a36Sopenharmony_ci	bool scsi3 = STp->device->scsi_level >= SCSI_3, needs_format = false;
338262306a36Sopenharmony_ci	int pgo, psd_cnt, psdo;
338362306a36Sopenharmony_ci	int psum = PP_MSK_PSUM_MB, units = 0;
338462306a36Sopenharmony_ci	unsigned char *bp;
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci	result = read_mode_page(STp, PART_PAGE, 0);
338762306a36Sopenharmony_ci	if (result) {
338862306a36Sopenharmony_ci		DEBC_printk(STp, "Can't read partition mode page.\n");
338962306a36Sopenharmony_ci		return result;
339062306a36Sopenharmony_ci	}
339162306a36Sopenharmony_ci	target_partition = 1;
339262306a36Sopenharmony_ci	if (size < 0) {
339362306a36Sopenharmony_ci		target_partition = 0;
339462306a36Sopenharmony_ci		size = -size;
339562306a36Sopenharmony_ci	}
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	/* The mode page is in the buffer. Let's modify it and write it. */
339862306a36Sopenharmony_ci	bp = (STp->buffer)->b_data;
339962306a36Sopenharmony_ci	pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
340062306a36Sopenharmony_ci	DEBC_printk(STp, "Partition page length is %d bytes.\n",
340162306a36Sopenharmony_ci		    bp[pgo + MP_OFF_PAGE_LENGTH] + 2);
340262306a36Sopenharmony_ci
340362306a36Sopenharmony_ci	psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
340462306a36Sopenharmony_ci
340562306a36Sopenharmony_ci	if (scsi3) {
340662306a36Sopenharmony_ci		needs_format = (bp[pgo + PP_OFF_FLAGS] & PP_MSK_POFM) != 0;
340762306a36Sopenharmony_ci		if (needs_format && size == 0) {
340862306a36Sopenharmony_ci			/* No need to write the mode page when clearing
340962306a36Sopenharmony_ci			 *  partitioning
341062306a36Sopenharmony_ci			 */
341162306a36Sopenharmony_ci			DEBC_printk(STp, "Formatting tape with one partition.\n");
341262306a36Sopenharmony_ci			result = format_medium(STp, 0);
341362306a36Sopenharmony_ci			goto out;
341462306a36Sopenharmony_ci		}
341562306a36Sopenharmony_ci		if (needs_format)  /* Leave the old value for HP DATs claiming SCSI_3 */
341662306a36Sopenharmony_ci			psd_cnt = 2;
341762306a36Sopenharmony_ci		if ((bp[pgo + PP_OFF_FLAGS] & PP_MSK_PSUM_UNITS) == PP_MSK_PSUM_UNITS) {
341862306a36Sopenharmony_ci			/* Use units scaling for large partitions if the device
341962306a36Sopenharmony_ci			 * suggests it and no precision lost. Required for IBM
342062306a36Sopenharmony_ci			 * TS1140/50 drives that don't support MB units.
342162306a36Sopenharmony_ci			 */
342262306a36Sopenharmony_ci			if (size >= 1000 && (size % 1000) == 0) {
342362306a36Sopenharmony_ci				size /= 1000;
342462306a36Sopenharmony_ci				psum = PP_MSK_PSUM_UNITS;
342562306a36Sopenharmony_ci				units = 9; /* GB */
342662306a36Sopenharmony_ci			}
342762306a36Sopenharmony_ci		}
342862306a36Sopenharmony_ci		/* Try it anyway if too large to specify in MB */
342962306a36Sopenharmony_ci		if (psum == PP_MSK_PSUM_MB && size >= 65534) {
343062306a36Sopenharmony_ci			size /= 1000;
343162306a36Sopenharmony_ci			psum = PP_MSK_PSUM_UNITS;
343262306a36Sopenharmony_ci			units = 9;  /* GB */
343362306a36Sopenharmony_ci		}
343462306a36Sopenharmony_ci	}
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci	if (size >= 65535 ||  /* Does not fit into two bytes */
343762306a36Sopenharmony_ci	    (target_partition == 0 && psd_cnt < 2)) {
343862306a36Sopenharmony_ci		result = -EINVAL;
343962306a36Sopenharmony_ci		goto out;
344062306a36Sopenharmony_ci	}
344162306a36Sopenharmony_ci
344262306a36Sopenharmony_ci	psdo = pgo + PART_PAGE_FIXED_LENGTH;
344362306a36Sopenharmony_ci	/* The second condition is for HP DDS which use only one partition size
344462306a36Sopenharmony_ci	 * descriptor
344562306a36Sopenharmony_ci	 */
344662306a36Sopenharmony_ci	if (target_partition > 0 &&
344762306a36Sopenharmony_ci	    (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS] ||
344862306a36Sopenharmony_ci	     bp[pgo + PP_OFF_MAX_ADD_PARTS] != 1)) {
344962306a36Sopenharmony_ci		bp[psdo] = bp[psdo + 1] = 0xff;  /* Rest to partition 0 */
345062306a36Sopenharmony_ci		psdo += 2;
345162306a36Sopenharmony_ci	}
345262306a36Sopenharmony_ci	memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	DEBC_printk(STp, "psd_cnt %d, max.parts %d, nbr_parts %d\n",
345562306a36Sopenharmony_ci		    psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
345662306a36Sopenharmony_ci		    bp[pgo + PP_OFF_NBR_ADD_PARTS]);
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci	if (size == 0) {
345962306a36Sopenharmony_ci		bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
346062306a36Sopenharmony_ci		if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
346162306a36Sopenharmony_ci		    bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
346262306a36Sopenharmony_ci		DEBC_printk(STp, "Formatting tape with one partition.\n");
346362306a36Sopenharmony_ci	} else {
346462306a36Sopenharmony_ci		bp[psdo] = (size >> 8) & 0xff;
346562306a36Sopenharmony_ci		bp[psdo + 1] = size & 0xff;
346662306a36Sopenharmony_ci		if (target_partition == 0)
346762306a36Sopenharmony_ci			bp[psdo + 2] = bp[psdo + 3] = 0xff;
346862306a36Sopenharmony_ci		bp[pgo + 3] = 1;
346962306a36Sopenharmony_ci		if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
347062306a36Sopenharmony_ci		    bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
347162306a36Sopenharmony_ci		DEBC_printk(STp,
347262306a36Sopenharmony_ci			    "Formatting tape with two partitions (%i = %d MB).\n",
347362306a36Sopenharmony_ci			    target_partition, units > 0 ? size * 1000 : size);
347462306a36Sopenharmony_ci	}
347562306a36Sopenharmony_ci	bp[pgo + PP_OFF_PART_UNITS] = 0;
347662306a36Sopenharmony_ci	bp[pgo + PP_OFF_RESERVED] = 0;
347762306a36Sopenharmony_ci	if (size != 1 || units != 0) {
347862306a36Sopenharmony_ci		bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | psum |
347962306a36Sopenharmony_ci			(bp[pgo + PP_OFF_FLAGS] & 0x07);
348062306a36Sopenharmony_ci		bp[pgo + PP_OFF_PART_UNITS] = units;
348162306a36Sopenharmony_ci	} else
348262306a36Sopenharmony_ci		bp[pgo + PP_OFF_FLAGS] = PP_BIT_FDP |
348362306a36Sopenharmony_ci			(bp[pgo + PP_OFF_FLAGS] & 0x1f);
348462306a36Sopenharmony_ci	bp[pgo + MP_OFF_PAGE_LENGTH] = 6 + psd_cnt * 2;
348562306a36Sopenharmony_ci
348662306a36Sopenharmony_ci	result = write_mode_page(STp, PART_PAGE, 1);
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci	if (!result && needs_format)
348962306a36Sopenharmony_ci		result = format_medium(STp, 1);
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci	if (result) {
349262306a36Sopenharmony_ci		st_printk(KERN_INFO, STp, "Partitioning of tape failed.\n");
349362306a36Sopenharmony_ci		result = (-EIO);
349462306a36Sopenharmony_ci	}
349562306a36Sopenharmony_ci
349662306a36Sopenharmony_ciout:
349762306a36Sopenharmony_ci	return result;
349862306a36Sopenharmony_ci}
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci
350262306a36Sopenharmony_ci/* The ioctl command */
350362306a36Sopenharmony_cistatic long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
350462306a36Sopenharmony_ci{
350562306a36Sopenharmony_ci	void __user *p = (void __user *)arg;
350662306a36Sopenharmony_ci	int i, cmd_nr, cmd_type, bt;
350762306a36Sopenharmony_ci	int retval = 0;
350862306a36Sopenharmony_ci	unsigned int blk;
350962306a36Sopenharmony_ci	struct scsi_tape *STp = file->private_data;
351062306a36Sopenharmony_ci	struct st_modedef *STm;
351162306a36Sopenharmony_ci	struct st_partstat *STps;
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	if (mutex_lock_interruptible(&STp->lock))
351462306a36Sopenharmony_ci		return -ERESTARTSYS;
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	DEB(
351762306a36Sopenharmony_ci	if (debugging && !STp->in_use) {
351862306a36Sopenharmony_ci		st_printk(ST_DEB_MSG, STp, "Incorrect device.\n");
351962306a36Sopenharmony_ci		retval = (-EIO);
352062306a36Sopenharmony_ci		goto out;
352162306a36Sopenharmony_ci	} ) /* end DEB */
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	STm = &(STp->modes[STp->current_mode]);
352462306a36Sopenharmony_ci	STps = &(STp->ps[STp->partition]);
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ci	/*
352762306a36Sopenharmony_ci	 * If we are in the middle of error recovery, don't let anyone
352862306a36Sopenharmony_ci	 * else try and use this device.  Also, if error recovery fails, it
352962306a36Sopenharmony_ci	 * may try and take the device offline, in which case all further
353062306a36Sopenharmony_ci	 * access to the device is prohibited.
353162306a36Sopenharmony_ci	 */
353262306a36Sopenharmony_ci	retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
353362306a36Sopenharmony_ci			file->f_flags & O_NDELAY);
353462306a36Sopenharmony_ci	if (retval)
353562306a36Sopenharmony_ci		goto out;
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci	cmd_type = _IOC_TYPE(cmd_in);
353862306a36Sopenharmony_ci	cmd_nr = _IOC_NR(cmd_in);
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
354162306a36Sopenharmony_ci		struct mtop mtc;
354262306a36Sopenharmony_ci
354362306a36Sopenharmony_ci		if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
354462306a36Sopenharmony_ci			retval = (-EINVAL);
354562306a36Sopenharmony_ci			goto out;
354662306a36Sopenharmony_ci		}
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci		i = copy_from_user(&mtc, p, sizeof(struct mtop));
354962306a36Sopenharmony_ci		if (i) {
355062306a36Sopenharmony_ci			retval = (-EFAULT);
355162306a36Sopenharmony_ci			goto out;
355262306a36Sopenharmony_ci		}
355362306a36Sopenharmony_ci
355462306a36Sopenharmony_ci		if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
355562306a36Sopenharmony_ci			st_printk(KERN_WARNING, STp,
355662306a36Sopenharmony_ci				  "MTSETDRVBUFFER only allowed for root.\n");
355762306a36Sopenharmony_ci			retval = (-EPERM);
355862306a36Sopenharmony_ci			goto out;
355962306a36Sopenharmony_ci		}
356062306a36Sopenharmony_ci		if (!STm->defined &&
356162306a36Sopenharmony_ci		    (mtc.mt_op != MTSETDRVBUFFER &&
356262306a36Sopenharmony_ci		     (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
356362306a36Sopenharmony_ci			retval = (-ENXIO);
356462306a36Sopenharmony_ci			goto out;
356562306a36Sopenharmony_ci		}
356662306a36Sopenharmony_ci
356762306a36Sopenharmony_ci		if (!STp->pos_unknown) {
356862306a36Sopenharmony_ci
356962306a36Sopenharmony_ci			if (STps->eof == ST_FM_HIT) {
357062306a36Sopenharmony_ci				if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
357162306a36Sopenharmony_ci                                    mtc.mt_op == MTEOM) {
357262306a36Sopenharmony_ci					mtc.mt_count -= 1;
357362306a36Sopenharmony_ci					if (STps->drv_file >= 0)
357462306a36Sopenharmony_ci						STps->drv_file += 1;
357562306a36Sopenharmony_ci				} else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
357662306a36Sopenharmony_ci					mtc.mt_count += 1;
357762306a36Sopenharmony_ci					if (STps->drv_file >= 0)
357862306a36Sopenharmony_ci						STps->drv_file += 1;
357962306a36Sopenharmony_ci				}
358062306a36Sopenharmony_ci			}
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_ci			if (mtc.mt_op == MTSEEK) {
358362306a36Sopenharmony_ci				/* Old position must be restored if partition will be
358462306a36Sopenharmony_ci                                   changed */
358562306a36Sopenharmony_ci				i = !STp->can_partitions ||
358662306a36Sopenharmony_ci				    (STp->new_partition != STp->partition);
358762306a36Sopenharmony_ci			} else {
358862306a36Sopenharmony_ci				i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
358962306a36Sopenharmony_ci				    mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
359062306a36Sopenharmony_ci				    mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
359162306a36Sopenharmony_ci				    mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
359262306a36Sopenharmony_ci				    mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
359362306a36Sopenharmony_ci				    mtc.mt_op == MTCOMPRESSION;
359462306a36Sopenharmony_ci			}
359562306a36Sopenharmony_ci			i = flush_buffer(STp, i);
359662306a36Sopenharmony_ci			if (i < 0) {
359762306a36Sopenharmony_ci				retval = i;
359862306a36Sopenharmony_ci				goto out;
359962306a36Sopenharmony_ci			}
360062306a36Sopenharmony_ci			if (STps->rw == ST_WRITING &&
360162306a36Sopenharmony_ci			    (mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
360262306a36Sopenharmony_ci			     mtc.mt_op == MTSEEK ||
360362306a36Sopenharmony_ci			     mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)) {
360462306a36Sopenharmony_ci				i = st_int_ioctl(STp, MTWEOF, 1);
360562306a36Sopenharmony_ci				if (i < 0) {
360662306a36Sopenharmony_ci					retval = i;
360762306a36Sopenharmony_ci					goto out;
360862306a36Sopenharmony_ci				}
360962306a36Sopenharmony_ci				if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)
361062306a36Sopenharmony_ci					mtc.mt_count++;
361162306a36Sopenharmony_ci				STps->rw = ST_IDLE;
361262306a36Sopenharmony_ci			     }
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci		} else {
361562306a36Sopenharmony_ci			/*
361662306a36Sopenharmony_ci			 * If there was a bus reset, block further access
361762306a36Sopenharmony_ci			 * to this device.  If the user wants to rewind the tape,
361862306a36Sopenharmony_ci			 * then reset the flag and allow access again.
361962306a36Sopenharmony_ci			 */
362062306a36Sopenharmony_ci			if (mtc.mt_op != MTREW &&
362162306a36Sopenharmony_ci			    mtc.mt_op != MTOFFL &&
362262306a36Sopenharmony_ci			    mtc.mt_op != MTRETEN &&
362362306a36Sopenharmony_ci			    mtc.mt_op != MTERASE &&
362462306a36Sopenharmony_ci			    mtc.mt_op != MTSEEK &&
362562306a36Sopenharmony_ci			    mtc.mt_op != MTEOM) {
362662306a36Sopenharmony_ci				retval = (-EIO);
362762306a36Sopenharmony_ci				goto out;
362862306a36Sopenharmony_ci			}
362962306a36Sopenharmony_ci			reset_state(STp);
363062306a36Sopenharmony_ci			/* remove this when the midlevel properly clears was_reset */
363162306a36Sopenharmony_ci			STp->device->was_reset = 0;
363262306a36Sopenharmony_ci		}
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci		if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
363562306a36Sopenharmony_ci		    mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
363662306a36Sopenharmony_ci		    mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
363762306a36Sopenharmony_ci			STps->rw = ST_IDLE;	/* Prevent automatic WEOF and fsf */
363862306a36Sopenharmony_ci
363962306a36Sopenharmony_ci		if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
364062306a36Sopenharmony_ci			do_door_lock(STp, 0);	/* Ignore result! */
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci		if (mtc.mt_op == MTSETDRVBUFFER &&
364362306a36Sopenharmony_ci		    (mtc.mt_count & MT_ST_OPTIONS) != 0) {
364462306a36Sopenharmony_ci			retval = st_set_options(STp, mtc.mt_count);
364562306a36Sopenharmony_ci			goto out;
364662306a36Sopenharmony_ci		}
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci		if (mtc.mt_op == MTSETPART) {
364962306a36Sopenharmony_ci			if (!STp->can_partitions ||
365062306a36Sopenharmony_ci			    mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) {
365162306a36Sopenharmony_ci				retval = (-EINVAL);
365262306a36Sopenharmony_ci				goto out;
365362306a36Sopenharmony_ci			}
365462306a36Sopenharmony_ci			if (mtc.mt_count >= STp->nbr_partitions &&
365562306a36Sopenharmony_ci			    (STp->nbr_partitions = nbr_partitions(STp)) < 0) {
365662306a36Sopenharmony_ci				retval = (-EIO);
365762306a36Sopenharmony_ci				goto out;
365862306a36Sopenharmony_ci			}
365962306a36Sopenharmony_ci			if (mtc.mt_count >= STp->nbr_partitions) {
366062306a36Sopenharmony_ci				retval = (-EINVAL);
366162306a36Sopenharmony_ci				goto out;
366262306a36Sopenharmony_ci			}
366362306a36Sopenharmony_ci			STp->new_partition = mtc.mt_count;
366462306a36Sopenharmony_ci			retval = 0;
366562306a36Sopenharmony_ci			goto out;
366662306a36Sopenharmony_ci		}
366762306a36Sopenharmony_ci
366862306a36Sopenharmony_ci		if (mtc.mt_op == MTMKPART) {
366962306a36Sopenharmony_ci			if (!STp->can_partitions) {
367062306a36Sopenharmony_ci				retval = (-EINVAL);
367162306a36Sopenharmony_ci				goto out;
367262306a36Sopenharmony_ci			}
367362306a36Sopenharmony_ci			i = do_load_unload(STp, file, 1);
367462306a36Sopenharmony_ci			if (i < 0) {
367562306a36Sopenharmony_ci				retval = i;
367662306a36Sopenharmony_ci				goto out;
367762306a36Sopenharmony_ci			}
367862306a36Sopenharmony_ci			i = partition_tape(STp, mtc.mt_count);
367962306a36Sopenharmony_ci			if (i < 0) {
368062306a36Sopenharmony_ci				retval = i;
368162306a36Sopenharmony_ci				goto out;
368262306a36Sopenharmony_ci			}
368362306a36Sopenharmony_ci			for (i = 0; i < ST_NBR_PARTITIONS; i++) {
368462306a36Sopenharmony_ci				STp->ps[i].rw = ST_IDLE;
368562306a36Sopenharmony_ci				STp->ps[i].at_sm = 0;
368662306a36Sopenharmony_ci				STp->ps[i].last_block_valid = 0;
368762306a36Sopenharmony_ci			}
368862306a36Sopenharmony_ci			STp->partition = STp->new_partition = 0;
368962306a36Sopenharmony_ci			STp->nbr_partitions = mtc.mt_count != 0 ? 2 : 1;
369062306a36Sopenharmony_ci			STps->drv_block = STps->drv_file = 0;
369162306a36Sopenharmony_ci			retval = 0;
369262306a36Sopenharmony_ci			goto out;
369362306a36Sopenharmony_ci		}
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci		if (mtc.mt_op == MTSEEK) {
369662306a36Sopenharmony_ci			i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
369762306a36Sopenharmony_ci			if (!STp->can_partitions)
369862306a36Sopenharmony_ci				STp->ps[0].rw = ST_IDLE;
369962306a36Sopenharmony_ci			retval = i;
370062306a36Sopenharmony_ci			goto out;
370162306a36Sopenharmony_ci		}
370262306a36Sopenharmony_ci
370362306a36Sopenharmony_ci		if (mtc.mt_op == MTUNLOAD || mtc.mt_op == MTOFFL) {
370462306a36Sopenharmony_ci			retval = do_load_unload(STp, file, 0);
370562306a36Sopenharmony_ci			goto out;
370662306a36Sopenharmony_ci		}
370762306a36Sopenharmony_ci
370862306a36Sopenharmony_ci		if (mtc.mt_op == MTLOAD) {
370962306a36Sopenharmony_ci			retval = do_load_unload(STp, file, max(1, mtc.mt_count));
371062306a36Sopenharmony_ci			goto out;
371162306a36Sopenharmony_ci		}
371262306a36Sopenharmony_ci
371362306a36Sopenharmony_ci		if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
371462306a36Sopenharmony_ci			retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
371562306a36Sopenharmony_ci			goto out;
371662306a36Sopenharmony_ci		}
371762306a36Sopenharmony_ci
371862306a36Sopenharmony_ci		if (STp->can_partitions && STp->ready == ST_READY &&
371962306a36Sopenharmony_ci		    (i = switch_partition(STp)) < 0) {
372062306a36Sopenharmony_ci			retval = i;
372162306a36Sopenharmony_ci			goto out;
372262306a36Sopenharmony_ci		}
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_ci		if (mtc.mt_op == MTCOMPRESSION)
372562306a36Sopenharmony_ci			retval = st_compression(STp, (mtc.mt_count & 1));
372662306a36Sopenharmony_ci		else
372762306a36Sopenharmony_ci			retval = st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
372862306a36Sopenharmony_ci		goto out;
372962306a36Sopenharmony_ci	}
373062306a36Sopenharmony_ci	if (!STm->defined) {
373162306a36Sopenharmony_ci		retval = (-ENXIO);
373262306a36Sopenharmony_ci		goto out;
373362306a36Sopenharmony_ci	}
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	if ((i = flush_buffer(STp, 0)) < 0) {
373662306a36Sopenharmony_ci		retval = i;
373762306a36Sopenharmony_ci		goto out;
373862306a36Sopenharmony_ci	}
373962306a36Sopenharmony_ci	if (STp->can_partitions &&
374062306a36Sopenharmony_ci	    (i = switch_partition(STp)) < 0) {
374162306a36Sopenharmony_ci		retval = i;
374262306a36Sopenharmony_ci		goto out;
374362306a36Sopenharmony_ci	}
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
374662306a36Sopenharmony_ci		struct mtget mt_status;
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci		if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
374962306a36Sopenharmony_ci			 retval = (-EINVAL);
375062306a36Sopenharmony_ci			 goto out;
375162306a36Sopenharmony_ci		}
375262306a36Sopenharmony_ci
375362306a36Sopenharmony_ci		mt_status.mt_type = STp->tape_type;
375462306a36Sopenharmony_ci		mt_status.mt_dsreg =
375562306a36Sopenharmony_ci		    ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
375662306a36Sopenharmony_ci		    ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
375762306a36Sopenharmony_ci		mt_status.mt_blkno = STps->drv_block;
375862306a36Sopenharmony_ci		mt_status.mt_fileno = STps->drv_file;
375962306a36Sopenharmony_ci		if (STp->block_size != 0) {
376062306a36Sopenharmony_ci			if (STps->rw == ST_WRITING)
376162306a36Sopenharmony_ci				mt_status.mt_blkno +=
376262306a36Sopenharmony_ci				    (STp->buffer)->buffer_bytes / STp->block_size;
376362306a36Sopenharmony_ci			else if (STps->rw == ST_READING)
376462306a36Sopenharmony_ci				mt_status.mt_blkno -=
376562306a36Sopenharmony_ci                                        ((STp->buffer)->buffer_bytes +
376662306a36Sopenharmony_ci                                         STp->block_size - 1) / STp->block_size;
376762306a36Sopenharmony_ci		}
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_ci		mt_status.mt_gstat = 0;
377062306a36Sopenharmony_ci		if (STp->drv_write_prot)
377162306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
377262306a36Sopenharmony_ci		if (mt_status.mt_blkno == 0) {
377362306a36Sopenharmony_ci			if (mt_status.mt_fileno == 0)
377462306a36Sopenharmony_ci				mt_status.mt_gstat |= GMT_BOT(0xffffffff);
377562306a36Sopenharmony_ci			else
377662306a36Sopenharmony_ci				mt_status.mt_gstat |= GMT_EOF(0xffffffff);
377762306a36Sopenharmony_ci		}
377862306a36Sopenharmony_ci		mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
377962306a36Sopenharmony_ci		mt_status.mt_resid = STp->partition;
378062306a36Sopenharmony_ci		if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
378162306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_EOT(0xffffffff);
378262306a36Sopenharmony_ci		else if (STps->eof >= ST_EOM_OK)
378362306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_EOD(0xffffffff);
378462306a36Sopenharmony_ci		if (STp->density == 1)
378562306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_D_800(0xffffffff);
378662306a36Sopenharmony_ci		else if (STp->density == 2)
378762306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
378862306a36Sopenharmony_ci		else if (STp->density == 3)
378962306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
379062306a36Sopenharmony_ci		if (STp->ready == ST_READY)
379162306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
379262306a36Sopenharmony_ci		if (STp->ready == ST_NO_TAPE)
379362306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
379462306a36Sopenharmony_ci		if (STps->at_sm)
379562306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_SM(0xffffffff);
379662306a36Sopenharmony_ci		if (STm->do_async_writes ||
379762306a36Sopenharmony_ci                    (STm->do_buffer_writes && STp->block_size != 0) ||
379862306a36Sopenharmony_ci		    STp->drv_buffer != 0)
379962306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
380062306a36Sopenharmony_ci		if (STp->cleaning_req)
380162306a36Sopenharmony_ci			mt_status.mt_gstat |= GMT_CLN(0xffffffff);
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci		retval = put_user_mtget(p, &mt_status);
380462306a36Sopenharmony_ci		if (retval)
380562306a36Sopenharmony_ci			goto out;
380662306a36Sopenharmony_ci
380762306a36Sopenharmony_ci		STp->recover_reg = 0;		/* Clear after read */
380862306a36Sopenharmony_ci		goto out;
380962306a36Sopenharmony_ci	}			/* End of MTIOCGET */
381062306a36Sopenharmony_ci	if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
381162306a36Sopenharmony_ci		struct mtpos mt_pos;
381262306a36Sopenharmony_ci		if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
381362306a36Sopenharmony_ci			 retval = (-EINVAL);
381462306a36Sopenharmony_ci			 goto out;
381562306a36Sopenharmony_ci		}
381662306a36Sopenharmony_ci		if ((i = get_location(STp, &blk, &bt, 0)) < 0) {
381762306a36Sopenharmony_ci			retval = i;
381862306a36Sopenharmony_ci			goto out;
381962306a36Sopenharmony_ci		}
382062306a36Sopenharmony_ci		mt_pos.mt_blkno = blk;
382162306a36Sopenharmony_ci		retval = put_user_mtpos(p, &mt_pos);
382262306a36Sopenharmony_ci		goto out;
382362306a36Sopenharmony_ci	}
382462306a36Sopenharmony_ci	mutex_unlock(&STp->lock);
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci	switch (cmd_in) {
382762306a36Sopenharmony_ci	case SG_IO:
382862306a36Sopenharmony_ci	case SCSI_IOCTL_SEND_COMMAND:
382962306a36Sopenharmony_ci	case CDROM_SEND_PACKET:
383062306a36Sopenharmony_ci		if (!capable(CAP_SYS_RAWIO))
383162306a36Sopenharmony_ci			return -EPERM;
383262306a36Sopenharmony_ci		break;
383362306a36Sopenharmony_ci	default:
383462306a36Sopenharmony_ci		break;
383562306a36Sopenharmony_ci	}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	retval = scsi_ioctl(STp->device, file->f_mode & FMODE_WRITE, cmd_in, p);
383862306a36Sopenharmony_ci	if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) {
383962306a36Sopenharmony_ci		/* unload */
384062306a36Sopenharmony_ci		STp->rew_at_close = 0;
384162306a36Sopenharmony_ci		STp->ready = ST_NO_TAPE;
384262306a36Sopenharmony_ci	}
384362306a36Sopenharmony_ci	return retval;
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci out:
384662306a36Sopenharmony_ci	mutex_unlock(&STp->lock);
384762306a36Sopenharmony_ci	return retval;
384862306a36Sopenharmony_ci}
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
385162306a36Sopenharmony_cistatic long st_compat_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
385262306a36Sopenharmony_ci{
385362306a36Sopenharmony_ci	/* argument conversion is handled using put_user_mtpos/put_user_mtget */
385462306a36Sopenharmony_ci	switch (cmd_in) {
385562306a36Sopenharmony_ci	case MTIOCPOS32:
385662306a36Sopenharmony_ci		cmd_in = MTIOCPOS;
385762306a36Sopenharmony_ci		break;
385862306a36Sopenharmony_ci	case MTIOCGET32:
385962306a36Sopenharmony_ci		cmd_in = MTIOCGET;
386062306a36Sopenharmony_ci		break;
386162306a36Sopenharmony_ci	}
386262306a36Sopenharmony_ci
386362306a36Sopenharmony_ci	return st_ioctl(file, cmd_in, arg);
386462306a36Sopenharmony_ci}
386562306a36Sopenharmony_ci#endif
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci/* Try to allocate a new tape buffer. Calling function must not hold
387062306a36Sopenharmony_ci   dev_arr_lock. */
387162306a36Sopenharmony_cistatic struct st_buffer *new_tape_buffer(int max_sg)
387262306a36Sopenharmony_ci{
387362306a36Sopenharmony_ci	struct st_buffer *tb;
387462306a36Sopenharmony_ci
387562306a36Sopenharmony_ci	tb = kzalloc(sizeof(struct st_buffer), GFP_KERNEL);
387662306a36Sopenharmony_ci	if (!tb) {
387762306a36Sopenharmony_ci		printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n");
387862306a36Sopenharmony_ci		return NULL;
387962306a36Sopenharmony_ci	}
388062306a36Sopenharmony_ci	tb->frp_segs = 0;
388162306a36Sopenharmony_ci	tb->use_sg = max_sg;
388262306a36Sopenharmony_ci	tb->buffer_size = 0;
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_ci	tb->reserved_pages = kcalloc(max_sg, sizeof(struct page *),
388562306a36Sopenharmony_ci				     GFP_KERNEL);
388662306a36Sopenharmony_ci	if (!tb->reserved_pages) {
388762306a36Sopenharmony_ci		kfree(tb);
388862306a36Sopenharmony_ci		return NULL;
388962306a36Sopenharmony_ci	}
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	return tb;
389262306a36Sopenharmony_ci}
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci/* Try to allocate enough space in the tape buffer */
389662306a36Sopenharmony_ci#define ST_MAX_ORDER 6
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_cistatic int enlarge_buffer(struct st_buffer * STbuffer, int new_size)
389962306a36Sopenharmony_ci{
390062306a36Sopenharmony_ci	int segs, max_segs, b_size, order, got;
390162306a36Sopenharmony_ci	gfp_t priority;
390262306a36Sopenharmony_ci
390362306a36Sopenharmony_ci	if (new_size <= STbuffer->buffer_size)
390462306a36Sopenharmony_ci		return 1;
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_ci	if (STbuffer->buffer_size <= PAGE_SIZE)
390762306a36Sopenharmony_ci		normalize_buffer(STbuffer);  /* Avoid extra segment */
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	max_segs = STbuffer->use_sg;
391062306a36Sopenharmony_ci
391162306a36Sopenharmony_ci	priority = GFP_KERNEL | __GFP_NOWARN;
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci	if (STbuffer->cleared)
391462306a36Sopenharmony_ci		priority |= __GFP_ZERO;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci	if (STbuffer->frp_segs) {
391762306a36Sopenharmony_ci		order = STbuffer->reserved_page_order;
391862306a36Sopenharmony_ci		b_size = PAGE_SIZE << order;
391962306a36Sopenharmony_ci	} else {
392062306a36Sopenharmony_ci		for (b_size = PAGE_SIZE, order = 0;
392162306a36Sopenharmony_ci		     order < ST_MAX_ORDER &&
392262306a36Sopenharmony_ci			     max_segs * (PAGE_SIZE << order) < new_size;
392362306a36Sopenharmony_ci		     order++, b_size *= 2)
392462306a36Sopenharmony_ci			;  /* empty */
392562306a36Sopenharmony_ci		STbuffer->reserved_page_order = order;
392662306a36Sopenharmony_ci	}
392762306a36Sopenharmony_ci	if (max_segs * (PAGE_SIZE << order) < new_size) {
392862306a36Sopenharmony_ci		if (order == ST_MAX_ORDER)
392962306a36Sopenharmony_ci			return 0;
393062306a36Sopenharmony_ci		normalize_buffer(STbuffer);
393162306a36Sopenharmony_ci		return enlarge_buffer(STbuffer, new_size);
393262306a36Sopenharmony_ci	}
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci	for (segs = STbuffer->frp_segs, got = STbuffer->buffer_size;
393562306a36Sopenharmony_ci	     segs < max_segs && got < new_size;) {
393662306a36Sopenharmony_ci		struct page *page;
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci		page = alloc_pages(priority, order);
393962306a36Sopenharmony_ci		if (!page) {
394062306a36Sopenharmony_ci			DEB(STbuffer->buffer_size = got);
394162306a36Sopenharmony_ci			normalize_buffer(STbuffer);
394262306a36Sopenharmony_ci			return 0;
394362306a36Sopenharmony_ci		}
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ci		STbuffer->frp_segs += 1;
394662306a36Sopenharmony_ci		got += b_size;
394762306a36Sopenharmony_ci		STbuffer->buffer_size = got;
394862306a36Sopenharmony_ci		STbuffer->reserved_pages[segs] = page;
394962306a36Sopenharmony_ci		segs++;
395062306a36Sopenharmony_ci	}
395162306a36Sopenharmony_ci	STbuffer->b_data = page_address(STbuffer->reserved_pages[0]);
395262306a36Sopenharmony_ci
395362306a36Sopenharmony_ci	return 1;
395462306a36Sopenharmony_ci}
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_ci
395762306a36Sopenharmony_ci/* Make sure that no data from previous user is in the internal buffer */
395862306a36Sopenharmony_cistatic void clear_buffer(struct st_buffer * st_bp)
395962306a36Sopenharmony_ci{
396062306a36Sopenharmony_ci	int i;
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci	for (i=0; i < st_bp->frp_segs; i++)
396362306a36Sopenharmony_ci		memset(page_address(st_bp->reserved_pages[i]), 0,
396462306a36Sopenharmony_ci		       PAGE_SIZE << st_bp->reserved_page_order);
396562306a36Sopenharmony_ci	st_bp->cleared = 1;
396662306a36Sopenharmony_ci}
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci
396962306a36Sopenharmony_ci/* Release the extra buffer */
397062306a36Sopenharmony_cistatic void normalize_buffer(struct st_buffer * STbuffer)
397162306a36Sopenharmony_ci{
397262306a36Sopenharmony_ci	int i, order = STbuffer->reserved_page_order;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci	for (i = 0; i < STbuffer->frp_segs; i++) {
397562306a36Sopenharmony_ci		__free_pages(STbuffer->reserved_pages[i], order);
397662306a36Sopenharmony_ci		STbuffer->buffer_size -= (PAGE_SIZE << order);
397762306a36Sopenharmony_ci	}
397862306a36Sopenharmony_ci	STbuffer->frp_segs = 0;
397962306a36Sopenharmony_ci	STbuffer->sg_segs = 0;
398062306a36Sopenharmony_ci	STbuffer->reserved_page_order = 0;
398162306a36Sopenharmony_ci	STbuffer->map_data.offset = 0;
398262306a36Sopenharmony_ci}
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci/* Move data from the user buffer to the tape buffer. Returns zero (success) or
398662306a36Sopenharmony_ci   negative error code. */
398762306a36Sopenharmony_cistatic int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count)
398862306a36Sopenharmony_ci{
398962306a36Sopenharmony_ci	int i, cnt, res, offset;
399062306a36Sopenharmony_ci	int length = PAGE_SIZE << st_bp->reserved_page_order;
399162306a36Sopenharmony_ci
399262306a36Sopenharmony_ci	for (i = 0, offset = st_bp->buffer_bytes;
399362306a36Sopenharmony_ci	     i < st_bp->frp_segs && offset >= length; i++)
399462306a36Sopenharmony_ci		offset -= length;
399562306a36Sopenharmony_ci	if (i == st_bp->frp_segs) {	/* Should never happen */
399662306a36Sopenharmony_ci		printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
399762306a36Sopenharmony_ci		return (-EIO);
399862306a36Sopenharmony_ci	}
399962306a36Sopenharmony_ci	for (; i < st_bp->frp_segs && do_count > 0; i++) {
400062306a36Sopenharmony_ci		struct page *page = st_bp->reserved_pages[i];
400162306a36Sopenharmony_ci		cnt = length - offset < do_count ? length - offset : do_count;
400262306a36Sopenharmony_ci		res = copy_from_user(page_address(page) + offset, ubp, cnt);
400362306a36Sopenharmony_ci		if (res)
400462306a36Sopenharmony_ci			return (-EFAULT);
400562306a36Sopenharmony_ci		do_count -= cnt;
400662306a36Sopenharmony_ci		st_bp->buffer_bytes += cnt;
400762306a36Sopenharmony_ci		ubp += cnt;
400862306a36Sopenharmony_ci		offset = 0;
400962306a36Sopenharmony_ci	}
401062306a36Sopenharmony_ci	if (do_count) /* Should never happen */
401162306a36Sopenharmony_ci		return (-EIO);
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ci	return 0;
401462306a36Sopenharmony_ci}
401562306a36Sopenharmony_ci
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci/* Move data from the tape buffer to the user buffer. Returns zero (success) or
401862306a36Sopenharmony_ci   negative error code. */
401962306a36Sopenharmony_cistatic int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
402062306a36Sopenharmony_ci{
402162306a36Sopenharmony_ci	int i, cnt, res, offset;
402262306a36Sopenharmony_ci	int length = PAGE_SIZE << st_bp->reserved_page_order;
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_ci	for (i = 0, offset = st_bp->read_pointer;
402562306a36Sopenharmony_ci	     i < st_bp->frp_segs && offset >= length; i++)
402662306a36Sopenharmony_ci		offset -= length;
402762306a36Sopenharmony_ci	if (i == st_bp->frp_segs) {	/* Should never happen */
402862306a36Sopenharmony_ci		printk(KERN_WARNING "st: from_buffer offset overflow.\n");
402962306a36Sopenharmony_ci		return (-EIO);
403062306a36Sopenharmony_ci	}
403162306a36Sopenharmony_ci	for (; i < st_bp->frp_segs && do_count > 0; i++) {
403262306a36Sopenharmony_ci		struct page *page = st_bp->reserved_pages[i];
403362306a36Sopenharmony_ci		cnt = length - offset < do_count ? length - offset : do_count;
403462306a36Sopenharmony_ci		res = copy_to_user(ubp, page_address(page) + offset, cnt);
403562306a36Sopenharmony_ci		if (res)
403662306a36Sopenharmony_ci			return (-EFAULT);
403762306a36Sopenharmony_ci		do_count -= cnt;
403862306a36Sopenharmony_ci		st_bp->buffer_bytes -= cnt;
403962306a36Sopenharmony_ci		st_bp->read_pointer += cnt;
404062306a36Sopenharmony_ci		ubp += cnt;
404162306a36Sopenharmony_ci		offset = 0;
404262306a36Sopenharmony_ci	}
404362306a36Sopenharmony_ci	if (do_count) /* Should never happen */
404462306a36Sopenharmony_ci		return (-EIO);
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	return 0;
404762306a36Sopenharmony_ci}
404862306a36Sopenharmony_ci
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci/* Move data towards start of buffer */
405162306a36Sopenharmony_cistatic void move_buffer_data(struct st_buffer * st_bp, int offset)
405262306a36Sopenharmony_ci{
405362306a36Sopenharmony_ci	int src_seg, dst_seg, src_offset = 0, dst_offset;
405462306a36Sopenharmony_ci	int count, total;
405562306a36Sopenharmony_ci	int length = PAGE_SIZE << st_bp->reserved_page_order;
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	if (offset == 0)
405862306a36Sopenharmony_ci		return;
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci	total=st_bp->buffer_bytes - offset;
406162306a36Sopenharmony_ci	for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) {
406262306a36Sopenharmony_ci		src_offset = offset;
406362306a36Sopenharmony_ci		if (src_offset < length)
406462306a36Sopenharmony_ci			break;
406562306a36Sopenharmony_ci		offset -= length;
406662306a36Sopenharmony_ci	}
406762306a36Sopenharmony_ci
406862306a36Sopenharmony_ci	st_bp->buffer_bytes = st_bp->read_pointer = total;
406962306a36Sopenharmony_ci	for (dst_seg=dst_offset=0; total > 0; ) {
407062306a36Sopenharmony_ci		struct page *dpage = st_bp->reserved_pages[dst_seg];
407162306a36Sopenharmony_ci		struct page *spage = st_bp->reserved_pages[src_seg];
407262306a36Sopenharmony_ci
407362306a36Sopenharmony_ci		count = min(length - dst_offset, length - src_offset);
407462306a36Sopenharmony_ci		memmove(page_address(dpage) + dst_offset,
407562306a36Sopenharmony_ci			page_address(spage) + src_offset, count);
407662306a36Sopenharmony_ci		src_offset += count;
407762306a36Sopenharmony_ci		if (src_offset >= length) {
407862306a36Sopenharmony_ci			src_seg++;
407962306a36Sopenharmony_ci			src_offset = 0;
408062306a36Sopenharmony_ci		}
408162306a36Sopenharmony_ci		dst_offset += count;
408262306a36Sopenharmony_ci		if (dst_offset >= length) {
408362306a36Sopenharmony_ci			dst_seg++;
408462306a36Sopenharmony_ci			dst_offset = 0;
408562306a36Sopenharmony_ci		}
408662306a36Sopenharmony_ci		total -= count;
408762306a36Sopenharmony_ci	}
408862306a36Sopenharmony_ci}
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci/* Validate the options from command line or module parameters */
409162306a36Sopenharmony_cistatic void validate_options(void)
409262306a36Sopenharmony_ci{
409362306a36Sopenharmony_ci	if (buffer_kbs > 0)
409462306a36Sopenharmony_ci		st_fixed_buffer_size = buffer_kbs * ST_KILOBYTE;
409562306a36Sopenharmony_ci	if (max_sg_segs >= ST_FIRST_SG)
409662306a36Sopenharmony_ci		st_max_sg_segs = max_sg_segs;
409762306a36Sopenharmony_ci}
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci#ifndef MODULE
410062306a36Sopenharmony_ci/* Set the boot options. Syntax is defined in Documenation/scsi/st.txt.
410162306a36Sopenharmony_ci */
410262306a36Sopenharmony_cistatic int __init st_setup(char *str)
410362306a36Sopenharmony_ci{
410462306a36Sopenharmony_ci	int i, len, ints[5];
410562306a36Sopenharmony_ci	char *stp;
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_ci	stp = get_options(str, ARRAY_SIZE(ints), ints);
410862306a36Sopenharmony_ci
410962306a36Sopenharmony_ci	if (ints[0] > 0) {
411062306a36Sopenharmony_ci		for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
411162306a36Sopenharmony_ci			if (parms[i].val)
411262306a36Sopenharmony_ci				*parms[i].val = ints[i + 1];
411362306a36Sopenharmony_ci	} else {
411462306a36Sopenharmony_ci		while (stp != NULL) {
411562306a36Sopenharmony_ci			for (i = 0; i < ARRAY_SIZE(parms); i++) {
411662306a36Sopenharmony_ci				len = strlen(parms[i].name);
411762306a36Sopenharmony_ci				if (!strncmp(stp, parms[i].name, len) &&
411862306a36Sopenharmony_ci				    (*(stp + len) == ':' || *(stp + len) == '=')) {
411962306a36Sopenharmony_ci					if (parms[i].val)
412062306a36Sopenharmony_ci						*parms[i].val =
412162306a36Sopenharmony_ci							simple_strtoul(stp + len + 1, NULL, 0);
412262306a36Sopenharmony_ci					else
412362306a36Sopenharmony_ci						printk(KERN_WARNING "st: Obsolete parameter %s\n",
412462306a36Sopenharmony_ci						       parms[i].name);
412562306a36Sopenharmony_ci					break;
412662306a36Sopenharmony_ci				}
412762306a36Sopenharmony_ci			}
412862306a36Sopenharmony_ci			if (i >= ARRAY_SIZE(parms))
412962306a36Sopenharmony_ci				 printk(KERN_WARNING "st: invalid parameter in '%s'\n",
413062306a36Sopenharmony_ci					stp);
413162306a36Sopenharmony_ci			stp = strchr(stp, ',');
413262306a36Sopenharmony_ci			if (stp)
413362306a36Sopenharmony_ci				stp++;
413462306a36Sopenharmony_ci		}
413562306a36Sopenharmony_ci	}
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci	validate_options();
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	return 1;
414062306a36Sopenharmony_ci}
414162306a36Sopenharmony_ci
414262306a36Sopenharmony_ci__setup("st=", st_setup);
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci#endif
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_cistatic const struct file_operations st_fops =
414762306a36Sopenharmony_ci{
414862306a36Sopenharmony_ci	.owner =	THIS_MODULE,
414962306a36Sopenharmony_ci	.read =		st_read,
415062306a36Sopenharmony_ci	.write =	st_write,
415162306a36Sopenharmony_ci	.unlocked_ioctl = st_ioctl,
415262306a36Sopenharmony_ci#ifdef CONFIG_COMPAT
415362306a36Sopenharmony_ci	.compat_ioctl = st_compat_ioctl,
415462306a36Sopenharmony_ci#endif
415562306a36Sopenharmony_ci	.open =		st_open,
415662306a36Sopenharmony_ci	.flush =	st_flush,
415762306a36Sopenharmony_ci	.release =	st_release,
415862306a36Sopenharmony_ci	.llseek =	noop_llseek,
415962306a36Sopenharmony_ci};
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_cistatic int create_one_cdev(struct scsi_tape *tape, int mode, int rew)
416262306a36Sopenharmony_ci{
416362306a36Sopenharmony_ci	int i, error;
416462306a36Sopenharmony_ci	dev_t cdev_devno;
416562306a36Sopenharmony_ci	struct cdev *cdev;
416662306a36Sopenharmony_ci	struct device *dev;
416762306a36Sopenharmony_ci	struct st_modedef *STm = &(tape->modes[mode]);
416862306a36Sopenharmony_ci	char name[10];
416962306a36Sopenharmony_ci	int dev_num = tape->index;
417062306a36Sopenharmony_ci
417162306a36Sopenharmony_ci	cdev_devno = MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, rew));
417262306a36Sopenharmony_ci
417362306a36Sopenharmony_ci	cdev = cdev_alloc();
417462306a36Sopenharmony_ci	if (!cdev) {
417562306a36Sopenharmony_ci		pr_err("st%d: out of memory. Device not attached.\n", dev_num);
417662306a36Sopenharmony_ci		error = -ENOMEM;
417762306a36Sopenharmony_ci		goto out;
417862306a36Sopenharmony_ci	}
417962306a36Sopenharmony_ci	cdev->owner = THIS_MODULE;
418062306a36Sopenharmony_ci	cdev->ops = &st_fops;
418162306a36Sopenharmony_ci	STm->cdevs[rew] = cdev;
418262306a36Sopenharmony_ci
418362306a36Sopenharmony_ci	error = cdev_add(cdev, cdev_devno, 1);
418462306a36Sopenharmony_ci	if (error) {
418562306a36Sopenharmony_ci		pr_err("st%d: Can't add %s-rewind mode %d\n", dev_num,
418662306a36Sopenharmony_ci		       rew ? "non" : "auto", mode);
418762306a36Sopenharmony_ci		pr_err("st%d: Device not attached.\n", dev_num);
418862306a36Sopenharmony_ci		goto out_free;
418962306a36Sopenharmony_ci	}
419062306a36Sopenharmony_ci
419162306a36Sopenharmony_ci	i = mode << (4 - ST_NBR_MODE_BITS);
419262306a36Sopenharmony_ci	snprintf(name, 10, "%s%s%s", rew ? "n" : "",
419362306a36Sopenharmony_ci		 tape->name, st_formats[i]);
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci	dev = device_create(&st_sysfs_class, &tape->device->sdev_gendev,
419662306a36Sopenharmony_ci			    cdev_devno, &tape->modes[mode], "%s", name);
419762306a36Sopenharmony_ci	if (IS_ERR(dev)) {
419862306a36Sopenharmony_ci		pr_err("st%d: device_create failed\n", dev_num);
419962306a36Sopenharmony_ci		error = PTR_ERR(dev);
420062306a36Sopenharmony_ci		goto out_free;
420162306a36Sopenharmony_ci	}
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	STm->devs[rew] = dev;
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	return 0;
420662306a36Sopenharmony_ciout_free:
420762306a36Sopenharmony_ci	cdev_del(STm->cdevs[rew]);
420862306a36Sopenharmony_ciout:
420962306a36Sopenharmony_ci	STm->cdevs[rew] = NULL;
421062306a36Sopenharmony_ci	STm->devs[rew] = NULL;
421162306a36Sopenharmony_ci	return error;
421262306a36Sopenharmony_ci}
421362306a36Sopenharmony_ci
421462306a36Sopenharmony_cistatic int create_cdevs(struct scsi_tape *tape)
421562306a36Sopenharmony_ci{
421662306a36Sopenharmony_ci	int mode, error;
421762306a36Sopenharmony_ci	for (mode = 0; mode < ST_NBR_MODES; ++mode) {
421862306a36Sopenharmony_ci		error = create_one_cdev(tape, mode, 0);
421962306a36Sopenharmony_ci		if (error)
422062306a36Sopenharmony_ci			return error;
422162306a36Sopenharmony_ci		error = create_one_cdev(tape, mode, 1);
422262306a36Sopenharmony_ci		if (error)
422362306a36Sopenharmony_ci			return error;
422462306a36Sopenharmony_ci	}
422562306a36Sopenharmony_ci
422662306a36Sopenharmony_ci	return sysfs_create_link(&tape->device->sdev_gendev.kobj,
422762306a36Sopenharmony_ci				 &tape->modes[0].devs[0]->kobj, "tape");
422862306a36Sopenharmony_ci}
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_cistatic void remove_cdevs(struct scsi_tape *tape)
423162306a36Sopenharmony_ci{
423262306a36Sopenharmony_ci	int mode, rew;
423362306a36Sopenharmony_ci	sysfs_remove_link(&tape->device->sdev_gendev.kobj, "tape");
423462306a36Sopenharmony_ci	for (mode = 0; mode < ST_NBR_MODES; mode++) {
423562306a36Sopenharmony_ci		struct st_modedef *STm = &(tape->modes[mode]);
423662306a36Sopenharmony_ci		for (rew = 0; rew < 2; rew++) {
423762306a36Sopenharmony_ci			if (STm->cdevs[rew])
423862306a36Sopenharmony_ci				cdev_del(STm->cdevs[rew]);
423962306a36Sopenharmony_ci			if (STm->devs[rew])
424062306a36Sopenharmony_ci				device_unregister(STm->devs[rew]);
424162306a36Sopenharmony_ci		}
424262306a36Sopenharmony_ci	}
424362306a36Sopenharmony_ci}
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_cistatic int st_probe(struct device *dev)
424662306a36Sopenharmony_ci{
424762306a36Sopenharmony_ci	struct scsi_device *SDp = to_scsi_device(dev);
424862306a36Sopenharmony_ci	struct scsi_tape *tpnt = NULL;
424962306a36Sopenharmony_ci	struct st_modedef *STm;
425062306a36Sopenharmony_ci	struct st_partstat *STps;
425162306a36Sopenharmony_ci	struct st_buffer *buffer;
425262306a36Sopenharmony_ci	int i, error;
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci	if (SDp->type != TYPE_TAPE)
425562306a36Sopenharmony_ci		return -ENODEV;
425662306a36Sopenharmony_ci	if (st_incompatible(SDp)) {
425762306a36Sopenharmony_ci		sdev_printk(KERN_INFO, SDp,
425862306a36Sopenharmony_ci			    "OnStream tapes are no longer supported;\n");
425962306a36Sopenharmony_ci		sdev_printk(KERN_INFO, SDp,
426062306a36Sopenharmony_ci			    "please mail to linux-scsi@vger.kernel.org.\n");
426162306a36Sopenharmony_ci		return -ENODEV;
426262306a36Sopenharmony_ci	}
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	scsi_autopm_get_device(SDp);
426562306a36Sopenharmony_ci	i = queue_max_segments(SDp->request_queue);
426662306a36Sopenharmony_ci	if (st_max_sg_segs < i)
426762306a36Sopenharmony_ci		i = st_max_sg_segs;
426862306a36Sopenharmony_ci	buffer = new_tape_buffer(i);
426962306a36Sopenharmony_ci	if (buffer == NULL) {
427062306a36Sopenharmony_ci		sdev_printk(KERN_ERR, SDp,
427162306a36Sopenharmony_ci			    "st: Can't allocate new tape buffer. "
427262306a36Sopenharmony_ci			    "Device not attached.\n");
427362306a36Sopenharmony_ci		goto out;
427462306a36Sopenharmony_ci	}
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	tpnt = kzalloc(sizeof(struct scsi_tape), GFP_KERNEL);
427762306a36Sopenharmony_ci	if (tpnt == NULL) {
427862306a36Sopenharmony_ci		sdev_printk(KERN_ERR, SDp,
427962306a36Sopenharmony_ci			    "st: Can't allocate device descriptor.\n");
428062306a36Sopenharmony_ci		goto out_buffer_free;
428162306a36Sopenharmony_ci	}
428262306a36Sopenharmony_ci	kref_init(&tpnt->kref);
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ci	tpnt->device = SDp;
428562306a36Sopenharmony_ci	if (SDp->scsi_level <= 2)
428662306a36Sopenharmony_ci		tpnt->tape_type = MT_ISSCSI1;
428762306a36Sopenharmony_ci	else
428862306a36Sopenharmony_ci		tpnt->tape_type = MT_ISSCSI2;
428962306a36Sopenharmony_ci
429062306a36Sopenharmony_ci	tpnt->buffer = buffer;
429162306a36Sopenharmony_ci	tpnt->buffer->last_SRpnt = NULL;
429262306a36Sopenharmony_ci
429362306a36Sopenharmony_ci	tpnt->inited = 0;
429462306a36Sopenharmony_ci	tpnt->dirty = 0;
429562306a36Sopenharmony_ci	tpnt->in_use = 0;
429662306a36Sopenharmony_ci	tpnt->drv_buffer = 1;	/* Try buffering if no mode sense */
429762306a36Sopenharmony_ci	tpnt->use_pf = (SDp->scsi_level >= SCSI_2);
429862306a36Sopenharmony_ci	tpnt->density = 0;
429962306a36Sopenharmony_ci	tpnt->do_auto_lock = ST_AUTO_LOCK;
430062306a36Sopenharmony_ci	tpnt->can_bsr = (SDp->scsi_level > 2 ? 1 : ST_IN_FILE_POS); /* BSR mandatory in SCSI3 */
430162306a36Sopenharmony_ci	tpnt->can_partitions = 0;
430262306a36Sopenharmony_ci	tpnt->two_fm = ST_TWO_FM;
430362306a36Sopenharmony_ci	tpnt->fast_mteom = ST_FAST_MTEOM;
430462306a36Sopenharmony_ci	tpnt->scsi2_logical = ST_SCSI2LOGICAL;
430562306a36Sopenharmony_ci	tpnt->sili = ST_SILI;
430662306a36Sopenharmony_ci	tpnt->immediate = ST_NOWAIT;
430762306a36Sopenharmony_ci	tpnt->immediate_filemark = 0;
430862306a36Sopenharmony_ci	tpnt->default_drvbuffer = 0xff;		/* No forced buffering */
430962306a36Sopenharmony_ci	tpnt->partition = 0;
431062306a36Sopenharmony_ci	tpnt->new_partition = 0;
431162306a36Sopenharmony_ci	tpnt->nbr_partitions = 0;
431262306a36Sopenharmony_ci	blk_queue_rq_timeout(tpnt->device->request_queue, ST_TIMEOUT);
431362306a36Sopenharmony_ci	tpnt->long_timeout = ST_LONG_TIMEOUT;
431462306a36Sopenharmony_ci	tpnt->try_dio = try_direct_io;
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_ci	for (i = 0; i < ST_NBR_MODES; i++) {
431762306a36Sopenharmony_ci		STm = &(tpnt->modes[i]);
431862306a36Sopenharmony_ci		STm->defined = 0;
431962306a36Sopenharmony_ci		STm->sysv = ST_SYSV;
432062306a36Sopenharmony_ci		STm->defaults_for_writes = 0;
432162306a36Sopenharmony_ci		STm->do_async_writes = ST_ASYNC_WRITES;
432262306a36Sopenharmony_ci		STm->do_buffer_writes = ST_BUFFER_WRITES;
432362306a36Sopenharmony_ci		STm->do_read_ahead = ST_READ_AHEAD;
432462306a36Sopenharmony_ci		STm->default_compression = ST_DONT_TOUCH;
432562306a36Sopenharmony_ci		STm->default_blksize = (-1);	/* No forced size */
432662306a36Sopenharmony_ci		STm->default_density = (-1);	/* No forced density */
432762306a36Sopenharmony_ci		STm->tape = tpnt;
432862306a36Sopenharmony_ci	}
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
433162306a36Sopenharmony_ci		STps = &(tpnt->ps[i]);
433262306a36Sopenharmony_ci		STps->rw = ST_IDLE;
433362306a36Sopenharmony_ci		STps->eof = ST_NOEOF;
433462306a36Sopenharmony_ci		STps->at_sm = 0;
433562306a36Sopenharmony_ci		STps->last_block_valid = 0;
433662306a36Sopenharmony_ci		STps->drv_block = (-1);
433762306a36Sopenharmony_ci		STps->drv_file = (-1);
433862306a36Sopenharmony_ci	}
433962306a36Sopenharmony_ci
434062306a36Sopenharmony_ci	tpnt->current_mode = 0;
434162306a36Sopenharmony_ci	tpnt->modes[0].defined = 1;
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci	tpnt->density_changed = tpnt->compression_changed =
434462306a36Sopenharmony_ci	    tpnt->blksize_changed = 0;
434562306a36Sopenharmony_ci	mutex_init(&tpnt->lock);
434662306a36Sopenharmony_ci
434762306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
434862306a36Sopenharmony_ci	spin_lock(&st_index_lock);
434962306a36Sopenharmony_ci	error = idr_alloc(&st_index_idr, tpnt, 0, ST_MAX_TAPES + 1, GFP_NOWAIT);
435062306a36Sopenharmony_ci	spin_unlock(&st_index_lock);
435162306a36Sopenharmony_ci	idr_preload_end();
435262306a36Sopenharmony_ci	if (error < 0) {
435362306a36Sopenharmony_ci		pr_warn("st: idr allocation failed: %d\n", error);
435462306a36Sopenharmony_ci		goto out_free_tape;
435562306a36Sopenharmony_ci	}
435662306a36Sopenharmony_ci	tpnt->index = error;
435762306a36Sopenharmony_ci	sprintf(tpnt->name, "st%d", tpnt->index);
435862306a36Sopenharmony_ci	tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL);
435962306a36Sopenharmony_ci	if (tpnt->stats == NULL) {
436062306a36Sopenharmony_ci		sdev_printk(KERN_ERR, SDp,
436162306a36Sopenharmony_ci			    "st: Can't allocate statistics.\n");
436262306a36Sopenharmony_ci		goto out_idr_remove;
436362306a36Sopenharmony_ci	}
436462306a36Sopenharmony_ci
436562306a36Sopenharmony_ci	dev_set_drvdata(dev, tpnt);
436662306a36Sopenharmony_ci
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci	error = create_cdevs(tpnt);
436962306a36Sopenharmony_ci	if (error)
437062306a36Sopenharmony_ci		goto out_remove_devs;
437162306a36Sopenharmony_ci	scsi_autopm_put_device(SDp);
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci	sdev_printk(KERN_NOTICE, SDp,
437462306a36Sopenharmony_ci		    "Attached scsi tape %s\n", tpnt->name);
437562306a36Sopenharmony_ci	sdev_printk(KERN_INFO, SDp, "%s: try direct i/o: %s (alignment %d B)\n",
437662306a36Sopenharmony_ci		    tpnt->name, tpnt->try_dio ? "yes" : "no",
437762306a36Sopenharmony_ci		    queue_dma_alignment(SDp->request_queue) + 1);
437862306a36Sopenharmony_ci
437962306a36Sopenharmony_ci	return 0;
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ciout_remove_devs:
438262306a36Sopenharmony_ci	remove_cdevs(tpnt);
438362306a36Sopenharmony_ci	kfree(tpnt->stats);
438462306a36Sopenharmony_ciout_idr_remove:
438562306a36Sopenharmony_ci	spin_lock(&st_index_lock);
438662306a36Sopenharmony_ci	idr_remove(&st_index_idr, tpnt->index);
438762306a36Sopenharmony_ci	spin_unlock(&st_index_lock);
438862306a36Sopenharmony_ciout_free_tape:
438962306a36Sopenharmony_ci	kfree(tpnt);
439062306a36Sopenharmony_ciout_buffer_free:
439162306a36Sopenharmony_ci	kfree(buffer);
439262306a36Sopenharmony_ciout:
439362306a36Sopenharmony_ci	scsi_autopm_put_device(SDp);
439462306a36Sopenharmony_ci	return -ENODEV;
439562306a36Sopenharmony_ci};
439662306a36Sopenharmony_ci
439762306a36Sopenharmony_ci
439862306a36Sopenharmony_cistatic int st_remove(struct device *dev)
439962306a36Sopenharmony_ci{
440062306a36Sopenharmony_ci	struct scsi_tape *tpnt = dev_get_drvdata(dev);
440162306a36Sopenharmony_ci	int index = tpnt->index;
440262306a36Sopenharmony_ci
440362306a36Sopenharmony_ci	scsi_autopm_get_device(to_scsi_device(dev));
440462306a36Sopenharmony_ci	remove_cdevs(tpnt);
440562306a36Sopenharmony_ci
440662306a36Sopenharmony_ci	mutex_lock(&st_ref_mutex);
440762306a36Sopenharmony_ci	kref_put(&tpnt->kref, scsi_tape_release);
440862306a36Sopenharmony_ci	mutex_unlock(&st_ref_mutex);
440962306a36Sopenharmony_ci	spin_lock(&st_index_lock);
441062306a36Sopenharmony_ci	idr_remove(&st_index_idr, index);
441162306a36Sopenharmony_ci	spin_unlock(&st_index_lock);
441262306a36Sopenharmony_ci	return 0;
441362306a36Sopenharmony_ci}
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci/**
441662306a36Sopenharmony_ci *      scsi_tape_release - Called to free the Scsi_Tape structure
441762306a36Sopenharmony_ci *      @kref: pointer to embedded kref
441862306a36Sopenharmony_ci *
441962306a36Sopenharmony_ci *      st_ref_mutex must be held entering this routine.  Because it is
442062306a36Sopenharmony_ci *      called on last put, you should always use the scsi_tape_get()
442162306a36Sopenharmony_ci *      scsi_tape_put() helpers which manipulate the semaphore directly
442262306a36Sopenharmony_ci *      and never do a direct kref_put().
442362306a36Sopenharmony_ci **/
442462306a36Sopenharmony_cistatic void scsi_tape_release(struct kref *kref)
442562306a36Sopenharmony_ci{
442662306a36Sopenharmony_ci	struct scsi_tape *tpnt = to_scsi_tape(kref);
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci	tpnt->device = NULL;
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	if (tpnt->buffer) {
443162306a36Sopenharmony_ci		normalize_buffer(tpnt->buffer);
443262306a36Sopenharmony_ci		kfree(tpnt->buffer->reserved_pages);
443362306a36Sopenharmony_ci		kfree(tpnt->buffer);
443462306a36Sopenharmony_ci	}
443562306a36Sopenharmony_ci
443662306a36Sopenharmony_ci	kfree(tpnt->stats);
443762306a36Sopenharmony_ci	kfree(tpnt);
443862306a36Sopenharmony_ci	return;
443962306a36Sopenharmony_ci}
444062306a36Sopenharmony_ci
444162306a36Sopenharmony_cistatic struct class st_sysfs_class = {
444262306a36Sopenharmony_ci	.name = "scsi_tape",
444362306a36Sopenharmony_ci	.dev_groups = st_dev_groups,
444462306a36Sopenharmony_ci};
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_cistatic int __init init_st(void)
444762306a36Sopenharmony_ci{
444862306a36Sopenharmony_ci	int err;
444962306a36Sopenharmony_ci
445062306a36Sopenharmony_ci	validate_options();
445162306a36Sopenharmony_ci
445262306a36Sopenharmony_ci	printk(KERN_INFO "st: Version %s, fixed bufsize %d, s/g segs %d\n",
445362306a36Sopenharmony_ci		verstr, st_fixed_buffer_size, st_max_sg_segs);
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	debugging = (debug_flag > 0) ? debug_flag : NO_DEBUG;
445662306a36Sopenharmony_ci	if (debugging) {
445762306a36Sopenharmony_ci		printk(KERN_INFO "st: Debugging enabled debug_flag = %d\n",
445862306a36Sopenharmony_ci			debugging);
445962306a36Sopenharmony_ci	}
446062306a36Sopenharmony_ci
446162306a36Sopenharmony_ci	err = class_register(&st_sysfs_class);
446262306a36Sopenharmony_ci	if (err) {
446362306a36Sopenharmony_ci		pr_err("Unable register sysfs class for SCSI tapes\n");
446462306a36Sopenharmony_ci		return err;
446562306a36Sopenharmony_ci	}
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci	err = register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
446862306a36Sopenharmony_ci				     ST_MAX_TAPE_ENTRIES, "st");
446962306a36Sopenharmony_ci	if (err) {
447062306a36Sopenharmony_ci		printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
447162306a36Sopenharmony_ci		       SCSI_TAPE_MAJOR);
447262306a36Sopenharmony_ci		goto err_class;
447362306a36Sopenharmony_ci	}
447462306a36Sopenharmony_ci
447562306a36Sopenharmony_ci	err = scsi_register_driver(&st_template.gendrv);
447662306a36Sopenharmony_ci	if (err)
447762306a36Sopenharmony_ci		goto err_chrdev;
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci	return 0;
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_cierr_chrdev:
448262306a36Sopenharmony_ci	unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
448362306a36Sopenharmony_ci				 ST_MAX_TAPE_ENTRIES);
448462306a36Sopenharmony_cierr_class:
448562306a36Sopenharmony_ci	class_unregister(&st_sysfs_class);
448662306a36Sopenharmony_ci	return err;
448762306a36Sopenharmony_ci}
448862306a36Sopenharmony_ci
448962306a36Sopenharmony_cistatic void __exit exit_st(void)
449062306a36Sopenharmony_ci{
449162306a36Sopenharmony_ci	scsi_unregister_driver(&st_template.gendrv);
449262306a36Sopenharmony_ci	unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
449362306a36Sopenharmony_ci				 ST_MAX_TAPE_ENTRIES);
449462306a36Sopenharmony_ci	class_unregister(&st_sysfs_class);
449562306a36Sopenharmony_ci	idr_destroy(&st_index_idr);
449662306a36Sopenharmony_ci	printk(KERN_INFO "st: Unloaded.\n");
449762306a36Sopenharmony_ci}
449862306a36Sopenharmony_ci
449962306a36Sopenharmony_cimodule_init(init_st);
450062306a36Sopenharmony_cimodule_exit(exit_st);
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ci
450362306a36Sopenharmony_ci/* The sysfs driver interface. Read-only at the moment */
450462306a36Sopenharmony_cistatic ssize_t try_direct_io_show(struct device_driver *ddp, char *buf)
450562306a36Sopenharmony_ci{
450662306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", try_direct_io);
450762306a36Sopenharmony_ci}
450862306a36Sopenharmony_cistatic DRIVER_ATTR_RO(try_direct_io);
450962306a36Sopenharmony_ci
451062306a36Sopenharmony_cistatic ssize_t fixed_buffer_size_show(struct device_driver *ddp, char *buf)
451162306a36Sopenharmony_ci{
451262306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", st_fixed_buffer_size);
451362306a36Sopenharmony_ci}
451462306a36Sopenharmony_cistatic DRIVER_ATTR_RO(fixed_buffer_size);
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_cistatic ssize_t max_sg_segs_show(struct device_driver *ddp, char *buf)
451762306a36Sopenharmony_ci{
451862306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", st_max_sg_segs);
451962306a36Sopenharmony_ci}
452062306a36Sopenharmony_cistatic DRIVER_ATTR_RO(max_sg_segs);
452162306a36Sopenharmony_ci
452262306a36Sopenharmony_cistatic ssize_t version_show(struct device_driver *ddd, char *buf)
452362306a36Sopenharmony_ci{
452462306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "[%s]\n", verstr);
452562306a36Sopenharmony_ci}
452662306a36Sopenharmony_cistatic DRIVER_ATTR_RO(version);
452762306a36Sopenharmony_ci
452862306a36Sopenharmony_ci#if DEBUG
452962306a36Sopenharmony_cistatic ssize_t debug_flag_store(struct device_driver *ddp,
453062306a36Sopenharmony_ci	const char *buf, size_t count)
453162306a36Sopenharmony_ci{
453262306a36Sopenharmony_ci/* We only care what the first byte of the data is the rest is unused.
453362306a36Sopenharmony_ci * if it's a '1' we turn on debug and if it's a '0' we disable it. All
453462306a36Sopenharmony_ci * other values have -EINVAL returned if they are passed in.
453562306a36Sopenharmony_ci */
453662306a36Sopenharmony_ci	if (count > 0) {
453762306a36Sopenharmony_ci		if (buf[0] == '0') {
453862306a36Sopenharmony_ci			debugging = NO_DEBUG;
453962306a36Sopenharmony_ci			return count;
454062306a36Sopenharmony_ci		} else if (buf[0] == '1') {
454162306a36Sopenharmony_ci			debugging = 1;
454262306a36Sopenharmony_ci			return count;
454362306a36Sopenharmony_ci		}
454462306a36Sopenharmony_ci	}
454562306a36Sopenharmony_ci	return -EINVAL;
454662306a36Sopenharmony_ci}
454762306a36Sopenharmony_ci
454862306a36Sopenharmony_cistatic ssize_t debug_flag_show(struct device_driver *ddp, char *buf)
454962306a36Sopenharmony_ci{
455062306a36Sopenharmony_ci	return scnprintf(buf, PAGE_SIZE, "%d\n", debugging);
455162306a36Sopenharmony_ci}
455262306a36Sopenharmony_cistatic DRIVER_ATTR_RW(debug_flag);
455362306a36Sopenharmony_ci#endif
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_cistatic struct attribute *st_drv_attrs[] = {
455662306a36Sopenharmony_ci	&driver_attr_try_direct_io.attr,
455762306a36Sopenharmony_ci	&driver_attr_fixed_buffer_size.attr,
455862306a36Sopenharmony_ci	&driver_attr_max_sg_segs.attr,
455962306a36Sopenharmony_ci	&driver_attr_version.attr,
456062306a36Sopenharmony_ci#if DEBUG
456162306a36Sopenharmony_ci	&driver_attr_debug_flag.attr,
456262306a36Sopenharmony_ci#endif
456362306a36Sopenharmony_ci	NULL,
456462306a36Sopenharmony_ci};
456562306a36Sopenharmony_ciATTRIBUTE_GROUPS(st_drv);
456662306a36Sopenharmony_ci
456762306a36Sopenharmony_ci/* The sysfs simple class interface */
456862306a36Sopenharmony_cistatic ssize_t
456962306a36Sopenharmony_cidefined_show(struct device *dev, struct device_attribute *attr, char *buf)
457062306a36Sopenharmony_ci{
457162306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
457262306a36Sopenharmony_ci	ssize_t l = 0;
457362306a36Sopenharmony_ci
457462306a36Sopenharmony_ci	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->defined);
457562306a36Sopenharmony_ci	return l;
457662306a36Sopenharmony_ci}
457762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(defined);
457862306a36Sopenharmony_ci
457962306a36Sopenharmony_cistatic ssize_t
458062306a36Sopenharmony_cidefault_blksize_show(struct device *dev, struct device_attribute *attr,
458162306a36Sopenharmony_ci		     char *buf)
458262306a36Sopenharmony_ci{
458362306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
458462306a36Sopenharmony_ci	ssize_t l = 0;
458562306a36Sopenharmony_ci
458662306a36Sopenharmony_ci	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_blksize);
458762306a36Sopenharmony_ci	return l;
458862306a36Sopenharmony_ci}
458962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(default_blksize);
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_cistatic ssize_t
459262306a36Sopenharmony_cidefault_density_show(struct device *dev, struct device_attribute *attr,
459362306a36Sopenharmony_ci		     char *buf)
459462306a36Sopenharmony_ci{
459562306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
459662306a36Sopenharmony_ci	ssize_t l = 0;
459762306a36Sopenharmony_ci	char *fmt;
459862306a36Sopenharmony_ci
459962306a36Sopenharmony_ci	fmt = STm->default_density >= 0 ? "0x%02x\n" : "%d\n";
460062306a36Sopenharmony_ci	l = snprintf(buf, PAGE_SIZE, fmt, STm->default_density);
460162306a36Sopenharmony_ci	return l;
460262306a36Sopenharmony_ci}
460362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(default_density);
460462306a36Sopenharmony_ci
460562306a36Sopenharmony_cistatic ssize_t
460662306a36Sopenharmony_cidefault_compression_show(struct device *dev, struct device_attribute *attr,
460762306a36Sopenharmony_ci			 char *buf)
460862306a36Sopenharmony_ci{
460962306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
461062306a36Sopenharmony_ci	ssize_t l = 0;
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_compression - 1);
461362306a36Sopenharmony_ci	return l;
461462306a36Sopenharmony_ci}
461562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(default_compression);
461662306a36Sopenharmony_ci
461762306a36Sopenharmony_cistatic ssize_t
461862306a36Sopenharmony_cioptions_show(struct device *dev, struct device_attribute *attr, char *buf)
461962306a36Sopenharmony_ci{
462062306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
462162306a36Sopenharmony_ci	struct scsi_tape *STp = STm->tape;
462262306a36Sopenharmony_ci	int options;
462362306a36Sopenharmony_ci	ssize_t l = 0;
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_ci	options = STm->do_buffer_writes ? MT_ST_BUFFER_WRITES : 0;
462662306a36Sopenharmony_ci	options |= STm->do_async_writes ? MT_ST_ASYNC_WRITES : 0;
462762306a36Sopenharmony_ci	options |= STm->do_read_ahead ? MT_ST_READ_AHEAD : 0;
462862306a36Sopenharmony_ci	DEB( options |= debugging ? MT_ST_DEBUGGING : 0 );
462962306a36Sopenharmony_ci	options |= STp->two_fm ? MT_ST_TWO_FM : 0;
463062306a36Sopenharmony_ci	options |= STp->fast_mteom ? MT_ST_FAST_MTEOM : 0;
463162306a36Sopenharmony_ci	options |= STm->defaults_for_writes ? MT_ST_DEF_WRITES : 0;
463262306a36Sopenharmony_ci	options |= STp->can_bsr ? MT_ST_CAN_BSR : 0;
463362306a36Sopenharmony_ci	options |= STp->omit_blklims ? MT_ST_NO_BLKLIMS : 0;
463462306a36Sopenharmony_ci	options |= STp->can_partitions ? MT_ST_CAN_PARTITIONS : 0;
463562306a36Sopenharmony_ci	options |= STp->scsi2_logical ? MT_ST_SCSI2LOGICAL : 0;
463662306a36Sopenharmony_ci	options |= STm->sysv ? MT_ST_SYSV : 0;
463762306a36Sopenharmony_ci	options |= STp->immediate ? MT_ST_NOWAIT : 0;
463862306a36Sopenharmony_ci	options |= STp->immediate_filemark ? MT_ST_NOWAIT_EOF : 0;
463962306a36Sopenharmony_ci	options |= STp->sili ? MT_ST_SILI : 0;
464062306a36Sopenharmony_ci
464162306a36Sopenharmony_ci	l = snprintf(buf, PAGE_SIZE, "0x%08x\n", options);
464262306a36Sopenharmony_ci	return l;
464362306a36Sopenharmony_ci}
464462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(options);
464562306a36Sopenharmony_ci
464662306a36Sopenharmony_ci/* Support for tape stats */
464762306a36Sopenharmony_ci
464862306a36Sopenharmony_ci/**
464962306a36Sopenharmony_ci * read_cnt_show - return read count - count of reads made from tape drive
465062306a36Sopenharmony_ci * @dev: struct device
465162306a36Sopenharmony_ci * @attr: attribute structure
465262306a36Sopenharmony_ci * @buf: buffer to return formatted data in
465362306a36Sopenharmony_ci */
465462306a36Sopenharmony_cistatic ssize_t read_cnt_show(struct device *dev,
465562306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
465662306a36Sopenharmony_ci{
465762306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci	return sprintf(buf, "%lld",
466062306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->read_cnt));
466162306a36Sopenharmony_ci}
466262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(read_cnt);
466362306a36Sopenharmony_ci
466462306a36Sopenharmony_ci/**
466562306a36Sopenharmony_ci * read_byte_cnt_show - return read byte count - tape drives
466662306a36Sopenharmony_ci * may use blocks less than 512 bytes this gives the raw byte count of
466762306a36Sopenharmony_ci * of data read from the tape drive.
466862306a36Sopenharmony_ci * @dev: struct device
466962306a36Sopenharmony_ci * @attr: attribute structure
467062306a36Sopenharmony_ci * @buf: buffer to return formatted data in
467162306a36Sopenharmony_ci */
467262306a36Sopenharmony_cistatic ssize_t read_byte_cnt_show(struct device *dev,
467362306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
467462306a36Sopenharmony_ci{
467562306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_ci	return sprintf(buf, "%lld",
467862306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->read_byte_cnt));
467962306a36Sopenharmony_ci}
468062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(read_byte_cnt);
468162306a36Sopenharmony_ci
468262306a36Sopenharmony_ci/**
468362306a36Sopenharmony_ci * read_ns_show - return read ns - overall time spent waiting on reads in ns.
468462306a36Sopenharmony_ci * @dev: struct device
468562306a36Sopenharmony_ci * @attr: attribute structure
468662306a36Sopenharmony_ci * @buf: buffer to return formatted data in
468762306a36Sopenharmony_ci */
468862306a36Sopenharmony_cistatic ssize_t read_ns_show(struct device *dev,
468962306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
469062306a36Sopenharmony_ci{
469162306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
469262306a36Sopenharmony_ci
469362306a36Sopenharmony_ci	return sprintf(buf, "%lld",
469462306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->tot_read_time));
469562306a36Sopenharmony_ci}
469662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(read_ns);
469762306a36Sopenharmony_ci
469862306a36Sopenharmony_ci/**
469962306a36Sopenharmony_ci * write_cnt_show - write count - number of user calls
470062306a36Sopenharmony_ci * to write(2) that have written data to tape.
470162306a36Sopenharmony_ci * @dev: struct device
470262306a36Sopenharmony_ci * @attr: attribute structure
470362306a36Sopenharmony_ci * @buf: buffer to return formatted data in
470462306a36Sopenharmony_ci */
470562306a36Sopenharmony_cistatic ssize_t write_cnt_show(struct device *dev,
470662306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
470762306a36Sopenharmony_ci{
470862306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci	return sprintf(buf, "%lld",
471162306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->write_cnt));
471262306a36Sopenharmony_ci}
471362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(write_cnt);
471462306a36Sopenharmony_ci
471562306a36Sopenharmony_ci/**
471662306a36Sopenharmony_ci * write_byte_cnt_show - write byte count - raw count of
471762306a36Sopenharmony_ci * bytes written to tape.
471862306a36Sopenharmony_ci * @dev: struct device
471962306a36Sopenharmony_ci * @attr: attribute structure
472062306a36Sopenharmony_ci * @buf: buffer to return formatted data in
472162306a36Sopenharmony_ci */
472262306a36Sopenharmony_cistatic ssize_t write_byte_cnt_show(struct device *dev,
472362306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
472462306a36Sopenharmony_ci{
472562306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_ci	return sprintf(buf, "%lld",
472862306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->write_byte_cnt));
472962306a36Sopenharmony_ci}
473062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(write_byte_cnt);
473162306a36Sopenharmony_ci
473262306a36Sopenharmony_ci/**
473362306a36Sopenharmony_ci * write_ns_show - write ns - number of nanoseconds waiting on write
473462306a36Sopenharmony_ci * requests to complete.
473562306a36Sopenharmony_ci * @dev: struct device
473662306a36Sopenharmony_ci * @attr: attribute structure
473762306a36Sopenharmony_ci * @buf: buffer to return formatted data in
473862306a36Sopenharmony_ci */
473962306a36Sopenharmony_cistatic ssize_t write_ns_show(struct device *dev,
474062306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
474162306a36Sopenharmony_ci{
474262306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
474362306a36Sopenharmony_ci
474462306a36Sopenharmony_ci	return sprintf(buf, "%lld",
474562306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->tot_write_time));
474662306a36Sopenharmony_ci}
474762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(write_ns);
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_ci/**
475062306a36Sopenharmony_ci * in_flight_show - number of I/Os currently in flight -
475162306a36Sopenharmony_ci * in most cases this will be either 0 or 1. It may be higher if someone
475262306a36Sopenharmony_ci * has also issued other SCSI commands such as via an ioctl.
475362306a36Sopenharmony_ci * @dev: struct device
475462306a36Sopenharmony_ci * @attr: attribute structure
475562306a36Sopenharmony_ci * @buf: buffer to return formatted data in
475662306a36Sopenharmony_ci */
475762306a36Sopenharmony_cistatic ssize_t in_flight_show(struct device *dev,
475862306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
475962306a36Sopenharmony_ci{
476062306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	return sprintf(buf, "%lld",
476362306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->in_flight));
476462306a36Sopenharmony_ci}
476562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(in_flight);
476662306a36Sopenharmony_ci
476762306a36Sopenharmony_ci/**
476862306a36Sopenharmony_ci * io_ns_show - io wait ns - this is the number of ns spent
476962306a36Sopenharmony_ci * waiting on all I/O to complete. This includes tape movement commands
477062306a36Sopenharmony_ci * such as rewinding, seeking to end of file or tape, it also includes
477162306a36Sopenharmony_ci * read and write. To determine the time spent on tape movement
477262306a36Sopenharmony_ci * subtract the read and write ns from this value.
477362306a36Sopenharmony_ci * @dev: struct device
477462306a36Sopenharmony_ci * @attr: attribute structure
477562306a36Sopenharmony_ci * @buf: buffer to return formatted data in
477662306a36Sopenharmony_ci */
477762306a36Sopenharmony_cistatic ssize_t io_ns_show(struct device *dev,
477862306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
477962306a36Sopenharmony_ci{
478062306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
478162306a36Sopenharmony_ci
478262306a36Sopenharmony_ci	return sprintf(buf, "%lld",
478362306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->tot_io_time));
478462306a36Sopenharmony_ci}
478562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(io_ns);
478662306a36Sopenharmony_ci
478762306a36Sopenharmony_ci/**
478862306a36Sopenharmony_ci * other_cnt_show - other io count - this is the number of
478962306a36Sopenharmony_ci * I/O requests other than read and write requests.
479062306a36Sopenharmony_ci * Typically these are tape movement requests but will include driver
479162306a36Sopenharmony_ci * tape movement. This includes only requests issued by the st driver.
479262306a36Sopenharmony_ci * @dev: struct device
479362306a36Sopenharmony_ci * @attr: attribute structure
479462306a36Sopenharmony_ci * @buf: buffer to return formatted data in
479562306a36Sopenharmony_ci */
479662306a36Sopenharmony_cistatic ssize_t other_cnt_show(struct device *dev,
479762306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
479862306a36Sopenharmony_ci{
479962306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
480062306a36Sopenharmony_ci
480162306a36Sopenharmony_ci	return sprintf(buf, "%lld",
480262306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->other_cnt));
480362306a36Sopenharmony_ci}
480462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(other_cnt);
480562306a36Sopenharmony_ci
480662306a36Sopenharmony_ci/**
480762306a36Sopenharmony_ci * resid_cnt_show - A count of the number of times we get a residual
480862306a36Sopenharmony_ci * count - this should indicate someone issuing reads larger than the
480962306a36Sopenharmony_ci * block size on tape.
481062306a36Sopenharmony_ci * @dev: struct device
481162306a36Sopenharmony_ci * @attr: attribute structure
481262306a36Sopenharmony_ci * @buf: buffer to return formatted data in
481362306a36Sopenharmony_ci */
481462306a36Sopenharmony_cistatic ssize_t resid_cnt_show(struct device *dev,
481562306a36Sopenharmony_ci	struct device_attribute *attr, char *buf)
481662306a36Sopenharmony_ci{
481762306a36Sopenharmony_ci	struct st_modedef *STm = dev_get_drvdata(dev);
481862306a36Sopenharmony_ci
481962306a36Sopenharmony_ci	return sprintf(buf, "%lld",
482062306a36Sopenharmony_ci		       (long long)atomic64_read(&STm->tape->stats->resid_cnt));
482162306a36Sopenharmony_ci}
482262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(resid_cnt);
482362306a36Sopenharmony_ci
482462306a36Sopenharmony_cistatic struct attribute *st_dev_attrs[] = {
482562306a36Sopenharmony_ci	&dev_attr_defined.attr,
482662306a36Sopenharmony_ci	&dev_attr_default_blksize.attr,
482762306a36Sopenharmony_ci	&dev_attr_default_density.attr,
482862306a36Sopenharmony_ci	&dev_attr_default_compression.attr,
482962306a36Sopenharmony_ci	&dev_attr_options.attr,
483062306a36Sopenharmony_ci	NULL,
483162306a36Sopenharmony_ci};
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_cistatic struct attribute *st_stats_attrs[] = {
483462306a36Sopenharmony_ci	&dev_attr_read_cnt.attr,
483562306a36Sopenharmony_ci	&dev_attr_read_byte_cnt.attr,
483662306a36Sopenharmony_ci	&dev_attr_read_ns.attr,
483762306a36Sopenharmony_ci	&dev_attr_write_cnt.attr,
483862306a36Sopenharmony_ci	&dev_attr_write_byte_cnt.attr,
483962306a36Sopenharmony_ci	&dev_attr_write_ns.attr,
484062306a36Sopenharmony_ci	&dev_attr_in_flight.attr,
484162306a36Sopenharmony_ci	&dev_attr_io_ns.attr,
484262306a36Sopenharmony_ci	&dev_attr_other_cnt.attr,
484362306a36Sopenharmony_ci	&dev_attr_resid_cnt.attr,
484462306a36Sopenharmony_ci	NULL,
484562306a36Sopenharmony_ci};
484662306a36Sopenharmony_ci
484762306a36Sopenharmony_cistatic struct attribute_group stats_group = {
484862306a36Sopenharmony_ci	.name = "stats",
484962306a36Sopenharmony_ci	.attrs = st_stats_attrs,
485062306a36Sopenharmony_ci};
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_cistatic struct attribute_group st_group = {
485362306a36Sopenharmony_ci	.attrs = st_dev_attrs,
485462306a36Sopenharmony_ci};
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_cistatic const struct attribute_group *st_dev_groups[] = {
485762306a36Sopenharmony_ci	&st_group,
485862306a36Sopenharmony_ci	&stats_group,
485962306a36Sopenharmony_ci	NULL,
486062306a36Sopenharmony_ci};
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci/* The following functions may be useful for a larger audience. */
486362306a36Sopenharmony_cistatic int sgl_map_user_pages(struct st_buffer *STbp,
486462306a36Sopenharmony_ci			      const unsigned int max_pages, unsigned long uaddr,
486562306a36Sopenharmony_ci			      size_t count, int rw)
486662306a36Sopenharmony_ci{
486762306a36Sopenharmony_ci	unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
486862306a36Sopenharmony_ci	unsigned long start = uaddr >> PAGE_SHIFT;
486962306a36Sopenharmony_ci	const int nr_pages = end - start;
487062306a36Sopenharmony_ci	int res, i;
487162306a36Sopenharmony_ci	struct page **pages;
487262306a36Sopenharmony_ci	struct rq_map_data *mdata = &STbp->map_data;
487362306a36Sopenharmony_ci
487462306a36Sopenharmony_ci	/* User attempted Overflow! */
487562306a36Sopenharmony_ci	if ((uaddr + count) < uaddr)
487662306a36Sopenharmony_ci		return -EINVAL;
487762306a36Sopenharmony_ci
487862306a36Sopenharmony_ci	/* Too big */
487962306a36Sopenharmony_ci        if (nr_pages > max_pages)
488062306a36Sopenharmony_ci		return -ENOMEM;
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci	/* Hmm? */
488362306a36Sopenharmony_ci	if (count == 0)
488462306a36Sopenharmony_ci		return 0;
488562306a36Sopenharmony_ci
488662306a36Sopenharmony_ci	pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL);
488762306a36Sopenharmony_ci	if (pages == NULL)
488862306a36Sopenharmony_ci		return -ENOMEM;
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci        /* Try to fault in all of the necessary pages */
489162306a36Sopenharmony_ci        /* rw==READ means read from drive, write into memory area */
489262306a36Sopenharmony_ci	res = pin_user_pages_fast(uaddr, nr_pages, rw == READ ? FOLL_WRITE : 0,
489362306a36Sopenharmony_ci				  pages);
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_ci	/* Errors and no page mapped should return here */
489662306a36Sopenharmony_ci	if (res < nr_pages)
489762306a36Sopenharmony_ci		goto out_unmap;
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_ci        for (i=0; i < nr_pages; i++) {
490062306a36Sopenharmony_ci                /* FIXME: flush superflous for rw==READ,
490162306a36Sopenharmony_ci                 * probably wrong function for rw==WRITE
490262306a36Sopenharmony_ci                 */
490362306a36Sopenharmony_ci		flush_dcache_page(pages[i]);
490462306a36Sopenharmony_ci        }
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci	mdata->offset = uaddr & ~PAGE_MASK;
490762306a36Sopenharmony_ci	STbp->mapped_pages = pages;
490862306a36Sopenharmony_ci
490962306a36Sopenharmony_ci	return nr_pages;
491062306a36Sopenharmony_ci out_unmap:
491162306a36Sopenharmony_ci	if (res > 0) {
491262306a36Sopenharmony_ci		unpin_user_pages(pages, res);
491362306a36Sopenharmony_ci		res = 0;
491462306a36Sopenharmony_ci	}
491562306a36Sopenharmony_ci	kfree(pages);
491662306a36Sopenharmony_ci	return res;
491762306a36Sopenharmony_ci}
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci
492062306a36Sopenharmony_ci/* And unmap them... */
492162306a36Sopenharmony_cistatic int sgl_unmap_user_pages(struct st_buffer *STbp,
492262306a36Sopenharmony_ci				const unsigned int nr_pages, int dirtied)
492362306a36Sopenharmony_ci{
492462306a36Sopenharmony_ci	/* FIXME: cache flush missing for rw==READ */
492562306a36Sopenharmony_ci	unpin_user_pages_dirty_lock(STbp->mapped_pages, nr_pages, dirtied);
492662306a36Sopenharmony_ci
492762306a36Sopenharmony_ci	kfree(STbp->mapped_pages);
492862306a36Sopenharmony_ci	STbp->mapped_pages = NULL;
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	return 0;
493162306a36Sopenharmony_ci}
4932