xref: /openbmc/linux/drivers/scsi/st.c (revision 9604eea5)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds    SCSI Tape Driver for Linux version 1.1 and newer. See the accompanying
4bf65c846SMauro Carvalho Chehab    file Documentation/scsi/st.rst for more information.
51da177e4SLinus Torvalds 
61da177e4SLinus Torvalds    History:
71da177e4SLinus Torvalds    Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
81da177e4SLinus Torvalds    Contribution and ideas from several people including (in alphabetical
91da177e4SLinus Torvalds    order) Klaus Ehrenfried, Eugene Exarevsky, Eric Lee Green, Wolfgang Denk,
101da177e4SLinus Torvalds    Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
111da177e4SLinus Torvalds    Michael Schaefer, J"org Weule, and Eric Youngdale.
121da177e4SLinus Torvalds 
138038e645SKai Makisara    Copyright 1992 - 2016 Kai Makisara
141da177e4SLinus Torvalds    email Kai.Makisara@kolumbus.fi
151da177e4SLinus Torvalds 
161da177e4SLinus Torvalds    Some small formal changes - aeb, 950809
171da177e4SLinus Torvalds 
181da177e4SLinus Torvalds    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
191da177e4SLinus Torvalds  */
201da177e4SLinus Torvalds 
218038e645SKai Makisara static const char *verstr = "20160209";
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds #include <linux/module.h>
241da177e4SLinus Torvalds 
251207045dSArnd Bergmann #include <linux/compat.h>
261da177e4SLinus Torvalds #include <linux/fs.h>
271da177e4SLinus Torvalds #include <linux/kernel.h>
28174cd4b1SIngo Molnar #include <linux/sched/signal.h>
291da177e4SLinus Torvalds #include <linux/mm.h>
301da177e4SLinus Torvalds #include <linux/init.h>
311da177e4SLinus Torvalds #include <linux/string.h>
325a0e3ad6STejun Heo #include <linux/slab.h>
331da177e4SLinus Torvalds #include <linux/errno.h>
341da177e4SLinus Torvalds #include <linux/mtio.h>
35b81e0c23SChristoph Hellwig #include <linux/major.h>
3616c4b3e2SKai Makisara  #include <linux/cdrom.h>
371da177e4SLinus Torvalds #include <linux/ioctl.h>
381da177e4SLinus Torvalds #include <linux/fcntl.h>
391da177e4SLinus Torvalds #include <linux/spinlock.h>
401da177e4SLinus Torvalds #include <linux/blkdev.h>
411da177e4SLinus Torvalds #include <linux/moduleparam.h>
421da177e4SLinus Torvalds #include <linux/cdev.h>
436c648d95SJeff Mahoney #include <linux/idr.h>
441da177e4SLinus Torvalds #include <linux/delay.h>
450b950672SArjan van de Ven #include <linux/mutex.h>
461da177e4SLinus Torvalds 
477c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
481da177e4SLinus Torvalds #include <asm/dma.h>
4935b703dbSBart Van Assche #include <asm/unaligned.h>
501da177e4SLinus Torvalds 
511da177e4SLinus Torvalds #include <scsi/scsi.h>
521da177e4SLinus Torvalds #include <scsi/scsi_dbg.h>
531da177e4SLinus Torvalds #include <scsi/scsi_device.h>
541da177e4SLinus Torvalds #include <scsi/scsi_driver.h>
551da177e4SLinus Torvalds #include <scsi/scsi_eh.h>
561da177e4SLinus Torvalds #include <scsi/scsi_host.h>
571da177e4SLinus Torvalds #include <scsi/scsi_ioctl.h>
5816c4b3e2SKai Makisara  #include <scsi/sg.h>
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 
611da177e4SLinus Torvalds /* The driver prints some debugging information on the console if DEBUG
621da177e4SLinus Torvalds    is defined and non-zero. */
632bec708aSLaurence Oberman #define DEBUG 1
642bec708aSLaurence Oberman #define NO_DEBUG 0
651da177e4SLinus Torvalds 
66b30d8bcaSHannes Reinecke #define ST_DEB_MSG  KERN_NOTICE
671da177e4SLinus Torvalds #if DEBUG
681da177e4SLinus Torvalds /* The message level for the debug messages is currently set to KERN_NOTICE
691da177e4SLinus Torvalds    so that people can easily see the messages. Later when the debugging messages
701da177e4SLinus Torvalds    in the drivers are more widely classified, this may be changed to KERN_DEBUG. */
711da177e4SLinus Torvalds #define DEB(a) a
721da177e4SLinus Torvalds #define DEBC(a) if (debugging) { a ; }
731da177e4SLinus Torvalds #else
741da177e4SLinus Torvalds #define DEB(a)
751da177e4SLinus Torvalds #define DEBC(a)
761da177e4SLinus Torvalds #endif
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds #define ST_KILOBYTE 1024
791da177e4SLinus Torvalds 
801da177e4SLinus Torvalds #include "st_options.h"
811da177e4SLinus Torvalds #include "st.h"
821da177e4SLinus Torvalds 
831da177e4SLinus Torvalds static int buffer_kbs;
841da177e4SLinus Torvalds static int max_sg_segs;
851da177e4SLinus Torvalds static int try_direct_io = TRY_DIRECT_IO;
861da177e4SLinus Torvalds static int try_rdio = 1;
871da177e4SLinus Torvalds static int try_wdio = 1;
882bec708aSLaurence Oberman static int debug_flag;
891da177e4SLinus Torvalds 
90af23782bSJeff Mahoney static struct class st_sysfs_class;
91c69c6be5SGreg Kroah-Hartman static const struct attribute_group *st_dev_groups[];
92442d7562SSeymour, Shane M static const struct attribute_group *st_drv_groups[];
931da177e4SLinus Torvalds 
941da177e4SLinus Torvalds MODULE_AUTHOR("Kai Makisara");
95f018fa55SRene Herman MODULE_DESCRIPTION("SCSI tape (st) driver");
961da177e4SLinus Torvalds MODULE_LICENSE("GPL");
97f018fa55SRene Herman MODULE_ALIAS_CHARDEV_MAJOR(SCSI_TAPE_MAJOR);
98d7b8bcb0SMichael Tokarev MODULE_ALIAS_SCSI_DEVICE(TYPE_TAPE);
991da177e4SLinus Torvalds 
1001da177e4SLinus Torvalds /* Set 'perm' (4th argument) to 0 to disable module_param's definition
1011da177e4SLinus Torvalds  * of sysfs parameters (which module_param doesn't yet support).
1021da177e4SLinus Torvalds  * Sysfs parameters defined explicitly later.
1031da177e4SLinus Torvalds  */
1041da177e4SLinus Torvalds module_param_named(buffer_kbs, buffer_kbs, int, 0);
1051da177e4SLinus Torvalds MODULE_PARM_DESC(buffer_kbs, "Default driver buffer size for fixed block mode (KB; 32)");
1061da177e4SLinus Torvalds module_param_named(max_sg_segs, max_sg_segs, int, 0);
1071da177e4SLinus Torvalds MODULE_PARM_DESC(max_sg_segs, "Maximum number of scatter/gather segments to use (256)");
1081da177e4SLinus Torvalds module_param_named(try_direct_io, try_direct_io, int, 0);
1091da177e4SLinus Torvalds MODULE_PARM_DESC(try_direct_io, "Try direct I/O between user buffer and tape drive (1)");
1102bec708aSLaurence Oberman module_param_named(debug_flag, debug_flag, int, 0);
1112bec708aSLaurence Oberman MODULE_PARM_DESC(debug_flag, "Enable DEBUG, same as setting debugging=1");
1122bec708aSLaurence Oberman 
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds /* Extra parameters for testing */
1151da177e4SLinus Torvalds module_param_named(try_rdio, try_rdio, int, 0);
1161da177e4SLinus Torvalds MODULE_PARM_DESC(try_rdio, "Try direct read i/o when possible");
1171da177e4SLinus Torvalds module_param_named(try_wdio, try_wdio, int, 0);
1181da177e4SLinus Torvalds MODULE_PARM_DESC(try_wdio, "Try direct write i/o when possible");
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds #ifndef MODULE
1211da177e4SLinus Torvalds static int write_threshold_kbs;  /* retained for compatibility */
1221da177e4SLinus Torvalds static struct st_dev_parm {
1231da177e4SLinus Torvalds 	char *name;
1241da177e4SLinus Torvalds 	int *val;
1251da177e4SLinus Torvalds } parms[] __initdata = {
1261da177e4SLinus Torvalds 	{
1271da177e4SLinus Torvalds 		"buffer_kbs", &buffer_kbs
1281da177e4SLinus Torvalds 	},
1291da177e4SLinus Torvalds 	{       /* Retained for compatibility with 2.4 */
1301da177e4SLinus Torvalds 		"write_threshold_kbs", &write_threshold_kbs
1311da177e4SLinus Torvalds 	},
1321da177e4SLinus Torvalds 	{
1331da177e4SLinus Torvalds 		"max_sg_segs", NULL
1341da177e4SLinus Torvalds 	},
1351da177e4SLinus Torvalds 	{
1361da177e4SLinus Torvalds 		"try_direct_io", &try_direct_io
1372bec708aSLaurence Oberman 	},
1382bec708aSLaurence Oberman 	{
1392bec708aSLaurence Oberman 		"debug_flag", &debug_flag
1401da177e4SLinus Torvalds 	}
1411da177e4SLinus Torvalds };
1421da177e4SLinus Torvalds #endif
1431da177e4SLinus Torvalds 
1441da177e4SLinus Torvalds /* Restrict the number of modes so that names for all are assigned */
1451da177e4SLinus Torvalds #if ST_NBR_MODES > 16
1461da177e4SLinus Torvalds #error "Maximum number of modes is 16"
1471da177e4SLinus Torvalds #endif
1481da177e4SLinus Torvalds /* Bit reversed order to get same names for same minors with all
1491da177e4SLinus Torvalds    mode counts */
1500ad78200SArjan van de Ven static const char *st_formats[] = {
1511da177e4SLinus Torvalds 	"",  "r", "k", "s", "l", "t", "o", "u",
1521da177e4SLinus Torvalds 	"m", "v", "p", "x", "a", "y", "q", "z"};
1531da177e4SLinus Torvalds 
1541da177e4SLinus Torvalds /* The default definitions have been moved to st_options.h */
1551da177e4SLinus Torvalds 
1561da177e4SLinus Torvalds #define ST_FIXED_BUFFER_SIZE (ST_FIXED_BUFFER_BLOCKS * ST_KILOBYTE)
1571da177e4SLinus Torvalds 
1581da177e4SLinus Torvalds /* The buffer size should fit into the 24 bits for length in the
1591da177e4SLinus Torvalds    6-byte SCSI read and write commands. */
1601da177e4SLinus Torvalds #if ST_FIXED_BUFFER_SIZE >= (2 << 24 - 1)
1611da177e4SLinus Torvalds #error "Buffer size should not exceed (2 << 24 - 1) bytes!"
1621da177e4SLinus Torvalds #endif
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds static int debugging = DEBUG;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds #define MAX_RETRIES 0
1671da177e4SLinus Torvalds #define MAX_WRITE_RETRIES 0
1681da177e4SLinus Torvalds #define MAX_READY_RETRIES 0
1691da177e4SLinus Torvalds #define NO_TAPE  NOT_READY
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds #define ST_TIMEOUT (900 * HZ)
1721da177e4SLinus Torvalds #define ST_LONG_TIMEOUT (14000 * HZ)
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds /* Remove mode bits and auto-rewind bit (7) */
1751da177e4SLinus Torvalds #define TAPE_NR(x) ( ((iminor(x) & ~255) >> (ST_NBR_MODE_BITS + 1)) | \
1766f46f718SIustin Pop 	(iminor(x) & ((1 << ST_MODE_SHIFT)-1)))
1771da177e4SLinus Torvalds #define TAPE_MODE(x) ((iminor(x) & ST_MODE_MASK) >> ST_MODE_SHIFT)
1781da177e4SLinus Torvalds 
1791da177e4SLinus Torvalds /* Construct the minor number from the device (d), mode (m), and non-rewind (n) data */
1801da177e4SLinus Torvalds #define TAPE_MINOR(d, m, n) (((d & ~(255 >> (ST_NBR_MODE_BITS + 1))) << (ST_NBR_MODE_BITS + 1)) | \
1811da177e4SLinus Torvalds   (d & (255 >> (ST_NBR_MODE_BITS + 1))) | (m << ST_MODE_SHIFT) | ((n != 0) << 7) )
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds /* Internal ioctl to set both density (uppermost 8 bits) and blocksize (lower
1841da177e4SLinus Torvalds    24 bits) */
1851da177e4SLinus Torvalds #define SET_DENS_AND_BLK 0x10001
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds static int st_fixed_buffer_size = ST_FIXED_BUFFER_SIZE;
1881da177e4SLinus Torvalds static int st_max_sg_segs = ST_MAX_SG;
1891da177e4SLinus Torvalds 
1901da177e4SLinus Torvalds static int modes_defined;
1911da177e4SLinus Torvalds 
192aaff5ebaSChristoph Hellwig static int enlarge_buffer(struct st_buffer *, int);
19340f6b36cSKai Makisara static void clear_buffer(struct st_buffer *);
1941da177e4SLinus Torvalds static void normalize_buffer(struct st_buffer *);
1951da177e4SLinus Torvalds static int append_to_buffer(const char __user *, struct st_buffer *, int);
1961da177e4SLinus Torvalds static int from_buffer(struct st_buffer *, char __user *, int);
1971da177e4SLinus Torvalds static void move_buffer_data(struct st_buffer *, int);
1981da177e4SLinus Torvalds 
1996620742fSFUJITA Tomonori static int sgl_map_user_pages(struct st_buffer *, const unsigned int,
2001da177e4SLinus Torvalds 			      unsigned long, size_t, int);
2016620742fSFUJITA Tomonori static int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int);
2021da177e4SLinus Torvalds 
2031da177e4SLinus Torvalds static int st_probe(struct device *);
2041da177e4SLinus Torvalds static int st_remove(struct device *);
2051da177e4SLinus Torvalds 
2061da177e4SLinus Torvalds static struct scsi_driver st_template = {
2071da177e4SLinus Torvalds 	.gendrv = {
2081da177e4SLinus Torvalds 		.name		= "st",
2093af6b352SChristoph Hellwig 		.owner		= THIS_MODULE,
2101da177e4SLinus Torvalds 		.probe		= st_probe,
2111da177e4SLinus Torvalds 		.remove		= st_remove,
212442d7562SSeymour, Shane M 		.groups		= st_drv_groups,
2131da177e4SLinus Torvalds 	},
2141da177e4SLinus Torvalds };
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds static int st_compression(struct scsi_tape *, int);
2171da177e4SLinus Torvalds 
2181da177e4SLinus Torvalds static int find_partition(struct scsi_tape *);
2191da177e4SLinus Torvalds static int switch_partition(struct scsi_tape *);
2201da177e4SLinus Torvalds 
2211da177e4SLinus Torvalds static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long);
2221da177e4SLinus Torvalds 
223f03a5670SKai Makisara static void scsi_tape_release(struct kref *);
224f03a5670SKai Makisara 
225f03a5670SKai Makisara #define to_scsi_tape(obj) container_of(obj, struct scsi_tape, kref)
226f03a5670SKai Makisara 
2270b950672SArjan van de Ven static DEFINE_MUTEX(st_ref_mutex);
2286c648d95SJeff Mahoney static DEFINE_SPINLOCK(st_index_lock);
2296c648d95SJeff Mahoney static DEFINE_SPINLOCK(st_use_lock);
2306c648d95SJeff Mahoney static DEFINE_IDR(st_index_idr);
2316c648d95SJeff Mahoney 
232f03a5670SKai Makisara 
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds #ifndef SIGS_FROM_OSST
2351da177e4SLinus Torvalds #define SIGS_FROM_OSST \
2361da177e4SLinus Torvalds 	{"OnStream", "SC-", "", "osst"}, \
2371da177e4SLinus Torvalds 	{"OnStream", "DI-", "", "osst"}, \
2381da177e4SLinus Torvalds 	{"OnStream", "DP-", "", "osst"}, \
2391da177e4SLinus Torvalds 	{"OnStream", "USB", "", "osst"}, \
2401da177e4SLinus Torvalds 	{"OnStream", "FW-", "", "osst"}
2411da177e4SLinus Torvalds #endif
2421da177e4SLinus Torvalds 
scsi_tape_get(int dev)243f03a5670SKai Makisara static struct scsi_tape *scsi_tape_get(int dev)
244f03a5670SKai Makisara {
245f03a5670SKai Makisara 	struct scsi_tape *STp = NULL;
246f03a5670SKai Makisara 
2470b950672SArjan van de Ven 	mutex_lock(&st_ref_mutex);
2486c648d95SJeff Mahoney 	spin_lock(&st_index_lock);
249f03a5670SKai Makisara 
2506c648d95SJeff Mahoney 	STp = idr_find(&st_index_idr, dev);
251f03a5670SKai Makisara 	if (!STp) goto out;
252f03a5670SKai Makisara 
253f03a5670SKai Makisara 	kref_get(&STp->kref);
254f03a5670SKai Makisara 
255f03a5670SKai Makisara 	if (!STp->device)
256f03a5670SKai Makisara 		goto out_put;
257f03a5670SKai Makisara 
258f03a5670SKai Makisara 	if (scsi_device_get(STp->device))
259f03a5670SKai Makisara 		goto out_put;
260f03a5670SKai Makisara 
261f03a5670SKai Makisara 	goto out;
262f03a5670SKai Makisara 
263f03a5670SKai Makisara out_put:
264f03a5670SKai Makisara 	kref_put(&STp->kref, scsi_tape_release);
265f03a5670SKai Makisara 	STp = NULL;
266f03a5670SKai Makisara out:
2676c648d95SJeff Mahoney 	spin_unlock(&st_index_lock);
2680b950672SArjan van de Ven 	mutex_unlock(&st_ref_mutex);
269f03a5670SKai Makisara 	return STp;
270f03a5670SKai Makisara }
271f03a5670SKai Makisara 
scsi_tape_put(struct scsi_tape * STp)272f03a5670SKai Makisara static void scsi_tape_put(struct scsi_tape *STp)
273f03a5670SKai Makisara {
274f03a5670SKai Makisara 	struct scsi_device *sdev = STp->device;
275f03a5670SKai Makisara 
2760b950672SArjan van de Ven 	mutex_lock(&st_ref_mutex);
277f03a5670SKai Makisara 	kref_put(&STp->kref, scsi_tape_release);
278f03a5670SKai Makisara 	scsi_device_put(sdev);
2790b950672SArjan van de Ven 	mutex_unlock(&st_ref_mutex);
280f03a5670SKai Makisara }
281f03a5670SKai Makisara 
2821da177e4SLinus Torvalds struct st_reject_data {
2831da177e4SLinus Torvalds 	char *vendor;
2841da177e4SLinus Torvalds 	char *model;
2851da177e4SLinus Torvalds 	char *rev;
2861da177e4SLinus Torvalds 	char *driver_hint; /* Name of the correct driver, NULL if unknown */
2871da177e4SLinus Torvalds };
2881da177e4SLinus Torvalds 
2891da177e4SLinus Torvalds static struct st_reject_data reject_list[] = {
2901da177e4SLinus Torvalds 	/* {"XXX", "Yy-", "", NULL},  example */
2911da177e4SLinus Torvalds 	SIGS_FROM_OSST,
2921da177e4SLinus Torvalds 	{NULL, }};
2931da177e4SLinus Torvalds 
2941da177e4SLinus Torvalds /* If the device signature is on the list of incompatible drives, the
2951da177e4SLinus Torvalds    function returns a pointer to the name of the correct driver (if known) */
st_incompatible(struct scsi_device * SDp)2961da177e4SLinus Torvalds static char * st_incompatible(struct scsi_device* SDp)
2971da177e4SLinus Torvalds {
2981da177e4SLinus Torvalds 	struct st_reject_data *rp;
2991da177e4SLinus Torvalds 
3001da177e4SLinus Torvalds 	for (rp=&(reject_list[0]); rp->vendor != NULL; rp++)
3011da177e4SLinus Torvalds 		if (!strncmp(rp->vendor, SDp->vendor, strlen(rp->vendor)) &&
3021da177e4SLinus Torvalds 		    !strncmp(rp->model, SDp->model, strlen(rp->model)) &&
3031da177e4SLinus Torvalds 		    !strncmp(rp->rev, SDp->rev, strlen(rp->rev))) {
3041da177e4SLinus Torvalds 			if (rp->driver_hint)
3051da177e4SLinus Torvalds 				return rp->driver_hint;
3061da177e4SLinus Torvalds 			else
3071da177e4SLinus Torvalds 				return "unknown";
3081da177e4SLinus Torvalds 		}
3091da177e4SLinus Torvalds 	return NULL;
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 
313b30d8bcaSHannes Reinecke #define st_printk(prefix, t, fmt, a...) \
31445938335SChristoph Hellwig 	sdev_prefix_printk(prefix, (t)->device, (t)->name, fmt, ##a)
315b30d8bcaSHannes Reinecke #ifdef DEBUG
316b30d8bcaSHannes Reinecke #define DEBC_printk(t, fmt, a...) \
317b30d8bcaSHannes Reinecke 	if (debugging) { st_printk(ST_DEB_MSG, t, fmt, ##a ); }
318b30d8bcaSHannes Reinecke #else
319b30d8bcaSHannes Reinecke #define DEBC_printk(t, fmt, a...)
320b30d8bcaSHannes Reinecke #endif
3211da177e4SLinus Torvalds 
st_analyze_sense(struct st_request * SRpnt,struct st_cmdstatus * s)3228b05b773SMike Christie static void st_analyze_sense(struct st_request *SRpnt, struct st_cmdstatus *s)
3231da177e4SLinus Torvalds {
3241da177e4SLinus Torvalds 	const u8 *ucp;
3258b05b773SMike Christie 	const u8 *sense = SRpnt->sense;
3261da177e4SLinus Torvalds 
3278b05b773SMike Christie 	s->have_sense = scsi_normalize_sense(SRpnt->sense,
3288b05b773SMike Christie 				SCSI_SENSE_BUFFERSIZE, &s->sense_hdr);
3291da177e4SLinus Torvalds 	s->flags = 0;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	if (s->have_sense) {
3321da177e4SLinus Torvalds 		s->deferred = 0;
3331da177e4SLinus Torvalds 		s->remainder_valid =
3341da177e4SLinus Torvalds 			scsi_get_sense_info_fld(sense, SCSI_SENSE_BUFFERSIZE, &s->uremainder64);
3351da177e4SLinus Torvalds 		switch (sense[0] & 0x7f) {
3361da177e4SLinus Torvalds 		case 0x71:
3371da177e4SLinus Torvalds 			s->deferred = 1;
338df561f66SGustavo A. R. Silva 			fallthrough;
3391da177e4SLinus Torvalds 		case 0x70:
3401da177e4SLinus Torvalds 			s->fixed_format = 1;
3411da177e4SLinus Torvalds 			s->flags = sense[2] & 0xe0;
3421da177e4SLinus Torvalds 			break;
3431da177e4SLinus Torvalds 		case 0x73:
3441da177e4SLinus Torvalds 			s->deferred = 1;
345df561f66SGustavo A. R. Silva 			fallthrough;
3461da177e4SLinus Torvalds 		case 0x72:
3471da177e4SLinus Torvalds 			s->fixed_format = 0;
3481da177e4SLinus Torvalds 			ucp = scsi_sense_desc_find(sense, SCSI_SENSE_BUFFERSIZE, 4);
3491da177e4SLinus Torvalds 			s->flags = ucp ? (ucp[3] & 0xe0) : 0;
3501da177e4SLinus Torvalds 			break;
3511da177e4SLinus Torvalds 		}
3521da177e4SLinus Torvalds 	}
3531da177e4SLinus Torvalds }
3541da177e4SLinus Torvalds 
3551da177e4SLinus Torvalds 
3561da177e4SLinus Torvalds /* Convert the result to success code */
st_chk_result(struct scsi_tape * STp,struct st_request * SRpnt)3578b05b773SMike Christie static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt)
3581da177e4SLinus Torvalds {
3598b05b773SMike Christie 	int result = SRpnt->result;
3601da177e4SLinus Torvalds 	u8 scode;
3611da177e4SLinus Torvalds 	DEB(const char *stp;)
36245938335SChristoph Hellwig 	char *name = STp->name;
3631da177e4SLinus Torvalds 	struct st_cmdstatus *cmdstatp;
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	if (!result)
3661da177e4SLinus Torvalds 		return 0;
3671da177e4SLinus Torvalds 
3681da177e4SLinus Torvalds 	cmdstatp = &STp->buffer->cmdstat;
369f03a5670SKai Makisara 	st_analyze_sense(SRpnt, cmdstatp);
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	if (cmdstatp->have_sense)
3721da177e4SLinus Torvalds 		scode = STp->buffer->cmdstat.sense_hdr.sense_key;
3731da177e4SLinus Torvalds 	else
3741da177e4SLinus Torvalds 		scode = 0;
3751da177e4SLinus Torvalds 
3761da177e4SLinus Torvalds 	DEB(
3771da177e4SLinus Torvalds 	if (debugging) {
378b30d8bcaSHannes Reinecke 		st_printk(ST_DEB_MSG, STp,
379b30d8bcaSHannes Reinecke 			    "Error: %x, cmd: %x %x %x %x %x %x\n", result,
3808b05b773SMike Christie 			    SRpnt->cmd[0], SRpnt->cmd[1], SRpnt->cmd[2],
3818b05b773SMike Christie 			    SRpnt->cmd[3], SRpnt->cmd[4], SRpnt->cmd[5]);
3821da177e4SLinus Torvalds 		if (cmdstatp->have_sense)
383d811b848SHannes Reinecke 			__scsi_print_sense(STp->device, name,
384d811b848SHannes Reinecke 					   SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
3851da177e4SLinus Torvalds 	} ) /* end DEB */
3861da177e4SLinus Torvalds 	if (!debugging) { /* Abnormal conditions for tape */
3871da177e4SLinus Torvalds 		if (!cmdstatp->have_sense)
388b30d8bcaSHannes Reinecke 			st_printk(KERN_WARNING, STp,
38954c29086SHannes Reinecke 			       "Error %x (driver bt 0, host bt 0x%x).\n",
39054c29086SHannes Reinecke 			       result, host_byte(result));
3911da177e4SLinus Torvalds 		else if (cmdstatp->have_sense &&
3921da177e4SLinus Torvalds 			 scode != NO_SENSE &&
3931da177e4SLinus Torvalds 			 scode != RECOVERED_ERROR &&
3941da177e4SLinus Torvalds 			 /* scode != UNIT_ATTENTION && */
3951da177e4SLinus Torvalds 			 scode != BLANK_CHECK &&
3961da177e4SLinus Torvalds 			 scode != VOLUME_OVERFLOW &&
3978b05b773SMike Christie 			 SRpnt->cmd[0] != MODE_SENSE &&
3988b05b773SMike Christie 			 SRpnt->cmd[0] != TEST_UNIT_READY) {
3994e73ea7bSLuben Tuikov 
400d811b848SHannes Reinecke 			__scsi_print_sense(STp->device, name,
401d811b848SHannes Reinecke 					   SRpnt->sense, SCSI_SENSE_BUFFERSIZE);
4021da177e4SLinus Torvalds 		}
4031da177e4SLinus Torvalds 	}
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds 	if (cmdstatp->fixed_format &&
4061da177e4SLinus Torvalds 	    STp->cln_mode >= EXTENDED_SENSE_START) {  /* Only fixed format sense */
4071da177e4SLinus Torvalds 		if (STp->cln_sense_value)
4088b05b773SMike Christie 			STp->cleaning_req |= ((SRpnt->sense[STp->cln_mode] &
4091da177e4SLinus Torvalds 					       STp->cln_sense_mask) == STp->cln_sense_value);
4101da177e4SLinus Torvalds 		else
4118b05b773SMike Christie 			STp->cleaning_req |= ((SRpnt->sense[STp->cln_mode] &
4121da177e4SLinus Torvalds 					       STp->cln_sense_mask) != 0);
4131da177e4SLinus Torvalds 	}
4141da177e4SLinus Torvalds 	if (cmdstatp->have_sense &&
4151da177e4SLinus Torvalds 	    cmdstatp->sense_hdr.asc == 0 && cmdstatp->sense_hdr.ascq == 0x17)
4161da177e4SLinus Torvalds 		STp->cleaning_req = 1; /* ASC and ASCQ => cleaning requested */
417*9604eea5SJohn Meneghini 	if (cmdstatp->have_sense && scode == UNIT_ATTENTION && cmdstatp->sense_hdr.asc == 0x29)
418*9604eea5SJohn Meneghini 		STp->pos_unknown = 1; /* ASC => power on / reset */
4191da177e4SLinus Torvalds 
4201da177e4SLinus Torvalds 	STp->pos_unknown |= STp->device->was_reset;
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds 	if (cmdstatp->have_sense &&
4231da177e4SLinus Torvalds 	    scode == RECOVERED_ERROR
4241da177e4SLinus Torvalds #if ST_RECOVERED_WRITE_FATAL
4258b05b773SMike Christie 	    && SRpnt->cmd[0] != WRITE_6
4268b05b773SMike Christie 	    && SRpnt->cmd[0] != WRITE_FILEMARKS
4271da177e4SLinus Torvalds #endif
4281da177e4SLinus Torvalds 	    ) {
4291da177e4SLinus Torvalds 		STp->recover_count++;
4301da177e4SLinus Torvalds 		STp->recover_reg++;
4311da177e4SLinus Torvalds 
4321da177e4SLinus Torvalds 		DEB(
4331da177e4SLinus Torvalds 		if (debugging) {
4348b05b773SMike Christie 			if (SRpnt->cmd[0] == READ_6)
4351da177e4SLinus Torvalds 				stp = "read";
4368b05b773SMike Christie 			else if (SRpnt->cmd[0] == WRITE_6)
4371da177e4SLinus Torvalds 				stp = "write";
4381da177e4SLinus Torvalds 			else
4391da177e4SLinus Torvalds 				stp = "ioctl";
440b30d8bcaSHannes Reinecke 			st_printk(ST_DEB_MSG, STp,
441b30d8bcaSHannes Reinecke 				  "Recovered %s error (%d).\n",
442b30d8bcaSHannes Reinecke 				  stp, STp->recover_count);
4431da177e4SLinus Torvalds 		} ) /* end DEB */
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 		if (cmdstatp->flags == 0)
4461da177e4SLinus Torvalds 			return 0;
4471da177e4SLinus Torvalds 	}
4481da177e4SLinus Torvalds 	return (-EIO);
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds 
st_allocate_request(struct scsi_tape * stp)4514deba245SFUJITA Tomonori static struct st_request *st_allocate_request(struct scsi_tape *stp)
4528b05b773SMike Christie {
4534deba245SFUJITA Tomonori 	struct st_request *streq;
4544deba245SFUJITA Tomonori 
4554deba245SFUJITA Tomonori 	streq = kzalloc(sizeof(*streq), GFP_KERNEL);
4564deba245SFUJITA Tomonori 	if (streq)
4574deba245SFUJITA Tomonori 		streq->stp = stp;
4584deba245SFUJITA Tomonori 	else {
459b30d8bcaSHannes Reinecke 		st_printk(KERN_ERR, stp,
460b30d8bcaSHannes Reinecke 			  "Can't get SCSI request.\n");
4614deba245SFUJITA Tomonori 		if (signal_pending(current))
4624deba245SFUJITA Tomonori 			stp->buffer->syscall_result = -EINTR;
4634deba245SFUJITA Tomonori 		else
4644deba245SFUJITA Tomonori 			stp->buffer->syscall_result = -EBUSY;
4654deba245SFUJITA Tomonori 	}
4664deba245SFUJITA Tomonori 
4674deba245SFUJITA Tomonori 	return streq;
4688b05b773SMike Christie }
4698b05b773SMike Christie 
st_release_request(struct st_request * streq)4708b05b773SMike Christie static void st_release_request(struct st_request *streq)
4718b05b773SMike Christie {
4728b05b773SMike Christie 	kfree(streq);
4731da177e4SLinus Torvalds }
4741da177e4SLinus Torvalds 
st_do_stats(struct scsi_tape * STp,struct request * req)47505545c92SSeymour, Shane M static void st_do_stats(struct scsi_tape *STp, struct request *req)
47605545c92SSeymour, Shane M {
477ce70fd9aSChristoph Hellwig 	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
47805545c92SSeymour, Shane M 	ktime_t now;
47905545c92SSeymour, Shane M 
48005545c92SSeymour, Shane M 	now = ktime_get();
481ce70fd9aSChristoph Hellwig 	if (scmd->cmnd[0] == WRITE_6) {
48205545c92SSeymour, Shane M 		now = ktime_sub(now, STp->stats->write_time);
48305545c92SSeymour, Shane M 		atomic64_add(ktime_to_ns(now), &STp->stats->tot_write_time);
48405545c92SSeymour, Shane M 		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
48505545c92SSeymour, Shane M 		atomic64_inc(&STp->stats->write_cnt);
486dbb4c84dSChristoph Hellwig 		if (scmd->result) {
48705545c92SSeymour, Shane M 			atomic64_add(atomic_read(&STp->stats->last_write_size)
48805545c92SSeymour, Shane M 				- STp->buffer->cmdstat.residual,
48905545c92SSeymour, Shane M 				&STp->stats->write_byte_cnt);
49005545c92SSeymour, Shane M 			if (STp->buffer->cmdstat.residual > 0)
49105545c92SSeymour, Shane M 				atomic64_inc(&STp->stats->resid_cnt);
49205545c92SSeymour, Shane M 		} else
49305545c92SSeymour, Shane M 			atomic64_add(atomic_read(&STp->stats->last_write_size),
49405545c92SSeymour, Shane M 				&STp->stats->write_byte_cnt);
495ce70fd9aSChristoph Hellwig 	} else if (scmd->cmnd[0] == READ_6) {
49605545c92SSeymour, Shane M 		now = ktime_sub(now, STp->stats->read_time);
49705545c92SSeymour, Shane M 		atomic64_add(ktime_to_ns(now), &STp->stats->tot_read_time);
49805545c92SSeymour, Shane M 		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
49905545c92SSeymour, Shane M 		atomic64_inc(&STp->stats->read_cnt);
500dbb4c84dSChristoph Hellwig 		if (scmd->result) {
50105545c92SSeymour, Shane M 			atomic64_add(atomic_read(&STp->stats->last_read_size)
50205545c92SSeymour, Shane M 				- STp->buffer->cmdstat.residual,
50305545c92SSeymour, Shane M 				&STp->stats->read_byte_cnt);
50405545c92SSeymour, Shane M 			if (STp->buffer->cmdstat.residual > 0)
50505545c92SSeymour, Shane M 				atomic64_inc(&STp->stats->resid_cnt);
50605545c92SSeymour, Shane M 		} else
50705545c92SSeymour, Shane M 			atomic64_add(atomic_read(&STp->stats->last_read_size),
50805545c92SSeymour, Shane M 				&STp->stats->read_byte_cnt);
50905545c92SSeymour, Shane M 	} else {
51005545c92SSeymour, Shane M 		now = ktime_sub(now, STp->stats->other_time);
51105545c92SSeymour, Shane M 		atomic64_add(ktime_to_ns(now), &STp->stats->tot_io_time);
51205545c92SSeymour, Shane M 		atomic64_inc(&STp->stats->other_cnt);
51305545c92SSeymour, Shane M 	}
51405545c92SSeymour, Shane M 	atomic64_dec(&STp->stats->in_flight);
51505545c92SSeymour, Shane M }
51605545c92SSeymour, Shane M 
st_scsi_execute_end(struct request * req,blk_status_t status)517de671d61SJens Axboe static enum rq_end_io_ret st_scsi_execute_end(struct request *req,
518de671d61SJens Axboe 					      blk_status_t status)
51913b53b44SFUJITA Tomonori {
5205b794f98SChristoph Hellwig 	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
52113b53b44SFUJITA Tomonori 	struct st_request *SRpnt = req->end_io_data;
52213b53b44SFUJITA Tomonori 	struct scsi_tape *STp = SRpnt->stp;
523c68bf8eeSPetr Uzel 	struct bio *tmp;
52413b53b44SFUJITA Tomonori 
525dbb4c84dSChristoph Hellwig 	STp->buffer->cmdstat.midlevel_result = SRpnt->result = scmd->result;
526a9a4ea11SChristoph Hellwig 	STp->buffer->cmdstat.residual = scmd->resid_len;
52713b53b44SFUJITA Tomonori 
52805545c92SSeymour, Shane M 	st_do_stats(STp, req);
52905545c92SSeymour, Shane M 
530c68bf8eeSPetr Uzel 	tmp = SRpnt->bio;
5315b794f98SChristoph Hellwig 	if (scmd->sense_len)
5325b794f98SChristoph Hellwig 		memcpy(SRpnt->sense, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
53313b53b44SFUJITA Tomonori 	if (SRpnt->waiting)
53413b53b44SFUJITA Tomonori 		complete(SRpnt->waiting);
53513b53b44SFUJITA Tomonori 
536c68bf8eeSPetr Uzel 	blk_rq_unmap_user(tmp);
5370bf6d96cSChristoph Hellwig 	blk_mq_free_request(req);
538de671d61SJens Axboe 	return RQ_END_IO_NONE;
53913b53b44SFUJITA Tomonori }
54013b53b44SFUJITA Tomonori 
st_scsi_execute(struct st_request * SRpnt,const unsigned char * cmd,int data_direction,void * buffer,unsigned bufflen,int timeout,int retries)54113b53b44SFUJITA Tomonori static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
54213b53b44SFUJITA Tomonori 			   int data_direction, void *buffer, unsigned bufflen,
54313b53b44SFUJITA Tomonori 			   int timeout, int retries)
54413b53b44SFUJITA Tomonori {
54513b53b44SFUJITA Tomonori 	struct request *req;
54613b53b44SFUJITA Tomonori 	struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
54713b53b44SFUJITA Tomonori 	int err = 0;
54805545c92SSeymour, Shane M 	struct scsi_tape *STp = SRpnt->stp;
549ce70fd9aSChristoph Hellwig 	struct scsi_cmnd *scmd;
55013b53b44SFUJITA Tomonori 
55168ec3b81SChristoph Hellwig 	req = scsi_alloc_request(SRpnt->stp->device->request_queue,
552aebf526bSChristoph Hellwig 			data_direction == DMA_TO_DEVICE ?
553da6269daSChristoph Hellwig 			REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0);
554a492f075SJoe Lawrence 	if (IS_ERR(req))
55500da6a70SHannes Reinecke 		return PTR_ERR(req);
556ce70fd9aSChristoph Hellwig 	scmd = blk_mq_rq_to_pdu(req);
557e8064021SChristoph Hellwig 	req->rq_flags |= RQF_QUIET;
55813b53b44SFUJITA Tomonori 
55913b53b44SFUJITA Tomonori 	mdata->null_mapped = 1;
56013b53b44SFUJITA Tomonori 
56102ae2c0eSKai Makisara 	if (bufflen) {
56202ae2c0eSKai Makisara 		err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen,
56302ae2c0eSKai Makisara 				      GFP_KERNEL);
56413b53b44SFUJITA Tomonori 		if (err) {
5650bf6d96cSChristoph Hellwig 			blk_mq_free_request(req);
56600da6a70SHannes Reinecke 			return err;
56713b53b44SFUJITA Tomonori 		}
56802ae2c0eSKai Makisara 	}
56913b53b44SFUJITA Tomonori 
57005545c92SSeymour, Shane M 	atomic64_inc(&STp->stats->in_flight);
57105545c92SSeymour, Shane M 	if (cmd[0] == WRITE_6) {
57205545c92SSeymour, Shane M 		atomic_set(&STp->stats->last_write_size, bufflen);
57305545c92SSeymour, Shane M 		STp->stats->write_time = ktime_get();
57405545c92SSeymour, Shane M 	} else if (cmd[0] == READ_6) {
57505545c92SSeymour, Shane M 		atomic_set(&STp->stats->last_read_size, bufflen);
57605545c92SSeymour, Shane M 		STp->stats->read_time = ktime_get();
57705545c92SSeymour, Shane M 	} else {
57805545c92SSeymour, Shane M 		STp->stats->other_time = ktime_get();
57905545c92SSeymour, Shane M 	}
58005545c92SSeymour, Shane M 
58113b53b44SFUJITA Tomonori 	SRpnt->bio = req->bio;
582ce70fd9aSChristoph Hellwig 	scmd->cmd_len = COMMAND_SIZE(cmd[0]);
583ce70fd9aSChristoph Hellwig 	memcpy(scmd->cmnd, cmd, scmd->cmd_len);
58413b53b44SFUJITA Tomonori 	req->timeout = timeout;
5856aded12bSChristoph Hellwig 	scmd->allowed = retries;
586e2e53086SChristoph Hellwig 	req->end_io = st_scsi_execute_end;
58713b53b44SFUJITA Tomonori 	req->end_io_data = SRpnt;
58813b53b44SFUJITA Tomonori 
589e2e53086SChristoph Hellwig 	blk_execute_rq_nowait(req, true);
59013b53b44SFUJITA Tomonori 	return 0;
59113b53b44SFUJITA Tomonori }
59213b53b44SFUJITA Tomonori 
5931da177e4SLinus Torvalds /* Do the scsi command. Waits until command performed if do_wait is true.
5941da177e4SLinus Torvalds    Otherwise write_behind_check() is used to check that the command
5951da177e4SLinus Torvalds    has finished. */
5968b05b773SMike Christie static struct st_request *
st_do_scsi(struct st_request * SRpnt,struct scsi_tape * STp,unsigned char * cmd,int bytes,int direction,int timeout,int retries,int do_wait)5978b05b773SMike Christie st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd,
5981da177e4SLinus Torvalds 	   int bytes, int direction, int timeout, int retries, int do_wait)
5991da177e4SLinus Torvalds {
600f03a5670SKai Makisara 	struct completion *waiting;
6016d476267SFUJITA Tomonori 	struct rq_map_data *mdata = &STp->buffer->map_data;
6026d476267SFUJITA Tomonori 	int ret;
6031da177e4SLinus Torvalds 
604f03a5670SKai Makisara 	/* if async, make sure there's no command outstanding */
605f03a5670SKai Makisara 	if (!do_wait && ((STp->buffer)->last_SRpnt)) {
606b30d8bcaSHannes Reinecke 		st_printk(KERN_ERR, STp,
607b30d8bcaSHannes Reinecke 			  "Async command already active.\n");
608f03a5670SKai Makisara 		if (signal_pending(current))
609f03a5670SKai Makisara 			(STp->buffer)->syscall_result = (-EINTR);
610f03a5670SKai Makisara 		else
611f03a5670SKai Makisara 			(STp->buffer)->syscall_result = (-EBUSY);
612f03a5670SKai Makisara 		return NULL;
613f03a5670SKai Makisara 	}
614f03a5670SKai Makisara 
6154deba245SFUJITA Tomonori 	if (!SRpnt) {
6164deba245SFUJITA Tomonori 		SRpnt = st_allocate_request(STp);
6174deba245SFUJITA Tomonori 		if (!SRpnt)
6181da177e4SLinus Torvalds 			return NULL;
6191da177e4SLinus Torvalds 	}
6201da177e4SLinus Torvalds 
621f03a5670SKai Makisara 	/* If async IO, set last_SRpnt. This ptr tells write_behind_check
622f03a5670SKai Makisara 	   which IO is outstanding. It's nulled out when the IO completes. */
623f03a5670SKai Makisara 	if (!do_wait)
624f03a5670SKai Makisara 		(STp->buffer)->last_SRpnt = SRpnt;
625f03a5670SKai Makisara 
626f03a5670SKai Makisara 	waiting = &STp->wait;
627f03a5670SKai Makisara 	init_completion(waiting);
6288b05b773SMike Christie 	SRpnt->waiting = waiting;
6298b05b773SMike Christie 
6306620742fSFUJITA Tomonori 	if (STp->buffer->do_dio) {
631c982c368SFUJITA Tomonori 		mdata->page_order = 0;
6326620742fSFUJITA Tomonori 		mdata->nr_entries = STp->buffer->sg_segs;
6336620742fSFUJITA Tomonori 		mdata->pages = STp->buffer->mapped_pages;
6346620742fSFUJITA Tomonori 	} else {
635c982c368SFUJITA Tomonori 		mdata->page_order = STp->buffer->reserved_page_order;
6366d476267SFUJITA Tomonori 		mdata->nr_entries =
6376d476267SFUJITA Tomonori 			DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order);
638c982c368SFUJITA Tomonori 		mdata->pages = STp->buffer->reserved_pages;
639c982c368SFUJITA Tomonori 		mdata->offset = 0;
6406d476267SFUJITA Tomonori 	}
6416d476267SFUJITA Tomonori 
6428b05b773SMike Christie 	memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd));
6431da177e4SLinus Torvalds 	STp->buffer->cmdstat.have_sense = 0;
6448b05b773SMike Christie 	STp->buffer->syscall_result = 0;
6451da177e4SLinus Torvalds 
6466620742fSFUJITA Tomonori 	ret = st_scsi_execute(SRpnt, cmd, direction, NULL, bytes, timeout,
6476620742fSFUJITA Tomonori 			      retries);
6486d476267SFUJITA Tomonori 	if (ret) {
6498b05b773SMike Christie 		/* could not allocate the buffer or request was too large */
6508b05b773SMike Christie 		(STp->buffer)->syscall_result = (-EBUSY);
651787926b1SKai Makisara 		(STp->buffer)->last_SRpnt = NULL;
6526d476267SFUJITA Tomonori 	} else if (do_wait) {
653f03a5670SKai Makisara 		wait_for_completion(waiting);
6548b05b773SMike Christie 		SRpnt->waiting = NULL;
6551da177e4SLinus Torvalds 		(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
6561da177e4SLinus Torvalds 	}
6578b05b773SMike Christie 
6581da177e4SLinus Torvalds 	return SRpnt;
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds 
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds /* Handle the write-behind checking (waits for completion). Returns -ENOSPC if
6631da177e4SLinus Torvalds    write has been correct but EOM early warning reached, -EIO if write ended in
6641da177e4SLinus Torvalds    error or zero if write successful. Asynchronous writes are used only in
6651da177e4SLinus Torvalds    variable block mode. */
write_behind_check(struct scsi_tape * STp)6661da177e4SLinus Torvalds static int write_behind_check(struct scsi_tape * STp)
6671da177e4SLinus Torvalds {
6681da177e4SLinus Torvalds 	int retval = 0;
6691da177e4SLinus Torvalds 	struct st_buffer *STbuffer;
6701da177e4SLinus Torvalds 	struct st_partstat *STps;
6711da177e4SLinus Torvalds 	struct st_cmdstatus *cmdstatp;
6728b05b773SMike Christie 	struct st_request *SRpnt;
6731da177e4SLinus Torvalds 
6741da177e4SLinus Torvalds 	STbuffer = STp->buffer;
6751da177e4SLinus Torvalds 	if (!STbuffer->writing)
6761da177e4SLinus Torvalds 		return 0;
6771da177e4SLinus Torvalds 
6781da177e4SLinus Torvalds 	DEB(
6791da177e4SLinus Torvalds 	if (STp->write_pending)
6801da177e4SLinus Torvalds 		STp->nbr_waits++;
6811da177e4SLinus Torvalds 	else
6821da177e4SLinus Torvalds 		STp->nbr_finished++;
6831da177e4SLinus Torvalds 	) /* end DEB */
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 	wait_for_completion(&(STp->wait));
686f03a5670SKai Makisara 	SRpnt = STbuffer->last_SRpnt;
687f03a5670SKai Makisara 	STbuffer->last_SRpnt = NULL;
6888b05b773SMike Christie 	SRpnt->waiting = NULL;
6891da177e4SLinus Torvalds 
690f03a5670SKai Makisara 	(STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
6918b05b773SMike Christie 	st_release_request(SRpnt);
6921da177e4SLinus Torvalds 
6931da177e4SLinus Torvalds 	STbuffer->buffer_bytes -= STbuffer->writing;
6941da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
6951da177e4SLinus Torvalds 	if (STps->drv_block >= 0) {
6961da177e4SLinus Torvalds 		if (STp->block_size == 0)
6971da177e4SLinus Torvalds 			STps->drv_block++;
6981da177e4SLinus Torvalds 		else
6991da177e4SLinus Torvalds 			STps->drv_block += STbuffer->writing / STp->block_size;
7001da177e4SLinus Torvalds 	}
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	cmdstatp = &STbuffer->cmdstat;
7031da177e4SLinus Torvalds 	if (STbuffer->syscall_result) {
7041da177e4SLinus Torvalds 		retval = -EIO;
7051da177e4SLinus Torvalds 		if (cmdstatp->have_sense && !cmdstatp->deferred &&
7061da177e4SLinus Torvalds 		    (cmdstatp->flags & SENSE_EOM) &&
7071da177e4SLinus Torvalds 		    (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
7081da177e4SLinus Torvalds 		     cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR)) {
7091da177e4SLinus Torvalds 			/* EOM at write-behind, has all data been written? */
7101da177e4SLinus Torvalds 			if (!cmdstatp->remainder_valid ||
7111da177e4SLinus Torvalds 			    cmdstatp->uremainder64 == 0)
7121da177e4SLinus Torvalds 				retval = -ENOSPC;
7131da177e4SLinus Torvalds 		}
7141da177e4SLinus Torvalds 		if (retval == -EIO)
7151da177e4SLinus Torvalds 			STps->drv_block = -1;
7161da177e4SLinus Torvalds 	}
7171da177e4SLinus Torvalds 	STbuffer->writing = 0;
7181da177e4SLinus Torvalds 
7191da177e4SLinus Torvalds 	DEB(if (debugging && retval)
720b30d8bcaSHannes Reinecke 		    st_printk(ST_DEB_MSG, STp,
721b30d8bcaSHannes Reinecke 				"Async write error %x, return value %d.\n",
722b30d8bcaSHannes Reinecke 				STbuffer->cmdstat.midlevel_result, retval);) /* end DEB */
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 	return retval;
7251da177e4SLinus Torvalds }
7261da177e4SLinus Torvalds 
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds /* Step over EOF if it has been inadvertently crossed (ioctl not used because
7291da177e4SLinus Torvalds    it messes up the block number). */
cross_eof(struct scsi_tape * STp,int forward)7301da177e4SLinus Torvalds static int cross_eof(struct scsi_tape * STp, int forward)
7311da177e4SLinus Torvalds {
7328b05b773SMike Christie 	struct st_request *SRpnt;
7331da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
7341da177e4SLinus Torvalds 
7351da177e4SLinus Torvalds 	cmd[0] = SPACE;
7361da177e4SLinus Torvalds 	cmd[1] = 0x01;		/* Space FileMarks */
7371da177e4SLinus Torvalds 	if (forward) {
7381da177e4SLinus Torvalds 		cmd[2] = cmd[3] = 0;
7391da177e4SLinus Torvalds 		cmd[4] = 1;
7401da177e4SLinus Torvalds 	} else
7411da177e4SLinus Torvalds 		cmd[2] = cmd[3] = cmd[4] = 0xff;	/* -1 filemarks */
7421da177e4SLinus Torvalds 	cmd[5] = 0;
7431da177e4SLinus Torvalds 
744b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Stepping over filemark %s.\n",
745b30d8bcaSHannes Reinecke 		    forward ? "forward" : "backward");
7461da177e4SLinus Torvalds 
74702ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
74839ade4b1SFUJITA Tomonori 			   STp->device->request_queue->rq_timeout,
74902ae2c0eSKai Makisara 			   MAX_RETRIES, 1);
75002ae2c0eSKai Makisara 	if (!SRpnt)
75102ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
75239ade4b1SFUJITA Tomonori 
75302ae2c0eSKai Makisara 	st_release_request(SRpnt);
75402ae2c0eSKai Makisara 	SRpnt = NULL;
7551da177e4SLinus Torvalds 
7561da177e4SLinus Torvalds 	if ((STp->buffer)->cmdstat.midlevel_result != 0)
757b30d8bcaSHannes Reinecke 		st_printk(KERN_ERR, STp,
758b30d8bcaSHannes Reinecke 			  "Stepping over filemark %s failed.\n",
759b30d8bcaSHannes Reinecke 			  forward ? "forward" : "backward");
7601da177e4SLinus Torvalds 
76102ae2c0eSKai Makisara 	return (STp->buffer)->syscall_result;
7621da177e4SLinus Torvalds }
7631da177e4SLinus Torvalds 
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds /* Flush the write buffer (never need to write if variable blocksize). */
st_flush_write_buffer(struct scsi_tape * STp)7668ef8d594SAdrian Bunk static int st_flush_write_buffer(struct scsi_tape * STp)
7671da177e4SLinus Torvalds {
768786231afSKai Makisara 	int transfer, blks;
7691da177e4SLinus Torvalds 	int result;
7701da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
7718b05b773SMike Christie 	struct st_request *SRpnt;
7721da177e4SLinus Torvalds 	struct st_partstat *STps;
7731da177e4SLinus Torvalds 
7741da177e4SLinus Torvalds 	result = write_behind_check(STp);
7751da177e4SLinus Torvalds 	if (result)
7761da177e4SLinus Torvalds 		return result;
7771da177e4SLinus Torvalds 
7781da177e4SLinus Torvalds 	result = 0;
7791da177e4SLinus Torvalds 	if (STp->dirty == 1) {
7801da177e4SLinus Torvalds 
781786231afSKai Makisara 		transfer = STp->buffer->buffer_bytes;
782b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Flushing %d bytes.\n", transfer);
7831da177e4SLinus Torvalds 
7841da177e4SLinus Torvalds 		memset(cmd, 0, MAX_COMMAND_SIZE);
7851da177e4SLinus Torvalds 		cmd[0] = WRITE_6;
7861da177e4SLinus Torvalds 		cmd[1] = 1;
7871da177e4SLinus Torvalds 		blks = transfer / STp->block_size;
7881da177e4SLinus Torvalds 		cmd[2] = blks >> 16;
7891da177e4SLinus Torvalds 		cmd[3] = blks >> 8;
7901da177e4SLinus Torvalds 		cmd[4] = blks;
7911da177e4SLinus Torvalds 
7921da177e4SLinus Torvalds 		SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE,
793a02488edSJames Bottomley 				   STp->device->request_queue->rq_timeout,
794a02488edSJames Bottomley 				   MAX_WRITE_RETRIES, 1);
7951da177e4SLinus Torvalds 		if (!SRpnt)
7961da177e4SLinus Torvalds 			return (STp->buffer)->syscall_result;
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 		STps = &(STp->ps[STp->partition]);
7991da177e4SLinus Torvalds 		if ((STp->buffer)->syscall_result != 0) {
8001da177e4SLinus Torvalds 			struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
8011da177e4SLinus Torvalds 
8021da177e4SLinus Torvalds 			if (cmdstatp->have_sense && !cmdstatp->deferred &&
8031da177e4SLinus Torvalds 			    (cmdstatp->flags & SENSE_EOM) &&
8041da177e4SLinus Torvalds 			    (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
8051da177e4SLinus Torvalds 			     cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
8061da177e4SLinus Torvalds 			    (!cmdstatp->remainder_valid ||
8071da177e4SLinus Torvalds 			     cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */
8081da177e4SLinus Torvalds 				STp->dirty = 0;
8091da177e4SLinus Torvalds 				(STp->buffer)->buffer_bytes = 0;
8101da177e4SLinus Torvalds 				if (STps->drv_block >= 0)
8111da177e4SLinus Torvalds 					STps->drv_block += blks;
8121da177e4SLinus Torvalds 				result = (-ENOSPC);
8131da177e4SLinus Torvalds 			} else {
814b30d8bcaSHannes Reinecke 				st_printk(KERN_ERR, STp, "Error on flush.\n");
8151da177e4SLinus Torvalds 				STps->drv_block = (-1);
8161da177e4SLinus Torvalds 				result = (-EIO);
8171da177e4SLinus Torvalds 			}
8181da177e4SLinus Torvalds 		} else {
8191da177e4SLinus Torvalds 			if (STps->drv_block >= 0)
8201da177e4SLinus Torvalds 				STps->drv_block += blks;
8211da177e4SLinus Torvalds 			STp->dirty = 0;
8221da177e4SLinus Torvalds 			(STp->buffer)->buffer_bytes = 0;
8231da177e4SLinus Torvalds 		}
8248b05b773SMike Christie 		st_release_request(SRpnt);
8251da177e4SLinus Torvalds 		SRpnt = NULL;
8261da177e4SLinus Torvalds 	}
8271da177e4SLinus Torvalds 	return result;
8281da177e4SLinus Torvalds }
8291da177e4SLinus Torvalds 
8301da177e4SLinus Torvalds 
8311da177e4SLinus Torvalds /* Flush the tape buffer. The tape will be positioned correctly unless
8321da177e4SLinus Torvalds    seek_next is true. */
flush_buffer(struct scsi_tape * STp,int seek_next)8331da177e4SLinus Torvalds static int flush_buffer(struct scsi_tape *STp, int seek_next)
8341da177e4SLinus Torvalds {
8351da177e4SLinus Torvalds 	int backspace, result;
8361da177e4SLinus Torvalds 	struct st_partstat *STps;
8371da177e4SLinus Torvalds 
8381da177e4SLinus Torvalds 	/*
8391da177e4SLinus Torvalds 	 * If there was a bus reset, block further access
8401da177e4SLinus Torvalds 	 * to this device.
8411da177e4SLinus Torvalds 	 */
8421da177e4SLinus Torvalds 	if (STp->pos_unknown)
8431da177e4SLinus Torvalds 		return (-EIO);
8441da177e4SLinus Torvalds 
8451da177e4SLinus Torvalds 	if (STp->ready != ST_READY)
8461da177e4SLinus Torvalds 		return 0;
8471da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
8481da177e4SLinus Torvalds 	if (STps->rw == ST_WRITING)	/* Writing */
8498ef8d594SAdrian Bunk 		return st_flush_write_buffer(STp);
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds 	if (STp->block_size == 0)
8521da177e4SLinus Torvalds 		return 0;
8531da177e4SLinus Torvalds 
8541da177e4SLinus Torvalds 	backspace = ((STp->buffer)->buffer_bytes +
8551da177e4SLinus Torvalds 		     (STp->buffer)->read_pointer) / STp->block_size -
8561da177e4SLinus Torvalds 	    ((STp->buffer)->read_pointer + STp->block_size - 1) /
8571da177e4SLinus Torvalds 	    STp->block_size;
8581da177e4SLinus Torvalds 	(STp->buffer)->buffer_bytes = 0;
8591da177e4SLinus Torvalds 	(STp->buffer)->read_pointer = 0;
8601da177e4SLinus Torvalds 	result = 0;
8611da177e4SLinus Torvalds 	if (!seek_next) {
8621da177e4SLinus Torvalds 		if (STps->eof == ST_FM_HIT) {
8631da177e4SLinus Torvalds 			result = cross_eof(STp, 0);	/* Back over the EOF hit */
8641da177e4SLinus Torvalds 			if (!result)
8651da177e4SLinus Torvalds 				STps->eof = ST_NOEOF;
8661da177e4SLinus Torvalds 			else {
8671da177e4SLinus Torvalds 				if (STps->drv_file >= 0)
8681da177e4SLinus Torvalds 					STps->drv_file++;
8691da177e4SLinus Torvalds 				STps->drv_block = 0;
8701da177e4SLinus Torvalds 			}
8711da177e4SLinus Torvalds 		}
8721da177e4SLinus Torvalds 		if (!result && backspace > 0)
8731da177e4SLinus Torvalds 			result = st_int_ioctl(STp, MTBSR, backspace);
8741da177e4SLinus Torvalds 	} else if (STps->eof == ST_FM_HIT) {
8751da177e4SLinus Torvalds 		if (STps->drv_file >= 0)
8761da177e4SLinus Torvalds 			STps->drv_file++;
8771da177e4SLinus Torvalds 		STps->drv_block = 0;
8781da177e4SLinus Torvalds 		STps->eof = ST_NOEOF;
8791da177e4SLinus Torvalds 	}
8801da177e4SLinus Torvalds 	return result;
8811da177e4SLinus Torvalds 
8821da177e4SLinus Torvalds }
8831da177e4SLinus Torvalds 
8841da177e4SLinus Torvalds /* Set the mode parameters */
set_mode_densblk(struct scsi_tape * STp,struct st_modedef * STm)8851da177e4SLinus Torvalds static int set_mode_densblk(struct scsi_tape * STp, struct st_modedef * STm)
8861da177e4SLinus Torvalds {
8871da177e4SLinus Torvalds 	int set_it = 0;
8881da177e4SLinus Torvalds 	unsigned long arg;
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	if (!STp->density_changed &&
8911da177e4SLinus Torvalds 	    STm->default_density >= 0 &&
8921da177e4SLinus Torvalds 	    STm->default_density != STp->density) {
8931da177e4SLinus Torvalds 		arg = STm->default_density;
8941da177e4SLinus Torvalds 		set_it = 1;
8951da177e4SLinus Torvalds 	} else
8961da177e4SLinus Torvalds 		arg = STp->density;
8971da177e4SLinus Torvalds 	arg <<= MT_ST_DENSITY_SHIFT;
8981da177e4SLinus Torvalds 	if (!STp->blksize_changed &&
8991da177e4SLinus Torvalds 	    STm->default_blksize >= 0 &&
9001da177e4SLinus Torvalds 	    STm->default_blksize != STp->block_size) {
9011da177e4SLinus Torvalds 		arg |= STm->default_blksize;
9021da177e4SLinus Torvalds 		set_it = 1;
9031da177e4SLinus Torvalds 	} else
9041da177e4SLinus Torvalds 		arg |= STp->block_size;
9051da177e4SLinus Torvalds 	if (set_it &&
9061da177e4SLinus Torvalds 	    st_int_ioctl(STp, SET_DENS_AND_BLK, arg)) {
907b30d8bcaSHannes Reinecke 		st_printk(KERN_WARNING, STp,
908b30d8bcaSHannes Reinecke 			  "Can't set default block size to %d bytes "
909b30d8bcaSHannes Reinecke 			  "and density %x.\n",
910b30d8bcaSHannes Reinecke 			  STm->default_blksize, STm->default_density);
9111da177e4SLinus Torvalds 		if (modes_defined)
9121da177e4SLinus Torvalds 			return (-EINVAL);
9131da177e4SLinus Torvalds 	}
9141da177e4SLinus Torvalds 	return 0;
9151da177e4SLinus Torvalds }
9161da177e4SLinus Torvalds 
9171da177e4SLinus Torvalds 
9188b05b773SMike Christie /* Lock or unlock the drive door. Don't use when st_request allocated. */
do_door_lock(struct scsi_tape * STp,int do_lock)9191da177e4SLinus Torvalds static int do_door_lock(struct scsi_tape * STp, int do_lock)
9201da177e4SLinus Torvalds {
921dccfa688SChristoph Hellwig 	int retval;
9221da177e4SLinus Torvalds 
923b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "%socking drive door.\n", do_lock ? "L" : "Unl");
924dccfa688SChristoph Hellwig 
925dccfa688SChristoph Hellwig 	retval = scsi_set_medium_removal(STp->device,
926dccfa688SChristoph Hellwig 			do_lock ? SCSI_REMOVAL_PREVENT : SCSI_REMOVAL_ALLOW);
927dccfa688SChristoph Hellwig 	if (!retval)
9281da177e4SLinus Torvalds 		STp->door_locked = do_lock ? ST_LOCKED_EXPLICIT : ST_UNLOCKED;
929dccfa688SChristoph Hellwig 	else
9301da177e4SLinus Torvalds 		STp->door_locked = ST_LOCK_FAILS;
9311da177e4SLinus Torvalds 	return retval;
9321da177e4SLinus Torvalds }
9331da177e4SLinus Torvalds 
9341da177e4SLinus Torvalds 
9351da177e4SLinus Torvalds /* Set the internal state after reset */
reset_state(struct scsi_tape * STp)9361da177e4SLinus Torvalds static void reset_state(struct scsi_tape *STp)
9371da177e4SLinus Torvalds {
9381da177e4SLinus Torvalds 	int i;
9391da177e4SLinus Torvalds 	struct st_partstat *STps;
9401da177e4SLinus Torvalds 
9411da177e4SLinus Torvalds 	STp->pos_unknown = 0;
9421da177e4SLinus Torvalds 	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
9431da177e4SLinus Torvalds 		STps = &(STp->ps[i]);
9441da177e4SLinus Torvalds 		STps->rw = ST_IDLE;
9451da177e4SLinus Torvalds 		STps->eof = ST_NOEOF;
9461da177e4SLinus Torvalds 		STps->at_sm = 0;
9471da177e4SLinus Torvalds 		STps->last_block_valid = 0;
9481da177e4SLinus Torvalds 		STps->drv_block = -1;
9491da177e4SLinus Torvalds 		STps->drv_file = -1;
9501da177e4SLinus Torvalds 	}
9511da177e4SLinus Torvalds 	if (STp->can_partitions) {
9521da177e4SLinus Torvalds 		STp->partition = find_partition(STp);
9531da177e4SLinus Torvalds 		if (STp->partition < 0)
9541da177e4SLinus Torvalds 			STp->partition = 0;
9551da177e4SLinus Torvalds 		STp->new_partition = STp->partition;
9561da177e4SLinus Torvalds 	}
9571da177e4SLinus Torvalds }
9581da177e4SLinus Torvalds 
9591da177e4SLinus Torvalds /* Test if the drive is ready. Returns either one of the codes below or a negative system
9601da177e4SLinus Torvalds    error code. */
9611da177e4SLinus Torvalds #define CHKRES_READY       0
9621da177e4SLinus Torvalds #define CHKRES_NEW_SESSION 1
9631da177e4SLinus Torvalds #define CHKRES_NOT_READY   2
9641da177e4SLinus Torvalds #define CHKRES_NO_TAPE     3
9651da177e4SLinus Torvalds 
9661da177e4SLinus Torvalds #define MAX_ATTENTIONS    10
9671da177e4SLinus Torvalds 
test_ready(struct scsi_tape * STp,int do_wait)9681da177e4SLinus Torvalds static int test_ready(struct scsi_tape *STp, int do_wait)
9691da177e4SLinus Torvalds {
9701da177e4SLinus Torvalds 	int attentions, waits, max_wait, scode;
9711da177e4SLinus Torvalds 	int retval = CHKRES_READY, new_session = 0;
9721da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
97302ae2c0eSKai Makisara 	struct st_request *SRpnt = NULL;
9741da177e4SLinus Torvalds 	struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
9751da177e4SLinus Torvalds 
9761da177e4SLinus Torvalds 	max_wait = do_wait ? ST_BLOCK_SECONDS : 0;
9771da177e4SLinus Torvalds 
9781da177e4SLinus Torvalds 	for (attentions=waits=0; ; ) {
9791da177e4SLinus Torvalds 		memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
9801da177e4SLinus Torvalds 		cmd[0] = TEST_UNIT_READY;
98102ae2c0eSKai Makisara 		SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE,
98202ae2c0eSKai Makisara 				   STp->long_timeout, MAX_READY_RETRIES, 1);
9831da177e4SLinus Torvalds 
98402ae2c0eSKai Makisara 		if (!SRpnt) {
98502ae2c0eSKai Makisara 			retval = (STp->buffer)->syscall_result;
9861da177e4SLinus Torvalds 			break;
98702ae2c0eSKai Makisara 		}
9881da177e4SLinus Torvalds 
9891da177e4SLinus Torvalds 		if (cmdstatp->have_sense) {
9901da177e4SLinus Torvalds 
9911da177e4SLinus Torvalds 			scode = cmdstatp->sense_hdr.sense_key;
9921da177e4SLinus Torvalds 
9931da177e4SLinus Torvalds 			if (scode == UNIT_ATTENTION) { /* New media? */
9941da177e4SLinus Torvalds 				new_session = 1;
9951da177e4SLinus Torvalds 				if (attentions < MAX_ATTENTIONS) {
9961da177e4SLinus Torvalds 					attentions++;
9971da177e4SLinus Torvalds 					continue;
9981da177e4SLinus Torvalds 				}
9991da177e4SLinus Torvalds 				else {
10001da177e4SLinus Torvalds 					retval = (-EIO);
10011da177e4SLinus Torvalds 					break;
10021da177e4SLinus Torvalds 				}
10031da177e4SLinus Torvalds 			}
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 			if (scode == NOT_READY) {
10061da177e4SLinus Torvalds 				if (waits < max_wait) {
10071da177e4SLinus Torvalds 					if (msleep_interruptible(1000)) {
10081da177e4SLinus Torvalds 						retval = (-EINTR);
10091da177e4SLinus Torvalds 						break;
10101da177e4SLinus Torvalds 					}
10111da177e4SLinus Torvalds 					waits++;
10121da177e4SLinus Torvalds 					continue;
10131da177e4SLinus Torvalds 				}
10141da177e4SLinus Torvalds 				else {
10151da177e4SLinus Torvalds 					if ((STp->device)->scsi_level >= SCSI_2 &&
10161da177e4SLinus Torvalds 					    cmdstatp->sense_hdr.asc == 0x3a)	/* Check ASC */
10171da177e4SLinus Torvalds 						retval = CHKRES_NO_TAPE;
10181da177e4SLinus Torvalds 					else
10191da177e4SLinus Torvalds 						retval = CHKRES_NOT_READY;
10201da177e4SLinus Torvalds 					break;
10211da177e4SLinus Torvalds 				}
10221da177e4SLinus Torvalds 			}
10231da177e4SLinus Torvalds 		}
10241da177e4SLinus Torvalds 
10251da177e4SLinus Torvalds 		retval = (STp->buffer)->syscall_result;
10261da177e4SLinus Torvalds 		if (!retval)
10271da177e4SLinus Torvalds 			retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY;
10281da177e4SLinus Torvalds 		break;
10291da177e4SLinus Torvalds 	}
10301da177e4SLinus Torvalds 
103102ae2c0eSKai Makisara 	if (SRpnt != NULL)
10328b05b773SMike Christie 		st_release_request(SRpnt);
10331da177e4SLinus Torvalds 	return retval;
10341da177e4SLinus Torvalds }
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 
10371da177e4SLinus Torvalds /* See if the drive is ready and gather information about the tape. Return values:
10381da177e4SLinus Torvalds    < 0   negative error code from errno.h
10391da177e4SLinus Torvalds    0     drive ready
10401da177e4SLinus Torvalds    1     drive not ready (possibly no tape)
10411da177e4SLinus Torvalds */
check_tape(struct scsi_tape * STp,struct file * filp)10421da177e4SLinus Torvalds static int check_tape(struct scsi_tape *STp, struct file *filp)
10431da177e4SLinus Torvalds {
10441da177e4SLinus Torvalds 	int i, retval, new_session = 0, do_wait;
10451da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE], saved_cleaning;
10461da177e4SLinus Torvalds 	unsigned short st_flags = filp->f_flags;
10478b05b773SMike Christie 	struct st_request *SRpnt = NULL;
10481da177e4SLinus Torvalds 	struct st_modedef *STm;
10491da177e4SLinus Torvalds 	struct st_partstat *STps;
1050496ad9aaSAl Viro 	struct inode *inode = file_inode(filp);
10511da177e4SLinus Torvalds 	int mode = TAPE_MODE(inode);
10521da177e4SLinus Torvalds 
10531da177e4SLinus Torvalds 	STp->ready = ST_READY;
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds 	if (mode != STp->current_mode) {
1056b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Mode change from %d to %d.\n",
1057b30d8bcaSHannes Reinecke 			    STp->current_mode, mode);
10581da177e4SLinus Torvalds 		new_session = 1;
10591da177e4SLinus Torvalds 		STp->current_mode = mode;
10601da177e4SLinus Torvalds 	}
10611da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
10621da177e4SLinus Torvalds 
10631da177e4SLinus Torvalds 	saved_cleaning = STp->cleaning_req;
10641da177e4SLinus Torvalds 	STp->cleaning_req = 0;
10651da177e4SLinus Torvalds 
10661da177e4SLinus Torvalds 	do_wait = ((filp->f_flags & O_NONBLOCK) == 0);
10671da177e4SLinus Torvalds 	retval = test_ready(STp, do_wait);
10681da177e4SLinus Torvalds 
10691da177e4SLinus Torvalds 	if (retval < 0)
10701da177e4SLinus Torvalds 	    goto err_out;
10711da177e4SLinus Torvalds 
10721da177e4SLinus Torvalds 	if (retval == CHKRES_NEW_SESSION) {
10731da177e4SLinus Torvalds 		STp->pos_unknown = 0;
10741da177e4SLinus Torvalds 		STp->partition = STp->new_partition = 0;
10751da177e4SLinus Torvalds 		if (STp->can_partitions)
10761da177e4SLinus Torvalds 			STp->nbr_partitions = 1; /* This guess will be updated later
10771da177e4SLinus Torvalds                                                     if necessary */
10781da177e4SLinus Torvalds 		for (i = 0; i < ST_NBR_PARTITIONS; i++) {
10791da177e4SLinus Torvalds 			STps = &(STp->ps[i]);
10801da177e4SLinus Torvalds 			STps->rw = ST_IDLE;
10811da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
10821da177e4SLinus Torvalds 			STps->at_sm = 0;
10831da177e4SLinus Torvalds 			STps->last_block_valid = 0;
10841da177e4SLinus Torvalds 			STps->drv_block = 0;
10851da177e4SLinus Torvalds 			STps->drv_file = 0;
10861da177e4SLinus Torvalds 		}
10871da177e4SLinus Torvalds 		new_session = 1;
10881da177e4SLinus Torvalds 	}
10891da177e4SLinus Torvalds 	else {
10901da177e4SLinus Torvalds 		STp->cleaning_req |= saved_cleaning;
10911da177e4SLinus Torvalds 
10921da177e4SLinus Torvalds 		if (retval == CHKRES_NOT_READY || retval == CHKRES_NO_TAPE) {
10931da177e4SLinus Torvalds 			if (retval == CHKRES_NO_TAPE)
10941da177e4SLinus Torvalds 				STp->ready = ST_NO_TAPE;
10951da177e4SLinus Torvalds 			else
10961da177e4SLinus Torvalds 				STp->ready = ST_NOT_READY;
10971da177e4SLinus Torvalds 
10981da177e4SLinus Torvalds 			STp->density = 0;	/* Clear the erroneous "residue" */
10991da177e4SLinus Torvalds 			STp->write_prot = 0;
11001da177e4SLinus Torvalds 			STp->block_size = 0;
11011da177e4SLinus Torvalds 			STp->ps[0].drv_file = STp->ps[0].drv_block = (-1);
11021da177e4SLinus Torvalds 			STp->partition = STp->new_partition = 0;
11031da177e4SLinus Torvalds 			STp->door_locked = ST_UNLOCKED;
11041da177e4SLinus Torvalds 			return CHKRES_NOT_READY;
11051da177e4SLinus Torvalds 		}
11061da177e4SLinus Torvalds 	}
11071da177e4SLinus Torvalds 
11081da177e4SLinus Torvalds 	if (STp->omit_blklims)
11091da177e4SLinus Torvalds 		STp->min_block = STp->max_block = (-1);
11101da177e4SLinus Torvalds 	else {
11111da177e4SLinus Torvalds 		memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
11121da177e4SLinus Torvalds 		cmd[0] = READ_BLOCK_LIMITS;
11131da177e4SLinus Torvalds 
111402ae2c0eSKai Makisara 		SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE,
1115a02488edSJames Bottomley 				   STp->device->request_queue->rq_timeout,
111602ae2c0eSKai Makisara 				   MAX_READY_RETRIES, 1);
111702ae2c0eSKai Makisara 		if (!SRpnt) {
111802ae2c0eSKai Makisara 			retval = (STp->buffer)->syscall_result;
11191da177e4SLinus Torvalds 			goto err_out;
11201da177e4SLinus Torvalds 		}
11211da177e4SLinus Torvalds 
11228b05b773SMike Christie 		if (!SRpnt->result && !STp->buffer->cmdstat.have_sense) {
11231da177e4SLinus Torvalds 			STp->max_block = ((STp->buffer)->b_data[1] << 16) |
11241da177e4SLinus Torvalds 			    ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3];
11251da177e4SLinus Torvalds 			STp->min_block = ((STp->buffer)->b_data[4] << 8) |
11261da177e4SLinus Torvalds 			    (STp->buffer)->b_data[5];
11271da177e4SLinus Torvalds 			if ( DEB( debugging || ) !STp->inited)
1128b30d8bcaSHannes Reinecke 				st_printk(KERN_INFO, STp,
1129b30d8bcaSHannes Reinecke 					  "Block limits %d - %d bytes.\n",
11301da177e4SLinus Torvalds 					  STp->min_block, STp->max_block);
11311da177e4SLinus Torvalds 		} else {
11321da177e4SLinus Torvalds 			STp->min_block = STp->max_block = (-1);
1133b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Can't read block limits.\n");
11341da177e4SLinus Torvalds 		}
11351da177e4SLinus Torvalds 	}
11361da177e4SLinus Torvalds 
11371da177e4SLinus Torvalds 	memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE);
11381da177e4SLinus Torvalds 	cmd[0] = MODE_SENSE;
11391da177e4SLinus Torvalds 	cmd[4] = 12;
11401da177e4SLinus Torvalds 
114102ae2c0eSKai Makisara 	SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE,
1142a02488edSJames Bottomley 			   STp->device->request_queue->rq_timeout,
114302ae2c0eSKai Makisara 			   MAX_READY_RETRIES, 1);
114402ae2c0eSKai Makisara 	if (!SRpnt) {
114502ae2c0eSKai Makisara 		retval = (STp->buffer)->syscall_result;
11461da177e4SLinus Torvalds 		goto err_out;
11471da177e4SLinus Torvalds 	}
11481da177e4SLinus Torvalds 
11491da177e4SLinus Torvalds 	if ((STp->buffer)->syscall_result != 0) {
1150b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "No Mode Sense.\n");
11511da177e4SLinus Torvalds 		STp->block_size = ST_DEFAULT_BLOCK;	/* Educated guess (?) */
11521da177e4SLinus Torvalds 		(STp->buffer)->syscall_result = 0;	/* Prevent error propagation */
11531da177e4SLinus Torvalds 		STp->drv_write_prot = 0;
11541da177e4SLinus Torvalds 	} else {
1155b30d8bcaSHannes Reinecke 		DEBC_printk(STp,"Mode sense. Length %d, "
1156b30d8bcaSHannes Reinecke 			    "medium %x, WBS %x, BLL %d\n",
1157b30d8bcaSHannes Reinecke 			    (STp->buffer)->b_data[0],
1158b30d8bcaSHannes Reinecke 			    (STp->buffer)->b_data[1],
1159b30d8bcaSHannes Reinecke 			    (STp->buffer)->b_data[2],
1160b30d8bcaSHannes Reinecke 			    (STp->buffer)->b_data[3]);
11611da177e4SLinus Torvalds 
11621da177e4SLinus Torvalds 		if ((STp->buffer)->b_data[3] >= 8) {
11631da177e4SLinus Torvalds 			STp->drv_buffer = ((STp->buffer)->b_data[2] >> 4) & 7;
11641da177e4SLinus Torvalds 			STp->density = (STp->buffer)->b_data[4];
11651da177e4SLinus Torvalds 			STp->block_size = (STp->buffer)->b_data[9] * 65536 +
11661da177e4SLinus Torvalds 			    (STp->buffer)->b_data[10] * 256 + (STp->buffer)->b_data[11];
1167b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Density %x, tape length: %x, "
1168b30d8bcaSHannes Reinecke 				    "drv buffer: %d\n",
1169b30d8bcaSHannes Reinecke 				    STp->density,
1170b30d8bcaSHannes Reinecke 				    (STp->buffer)->b_data[5] * 65536 +
1171b30d8bcaSHannes Reinecke 				    (STp->buffer)->b_data[6] * 256 +
1172b30d8bcaSHannes Reinecke 				    (STp->buffer)->b_data[7],
1173b30d8bcaSHannes Reinecke 				    STp->drv_buffer);
11741da177e4SLinus Torvalds 		}
11751da177e4SLinus Torvalds 		STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0;
1176c743e44fSLee Duncan 		if (!STp->drv_buffer && STp->immediate_filemark) {
1177b30d8bcaSHannes Reinecke 			st_printk(KERN_WARNING, STp,
1178b30d8bcaSHannes Reinecke 				  "non-buffered tape: disabling "
1179b30d8bcaSHannes Reinecke 				  "writing immediate filemarks\n");
1180c743e44fSLee Duncan 			STp->immediate_filemark = 0;
1181c743e44fSLee Duncan 		}
11821da177e4SLinus Torvalds 	}
11838b05b773SMike Christie 	st_release_request(SRpnt);
11841da177e4SLinus Torvalds 	SRpnt = NULL;
11851da177e4SLinus Torvalds 	STp->inited = 1;
11861da177e4SLinus Torvalds 
11871da177e4SLinus Torvalds 	if (STp->block_size > 0)
11881da177e4SLinus Torvalds 		(STp->buffer)->buffer_blocks =
11891da177e4SLinus Torvalds 			(STp->buffer)->buffer_size / STp->block_size;
11901da177e4SLinus Torvalds 	else
11911da177e4SLinus Torvalds 		(STp->buffer)->buffer_blocks = 1;
11921da177e4SLinus Torvalds 	(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
11931da177e4SLinus Torvalds 
1194b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Block size: %d, buffer size: %d (%d blocks).\n",
11951da177e4SLinus Torvalds 		    STp->block_size, (STp->buffer)->buffer_size,
1196b30d8bcaSHannes Reinecke 		    (STp->buffer)->buffer_blocks);
11971da177e4SLinus Torvalds 
11981da177e4SLinus Torvalds 	if (STp->drv_write_prot) {
11991da177e4SLinus Torvalds 		STp->write_prot = 1;
12001da177e4SLinus Torvalds 
1201b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Write protected\n");
12021da177e4SLinus Torvalds 
12031da177e4SLinus Torvalds 		if (do_wait &&
12041da177e4SLinus Torvalds 		    ((st_flags & O_ACCMODE) == O_WRONLY ||
12051da177e4SLinus Torvalds 		     (st_flags & O_ACCMODE) == O_RDWR)) {
12061da177e4SLinus Torvalds 			retval = (-EROFS);
12071da177e4SLinus Torvalds 			goto err_out;
12081da177e4SLinus Torvalds 		}
12091da177e4SLinus Torvalds 	}
12101da177e4SLinus Torvalds 
12111da177e4SLinus Torvalds 	if (STp->can_partitions && STp->nbr_partitions < 1) {
12121da177e4SLinus Torvalds 		/* This code is reached when the device is opened for the first time
12131da177e4SLinus Torvalds 		   after the driver has been initialized with tape in the drive and the
12141da177e4SLinus Torvalds 		   partition support has been enabled. */
1215b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Updating partition number in status.\n");
12161da177e4SLinus Torvalds 		if ((STp->partition = find_partition(STp)) < 0) {
12171da177e4SLinus Torvalds 			retval = STp->partition;
12181da177e4SLinus Torvalds 			goto err_out;
12191da177e4SLinus Torvalds 		}
12201da177e4SLinus Torvalds 		STp->new_partition = STp->partition;
12211da177e4SLinus Torvalds 		STp->nbr_partitions = 1; /* This guess will be updated when necessary */
12221da177e4SLinus Torvalds 	}
12231da177e4SLinus Torvalds 
12241da177e4SLinus Torvalds 	if (new_session) {	/* Change the drive parameters for the new mode */
12251da177e4SLinus Torvalds 		STp->density_changed = STp->blksize_changed = 0;
12261da177e4SLinus Torvalds 		STp->compression_changed = 0;
12271da177e4SLinus Torvalds 		if (!(STm->defaults_for_writes) &&
12281da177e4SLinus Torvalds 		    (retval = set_mode_densblk(STp, STm)) < 0)
12291da177e4SLinus Torvalds 		    goto err_out;
12301da177e4SLinus Torvalds 
12311da177e4SLinus Torvalds 		if (STp->default_drvbuffer != 0xff) {
12321da177e4SLinus Torvalds 			if (st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer))
1233b30d8bcaSHannes Reinecke 				st_printk(KERN_WARNING, STp,
1234b30d8bcaSHannes Reinecke 					  "Can't set default drive "
1235b30d8bcaSHannes Reinecke 					  "buffering to %d.\n",
1236b30d8bcaSHannes Reinecke 					  STp->default_drvbuffer);
12371da177e4SLinus Torvalds 		}
12381da177e4SLinus Torvalds 	}
12391da177e4SLinus Torvalds 
12401da177e4SLinus Torvalds 	return CHKRES_READY;
12411da177e4SLinus Torvalds 
12421da177e4SLinus Torvalds  err_out:
12431da177e4SLinus Torvalds 	return retval;
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds 
12461da177e4SLinus Torvalds 
1247b3369c68SJonathan Corbet /* Open the device. Needs to take the BKL only because of incrementing the SCSI host
12481da177e4SLinus Torvalds    module count. */
st_open(struct inode * inode,struct file * filp)12491da177e4SLinus Torvalds static int st_open(struct inode *inode, struct file *filp)
12501da177e4SLinus Torvalds {
12511da177e4SLinus Torvalds 	int i, retval = (-EIO);
125246a243f7SOliver Neukum 	int resumed = 0;
12531da177e4SLinus Torvalds 	struct scsi_tape *STp;
12541da177e4SLinus Torvalds 	struct st_partstat *STps;
12551da177e4SLinus Torvalds 	int dev = TAPE_NR(inode);
12561da177e4SLinus Torvalds 
12571da177e4SLinus Torvalds 	/*
12581da177e4SLinus Torvalds 	 * We really want to do nonseekable_open(inode, filp); here, but some
12591da177e4SLinus Torvalds 	 * versions of tar incorrectly call lseek on tapes and bail out if that
12601da177e4SLinus Torvalds 	 * fails.  So we disallow pread() and pwrite(), but permit lseeks.
12611da177e4SLinus Torvalds 	 */
12621da177e4SLinus Torvalds 	filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
12631da177e4SLinus Torvalds 
1264b3369c68SJonathan Corbet 	if (!(STp = scsi_tape_get(dev))) {
1265f03a5670SKai Makisara 		return -ENXIO;
1266b3369c68SJonathan Corbet 	}
1267f03a5670SKai Makisara 
12681da177e4SLinus Torvalds 	filp->private_data = STp;
12691da177e4SLinus Torvalds 
12706c648d95SJeff Mahoney 	spin_lock(&st_use_lock);
12711da177e4SLinus Torvalds 	if (STp->in_use) {
12726c648d95SJeff Mahoney 		spin_unlock(&st_use_lock);
1273b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Device already in use.\n");
1274c8c165deSLv Yunlong 		scsi_tape_put(STp);
12751da177e4SLinus Torvalds 		return (-EBUSY);
12761da177e4SLinus Torvalds 	}
12771da177e4SLinus Torvalds 
12781da177e4SLinus Torvalds 	STp->in_use = 1;
12796c648d95SJeff Mahoney 	spin_unlock(&st_use_lock);
12801da177e4SLinus Torvalds 	STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0;
12811da177e4SLinus Torvalds 
128246a243f7SOliver Neukum 	if (scsi_autopm_get_device(STp->device) < 0) {
128346a243f7SOliver Neukum 		retval = -EIO;
128446a243f7SOliver Neukum 		goto err_out;
128546a243f7SOliver Neukum 	}
128646a243f7SOliver Neukum 	resumed = 1;
12871da177e4SLinus Torvalds 	if (!scsi_block_when_processing_errors(STp->device)) {
12881da177e4SLinus Torvalds 		retval = (-ENXIO);
12891da177e4SLinus Torvalds 		goto err_out;
12901da177e4SLinus Torvalds 	}
12911da177e4SLinus Torvalds 
12921da177e4SLinus Torvalds 	/* See that we have at least a one page buffer available */
1293aaff5ebaSChristoph Hellwig 	if (!enlarge_buffer(STp->buffer, PAGE_SIZE)) {
1294b30d8bcaSHannes Reinecke 		st_printk(KERN_WARNING, STp,
1295b30d8bcaSHannes Reinecke 			  "Can't allocate one page tape buffer.\n");
12961da177e4SLinus Torvalds 		retval = (-EOVERFLOW);
12971da177e4SLinus Torvalds 		goto err_out;
12981da177e4SLinus Torvalds 	}
12991da177e4SLinus Torvalds 
130040f6b36cSKai Makisara 	(STp->buffer)->cleared = 0;
13011da177e4SLinus Torvalds 	(STp->buffer)->writing = 0;
13021da177e4SLinus Torvalds 	(STp->buffer)->syscall_result = 0;
13031da177e4SLinus Torvalds 
13041da177e4SLinus Torvalds 	STp->write_prot = ((filp->f_flags & O_ACCMODE) == O_RDONLY);
13051da177e4SLinus Torvalds 
13061da177e4SLinus Torvalds 	STp->dirty = 0;
13071da177e4SLinus Torvalds 	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
13081da177e4SLinus Torvalds 		STps = &(STp->ps[i]);
13091da177e4SLinus Torvalds 		STps->rw = ST_IDLE;
13101da177e4SLinus Torvalds 	}
13119abe16c6SKai Makisara 	STp->try_dio_now = STp->try_dio;
13121da177e4SLinus Torvalds 	STp->recover_count = 0;
13131da177e4SLinus Torvalds 	DEB( STp->nbr_waits = STp->nbr_finished = 0;
1314deee13dfSKai Makisara 	     STp->nbr_requests = STp->nbr_dio = STp->nbr_pages = 0; )
13151da177e4SLinus Torvalds 
13161da177e4SLinus Torvalds 	retval = check_tape(STp, filp);
13171da177e4SLinus Torvalds 	if (retval < 0)
13181da177e4SLinus Torvalds 		goto err_out;
13191da177e4SLinus Torvalds 	if ((filp->f_flags & O_NONBLOCK) == 0 &&
13201da177e4SLinus Torvalds 	    retval != CHKRES_READY) {
1321413f7327SKai Makisara 		if (STp->ready == NO_TAPE)
1322413f7327SKai Makisara 			retval = (-ENOMEDIUM);
1323413f7327SKai Makisara 		else
13241da177e4SLinus Torvalds 			retval = (-EIO);
13251da177e4SLinus Torvalds 		goto err_out;
13261da177e4SLinus Torvalds 	}
13271da177e4SLinus Torvalds 	return 0;
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds  err_out:
13301da177e4SLinus Torvalds 	normalize_buffer(STp->buffer);
13310644f539SHannes Reinecke 	spin_lock(&st_use_lock);
13321da177e4SLinus Torvalds 	STp->in_use = 0;
13330644f539SHannes Reinecke 	spin_unlock(&st_use_lock);
133446a243f7SOliver Neukum 	if (resumed)
133546a243f7SOliver Neukum 		scsi_autopm_put_device(STp->device);
1336e7ac6c66SSeymour, Shane M 	scsi_tape_put(STp);
13371da177e4SLinus Torvalds 	return retval;
13381da177e4SLinus Torvalds 
13391da177e4SLinus Torvalds }
13401da177e4SLinus Torvalds 
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds /* Flush the tape buffer before close */
st_flush(struct file * filp,fl_owner_t id)134375e1fcc0SMiklos Szeredi static int st_flush(struct file *filp, fl_owner_t id)
13441da177e4SLinus Torvalds {
13451da177e4SLinus Torvalds 	int result = 0, result2;
13461da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
13478b05b773SMike Christie 	struct st_request *SRpnt;
13481da177e4SLinus Torvalds 	struct scsi_tape *STp = filp->private_data;
13491da177e4SLinus Torvalds 	struct st_modedef *STm = &(STp->modes[STp->current_mode]);
13501da177e4SLinus Torvalds 	struct st_partstat *STps = &(STp->ps[STp->partition]);
13511da177e4SLinus Torvalds 
13521da177e4SLinus Torvalds 	if (file_count(filp) > 1)
13531da177e4SLinus Torvalds 		return 0;
13541da177e4SLinus Torvalds 
13551da177e4SLinus Torvalds 	if (STps->rw == ST_WRITING && !STp->pos_unknown) {
13568ef8d594SAdrian Bunk 		result = st_flush_write_buffer(STp);
13571da177e4SLinus Torvalds 		if (result != 0 && result != (-ENOSPC))
13581da177e4SLinus Torvalds 			goto out;
13591da177e4SLinus Torvalds 	}
13601da177e4SLinus Torvalds 
13611da177e4SLinus Torvalds 	if (STp->can_partitions &&
13621da177e4SLinus Torvalds 	    (result2 = switch_partition(STp)) < 0) {
1363b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "switch_partition at close failed.\n");
13641da177e4SLinus Torvalds 		if (result == 0)
13651da177e4SLinus Torvalds 			result = result2;
13661da177e4SLinus Torvalds 		goto out;
13671da177e4SLinus Torvalds 	}
13681da177e4SLinus Torvalds 
13691da177e4SLinus Torvalds 	DEBC( if (STp->nbr_requests)
1370b30d8bcaSHannes Reinecke 		st_printk(KERN_DEBUG, STp,
1371b30d8bcaSHannes Reinecke 			  "Number of r/w requests %d, dio used in %d, "
1372b30d8bcaSHannes Reinecke 			  "pages %d.\n", STp->nbr_requests, STp->nbr_dio,
1373b30d8bcaSHannes Reinecke 			  STp->nbr_pages));
13741da177e4SLinus Torvalds 
13751da177e4SLinus Torvalds 	if (STps->rw == ST_WRITING && !STp->pos_unknown) {
13761da177e4SLinus Torvalds 		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
13771da177e4SLinus Torvalds 
1378b30d8bcaSHannes Reinecke #if DEBUG
1379b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Async write waits %d, finished %d.\n",
1380b30d8bcaSHannes Reinecke 			    STp->nbr_waits, STp->nbr_finished);
1381b30d8bcaSHannes Reinecke #endif
13821da177e4SLinus Torvalds 		memset(cmd, 0, MAX_COMMAND_SIZE);
13831da177e4SLinus Torvalds 		cmd[0] = WRITE_FILEMARKS;
1384c743e44fSLee Duncan 		if (STp->immediate_filemark)
1385c743e44fSLee Duncan 			cmd[1] = 1;
13861da177e4SLinus Torvalds 		cmd[4] = 1 + STp->two_fm;
13871da177e4SLinus Torvalds 
138802ae2c0eSKai Makisara 		SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
1389212cd8bfSFUJITA Tomonori 				   STp->device->request_queue->rq_timeout,
139002ae2c0eSKai Makisara 				   MAX_WRITE_RETRIES, 1);
139102ae2c0eSKai Makisara 		if (!SRpnt) {
139202ae2c0eSKai Makisara 			result = (STp->buffer)->syscall_result;
13931da177e4SLinus Torvalds 			goto out;
13941da177e4SLinus Torvalds 		}
13951da177e4SLinus Torvalds 
13961da177e4SLinus Torvalds 		if (STp->buffer->syscall_result == 0 ||
13971da177e4SLinus Torvalds 		    (cmdstatp->have_sense && !cmdstatp->deferred &&
13981da177e4SLinus Torvalds 		     (cmdstatp->flags & SENSE_EOM) &&
13991da177e4SLinus Torvalds 		     (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
14001da177e4SLinus Torvalds 		      cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
14011da177e4SLinus Torvalds 		     (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0))) {
14021da177e4SLinus Torvalds 			/* Write successful at EOM */
14038b05b773SMike Christie 			st_release_request(SRpnt);
14041da177e4SLinus Torvalds 			SRpnt = NULL;
14051da177e4SLinus Torvalds 			if (STps->drv_file >= 0)
14061da177e4SLinus Torvalds 				STps->drv_file++;
14071da177e4SLinus Torvalds 			STps->drv_block = 0;
14081da177e4SLinus Torvalds 			if (STp->two_fm)
14091da177e4SLinus Torvalds 				cross_eof(STp, 0);
14101da177e4SLinus Torvalds 			STps->eof = ST_FM;
14111da177e4SLinus Torvalds 		}
14121da177e4SLinus Torvalds 		else { /* Write error */
14138b05b773SMike Christie 			st_release_request(SRpnt);
14141da177e4SLinus Torvalds 			SRpnt = NULL;
1415b30d8bcaSHannes Reinecke 			st_printk(KERN_ERR, STp,
1416b30d8bcaSHannes Reinecke 				  "Error on write filemark.\n");
14171da177e4SLinus Torvalds 			if (result == 0)
14181da177e4SLinus Torvalds 				result = (-EIO);
14191da177e4SLinus Torvalds 		}
14201da177e4SLinus Torvalds 
1421b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Buffer flushed, %d EOF(s) written\n", cmd[4]);
14221da177e4SLinus Torvalds 	} else if (!STp->rew_at_close) {
14231da177e4SLinus Torvalds 		STps = &(STp->ps[STp->partition]);
14241da177e4SLinus Torvalds 		if (!STm->sysv || STps->rw != ST_READING) {
14251da177e4SLinus Torvalds 			if (STp->can_bsr)
14261da177e4SLinus Torvalds 				result = flush_buffer(STp, 0);
14271da177e4SLinus Torvalds 			else if (STps->eof == ST_FM_HIT) {
14281da177e4SLinus Torvalds 				result = cross_eof(STp, 0);
14291da177e4SLinus Torvalds 				if (result) {
14301da177e4SLinus Torvalds 					if (STps->drv_file >= 0)
14311da177e4SLinus Torvalds 						STps->drv_file++;
14321da177e4SLinus Torvalds 					STps->drv_block = 0;
14331da177e4SLinus Torvalds 					STps->eof = ST_FM;
14341da177e4SLinus Torvalds 				} else
14351da177e4SLinus Torvalds 					STps->eof = ST_NOEOF;
14361da177e4SLinus Torvalds 			}
14371da177e4SLinus Torvalds 		} else if ((STps->eof == ST_NOEOF &&
14381da177e4SLinus Torvalds 			    !(result = cross_eof(STp, 1))) ||
14391da177e4SLinus Torvalds 			   STps->eof == ST_FM_HIT) {
14401da177e4SLinus Torvalds 			if (STps->drv_file >= 0)
14411da177e4SLinus Torvalds 				STps->drv_file++;
14421da177e4SLinus Torvalds 			STps->drv_block = 0;
14431da177e4SLinus Torvalds 			STps->eof = ST_FM;
14441da177e4SLinus Torvalds 		}
14451da177e4SLinus Torvalds 	}
14461da177e4SLinus Torvalds 
14471da177e4SLinus Torvalds       out:
14481da177e4SLinus Torvalds 	if (STp->rew_at_close) {
14491da177e4SLinus Torvalds 		result2 = st_int_ioctl(STp, MTREW, 1);
14501da177e4SLinus Torvalds 		if (result == 0)
14511da177e4SLinus Torvalds 			result = result2;
14521da177e4SLinus Torvalds 	}
14531da177e4SLinus Torvalds 	return result;
14541da177e4SLinus Torvalds }
14551da177e4SLinus Torvalds 
14561da177e4SLinus Torvalds 
14571da177e4SLinus Torvalds /* Close the device and release it. BKL is not needed: this is the only thread
14581da177e4SLinus Torvalds    accessing this tape. */
st_release(struct inode * inode,struct file * filp)14591da177e4SLinus Torvalds static int st_release(struct inode *inode, struct file *filp)
14601da177e4SLinus Torvalds {
14611da177e4SLinus Torvalds 	struct scsi_tape *STp = filp->private_data;
14621da177e4SLinus Torvalds 
14631da177e4SLinus Torvalds 	if (STp->door_locked == ST_LOCKED_AUTO)
14641da177e4SLinus Torvalds 		do_door_lock(STp, 0);
14651da177e4SLinus Torvalds 
14661da177e4SLinus Torvalds 	normalize_buffer(STp->buffer);
14676c648d95SJeff Mahoney 	spin_lock(&st_use_lock);
14681da177e4SLinus Torvalds 	STp->in_use = 0;
14696c648d95SJeff Mahoney 	spin_unlock(&st_use_lock);
147046a243f7SOliver Neukum 	scsi_autopm_put_device(STp->device);
1471f03a5670SKai Makisara 	scsi_tape_put(STp);
14721da177e4SLinus Torvalds 
1473ec341439SJason Yan 	return 0;
14741da177e4SLinus Torvalds }
1475ec341439SJason Yan 
14761da177e4SLinus Torvalds /* The checks common to both reading and writing */
rw_checks(struct scsi_tape * STp,struct file * filp,size_t count)14771da177e4SLinus Torvalds static ssize_t rw_checks(struct scsi_tape *STp, struct file *filp, size_t count)
14781da177e4SLinus Torvalds {
14791da177e4SLinus Torvalds 	ssize_t retval = 0;
14801da177e4SLinus Torvalds 
14811da177e4SLinus Torvalds 	/*
14821da177e4SLinus Torvalds 	 * If we are in the middle of error recovery, don't let anyone
14831da177e4SLinus Torvalds 	 * else try and use this device.  Also, if error recovery fails, it
14841da177e4SLinus Torvalds 	 * may try and take the device offline, in which case all further
14851da177e4SLinus Torvalds 	 * access to the device is prohibited.
14861da177e4SLinus Torvalds 	 */
14871da177e4SLinus Torvalds 	if (!scsi_block_when_processing_errors(STp->device)) {
14881da177e4SLinus Torvalds 		retval = (-ENXIO);
14891da177e4SLinus Torvalds 		goto out;
14901da177e4SLinus Torvalds 	}
14911da177e4SLinus Torvalds 
14921da177e4SLinus Torvalds 	if (STp->ready != ST_READY) {
14931da177e4SLinus Torvalds 		if (STp->ready == ST_NO_TAPE)
14941da177e4SLinus Torvalds 			retval = (-ENOMEDIUM);
14951da177e4SLinus Torvalds 		else
14961da177e4SLinus Torvalds 			retval = (-EIO);
14971da177e4SLinus Torvalds 		goto out;
14981da177e4SLinus Torvalds 	}
14991da177e4SLinus Torvalds 
15001da177e4SLinus Torvalds 	if (! STp->modes[STp->current_mode].defined) {
15011da177e4SLinus Torvalds 		retval = (-ENXIO);
15021da177e4SLinus Torvalds 		goto out;
15031da177e4SLinus Torvalds 	}
15041da177e4SLinus Torvalds 
15051da177e4SLinus Torvalds 
15061da177e4SLinus Torvalds 	/*
15071da177e4SLinus Torvalds 	 * If there was a bus reset, block further access
15081da177e4SLinus Torvalds 	 * to this device.
15091da177e4SLinus Torvalds 	 */
15101da177e4SLinus Torvalds 	if (STp->pos_unknown) {
15111da177e4SLinus Torvalds 		retval = (-EIO);
15121da177e4SLinus Torvalds 		goto out;
15131da177e4SLinus Torvalds 	}
15141da177e4SLinus Torvalds 
15151da177e4SLinus Torvalds 	if (count == 0)
15161da177e4SLinus Torvalds 		goto out;
15171da177e4SLinus Torvalds 
15181da177e4SLinus Torvalds 	DEB(
15191da177e4SLinus Torvalds 	if (!STp->in_use) {
1520b30d8bcaSHannes Reinecke 		st_printk(ST_DEB_MSG, STp,
1521b30d8bcaSHannes Reinecke 			  "Incorrect device.\n");
15221da177e4SLinus Torvalds 		retval = (-EIO);
15231da177e4SLinus Torvalds 		goto out;
15241da177e4SLinus Torvalds 	} ) /* end DEB */
15251da177e4SLinus Torvalds 
15261da177e4SLinus Torvalds 	if (STp->can_partitions &&
15271da177e4SLinus Torvalds 	    (retval = switch_partition(STp)) < 0)
15281da177e4SLinus Torvalds 		goto out;
15291da177e4SLinus Torvalds 
15301da177e4SLinus Torvalds 	if (STp->block_size == 0 && STp->max_block > 0 &&
15311da177e4SLinus Torvalds 	    (count < STp->min_block || count > STp->max_block)) {
15321da177e4SLinus Torvalds 		retval = (-EINVAL);
15331da177e4SLinus Torvalds 		goto out;
15341da177e4SLinus Torvalds 	}
15351da177e4SLinus Torvalds 
15361da177e4SLinus Torvalds 	if (STp->do_auto_lock && STp->door_locked == ST_UNLOCKED &&
15371da177e4SLinus Torvalds 	    !do_door_lock(STp, 1))
15381da177e4SLinus Torvalds 		STp->door_locked = ST_LOCKED_AUTO;
15391da177e4SLinus Torvalds 
15401da177e4SLinus Torvalds  out:
15411da177e4SLinus Torvalds 	return retval;
15421da177e4SLinus Torvalds }
15431da177e4SLinus Torvalds 
15441da177e4SLinus Torvalds 
setup_buffering(struct scsi_tape * STp,const char __user * buf,size_t count,int is_read)15451da177e4SLinus Torvalds static int setup_buffering(struct scsi_tape *STp, const char __user *buf,
15461da177e4SLinus Torvalds 			   size_t count, int is_read)
15471da177e4SLinus Torvalds {
15481da177e4SLinus Torvalds 	int i, bufsize, retval = 0;
15491da177e4SLinus Torvalds 	struct st_buffer *STbp = STp->buffer;
15501da177e4SLinus Torvalds 
15511da177e4SLinus Torvalds 	if (is_read)
15529abe16c6SKai Makisara 		i = STp->try_dio_now && try_rdio;
15531da177e4SLinus Torvalds 	else
15549abe16c6SKai Makisara 		i = STp->try_dio_now && try_wdio;
15558b05b773SMike Christie 
15561da177e4SLinus Torvalds 	if (i && ((unsigned long)buf & queue_dma_alignment(
15571da177e4SLinus Torvalds 					STp->device->request_queue)) == 0) {
15586620742fSFUJITA Tomonori 		i = sgl_map_user_pages(STbp, STbp->use_sg, (unsigned long)buf,
15596620742fSFUJITA Tomonori 				       count, (is_read ? READ : WRITE));
15601da177e4SLinus Torvalds 		if (i > 0) {
15611da177e4SLinus Torvalds 			STbp->do_dio = i;
15621da177e4SLinus Torvalds 			STbp->buffer_bytes = 0;   /* can be used as transfer counter */
15631da177e4SLinus Torvalds 		}
15641da177e4SLinus Torvalds 		else
15651da177e4SLinus Torvalds 			STbp->do_dio = 0;  /* fall back to buffering with any error */
15661da177e4SLinus Torvalds 		STbp->sg_segs = STbp->do_dio;
15671da177e4SLinus Torvalds 		DEB(
15681da177e4SLinus Torvalds 		     if (STbp->do_dio) {
15691da177e4SLinus Torvalds 			STp->nbr_dio++;
15701da177e4SLinus Torvalds 			STp->nbr_pages += STbp->do_dio;
15711da177e4SLinus Torvalds 		     }
15721da177e4SLinus Torvalds 		)
15731da177e4SLinus Torvalds 	} else
15741da177e4SLinus Torvalds 		STbp->do_dio = 0;
15751da177e4SLinus Torvalds 	DEB( STp->nbr_requests++; )
15761da177e4SLinus Torvalds 
15771da177e4SLinus Torvalds 	if (!STbp->do_dio) {
15781da177e4SLinus Torvalds 		if (STp->block_size)
15791da177e4SLinus Torvalds 			bufsize = STp->block_size > st_fixed_buffer_size ?
15801da177e4SLinus Torvalds 				STp->block_size : st_fixed_buffer_size;
158140f6b36cSKai Makisara 		else {
15821da177e4SLinus Torvalds 			bufsize = count;
158340f6b36cSKai Makisara 			/* Make sure that data from previous user is not leaked even if
158440f6b36cSKai Makisara 			   HBA does not return correct residual */
158540f6b36cSKai Makisara 			if (is_read && STp->sili && !STbp->cleared)
158640f6b36cSKai Makisara 				clear_buffer(STbp);
158740f6b36cSKai Makisara 		}
158840f6b36cSKai Makisara 
15891da177e4SLinus Torvalds 		if (bufsize > STbp->buffer_size &&
1590aaff5ebaSChristoph Hellwig 		    !enlarge_buffer(STbp, bufsize)) {
1591b30d8bcaSHannes Reinecke 			st_printk(KERN_WARNING, STp,
1592b30d8bcaSHannes Reinecke 				  "Can't allocate %d byte tape buffer.\n",
1593b30d8bcaSHannes Reinecke 				  bufsize);
15941da177e4SLinus Torvalds 			retval = (-EOVERFLOW);
15951da177e4SLinus Torvalds 			goto out;
15961da177e4SLinus Torvalds 		}
15971da177e4SLinus Torvalds 		if (STp->block_size)
15981da177e4SLinus Torvalds 			STbp->buffer_blocks = bufsize / STp->block_size;
15991da177e4SLinus Torvalds 	}
16001da177e4SLinus Torvalds 
16011da177e4SLinus Torvalds  out:
16021da177e4SLinus Torvalds 	return retval;
16031da177e4SLinus Torvalds }
16041da177e4SLinus Torvalds 
16051da177e4SLinus Torvalds 
16061da177e4SLinus Torvalds /* Can be called more than once after each setup_buffer() */
release_buffering(struct scsi_tape * STp,int is_read)1607787926b1SKai Makisara static void release_buffering(struct scsi_tape *STp, int is_read)
16081da177e4SLinus Torvalds {
16091da177e4SLinus Torvalds 	struct st_buffer *STbp;
16101da177e4SLinus Torvalds 
16111da177e4SLinus Torvalds 	STbp = STp->buffer;
16121da177e4SLinus Torvalds 	if (STbp->do_dio) {
16136620742fSFUJITA Tomonori 		sgl_unmap_user_pages(STbp, STbp->do_dio, is_read);
16141da177e4SLinus Torvalds 		STbp->do_dio = 0;
1615787926b1SKai Makisara 		STbp->sg_segs = 0;
16161da177e4SLinus Torvalds 	}
16171da177e4SLinus Torvalds }
16181da177e4SLinus Torvalds 
16191da177e4SLinus Torvalds 
16201da177e4SLinus Torvalds /* Write command */
16211da177e4SLinus Torvalds static ssize_t
st_write(struct file * filp,const char __user * buf,size_t count,loff_t * ppos)16221da177e4SLinus Torvalds st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
16231da177e4SLinus Torvalds {
16241da177e4SLinus Torvalds 	ssize_t total;
16251da177e4SLinus Torvalds 	ssize_t i, do_count, blks, transfer;
16261da177e4SLinus Torvalds 	ssize_t retval;
16271da177e4SLinus Torvalds 	int undone, retry_eot = 0, scode;
16281da177e4SLinus Torvalds 	int async_write;
16291da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
16301da177e4SLinus Torvalds 	const char __user *b_point;
16318b05b773SMike Christie 	struct st_request *SRpnt = NULL;
16321da177e4SLinus Torvalds 	struct scsi_tape *STp = filp->private_data;
16331da177e4SLinus Torvalds 	struct st_modedef *STm;
16341da177e4SLinus Torvalds 	struct st_partstat *STps;
16351da177e4SLinus Torvalds 	struct st_buffer *STbp;
16361da177e4SLinus Torvalds 
163728f85009SMatthias Kaehlcke 	if (mutex_lock_interruptible(&STp->lock))
16381da177e4SLinus Torvalds 		return -ERESTARTSYS;
16391da177e4SLinus Torvalds 
16401da177e4SLinus Torvalds 	retval = rw_checks(STp, filp, count);
16411da177e4SLinus Torvalds 	if (retval || count == 0)
16421da177e4SLinus Torvalds 		goto out;
16431da177e4SLinus Torvalds 
16441da177e4SLinus Torvalds 	/* Write must be integral number of blocks */
16451da177e4SLinus Torvalds 	if (STp->block_size != 0 && (count % STp->block_size) != 0) {
1646b30d8bcaSHannes Reinecke 		st_printk(KERN_WARNING, STp,
1647b30d8bcaSHannes Reinecke 			  "Write not multiple of tape block size.\n");
16481da177e4SLinus Torvalds 		retval = (-EINVAL);
16491da177e4SLinus Torvalds 		goto out;
16501da177e4SLinus Torvalds 	}
16511da177e4SLinus Torvalds 
16521da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
16531da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
16541da177e4SLinus Torvalds 
16551da177e4SLinus Torvalds 	if (STp->write_prot) {
16561da177e4SLinus Torvalds 		retval = (-EACCES);
16571da177e4SLinus Torvalds 		goto out;
16581da177e4SLinus Torvalds 	}
16591da177e4SLinus Torvalds 
16601da177e4SLinus Torvalds 
16611da177e4SLinus Torvalds 	if (STps->rw == ST_READING) {
16621da177e4SLinus Torvalds 		retval = flush_buffer(STp, 0);
16631da177e4SLinus Torvalds 		if (retval)
16641da177e4SLinus Torvalds 			goto out;
16651da177e4SLinus Torvalds 		STps->rw = ST_WRITING;
16661da177e4SLinus Torvalds 	} else if (STps->rw != ST_WRITING &&
16671da177e4SLinus Torvalds 		   STps->drv_file == 0 && STps->drv_block == 0) {
16681da177e4SLinus Torvalds 		if ((retval = set_mode_densblk(STp, STm)) < 0)
16691da177e4SLinus Torvalds 			goto out;
16701da177e4SLinus Torvalds 		if (STm->default_compression != ST_DONT_TOUCH &&
16711da177e4SLinus Torvalds 		    !(STp->compression_changed)) {
16721da177e4SLinus Torvalds 			if (st_compression(STp, (STm->default_compression == ST_YES))) {
1673b30d8bcaSHannes Reinecke 				st_printk(KERN_WARNING, STp,
1674b30d8bcaSHannes Reinecke 					  "Can't set default compression.\n");
16751da177e4SLinus Torvalds 				if (modes_defined) {
16761da177e4SLinus Torvalds 					retval = (-EINVAL);
16771da177e4SLinus Torvalds 					goto out;
16781da177e4SLinus Torvalds 				}
16791da177e4SLinus Torvalds 			}
16801da177e4SLinus Torvalds 		}
16811da177e4SLinus Torvalds 	}
16821da177e4SLinus Torvalds 
16831da177e4SLinus Torvalds 	STbp = STp->buffer;
16841da177e4SLinus Torvalds 	i = write_behind_check(STp);
16851da177e4SLinus Torvalds 	if (i) {
16861da177e4SLinus Torvalds 		if (i == -ENOSPC)
16871da177e4SLinus Torvalds 			STps->eof = ST_EOM_OK;
16881da177e4SLinus Torvalds 		else
16891da177e4SLinus Torvalds 			STps->eof = ST_EOM_ERROR;
16901da177e4SLinus Torvalds 	}
16911da177e4SLinus Torvalds 
16921da177e4SLinus Torvalds 	if (STps->eof == ST_EOM_OK) {
16931da177e4SLinus Torvalds 		STps->eof = ST_EOD_1;  /* allow next write */
16941da177e4SLinus Torvalds 		retval = (-ENOSPC);
16951da177e4SLinus Torvalds 		goto out;
16961da177e4SLinus Torvalds 	}
16971da177e4SLinus Torvalds 	else if (STps->eof == ST_EOM_ERROR) {
16981da177e4SLinus Torvalds 		retval = (-EIO);
16991da177e4SLinus Torvalds 		goto out;
17001da177e4SLinus Torvalds 	}
17011da177e4SLinus Torvalds 
17021da177e4SLinus Torvalds 	/* Check the buffer readability in cases where copy_user might catch
17031da177e4SLinus Torvalds 	   the problems after some tape movement. */
17041da177e4SLinus Torvalds 	if (STp->block_size != 0 &&
17051da177e4SLinus Torvalds 	    !STbp->do_dio &&
17061da177e4SLinus Torvalds 	    (copy_from_user(&i, buf, 1) != 0 ||
17071da177e4SLinus Torvalds 	     copy_from_user(&i, buf + count - 1, 1) != 0)) {
17081da177e4SLinus Torvalds 		retval = (-EFAULT);
17091da177e4SLinus Torvalds 		goto out;
17101da177e4SLinus Torvalds 	}
17111da177e4SLinus Torvalds 
17121da177e4SLinus Torvalds 	retval = setup_buffering(STp, buf, count, 0);
17131da177e4SLinus Torvalds 	if (retval)
17141da177e4SLinus Torvalds 		goto out;
17151da177e4SLinus Torvalds 
17161da177e4SLinus Torvalds 	total = count;
17171da177e4SLinus Torvalds 
17181da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
17191da177e4SLinus Torvalds 	cmd[0] = WRITE_6;
17201da177e4SLinus Torvalds 	cmd[1] = (STp->block_size != 0);
17211da177e4SLinus Torvalds 
17221da177e4SLinus Torvalds 	STps->rw = ST_WRITING;
17231da177e4SLinus Torvalds 
17241da177e4SLinus Torvalds 	b_point = buf;
17251da177e4SLinus Torvalds 	while (count > 0 && !retry_eot) {
17261da177e4SLinus Torvalds 
17271da177e4SLinus Torvalds 		if (STbp->do_dio) {
17281da177e4SLinus Torvalds 			do_count = count;
17291da177e4SLinus Torvalds 		}
17301da177e4SLinus Torvalds 		else {
17311da177e4SLinus Torvalds 			if (STp->block_size == 0)
17321da177e4SLinus Torvalds 				do_count = count;
17331da177e4SLinus Torvalds 			else {
17341da177e4SLinus Torvalds 				do_count = STbp->buffer_blocks * STp->block_size -
17351da177e4SLinus Torvalds 					STbp->buffer_bytes;
17361da177e4SLinus Torvalds 				if (do_count > count)
17371da177e4SLinus Torvalds 					do_count = count;
17381da177e4SLinus Torvalds 			}
17391da177e4SLinus Torvalds 
17401da177e4SLinus Torvalds 			i = append_to_buffer(b_point, STbp, do_count);
17411da177e4SLinus Torvalds 			if (i) {
17421da177e4SLinus Torvalds 				retval = i;
17431da177e4SLinus Torvalds 				goto out;
17441da177e4SLinus Torvalds 			}
17451da177e4SLinus Torvalds 		}
17461da177e4SLinus Torvalds 		count -= do_count;
17471da177e4SLinus Torvalds 		b_point += do_count;
17481da177e4SLinus Torvalds 
17491da177e4SLinus Torvalds 		async_write = STp->block_size == 0 && !STbp->do_dio &&
17501da177e4SLinus Torvalds 			STm->do_async_writes && STps->eof < ST_EOM_OK;
17511da177e4SLinus Torvalds 
17521da177e4SLinus Torvalds 		if (STp->block_size != 0 && STm->do_buffer_writes &&
17539abe16c6SKai Makisara 		    !(STp->try_dio_now && try_wdio) && STps->eof < ST_EOM_OK &&
17541da177e4SLinus Torvalds 		    STbp->buffer_bytes < STbp->buffer_size) {
17551da177e4SLinus Torvalds 			STp->dirty = 1;
17561da177e4SLinus Torvalds 			/* Don't write a buffer that is not full enough. */
17571da177e4SLinus Torvalds 			if (!async_write && count == 0)
17581da177e4SLinus Torvalds 				break;
17591da177e4SLinus Torvalds 		}
17601da177e4SLinus Torvalds 
17611da177e4SLinus Torvalds 	retry_write:
17621da177e4SLinus Torvalds 		if (STp->block_size == 0)
17631da177e4SLinus Torvalds 			blks = transfer = do_count;
17641da177e4SLinus Torvalds 		else {
17651da177e4SLinus Torvalds 			if (!STbp->do_dio)
17661da177e4SLinus Torvalds 				blks = STbp->buffer_bytes;
17671da177e4SLinus Torvalds 			else
17681da177e4SLinus Torvalds 				blks = do_count;
17691da177e4SLinus Torvalds 			blks /= STp->block_size;
17701da177e4SLinus Torvalds 			transfer = blks * STp->block_size;
17711da177e4SLinus Torvalds 		}
17721da177e4SLinus Torvalds 		cmd[2] = blks >> 16;
17731da177e4SLinus Torvalds 		cmd[3] = blks >> 8;
17741da177e4SLinus Torvalds 		cmd[4] = blks;
17751da177e4SLinus Torvalds 
17761da177e4SLinus Torvalds 		SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE,
1777a02488edSJames Bottomley 				   STp->device->request_queue->rq_timeout,
1778a02488edSJames Bottomley 				   MAX_WRITE_RETRIES, !async_write);
17791da177e4SLinus Torvalds 		if (!SRpnt) {
17801da177e4SLinus Torvalds 			retval = STbp->syscall_result;
17811da177e4SLinus Torvalds 			goto out;
17821da177e4SLinus Torvalds 		}
17838b05b773SMike Christie 		if (async_write && !STbp->syscall_result) {
17841da177e4SLinus Torvalds 			STbp->writing = transfer;
17851da177e4SLinus Torvalds 			STp->dirty = !(STbp->writing ==
17861da177e4SLinus Torvalds 				       STbp->buffer_bytes);
17871da177e4SLinus Torvalds 			SRpnt = NULL;  /* Prevent releasing this request! */
17881da177e4SLinus Torvalds 			DEB( STp->write_pending = 1; )
17891da177e4SLinus Torvalds 			break;
17901da177e4SLinus Torvalds 		}
17911da177e4SLinus Torvalds 
17921da177e4SLinus Torvalds 		if (STbp->syscall_result != 0) {
17931da177e4SLinus Torvalds 			struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
17941da177e4SLinus Torvalds 
1795b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Error on write:\n");
17961da177e4SLinus Torvalds 			if (cmdstatp->have_sense && (cmdstatp->flags & SENSE_EOM)) {
17971da177e4SLinus Torvalds 				scode = cmdstatp->sense_hdr.sense_key;
17981da177e4SLinus Torvalds 				if (cmdstatp->remainder_valid)
17991da177e4SLinus Torvalds 					undone = (int)cmdstatp->uremainder64;
18001da177e4SLinus Torvalds 				else if (STp->block_size == 0 &&
18011da177e4SLinus Torvalds 					 scode == VOLUME_OVERFLOW)
18021da177e4SLinus Torvalds 					undone = transfer;
18031da177e4SLinus Torvalds 				else
18041da177e4SLinus Torvalds 					undone = 0;
18051da177e4SLinus Torvalds 				if (STp->block_size != 0)
18061da177e4SLinus Torvalds 					undone *= STp->block_size;
18071da177e4SLinus Torvalds 				if (undone <= do_count) {
18081da177e4SLinus Torvalds 					/* Only data from this write is not written */
18091da177e4SLinus Torvalds 					count += undone;
1810626dcb1eSKai Makisara 					b_point -= undone;
18111da177e4SLinus Torvalds 					do_count -= undone;
18121da177e4SLinus Torvalds 					if (STp->block_size)
18131da177e4SLinus Torvalds 						blks = (transfer - undone) / STp->block_size;
18141da177e4SLinus Torvalds 					STps->eof = ST_EOM_OK;
18151da177e4SLinus Torvalds 					/* Continue in fixed block mode if all written
18161da177e4SLinus Torvalds 					   in this request but still something left to write
18171da177e4SLinus Torvalds 					   (retval left to zero)
18181da177e4SLinus Torvalds 					*/
18191da177e4SLinus Torvalds 					if (STp->block_size == 0 ||
18201da177e4SLinus Torvalds 					    undone > 0 || count == 0)
18211da177e4SLinus Torvalds 						retval = (-ENOSPC); /* EOM within current request */
1822b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "EOM with %d "
1823b30d8bcaSHannes Reinecke 						    "bytes unwritten.\n",
1824b30d8bcaSHannes Reinecke 						    (int)count);
18251da177e4SLinus Torvalds 				} else {
18261da177e4SLinus Torvalds 					/* EOT within data buffered earlier (possible only
18271da177e4SLinus Torvalds 					   in fixed block mode without direct i/o) */
18281da177e4SLinus Torvalds 					if (!retry_eot && !cmdstatp->deferred &&
18291da177e4SLinus Torvalds 					    (scode == NO_SENSE || scode == RECOVERED_ERROR)) {
18301da177e4SLinus Torvalds 						move_buffer_data(STp->buffer, transfer - undone);
18311da177e4SLinus Torvalds 						retry_eot = 1;
18321da177e4SLinus Torvalds 						if (STps->drv_block >= 0) {
18331da177e4SLinus Torvalds 							STps->drv_block += (transfer - undone) /
18341da177e4SLinus Torvalds 								STp->block_size;
18351da177e4SLinus Torvalds 						}
18361da177e4SLinus Torvalds 						STps->eof = ST_EOM_OK;
1837b30d8bcaSHannes Reinecke 						DEBC_printk(STp, "Retry "
1838b30d8bcaSHannes Reinecke 							    "write of %d "
1839b30d8bcaSHannes Reinecke 							    "bytes at EOM.\n",
1840b30d8bcaSHannes Reinecke 							    STp->buffer->buffer_bytes);
18411da177e4SLinus Torvalds 						goto retry_write;
18421da177e4SLinus Torvalds 					}
18431da177e4SLinus Torvalds 					else {
18441da177e4SLinus Torvalds 						/* Either error within data buffered by driver or
18451da177e4SLinus Torvalds 						   failed retry */
18461da177e4SLinus Torvalds 						count -= do_count;
18471da177e4SLinus Torvalds 						blks = do_count = 0;
18481da177e4SLinus Torvalds 						STps->eof = ST_EOM_ERROR;
18491da177e4SLinus Torvalds 						STps->drv_block = (-1); /* Too cautious? */
18501da177e4SLinus Torvalds 						retval = (-EIO);	/* EOM for old data */
1851b30d8bcaSHannes Reinecke 						DEBC_printk(STp, "EOM with "
1852b30d8bcaSHannes Reinecke 							    "lost data.\n");
18531da177e4SLinus Torvalds 					}
18541da177e4SLinus Torvalds 				}
18551da177e4SLinus Torvalds 			} else {
18561da177e4SLinus Torvalds 				count += do_count;
18571da177e4SLinus Torvalds 				STps->drv_block = (-1);		/* Too cautious? */
18588b05b773SMike Christie 				retval = STbp->syscall_result;
18591da177e4SLinus Torvalds 			}
18601da177e4SLinus Torvalds 
18611da177e4SLinus Torvalds 		}
18621da177e4SLinus Torvalds 
18631da177e4SLinus Torvalds 		if (STps->drv_block >= 0) {
18641da177e4SLinus Torvalds 			if (STp->block_size == 0)
18651da177e4SLinus Torvalds 				STps->drv_block += (do_count > 0);
18661da177e4SLinus Torvalds 			else
18671da177e4SLinus Torvalds 				STps->drv_block += blks;
18681da177e4SLinus Torvalds 		}
18691da177e4SLinus Torvalds 
18701da177e4SLinus Torvalds 		STbp->buffer_bytes = 0;
18711da177e4SLinus Torvalds 		STp->dirty = 0;
18721da177e4SLinus Torvalds 
18731da177e4SLinus Torvalds 		if (retval || retry_eot) {
18741da177e4SLinus Torvalds 			if (count < total)
18751da177e4SLinus Torvalds 				retval = total - count;
18761da177e4SLinus Torvalds 			goto out;
18771da177e4SLinus Torvalds 		}
18781da177e4SLinus Torvalds 	}
18791da177e4SLinus Torvalds 
18801da177e4SLinus Torvalds 	if (STps->eof == ST_EOD_1)
18811da177e4SLinus Torvalds 		STps->eof = ST_EOM_OK;
18821da177e4SLinus Torvalds 	else if (STps->eof != ST_EOM_OK)
18831da177e4SLinus Torvalds 		STps->eof = ST_NOEOF;
18841da177e4SLinus Torvalds 	retval = total - count;
18851da177e4SLinus Torvalds 
18861da177e4SLinus Torvalds  out:
18871da177e4SLinus Torvalds 	if (SRpnt != NULL)
18888b05b773SMike Christie 		st_release_request(SRpnt);
1889787926b1SKai Makisara 	release_buffering(STp, 0);
189028f85009SMatthias Kaehlcke 	mutex_unlock(&STp->lock);
18911da177e4SLinus Torvalds 
18921da177e4SLinus Torvalds 	return retval;
18931da177e4SLinus Torvalds }
18941da177e4SLinus Torvalds 
18951da177e4SLinus Torvalds /* Read data from the tape. Returns zero in the normal case, one if the
18961da177e4SLinus Torvalds    eof status has changed, and the negative error code in case of a
18971da177e4SLinus Torvalds    fatal error. Otherwise updates the buffer and the eof state.
18981da177e4SLinus Torvalds 
18991da177e4SLinus Torvalds    Does release user buffer mapping if it is set.
19001da177e4SLinus Torvalds */
read_tape(struct scsi_tape * STp,long count,struct st_request ** aSRpnt)19011da177e4SLinus Torvalds static long read_tape(struct scsi_tape *STp, long count,
19028b05b773SMike Christie 		      struct st_request ** aSRpnt)
19031da177e4SLinus Torvalds {
19041da177e4SLinus Torvalds 	int transfer, blks, bytes;
19051da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
19068b05b773SMike Christie 	struct st_request *SRpnt;
19071da177e4SLinus Torvalds 	struct st_modedef *STm;
19081da177e4SLinus Torvalds 	struct st_partstat *STps;
19091da177e4SLinus Torvalds 	struct st_buffer *STbp;
19101da177e4SLinus Torvalds 	int retval = 0;
19111da177e4SLinus Torvalds 
19121da177e4SLinus Torvalds 	if (count == 0)
19131da177e4SLinus Torvalds 		return 0;
19141da177e4SLinus Torvalds 
19151da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
19161da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
19171da177e4SLinus Torvalds 	if (STps->eof == ST_FM_HIT)
19181da177e4SLinus Torvalds 		return 1;
19191da177e4SLinus Torvalds 	STbp = STp->buffer;
19201da177e4SLinus Torvalds 
19211da177e4SLinus Torvalds 	if (STp->block_size == 0)
19221da177e4SLinus Torvalds 		blks = bytes = count;
19231da177e4SLinus Torvalds 	else {
19249abe16c6SKai Makisara 		if (!(STp->try_dio_now && try_rdio) && STm->do_read_ahead) {
19251da177e4SLinus Torvalds 			blks = (STp->buffer)->buffer_blocks;
19261da177e4SLinus Torvalds 			bytes = blks * STp->block_size;
19271da177e4SLinus Torvalds 		} else {
19281da177e4SLinus Torvalds 			bytes = count;
19291da177e4SLinus Torvalds 			if (!STbp->do_dio && bytes > (STp->buffer)->buffer_size)
19301da177e4SLinus Torvalds 				bytes = (STp->buffer)->buffer_size;
19311da177e4SLinus Torvalds 			blks = bytes / STp->block_size;
19321da177e4SLinus Torvalds 			bytes = blks * STp->block_size;
19331da177e4SLinus Torvalds 		}
19341da177e4SLinus Torvalds 	}
19351da177e4SLinus Torvalds 
19361da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
19371da177e4SLinus Torvalds 	cmd[0] = READ_6;
19381da177e4SLinus Torvalds 	cmd[1] = (STp->block_size != 0);
193940f6b36cSKai Makisara 	if (!cmd[1] && STp->sili)
194040f6b36cSKai Makisara 		cmd[1] |= 2;
19411da177e4SLinus Torvalds 	cmd[2] = blks >> 16;
19421da177e4SLinus Torvalds 	cmd[3] = blks >> 8;
19431da177e4SLinus Torvalds 	cmd[4] = blks;
19441da177e4SLinus Torvalds 
19451da177e4SLinus Torvalds 	SRpnt = *aSRpnt;
19461da177e4SLinus Torvalds 	SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE,
1947a02488edSJames Bottomley 			   STp->device->request_queue->rq_timeout,
1948a02488edSJames Bottomley 			   MAX_RETRIES, 1);
1949787926b1SKai Makisara 	release_buffering(STp, 1);
19501da177e4SLinus Torvalds 	*aSRpnt = SRpnt;
19511da177e4SLinus Torvalds 	if (!SRpnt)
19521da177e4SLinus Torvalds 		return STbp->syscall_result;
19531da177e4SLinus Torvalds 
19541da177e4SLinus Torvalds 	STbp->read_pointer = 0;
19551da177e4SLinus Torvalds 	STps->at_sm = 0;
19561da177e4SLinus Torvalds 
19571da177e4SLinus Torvalds 	/* Something to check */
19581da177e4SLinus Torvalds 	if (STbp->syscall_result) {
19591da177e4SLinus Torvalds 		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
19601da177e4SLinus Torvalds 
19611da177e4SLinus Torvalds 		retval = 1;
1962b30d8bcaSHannes Reinecke 		DEBC_printk(STp,
1963b30d8bcaSHannes Reinecke 			    "Sense: %2x %2x %2x %2x %2x %2x %2x %2x\n",
19648b05b773SMike Christie 			    SRpnt->sense[0], SRpnt->sense[1],
19658b05b773SMike Christie 			    SRpnt->sense[2], SRpnt->sense[3],
19668b05b773SMike Christie 			    SRpnt->sense[4], SRpnt->sense[5],
1967b30d8bcaSHannes Reinecke 			    SRpnt->sense[6], SRpnt->sense[7]);
19681da177e4SLinus Torvalds 		if (cmdstatp->have_sense) {
19691da177e4SLinus Torvalds 
19701da177e4SLinus Torvalds 			if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
19711da177e4SLinus Torvalds 				cmdstatp->flags &= 0xcf;	/* No need for EOM in this case */
19721da177e4SLinus Torvalds 
19731da177e4SLinus Torvalds 			if (cmdstatp->flags != 0) { /* EOF, EOM, or ILI */
19741da177e4SLinus Torvalds 				/* Compute the residual count */
19751da177e4SLinus Torvalds 				if (cmdstatp->remainder_valid)
19761da177e4SLinus Torvalds 					transfer = (int)cmdstatp->uremainder64;
19771da177e4SLinus Torvalds 				else
19781da177e4SLinus Torvalds 					transfer = 0;
19795e4fabb6SKai Makisara 				if (cmdstatp->sense_hdr.sense_key == MEDIUM_ERROR) {
19805e4fabb6SKai Makisara 					if (STp->block_size == 0)
19811da177e4SLinus Torvalds 						transfer = bytes;
19825e4fabb6SKai Makisara 					/* Some drives set ILI with MEDIUM ERROR */
19835e4fabb6SKai Makisara 					cmdstatp->flags &= ~SENSE_ILI;
19845e4fabb6SKai Makisara 				}
19851da177e4SLinus Torvalds 
19861da177e4SLinus Torvalds 				if (cmdstatp->flags & SENSE_ILI) {	/* ILI */
1987b30d8bcaSHannes Reinecke 					if (STp->block_size == 0 &&
1988b30d8bcaSHannes Reinecke 					    transfer < 0) {
1989b30d8bcaSHannes Reinecke 						st_printk(KERN_NOTICE, STp,
1990b30d8bcaSHannes Reinecke 							  "Failed to read %d "
1991b30d8bcaSHannes Reinecke 							  "byte block with %d "
1992b30d8bcaSHannes Reinecke 							  "byte transfer.\n",
1993b30d8bcaSHannes Reinecke 							  bytes - transfer,
1994b30d8bcaSHannes Reinecke 							  bytes);
19951da177e4SLinus Torvalds 						if (STps->drv_block >= 0)
19961da177e4SLinus Torvalds 							STps->drv_block += 1;
19971da177e4SLinus Torvalds 						STbp->buffer_bytes = 0;
19981da177e4SLinus Torvalds 						return (-ENOMEM);
1999b30d8bcaSHannes Reinecke 					} else if (STp->block_size == 0) {
20001da177e4SLinus Torvalds 						STbp->buffer_bytes = bytes - transfer;
20011da177e4SLinus Torvalds 					} else {
20028b05b773SMike Christie 						st_release_request(SRpnt);
20031da177e4SLinus Torvalds 						SRpnt = *aSRpnt = NULL;
20041da177e4SLinus Torvalds 						if (transfer == blks) {	/* We did not get anything, error */
2005b30d8bcaSHannes Reinecke 							st_printk(KERN_NOTICE, STp,
2006b30d8bcaSHannes Reinecke 								  "Incorrect "
2007b30d8bcaSHannes Reinecke 								  "block size.\n");
20081da177e4SLinus Torvalds 							if (STps->drv_block >= 0)
20091da177e4SLinus Torvalds 								STps->drv_block += blks - transfer + 1;
20101da177e4SLinus Torvalds 							st_int_ioctl(STp, MTBSR, 1);
20111da177e4SLinus Torvalds 							return (-EIO);
20121da177e4SLinus Torvalds 						}
20131da177e4SLinus Torvalds 						/* We have some data, deliver it */
20141da177e4SLinus Torvalds 						STbp->buffer_bytes = (blks - transfer) *
20151da177e4SLinus Torvalds 						    STp->block_size;
2016b30d8bcaSHannes Reinecke 						DEBC_printk(STp, "ILI but "
2017b30d8bcaSHannes Reinecke 							    "enough data "
2018b30d8bcaSHannes Reinecke 							    "received %ld "
2019b30d8bcaSHannes Reinecke 							    "%d.\n", count,
2020b30d8bcaSHannes Reinecke 							    STbp->buffer_bytes);
20211da177e4SLinus Torvalds 						if (STps->drv_block >= 0)
20221da177e4SLinus Torvalds 							STps->drv_block += 1;
20231da177e4SLinus Torvalds 						if (st_int_ioctl(STp, MTBSR, 1))
20241da177e4SLinus Torvalds 							return (-EIO);
20251da177e4SLinus Torvalds 					}
20261da177e4SLinus Torvalds 				} else if (cmdstatp->flags & SENSE_FMK) {	/* FM overrides EOM */
20271da177e4SLinus Torvalds 					if (STps->eof != ST_FM_HIT)
20281da177e4SLinus Torvalds 						STps->eof = ST_FM_HIT;
20291da177e4SLinus Torvalds 					else
20301da177e4SLinus Torvalds 						STps->eof = ST_EOD_2;
20311da177e4SLinus Torvalds 					if (STp->block_size == 0)
20321da177e4SLinus Torvalds 						STbp->buffer_bytes = 0;
20331da177e4SLinus Torvalds 					else
20341da177e4SLinus Torvalds 						STbp->buffer_bytes =
20351da177e4SLinus Torvalds 						    bytes - transfer * STp->block_size;
2036b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "EOF detected (%d "
2037b30d8bcaSHannes Reinecke 						    "bytes read).\n",
2038b30d8bcaSHannes Reinecke 						    STbp->buffer_bytes);
20391da177e4SLinus Torvalds 				} else if (cmdstatp->flags & SENSE_EOM) {
20401da177e4SLinus Torvalds 					if (STps->eof == ST_FM)
20411da177e4SLinus Torvalds 						STps->eof = ST_EOD_1;
20421da177e4SLinus Torvalds 					else
20431da177e4SLinus Torvalds 						STps->eof = ST_EOM_OK;
20441da177e4SLinus Torvalds 					if (STp->block_size == 0)
20451da177e4SLinus Torvalds 						STbp->buffer_bytes = bytes - transfer;
20461da177e4SLinus Torvalds 					else
20471da177e4SLinus Torvalds 						STbp->buffer_bytes =
20481da177e4SLinus Torvalds 						    bytes - transfer * STp->block_size;
20491da177e4SLinus Torvalds 
2050b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "EOM detected (%d "
2051b30d8bcaSHannes Reinecke 						    "bytes read).\n",
2052b30d8bcaSHannes Reinecke 						    STbp->buffer_bytes);
20531da177e4SLinus Torvalds 				}
20541da177e4SLinus Torvalds 			}
20551da177e4SLinus Torvalds 			/* end of EOF, EOM, ILI test */
20561da177e4SLinus Torvalds 			else {	/* nonzero sense key */
2057b30d8bcaSHannes Reinecke 				DEBC_printk(STp, "Tape error while reading.\n");
20581da177e4SLinus Torvalds 				STps->drv_block = (-1);
20591da177e4SLinus Torvalds 				if (STps->eof == ST_FM &&
20601da177e4SLinus Torvalds 				    cmdstatp->sense_hdr.sense_key == BLANK_CHECK) {
2061b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "Zero returned for "
2062b30d8bcaSHannes Reinecke 						    "first BLANK CHECK "
2063b30d8bcaSHannes Reinecke 						    "after EOF.\n");
20641da177e4SLinus Torvalds 					STps->eof = ST_EOD_2;	/* First BLANK_CHECK after FM */
20651da177e4SLinus Torvalds 				} else	/* Some other extended sense code */
20661da177e4SLinus Torvalds 					retval = (-EIO);
20671da177e4SLinus Torvalds 			}
20681da177e4SLinus Torvalds 
20691da177e4SLinus Torvalds 			if (STbp->buffer_bytes < 0)  /* Caused by bogus sense data */
20701da177e4SLinus Torvalds 				STbp->buffer_bytes = 0;
20711da177e4SLinus Torvalds 		}
20721da177e4SLinus Torvalds 		/* End of extended sense test */
20731da177e4SLinus Torvalds 		else {		/* Non-extended sense */
20741da177e4SLinus Torvalds 			retval = STbp->syscall_result;
20751da177e4SLinus Torvalds 		}
20761da177e4SLinus Torvalds 
20771da177e4SLinus Torvalds 	}
20781da177e4SLinus Torvalds 	/* End of error handling */
207940f6b36cSKai Makisara 	else {			/* Read successful */
20801da177e4SLinus Torvalds 		STbp->buffer_bytes = bytes;
208140f6b36cSKai Makisara 		if (STp->sili) /* In fixed block mode residual is always zero here */
208240f6b36cSKai Makisara 			STbp->buffer_bytes -= STp->buffer->cmdstat.residual;
208340f6b36cSKai Makisara 	}
20841da177e4SLinus Torvalds 
20851da177e4SLinus Torvalds 	if (STps->drv_block >= 0) {
20861da177e4SLinus Torvalds 		if (STp->block_size == 0)
20871da177e4SLinus Torvalds 			STps->drv_block++;
20881da177e4SLinus Torvalds 		else
20891da177e4SLinus Torvalds 			STps->drv_block += STbp->buffer_bytes / STp->block_size;
20901da177e4SLinus Torvalds 	}
20911da177e4SLinus Torvalds 	return retval;
20921da177e4SLinus Torvalds }
20931da177e4SLinus Torvalds 
20941da177e4SLinus Torvalds 
20951da177e4SLinus Torvalds /* Read command */
20961da177e4SLinus Torvalds static ssize_t
st_read(struct file * filp,char __user * buf,size_t count,loff_t * ppos)20971da177e4SLinus Torvalds st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos)
20981da177e4SLinus Torvalds {
20991da177e4SLinus Torvalds 	ssize_t total;
21001da177e4SLinus Torvalds 	ssize_t retval = 0;
21011da177e4SLinus Torvalds 	ssize_t i, transfer;
21021da177e4SLinus Torvalds 	int special, do_dio = 0;
21038b05b773SMike Christie 	struct st_request *SRpnt = NULL;
21041da177e4SLinus Torvalds 	struct scsi_tape *STp = filp->private_data;
21051da177e4SLinus Torvalds 	struct st_modedef *STm;
21061da177e4SLinus Torvalds 	struct st_partstat *STps;
21071da177e4SLinus Torvalds 	struct st_buffer *STbp = STp->buffer;
21081da177e4SLinus Torvalds 
210928f85009SMatthias Kaehlcke 	if (mutex_lock_interruptible(&STp->lock))
21101da177e4SLinus Torvalds 		return -ERESTARTSYS;
21111da177e4SLinus Torvalds 
21121da177e4SLinus Torvalds 	retval = rw_checks(STp, filp, count);
21131da177e4SLinus Torvalds 	if (retval || count == 0)
21141da177e4SLinus Torvalds 		goto out;
21151da177e4SLinus Torvalds 
21161da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
21179abe16c6SKai Makisara 	if (STp->block_size != 0 && (count % STp->block_size) != 0) {
21189abe16c6SKai Makisara 		if (!STm->do_read_ahead) {
21191da177e4SLinus Torvalds 			retval = (-EINVAL);	/* Read must be integral number of blocks */
21201da177e4SLinus Torvalds 			goto out;
21211da177e4SLinus Torvalds 		}
21229abe16c6SKai Makisara 		STp->try_dio_now = 0;  /* Direct i/o can't handle split blocks */
21239abe16c6SKai Makisara 	}
21241da177e4SLinus Torvalds 
21251da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
21261da177e4SLinus Torvalds 	if (STps->rw == ST_WRITING) {
21271da177e4SLinus Torvalds 		retval = flush_buffer(STp, 0);
21281da177e4SLinus Torvalds 		if (retval)
21291da177e4SLinus Torvalds 			goto out;
21301da177e4SLinus Torvalds 		STps->rw = ST_READING;
21311da177e4SLinus Torvalds 	}
21321da177e4SLinus Torvalds 	DEB(
21331da177e4SLinus Torvalds 	if (debugging && STps->eof != ST_NOEOF)
2134b30d8bcaSHannes Reinecke 		st_printk(ST_DEB_MSG, STp,
2135b30d8bcaSHannes Reinecke 			  "EOF/EOM flag up (%d). Bytes %d\n",
21361da177e4SLinus Torvalds 			  STps->eof, STbp->buffer_bytes);
21371da177e4SLinus Torvalds 	) /* end DEB */
21381da177e4SLinus Torvalds 
21391da177e4SLinus Torvalds 	retval = setup_buffering(STp, buf, count, 1);
21401da177e4SLinus Torvalds 	if (retval)
21411da177e4SLinus Torvalds 		goto out;
21421da177e4SLinus Torvalds 	do_dio = STbp->do_dio;
21431da177e4SLinus Torvalds 
21441da177e4SLinus Torvalds 	if (STbp->buffer_bytes == 0 &&
21451da177e4SLinus Torvalds 	    STps->eof >= ST_EOD_1) {
21461da177e4SLinus Torvalds 		if (STps->eof < ST_EOD) {
21471da177e4SLinus Torvalds 			STps->eof += 1;
21481da177e4SLinus Torvalds 			retval = 0;
21491da177e4SLinus Torvalds 			goto out;
21501da177e4SLinus Torvalds 		}
21511da177e4SLinus Torvalds 		retval = (-EIO);	/* EOM or Blank Check */
21521da177e4SLinus Torvalds 		goto out;
21531da177e4SLinus Torvalds 	}
21541da177e4SLinus Torvalds 
21551da177e4SLinus Torvalds 	if (do_dio) {
21561da177e4SLinus Torvalds 		/* Check the buffer writability before any tape movement. Don't alter
21571da177e4SLinus Torvalds 		   buffer data. */
21581da177e4SLinus Torvalds 		if (copy_from_user(&i, buf, 1) != 0 ||
21591da177e4SLinus Torvalds 		    copy_to_user(buf, &i, 1) != 0 ||
21601da177e4SLinus Torvalds 		    copy_from_user(&i, buf + count - 1, 1) != 0 ||
21611da177e4SLinus Torvalds 		    copy_to_user(buf + count - 1, &i, 1) != 0) {
21621da177e4SLinus Torvalds 			retval = (-EFAULT);
21631da177e4SLinus Torvalds 			goto out;
21641da177e4SLinus Torvalds 		}
21651da177e4SLinus Torvalds 	}
21661da177e4SLinus Torvalds 
21671da177e4SLinus Torvalds 	STps->rw = ST_READING;
21681da177e4SLinus Torvalds 
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds 	/* Loop until enough data in buffer or a special condition found */
21711da177e4SLinus Torvalds 	for (total = 0, special = 0; total < count && !special;) {
21721da177e4SLinus Torvalds 
21731da177e4SLinus Torvalds 		/* Get new data if the buffer is empty */
21741da177e4SLinus Torvalds 		if (STbp->buffer_bytes == 0) {
21751da177e4SLinus Torvalds 			special = read_tape(STp, count - total, &SRpnt);
21761da177e4SLinus Torvalds 			if (special < 0) {	/* No need to continue read */
21771da177e4SLinus Torvalds 				retval = special;
21781da177e4SLinus Torvalds 				goto out;
21791da177e4SLinus Torvalds 			}
21801da177e4SLinus Torvalds 		}
21811da177e4SLinus Torvalds 
21821da177e4SLinus Torvalds 		/* Move the data from driver buffer to user buffer */
21831da177e4SLinus Torvalds 		if (STbp->buffer_bytes > 0) {
21841da177e4SLinus Torvalds 			DEB(
21851da177e4SLinus Torvalds 			if (debugging && STps->eof != ST_NOEOF)
2186b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2187b30d8bcaSHannes Reinecke 					  "EOF up (%d). Left %d, needed %d.\n",
21881da177e4SLinus Torvalds 					  STps->eof, STbp->buffer_bytes,
21891da177e4SLinus Torvalds 					  (int)(count - total));
21901da177e4SLinus Torvalds 			) /* end DEB */
21911da177e4SLinus Torvalds 			transfer = STbp->buffer_bytes < count - total ?
21921da177e4SLinus Torvalds 			    STbp->buffer_bytes : count - total;
21931da177e4SLinus Torvalds 			if (!do_dio) {
21941da177e4SLinus Torvalds 				i = from_buffer(STbp, buf, transfer);
21951da177e4SLinus Torvalds 				if (i) {
21961da177e4SLinus Torvalds 					retval = i;
21971da177e4SLinus Torvalds 					goto out;
21981da177e4SLinus Torvalds 				}
21991da177e4SLinus Torvalds 			}
22001da177e4SLinus Torvalds 			buf += transfer;
22011da177e4SLinus Torvalds 			total += transfer;
22021da177e4SLinus Torvalds 		}
22031da177e4SLinus Torvalds 
22041da177e4SLinus Torvalds 		if (STp->block_size == 0)
22051da177e4SLinus Torvalds 			break;	/* Read only one variable length block */
22061da177e4SLinus Torvalds 
22071da177e4SLinus Torvalds 	}			/* for (total = 0, special = 0;
22081da177e4SLinus Torvalds                                    total < count && !special; ) */
22091da177e4SLinus Torvalds 
22101da177e4SLinus Torvalds 	/* Change the eof state if no data from tape or buffer */
22111da177e4SLinus Torvalds 	if (total == 0) {
22121da177e4SLinus Torvalds 		if (STps->eof == ST_FM_HIT) {
22131da177e4SLinus Torvalds 			STps->eof = ST_FM;
22141da177e4SLinus Torvalds 			STps->drv_block = 0;
22151da177e4SLinus Torvalds 			if (STps->drv_file >= 0)
22161da177e4SLinus Torvalds 				STps->drv_file++;
22171da177e4SLinus Torvalds 		} else if (STps->eof == ST_EOD_1) {
22181da177e4SLinus Torvalds 			STps->eof = ST_EOD_2;
22191da177e4SLinus Torvalds 			STps->drv_block = 0;
22201da177e4SLinus Torvalds 			if (STps->drv_file >= 0)
22211da177e4SLinus Torvalds 				STps->drv_file++;
22221da177e4SLinus Torvalds 		} else if (STps->eof == ST_EOD_2)
22231da177e4SLinus Torvalds 			STps->eof = ST_EOD;
22241da177e4SLinus Torvalds 	} else if (STps->eof == ST_FM)
22251da177e4SLinus Torvalds 		STps->eof = ST_NOEOF;
22261da177e4SLinus Torvalds 	retval = total;
22271da177e4SLinus Torvalds 
22281da177e4SLinus Torvalds  out:
22291da177e4SLinus Torvalds 	if (SRpnt != NULL) {
22308b05b773SMike Christie 		st_release_request(SRpnt);
22311da177e4SLinus Torvalds 		SRpnt = NULL;
22321da177e4SLinus Torvalds 	}
22331da177e4SLinus Torvalds 	if (do_dio) {
2234787926b1SKai Makisara 		release_buffering(STp, 1);
22351da177e4SLinus Torvalds 		STbp->buffer_bytes = 0;
22361da177e4SLinus Torvalds 	}
223728f85009SMatthias Kaehlcke 	mutex_unlock(&STp->lock);
22381da177e4SLinus Torvalds 
22391da177e4SLinus Torvalds 	return retval;
22401da177e4SLinus Torvalds }
22411da177e4SLinus Torvalds 
22421da177e4SLinus Torvalds 
22431da177e4SLinus Torvalds 
DEB(static void st_log_options (struct scsi_tape * STp,struct st_modedef * STm){ if (debugging) { st_printk(KERN_INFO, STp, "Mode %d options: buffer writes: %d, " "async writes: %d, read ahead: %d\\n", STp->current_mode, STm->do_buffer_writes, STm->do_async_writes, STm->do_read_ahead); st_printk(KERN_INFO, STp, "    can bsr: %d, two FMs: %d, " "fast mteom: %d, auto lock: %d,\\n", STp->can_bsr, STp->two_fm, STp->fast_mteom, STp->do_auto_lock); st_printk(KERN_INFO, STp, "    defs for wr: %d, no block limits: %d, " "partitions: %d, s2 log: %d\\n", STm->defaults_for_writes, STp->omit_blklims, STp->can_partitions, STp->scsi2_logical); st_printk(KERN_INFO, STp, "    sysv: %d nowait: %d sili: %d " "nowait_filemark: %d\\n", STm->sysv, STp->immediate, STp->sili, STp->immediate_filemark); st_printk(KERN_INFO, STp, "    debugging: %d\\n", debugging); } } )22441da177e4SLinus Torvalds DEB(
22451da177e4SLinus Torvalds /* Set the driver options */
2246b30d8bcaSHannes Reinecke static void st_log_options(struct scsi_tape * STp, struct st_modedef * STm)
22471da177e4SLinus Torvalds {
22481da177e4SLinus Torvalds 	if (debugging) {
2249b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp,
2250b30d8bcaSHannes Reinecke 			  "Mode %d options: buffer writes: %d, "
2251b30d8bcaSHannes Reinecke 			  "async writes: %d, read ahead: %d\n",
2252b30d8bcaSHannes Reinecke 			  STp->current_mode, STm->do_buffer_writes,
2253b30d8bcaSHannes Reinecke 			  STm->do_async_writes, STm->do_read_ahead);
2254b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp,
2255b30d8bcaSHannes Reinecke 			  "    can bsr: %d, two FMs: %d, "
2256b30d8bcaSHannes Reinecke 			  "fast mteom: %d, auto lock: %d,\n",
2257b30d8bcaSHannes Reinecke 			  STp->can_bsr, STp->two_fm, STp->fast_mteom,
2258b30d8bcaSHannes Reinecke 			  STp->do_auto_lock);
2259b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp,
2260b30d8bcaSHannes Reinecke 			  "    defs for wr: %d, no block limits: %d, "
2261b30d8bcaSHannes Reinecke 			  "partitions: %d, s2 log: %d\n",
2262b30d8bcaSHannes Reinecke 			  STm->defaults_for_writes, STp->omit_blklims,
2263b30d8bcaSHannes Reinecke 			  STp->can_partitions, STp->scsi2_logical);
2264b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp,
2265b30d8bcaSHannes Reinecke 			  "    sysv: %d nowait: %d sili: %d "
2266b30d8bcaSHannes Reinecke 			  "nowait_filemark: %d\n",
2267b30d8bcaSHannes Reinecke 			  STm->sysv, STp->immediate, STp->sili,
2268c743e44fSLee Duncan 			  STp->immediate_filemark);
2269b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp, "    debugging: %d\n", debugging);
22701da177e4SLinus Torvalds 	}
22711da177e4SLinus Torvalds }
22721da177e4SLinus Torvalds 	)
22731da177e4SLinus Torvalds 
22741da177e4SLinus Torvalds 
22751da177e4SLinus Torvalds static int st_set_options(struct scsi_tape *STp, long options)
22761da177e4SLinus Torvalds {
22771da177e4SLinus Torvalds 	int value;
22781da177e4SLinus Torvalds 	long code;
22791da177e4SLinus Torvalds 	struct st_modedef *STm;
22801da177e4SLinus Torvalds 	struct cdev *cd0, *cd1;
2281d6216c47SMaurizio Lombardi 	struct device *d0, *d1;
22821da177e4SLinus Torvalds 
22831da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
22841da177e4SLinus Torvalds 	if (!STm->defined) {
2285d6216c47SMaurizio Lombardi 		cd0 = STm->cdevs[0];
2286d6216c47SMaurizio Lombardi 		cd1 = STm->cdevs[1];
2287d6216c47SMaurizio Lombardi 		d0  = STm->devs[0];
2288d6216c47SMaurizio Lombardi 		d1  = STm->devs[1];
22891da177e4SLinus Torvalds 		memcpy(STm, &(STp->modes[0]), sizeof(struct st_modedef));
2290d6216c47SMaurizio Lombardi 		STm->cdevs[0] = cd0;
2291d6216c47SMaurizio Lombardi 		STm->cdevs[1] = cd1;
2292d6216c47SMaurizio Lombardi 		STm->devs[0]  = d0;
2293d6216c47SMaurizio Lombardi 		STm->devs[1]  = d1;
22941da177e4SLinus Torvalds 		modes_defined = 1;
2295b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Initialized mode %d definition from mode 0\n",
2296b30d8bcaSHannes Reinecke 			    STp->current_mode);
22971da177e4SLinus Torvalds 	}
22981da177e4SLinus Torvalds 
22991da177e4SLinus Torvalds 	code = options & MT_ST_OPTIONS;
23001da177e4SLinus Torvalds 	if (code == MT_ST_BOOLEANS) {
23011da177e4SLinus Torvalds 		STm->do_buffer_writes = (options & MT_ST_BUFFER_WRITES) != 0;
23021da177e4SLinus Torvalds 		STm->do_async_writes = (options & MT_ST_ASYNC_WRITES) != 0;
23031da177e4SLinus Torvalds 		STm->defaults_for_writes = (options & MT_ST_DEF_WRITES) != 0;
23041da177e4SLinus Torvalds 		STm->do_read_ahead = (options & MT_ST_READ_AHEAD) != 0;
23051da177e4SLinus Torvalds 		STp->two_fm = (options & MT_ST_TWO_FM) != 0;
23061da177e4SLinus Torvalds 		STp->fast_mteom = (options & MT_ST_FAST_MTEOM) != 0;
23071da177e4SLinus Torvalds 		STp->do_auto_lock = (options & MT_ST_AUTO_LOCK) != 0;
23081da177e4SLinus Torvalds 		STp->can_bsr = (options & MT_ST_CAN_BSR) != 0;
23091da177e4SLinus Torvalds 		STp->omit_blklims = (options & MT_ST_NO_BLKLIMS) != 0;
23101da177e4SLinus Torvalds 		if ((STp->device)->scsi_level >= SCSI_2)
23111da177e4SLinus Torvalds 			STp->can_partitions = (options & MT_ST_CAN_PARTITIONS) != 0;
23121da177e4SLinus Torvalds 		STp->scsi2_logical = (options & MT_ST_SCSI2LOGICAL) != 0;
23131da177e4SLinus Torvalds 		STp->immediate = (options & MT_ST_NOWAIT) != 0;
2314c743e44fSLee Duncan 		STp->immediate_filemark = (options & MT_ST_NOWAIT_EOF) != 0;
23151da177e4SLinus Torvalds 		STm->sysv = (options & MT_ST_SYSV) != 0;
231640f6b36cSKai Makisara 		STp->sili = (options & MT_ST_SILI) != 0;
23171da177e4SLinus Torvalds 		DEB( debugging = (options & MT_ST_DEBUGGING) != 0;
2318b30d8bcaSHannes Reinecke 		     st_log_options(STp, STm); )
23191da177e4SLinus Torvalds 	} else if (code == MT_ST_SETBOOLEANS || code == MT_ST_CLEARBOOLEANS) {
23201da177e4SLinus Torvalds 		value = (code == MT_ST_SETBOOLEANS);
23211da177e4SLinus Torvalds 		if ((options & MT_ST_BUFFER_WRITES) != 0)
23221da177e4SLinus Torvalds 			STm->do_buffer_writes = value;
23231da177e4SLinus Torvalds 		if ((options & MT_ST_ASYNC_WRITES) != 0)
23241da177e4SLinus Torvalds 			STm->do_async_writes = value;
23251da177e4SLinus Torvalds 		if ((options & MT_ST_DEF_WRITES) != 0)
23261da177e4SLinus Torvalds 			STm->defaults_for_writes = value;
23271da177e4SLinus Torvalds 		if ((options & MT_ST_READ_AHEAD) != 0)
23281da177e4SLinus Torvalds 			STm->do_read_ahead = value;
23291da177e4SLinus Torvalds 		if ((options & MT_ST_TWO_FM) != 0)
23301da177e4SLinus Torvalds 			STp->two_fm = value;
23311da177e4SLinus Torvalds 		if ((options & MT_ST_FAST_MTEOM) != 0)
23321da177e4SLinus Torvalds 			STp->fast_mteom = value;
23331da177e4SLinus Torvalds 		if ((options & MT_ST_AUTO_LOCK) != 0)
23341da177e4SLinus Torvalds 			STp->do_auto_lock = value;
23351da177e4SLinus Torvalds 		if ((options & MT_ST_CAN_BSR) != 0)
23361da177e4SLinus Torvalds 			STp->can_bsr = value;
23371da177e4SLinus Torvalds 		if ((options & MT_ST_NO_BLKLIMS) != 0)
23381da177e4SLinus Torvalds 			STp->omit_blklims = value;
23391da177e4SLinus Torvalds 		if ((STp->device)->scsi_level >= SCSI_2 &&
23401da177e4SLinus Torvalds 		    (options & MT_ST_CAN_PARTITIONS) != 0)
23411da177e4SLinus Torvalds 			STp->can_partitions = value;
23421da177e4SLinus Torvalds 		if ((options & MT_ST_SCSI2LOGICAL) != 0)
23431da177e4SLinus Torvalds 			STp->scsi2_logical = value;
23441da177e4SLinus Torvalds 		if ((options & MT_ST_NOWAIT) != 0)
23451da177e4SLinus Torvalds 			STp->immediate = value;
2346c743e44fSLee Duncan 		if ((options & MT_ST_NOWAIT_EOF) != 0)
2347c743e44fSLee Duncan 			STp->immediate_filemark = value;
23481da177e4SLinus Torvalds 		if ((options & MT_ST_SYSV) != 0)
23491da177e4SLinus Torvalds 			STm->sysv = value;
235040f6b36cSKai Makisara 		if ((options & MT_ST_SILI) != 0)
235140f6b36cSKai Makisara 			STp->sili = value;
23521da177e4SLinus Torvalds 		DEB(
23531da177e4SLinus Torvalds 		if ((options & MT_ST_DEBUGGING) != 0)
23541da177e4SLinus Torvalds 			debugging = value;
2355b30d8bcaSHannes Reinecke 			st_log_options(STp, STm); )
23561da177e4SLinus Torvalds 	} else if (code == MT_ST_WRITE_THRESHOLD) {
23571da177e4SLinus Torvalds 		/* Retained for compatibility */
23581da177e4SLinus Torvalds 	} else if (code == MT_ST_DEF_BLKSIZE) {
23591da177e4SLinus Torvalds 		value = (options & ~MT_ST_OPTIONS);
23601da177e4SLinus Torvalds 		if (value == ~MT_ST_OPTIONS) {
23611da177e4SLinus Torvalds 			STm->default_blksize = (-1);
2362b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Default block size disabled.\n");
23631da177e4SLinus Torvalds 		} else {
23641da177e4SLinus Torvalds 			STm->default_blksize = value;
2365b30d8bcaSHannes Reinecke 			DEBC_printk(STp,"Default block size set to "
2366b30d8bcaSHannes Reinecke 				    "%d bytes.\n", STm->default_blksize);
23671da177e4SLinus Torvalds 			if (STp->ready == ST_READY) {
23681da177e4SLinus Torvalds 				STp->blksize_changed = 0;
23691da177e4SLinus Torvalds 				set_mode_densblk(STp, STm);
23701da177e4SLinus Torvalds 			}
23711da177e4SLinus Torvalds 		}
23721da177e4SLinus Torvalds 	} else if (code == MT_ST_TIMEOUTS) {
23731da177e4SLinus Torvalds 		value = (options & ~MT_ST_OPTIONS);
23741da177e4SLinus Torvalds 		if ((value & MT_ST_SET_LONG_TIMEOUT) != 0) {
23751da177e4SLinus Torvalds 			STp->long_timeout = (value & ~MT_ST_SET_LONG_TIMEOUT) * HZ;
2376b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Long timeout set to %d seconds.\n",
2377b30d8bcaSHannes Reinecke 				    (value & ~MT_ST_SET_LONG_TIMEOUT));
23781da177e4SLinus Torvalds 		} else {
2379a02488edSJames Bottomley 			blk_queue_rq_timeout(STp->device->request_queue,
2380a02488edSJames Bottomley 					     value * HZ);
2381b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Normal timeout set to %d seconds.\n",
2382b30d8bcaSHannes Reinecke 				    value);
23831da177e4SLinus Torvalds 		}
23841da177e4SLinus Torvalds 	} else if (code == MT_ST_SET_CLN) {
23851da177e4SLinus Torvalds 		value = (options & ~MT_ST_OPTIONS) & 0xff;
23861da177e4SLinus Torvalds 		if (value != 0 &&
2387832151f4SRoel Kluin 			(value < EXTENDED_SENSE_START ||
2388832151f4SRoel Kluin 				value >= SCSI_SENSE_BUFFERSIZE))
23891da177e4SLinus Torvalds 			return (-EINVAL);
23901da177e4SLinus Torvalds 		STp->cln_mode = value;
23911da177e4SLinus Torvalds 		STp->cln_sense_mask = (options >> 8) & 0xff;
23921da177e4SLinus Torvalds 		STp->cln_sense_value = (options >> 16) & 0xff;
2393b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp,
2394b30d8bcaSHannes Reinecke 			  "Cleaning request mode %d, mask %02x, value %02x\n",
2395b30d8bcaSHannes Reinecke 			  value, STp->cln_sense_mask, STp->cln_sense_value);
23961da177e4SLinus Torvalds 	} else if (code == MT_ST_DEF_OPTIONS) {
23971da177e4SLinus Torvalds 		code = (options & ~MT_ST_CLEAR_DEFAULT);
23981da177e4SLinus Torvalds 		value = (options & MT_ST_CLEAR_DEFAULT);
23991da177e4SLinus Torvalds 		if (code == MT_ST_DEF_DENSITY) {
24001da177e4SLinus Torvalds 			if (value == MT_ST_CLEAR_DEFAULT) {
24011da177e4SLinus Torvalds 				STm->default_density = (-1);
2402b30d8bcaSHannes Reinecke 				DEBC_printk(STp,
2403b30d8bcaSHannes Reinecke 					    "Density default disabled.\n");
24041da177e4SLinus Torvalds 			} else {
24051da177e4SLinus Torvalds 				STm->default_density = value & 0xff;
2406b30d8bcaSHannes Reinecke 				DEBC_printk(STp, "Density default set to %x\n",
2407b30d8bcaSHannes Reinecke 					    STm->default_density);
24081da177e4SLinus Torvalds 				if (STp->ready == ST_READY) {
24091da177e4SLinus Torvalds 					STp->density_changed = 0;
24101da177e4SLinus Torvalds 					set_mode_densblk(STp, STm);
24111da177e4SLinus Torvalds 				}
24121da177e4SLinus Torvalds 			}
24131da177e4SLinus Torvalds 		} else if (code == MT_ST_DEF_DRVBUFFER) {
24141da177e4SLinus Torvalds 			if (value == MT_ST_CLEAR_DEFAULT) {
24151da177e4SLinus Torvalds 				STp->default_drvbuffer = 0xff;
2416b30d8bcaSHannes Reinecke 				DEBC_printk(STp,
2417b30d8bcaSHannes Reinecke 					    "Drive buffer default disabled.\n");
24181da177e4SLinus Torvalds 			} else {
24191da177e4SLinus Torvalds 				STp->default_drvbuffer = value & 7;
2420b30d8bcaSHannes Reinecke 				DEBC_printk(STp,
2421b30d8bcaSHannes Reinecke 					    "Drive buffer default set to %x\n",
2422b30d8bcaSHannes Reinecke 					    STp->default_drvbuffer);
24231da177e4SLinus Torvalds 				if (STp->ready == ST_READY)
24241da177e4SLinus Torvalds 					st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer);
24251da177e4SLinus Torvalds 			}
24261da177e4SLinus Torvalds 		} else if (code == MT_ST_DEF_COMPRESSION) {
24271da177e4SLinus Torvalds 			if (value == MT_ST_CLEAR_DEFAULT) {
24281da177e4SLinus Torvalds 				STm->default_compression = ST_DONT_TOUCH;
2429b30d8bcaSHannes Reinecke 				DEBC_printk(STp,
2430b30d8bcaSHannes Reinecke 					    "Compression default disabled.\n");
24311da177e4SLinus Torvalds 			} else {
24321da177e4SLinus Torvalds 				if ((value & 0xff00) != 0) {
24331da177e4SLinus Torvalds 					STp->c_algo = (value & 0xff00) >> 8;
2434b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "Compression "
2435b30d8bcaSHannes Reinecke 						    "algorithm set to 0x%x.\n",
2436b30d8bcaSHannes Reinecke 						    STp->c_algo);
24371da177e4SLinus Torvalds 				}
24381da177e4SLinus Torvalds 				if ((value & 0xff) != 0xff) {
24391da177e4SLinus Torvalds 					STm->default_compression = (value & 1 ? ST_YES : ST_NO);
2440b30d8bcaSHannes Reinecke 					DEBC_printk(STp, "Compression default "
2441b30d8bcaSHannes Reinecke 						    "set to %x\n",
2442b30d8bcaSHannes Reinecke 						    (value & 1));
24431da177e4SLinus Torvalds 					if (STp->ready == ST_READY) {
24441da177e4SLinus Torvalds 						STp->compression_changed = 0;
24451da177e4SLinus Torvalds 						st_compression(STp, (STm->default_compression == ST_YES));
24461da177e4SLinus Torvalds 					}
24471da177e4SLinus Torvalds 				}
24481da177e4SLinus Torvalds 			}
24491da177e4SLinus Torvalds 		}
24501da177e4SLinus Torvalds 	} else
24511da177e4SLinus Torvalds 		return (-EIO);
24521da177e4SLinus Torvalds 
24531da177e4SLinus Torvalds 	return 0;
24541da177e4SLinus Torvalds }
24551da177e4SLinus Torvalds 
24561da177e4SLinus Torvalds #define MODE_HEADER_LENGTH  4
24571da177e4SLinus Torvalds 
24581da177e4SLinus Torvalds /* Mode header and page byte offsets */
24591da177e4SLinus Torvalds #define MH_OFF_DATA_LENGTH     0
24601da177e4SLinus Torvalds #define MH_OFF_MEDIUM_TYPE     1
24611da177e4SLinus Torvalds #define MH_OFF_DEV_SPECIFIC    2
24621da177e4SLinus Torvalds #define MH_OFF_BDESCS_LENGTH   3
24631da177e4SLinus Torvalds #define MP_OFF_PAGE_NBR        0
24641da177e4SLinus Torvalds #define MP_OFF_PAGE_LENGTH     1
24651da177e4SLinus Torvalds 
24661da177e4SLinus Torvalds /* Mode header and page bit masks */
24671da177e4SLinus Torvalds #define MH_BIT_WP              0x80
24681da177e4SLinus Torvalds #define MP_MSK_PAGE_NBR        0x3f
24691da177e4SLinus Torvalds 
24701da177e4SLinus Torvalds /* Don't return block descriptors */
24711da177e4SLinus Torvalds #define MODE_SENSE_OMIT_BDESCS 0x08
24721da177e4SLinus Torvalds 
24731da177e4SLinus Torvalds #define MODE_SELECT_PAGE_FORMAT 0x10
24741da177e4SLinus Torvalds 
24751da177e4SLinus Torvalds /* Read a mode page into the tape buffer. The block descriptors are included
24761da177e4SLinus Torvalds    if incl_block_descs is true. The page control is ored to the page number
24771da177e4SLinus Torvalds    parameter, if necessary. */
read_mode_page(struct scsi_tape * STp,int page,int omit_block_descs)24781da177e4SLinus Torvalds static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs)
24791da177e4SLinus Torvalds {
24801da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
24818ecf0d99SFUJITA Tomonori 	struct st_request *SRpnt;
24821da177e4SLinus Torvalds 
24831da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
24841da177e4SLinus Torvalds 	cmd[0] = MODE_SENSE;
24851da177e4SLinus Torvalds 	if (omit_block_descs)
24861da177e4SLinus Torvalds 		cmd[1] = MODE_SENSE_OMIT_BDESCS;
24871da177e4SLinus Torvalds 	cmd[2] = page;
24881da177e4SLinus Torvalds 	cmd[4] = 255;
24891da177e4SLinus Torvalds 
249002ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_FROM_DEVICE,
249102ae2c0eSKai Makisara 			   STp->device->request_queue->rq_timeout, 0, 1);
249202ae2c0eSKai Makisara 	if (SRpnt == NULL)
249302ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
24941da177e4SLinus Torvalds 
24958b05b773SMike Christie 	st_release_request(SRpnt);
24961da177e4SLinus Torvalds 
249702ae2c0eSKai Makisara 	return STp->buffer->syscall_result;
24981da177e4SLinus Torvalds }
24991da177e4SLinus Torvalds 
25001da177e4SLinus Torvalds 
25011da177e4SLinus Torvalds /* Send the mode page in the tape buffer to the drive. Assumes that the mode data
25021da177e4SLinus Torvalds    in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */
write_mode_page(struct scsi_tape * STp,int page,int slow)25031da177e4SLinus Torvalds static int write_mode_page(struct scsi_tape *STp, int page, int slow)
25041da177e4SLinus Torvalds {
250502ae2c0eSKai Makisara 	int pgo;
25061da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
250718c87015SFUJITA Tomonori 	struct st_request *SRpnt;
250802ae2c0eSKai Makisara 	int timeout;
25091da177e4SLinus Torvalds 
25101da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
25111da177e4SLinus Torvalds 	cmd[0] = MODE_SELECT;
25121da177e4SLinus Torvalds 	cmd[1] = MODE_SELECT_PAGE_FORMAT;
25131da177e4SLinus Torvalds 	pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH];
25141da177e4SLinus Torvalds 	cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2;
25151da177e4SLinus Torvalds 
25161da177e4SLinus Torvalds 	/* Clear reserved fields */
25171da177e4SLinus Torvalds 	(STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0;
25181da177e4SLinus Torvalds 	(STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0;
25191da177e4SLinus Torvalds 	(STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP;
25201da177e4SLinus Torvalds 	(STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR;
25211da177e4SLinus Torvalds 
252202ae2c0eSKai Makisara 	timeout = slow ?
252302ae2c0eSKai Makisara 		STp->long_timeout : STp->device->request_queue->rq_timeout;
252402ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_TO_DEVICE,
252502ae2c0eSKai Makisara 			   timeout, 0, 1);
252602ae2c0eSKai Makisara 	if (SRpnt == NULL)
252702ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
25281da177e4SLinus Torvalds 
25298b05b773SMike Christie 	st_release_request(SRpnt);
25301da177e4SLinus Torvalds 
253102ae2c0eSKai Makisara 	return STp->buffer->syscall_result;
25321da177e4SLinus Torvalds }
25331da177e4SLinus Torvalds 
25341da177e4SLinus Torvalds 
25351da177e4SLinus Torvalds #define COMPRESSION_PAGE        0x0f
25361da177e4SLinus Torvalds #define COMPRESSION_PAGE_LENGTH 16
25371da177e4SLinus Torvalds 
25381da177e4SLinus Torvalds #define CP_OFF_DCE_DCC          2
25391da177e4SLinus Torvalds #define CP_OFF_C_ALGO           7
25401da177e4SLinus Torvalds 
25411da177e4SLinus Torvalds #define DCE_MASK  0x80
25421da177e4SLinus Torvalds #define DCC_MASK  0x40
25431da177e4SLinus Torvalds #define RED_MASK  0x60
25441da177e4SLinus Torvalds 
25451da177e4SLinus Torvalds 
25461da177e4SLinus Torvalds /* Control the compression with mode page 15. Algorithm not changed if zero.
25471da177e4SLinus Torvalds 
25481da177e4SLinus Torvalds    The block descriptors are read and written because Sony SDT-7000 does not
25491da177e4SLinus Torvalds    work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
25501da177e4SLinus Torvalds    Including block descriptors should not cause any harm to other drives. */
25511da177e4SLinus Torvalds 
st_compression(struct scsi_tape * STp,int state)25521da177e4SLinus Torvalds static int st_compression(struct scsi_tape * STp, int state)
25531da177e4SLinus Torvalds {
25541da177e4SLinus Torvalds 	int retval;
25551da177e4SLinus Torvalds 	int mpoffs;  /* Offset to mode page start */
25561da177e4SLinus Torvalds 	unsigned char *b_data = (STp->buffer)->b_data;
25571da177e4SLinus Torvalds 
25581da177e4SLinus Torvalds 	if (STp->ready != ST_READY)
25591da177e4SLinus Torvalds 		return (-EIO);
25601da177e4SLinus Torvalds 
25611da177e4SLinus Torvalds 	/* Read the current page contents */
25621da177e4SLinus Torvalds 	retval = read_mode_page(STp, COMPRESSION_PAGE, 0);
25631da177e4SLinus Torvalds 	if (retval) {
2564b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Compression mode page not supported.\n");
25651da177e4SLinus Torvalds 		return (-EIO);
25661da177e4SLinus Torvalds 	}
25671da177e4SLinus Torvalds 
25681da177e4SLinus Torvalds 	mpoffs = MODE_HEADER_LENGTH + b_data[MH_OFF_BDESCS_LENGTH];
2569b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Compression state is %d.\n",
2570b30d8bcaSHannes Reinecke 		    (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0));
25711da177e4SLinus Torvalds 
25721da177e4SLinus Torvalds 	/* Check if compression can be changed */
25731da177e4SLinus Torvalds 	if ((b_data[mpoffs + CP_OFF_DCE_DCC] & DCC_MASK) == 0) {
2574b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Compression not supported.\n");
25751da177e4SLinus Torvalds 		return (-EIO);
25761da177e4SLinus Torvalds 	}
25771da177e4SLinus Torvalds 
25781da177e4SLinus Torvalds 	/* Do the change */
25791da177e4SLinus Torvalds 	if (state) {
25801da177e4SLinus Torvalds 		b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
25811da177e4SLinus Torvalds 		if (STp->c_algo != 0)
25821da177e4SLinus Torvalds 			b_data[mpoffs + CP_OFF_C_ALGO] = STp->c_algo;
25831da177e4SLinus Torvalds 	}
25841da177e4SLinus Torvalds 	else {
25851da177e4SLinus Torvalds 		b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
25861da177e4SLinus Torvalds 		if (STp->c_algo != 0)
25871da177e4SLinus Torvalds 			b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */
25881da177e4SLinus Torvalds 	}
25891da177e4SLinus Torvalds 
25901da177e4SLinus Torvalds 	retval = write_mode_page(STp, COMPRESSION_PAGE, 0);
25911da177e4SLinus Torvalds 	if (retval) {
2592b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Compression change failed.\n");
25931da177e4SLinus Torvalds 		return (-EIO);
25941da177e4SLinus Torvalds 	}
2595b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Compression state changed to %d.\n", state);
25961da177e4SLinus Torvalds 
25971da177e4SLinus Torvalds 	STp->compression_changed = 1;
25981da177e4SLinus Torvalds 	return 0;
25991da177e4SLinus Torvalds }
26001da177e4SLinus Torvalds 
26011da177e4SLinus Torvalds 
26021da177e4SLinus Torvalds /* Process the load and unload commands (does unload if the load code is zero) */
do_load_unload(struct scsi_tape * STp,struct file * filp,int load_code)26031da177e4SLinus Torvalds static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_code)
26041da177e4SLinus Torvalds {
26051da177e4SLinus Torvalds 	int retval = (-EIO), timeout;
26061da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
26071da177e4SLinus Torvalds 	struct st_partstat *STps;
26088b05b773SMike Christie 	struct st_request *SRpnt;
26091da177e4SLinus Torvalds 
26101da177e4SLinus Torvalds 	if (STp->ready != ST_READY && !load_code) {
26111da177e4SLinus Torvalds 		if (STp->ready == ST_NO_TAPE)
26121da177e4SLinus Torvalds 			return (-ENOMEDIUM);
26131da177e4SLinus Torvalds 		else
26141da177e4SLinus Torvalds 			return (-EIO);
26151da177e4SLinus Torvalds 	}
26161da177e4SLinus Torvalds 
26171da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
26181da177e4SLinus Torvalds 	cmd[0] = START_STOP;
26191da177e4SLinus Torvalds 	if (load_code)
26201da177e4SLinus Torvalds 		cmd[4] |= 1;
26211da177e4SLinus Torvalds 	/*
26221da177e4SLinus Torvalds 	 * If arg >= 1 && arg <= 6 Enhanced load/unload in HP C1553A
26231da177e4SLinus Torvalds 	 */
26241da177e4SLinus Torvalds 	if (load_code >= 1 + MT_ST_HPLOADER_OFFSET
26251da177e4SLinus Torvalds 	    && load_code <= 6 + MT_ST_HPLOADER_OFFSET) {
2626b30d8bcaSHannes Reinecke 		DEBC_printk(STp, " Enhanced %sload slot %2d.\n",
2627b30d8bcaSHannes Reinecke 			    (cmd[4]) ? "" : "un",
2628b30d8bcaSHannes Reinecke 			    load_code - MT_ST_HPLOADER_OFFSET);
26291da177e4SLinus Torvalds 		cmd[3] = load_code - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
26301da177e4SLinus Torvalds 	}
26311da177e4SLinus Torvalds 	if (STp->immediate) {
26321da177e4SLinus Torvalds 		cmd[1] = 1;	/* Don't wait for completion */
2633a02488edSJames Bottomley 		timeout = STp->device->request_queue->rq_timeout;
26341da177e4SLinus Torvalds 	}
26351da177e4SLinus Torvalds 	else
26361da177e4SLinus Torvalds 		timeout = STp->long_timeout;
26371da177e4SLinus Torvalds 
26381da177e4SLinus Torvalds 	DEBC(
26391da177e4SLinus Torvalds 		if (!load_code)
2640b30d8bcaSHannes Reinecke 			st_printk(ST_DEB_MSG, STp, "Unloading tape.\n");
26411da177e4SLinus Torvalds 		else
2642b30d8bcaSHannes Reinecke 			st_printk(ST_DEB_MSG, STp, "Loading tape.\n");
26431da177e4SLinus Torvalds 		);
26441da177e4SLinus Torvalds 
264502ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE,
264602ae2c0eSKai Makisara 			   timeout, MAX_RETRIES, 1);
26471da177e4SLinus Torvalds 	if (!SRpnt)
264802ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
26491da177e4SLinus Torvalds 
26501da177e4SLinus Torvalds 	retval = (STp->buffer)->syscall_result;
265102ae2c0eSKai Makisara 	st_release_request(SRpnt);
26521da177e4SLinus Torvalds 
26531da177e4SLinus Torvalds 	if (!retval) {	/* SCSI command successful */
26541da177e4SLinus Torvalds 
26551da177e4SLinus Torvalds 		if (!load_code) {
26561da177e4SLinus Torvalds 			STp->rew_at_close = 0;
26571da177e4SLinus Torvalds 			STp->ready = ST_NO_TAPE;
26581da177e4SLinus Torvalds 		}
26591da177e4SLinus Torvalds 		else {
26601da177e4SLinus Torvalds 			STp->rew_at_close = STp->autorew_dev;
26611da177e4SLinus Torvalds 			retval = check_tape(STp, filp);
26621da177e4SLinus Torvalds 			if (retval > 0)
26631da177e4SLinus Torvalds 				retval = 0;
26641da177e4SLinus Torvalds 		}
26651da177e4SLinus Torvalds 	}
26661da177e4SLinus Torvalds 	else {
26671da177e4SLinus Torvalds 		STps = &(STp->ps[STp->partition]);
26681da177e4SLinus Torvalds 		STps->drv_file = STps->drv_block = (-1);
26691da177e4SLinus Torvalds 	}
26701da177e4SLinus Torvalds 
26711da177e4SLinus Torvalds 	return retval;
26721da177e4SLinus Torvalds }
26731da177e4SLinus Torvalds 
26741da177e4SLinus Torvalds #if DEBUG
26751da177e4SLinus Torvalds #define ST_DEB_FORWARD  0
26761da177e4SLinus Torvalds #define ST_DEB_BACKWARD 1
deb_space_print(struct scsi_tape * STp,int direction,char * units,unsigned char * cmd)2677b30d8bcaSHannes Reinecke static void deb_space_print(struct scsi_tape *STp, int direction, char *units, unsigned char *cmd)
26781da177e4SLinus Torvalds {
26791da177e4SLinus Torvalds 	s32 sc;
26801da177e4SLinus Torvalds 
2681b30d8bcaSHannes Reinecke 	if (!debugging)
2682b30d8bcaSHannes Reinecke 		return;
2683b30d8bcaSHannes Reinecke 
268435b703dbSBart Van Assche 	sc = sign_extend32(get_unaligned_be24(&cmd[2]), 23);
26851da177e4SLinus Torvalds 	if (direction)
26861da177e4SLinus Torvalds 		sc = -sc;
2687b30d8bcaSHannes Reinecke 	st_printk(ST_DEB_MSG, STp, "Spacing tape %s over %d %s.\n",
26881da177e4SLinus Torvalds 		  direction ? "backward" : "forward", sc, units);
26891da177e4SLinus Torvalds }
2690b30d8bcaSHannes Reinecke #else
2691b30d8bcaSHannes Reinecke #define ST_DEB_FORWARD  0
2692b30d8bcaSHannes Reinecke #define ST_DEB_BACKWARD 1
deb_space_print(struct scsi_tape * STp,int direction,char * units,unsigned char * cmd)2693b30d8bcaSHannes Reinecke static void deb_space_print(struct scsi_tape *STp, int direction, char *units, unsigned char *cmd) {}
26941da177e4SLinus Torvalds #endif
26951da177e4SLinus Torvalds 
26961da177e4SLinus Torvalds 
26971da177e4SLinus Torvalds /* Internal ioctl function */
st_int_ioctl(struct scsi_tape * STp,unsigned int cmd_in,unsigned long arg)26981da177e4SLinus Torvalds static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned long arg)
26991da177e4SLinus Torvalds {
27001da177e4SLinus Torvalds 	int timeout;
27011da177e4SLinus Torvalds 	long ltmp;
27021da177e4SLinus Torvalds 	int ioctl_result;
27031da177e4SLinus Torvalds 	int chg_eof = 1;
27041da177e4SLinus Torvalds 	unsigned char cmd[MAX_COMMAND_SIZE];
27058b05b773SMike Christie 	struct st_request *SRpnt;
27061da177e4SLinus Torvalds 	struct st_partstat *STps;
27071da177e4SLinus Torvalds 	int fileno, blkno, at_sm, undone;
27081da177e4SLinus Torvalds 	int datalen = 0, direction = DMA_NONE;
27091da177e4SLinus Torvalds 
27101da177e4SLinus Torvalds 	WARN_ON(STp->buffer->do_dio != 0);
27111da177e4SLinus Torvalds 	if (STp->ready != ST_READY) {
27121da177e4SLinus Torvalds 		if (STp->ready == ST_NO_TAPE)
27131da177e4SLinus Torvalds 			return (-ENOMEDIUM);
27141da177e4SLinus Torvalds 		else
27151da177e4SLinus Torvalds 			return (-EIO);
27161da177e4SLinus Torvalds 	}
27171da177e4SLinus Torvalds 	timeout = STp->long_timeout;
27181da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
27191da177e4SLinus Torvalds 	fileno = STps->drv_file;
27201da177e4SLinus Torvalds 	blkno = STps->drv_block;
27211da177e4SLinus Torvalds 	at_sm = STps->at_sm;
27221da177e4SLinus Torvalds 
27231da177e4SLinus Torvalds 	memset(cmd, 0, MAX_COMMAND_SIZE);
27241da177e4SLinus Torvalds 	switch (cmd_in) {
27251da177e4SLinus Torvalds 	case MTFSFM:
27261da177e4SLinus Torvalds 		chg_eof = 0;	/* Changed from the FSF after this */
2727df561f66SGustavo A. R. Silva 		fallthrough;
27281da177e4SLinus Torvalds 	case MTFSF:
27291da177e4SLinus Torvalds 		cmd[0] = SPACE;
27301da177e4SLinus Torvalds 		cmd[1] = 0x01;	/* Space FileMarks */
27311da177e4SLinus Torvalds 		cmd[2] = (arg >> 16);
27321da177e4SLinus Torvalds 		cmd[3] = (arg >> 8);
27331da177e4SLinus Torvalds 		cmd[4] = arg;
2734b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_FORWARD, "filemarks", cmd);
27351da177e4SLinus Torvalds 		if (fileno >= 0)
27361da177e4SLinus Torvalds 			fileno += arg;
27371da177e4SLinus Torvalds 		blkno = 0;
27381da177e4SLinus Torvalds 		at_sm &= (arg == 0);
27391da177e4SLinus Torvalds 		break;
27401da177e4SLinus Torvalds 	case MTBSFM:
27411da177e4SLinus Torvalds 		chg_eof = 0;	/* Changed from the FSF after this */
2742df561f66SGustavo A. R. Silva 		fallthrough;
27431da177e4SLinus Torvalds 	case MTBSF:
27441da177e4SLinus Torvalds 		cmd[0] = SPACE;
27451da177e4SLinus Torvalds 		cmd[1] = 0x01;	/* Space FileMarks */
27461da177e4SLinus Torvalds 		ltmp = (-arg);
27471da177e4SLinus Torvalds 		cmd[2] = (ltmp >> 16);
27481da177e4SLinus Torvalds 		cmd[3] = (ltmp >> 8);
27491da177e4SLinus Torvalds 		cmd[4] = ltmp;
2750b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_BACKWARD, "filemarks", cmd);
27511da177e4SLinus Torvalds 		if (fileno >= 0)
27521da177e4SLinus Torvalds 			fileno -= arg;
27531da177e4SLinus Torvalds 		blkno = (-1);	/* We can't know the block number */
27541da177e4SLinus Torvalds 		at_sm &= (arg == 0);
27551da177e4SLinus Torvalds 		break;
27561da177e4SLinus Torvalds 	case MTFSR:
27571da177e4SLinus Torvalds 		cmd[0] = SPACE;
27581da177e4SLinus Torvalds 		cmd[1] = 0x00;	/* Space Blocks */
27591da177e4SLinus Torvalds 		cmd[2] = (arg >> 16);
27601da177e4SLinus Torvalds 		cmd[3] = (arg >> 8);
27611da177e4SLinus Torvalds 		cmd[4] = arg;
2762b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_FORWARD, "blocks", cmd);
27631da177e4SLinus Torvalds 		if (blkno >= 0)
27641da177e4SLinus Torvalds 			blkno += arg;
27651da177e4SLinus Torvalds 		at_sm &= (arg == 0);
27661da177e4SLinus Torvalds 		break;
27671da177e4SLinus Torvalds 	case MTBSR:
27681da177e4SLinus Torvalds 		cmd[0] = SPACE;
27691da177e4SLinus Torvalds 		cmd[1] = 0x00;	/* Space Blocks */
27701da177e4SLinus Torvalds 		ltmp = (-arg);
27711da177e4SLinus Torvalds 		cmd[2] = (ltmp >> 16);
27721da177e4SLinus Torvalds 		cmd[3] = (ltmp >> 8);
27731da177e4SLinus Torvalds 		cmd[4] = ltmp;
2774b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_BACKWARD, "blocks", cmd);
27751da177e4SLinus Torvalds 		if (blkno >= 0)
27761da177e4SLinus Torvalds 			blkno -= arg;
27771da177e4SLinus Torvalds 		at_sm &= (arg == 0);
27781da177e4SLinus Torvalds 		break;
27791da177e4SLinus Torvalds 	case MTFSS:
27801da177e4SLinus Torvalds 		cmd[0] = SPACE;
27811da177e4SLinus Torvalds 		cmd[1] = 0x04;	/* Space Setmarks */
27821da177e4SLinus Torvalds 		cmd[2] = (arg >> 16);
27831da177e4SLinus Torvalds 		cmd[3] = (arg >> 8);
27841da177e4SLinus Torvalds 		cmd[4] = arg;
2785b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_FORWARD, "setmarks", cmd);
27861da177e4SLinus Torvalds 		if (arg != 0) {
27871da177e4SLinus Torvalds 			blkno = fileno = (-1);
27881da177e4SLinus Torvalds 			at_sm = 1;
27891da177e4SLinus Torvalds 		}
27901da177e4SLinus Torvalds 		break;
27911da177e4SLinus Torvalds 	case MTBSS:
27921da177e4SLinus Torvalds 		cmd[0] = SPACE;
27931da177e4SLinus Torvalds 		cmd[1] = 0x04;	/* Space Setmarks */
27941da177e4SLinus Torvalds 		ltmp = (-arg);
27951da177e4SLinus Torvalds 		cmd[2] = (ltmp >> 16);
27961da177e4SLinus Torvalds 		cmd[3] = (ltmp >> 8);
27971da177e4SLinus Torvalds 		cmd[4] = ltmp;
2798b30d8bcaSHannes Reinecke 		deb_space_print(STp, ST_DEB_BACKWARD, "setmarks", cmd);
27991da177e4SLinus Torvalds 		if (arg != 0) {
28001da177e4SLinus Torvalds 			blkno = fileno = (-1);
28011da177e4SLinus Torvalds 			at_sm = 1;
28021da177e4SLinus Torvalds 		}
28031da177e4SLinus Torvalds 		break;
28041da177e4SLinus Torvalds 	case MTWEOF:
28053e51d3c9SKai Makisara 	case MTWEOFI:
28061da177e4SLinus Torvalds 	case MTWSM:
28071da177e4SLinus Torvalds 		if (STp->write_prot)
28081da177e4SLinus Torvalds 			return (-EACCES);
28091da177e4SLinus Torvalds 		cmd[0] = WRITE_FILEMARKS;
28101da177e4SLinus Torvalds 		if (cmd_in == MTWSM)
28111da177e4SLinus Torvalds 			cmd[1] = 2;
2812c743e44fSLee Duncan 		if (cmd_in == MTWEOFI ||
2813c743e44fSLee Duncan 		    (cmd_in == MTWEOF && STp->immediate_filemark))
28143e51d3c9SKai Makisara 			cmd[1] |= 1;
28151da177e4SLinus Torvalds 		cmd[2] = (arg >> 16);
28161da177e4SLinus Torvalds 		cmd[3] = (arg >> 8);
28171da177e4SLinus Torvalds 		cmd[4] = arg;
2818a02488edSJames Bottomley 		timeout = STp->device->request_queue->rq_timeout;
28191da177e4SLinus Torvalds 		DEBC(
28203e51d3c9SKai Makisara 			if (cmd_in != MTWSM)
2821b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2822b30d8bcaSHannes Reinecke 					  "Writing %d filemarks.\n",
2823b30d8bcaSHannes Reinecke 					  cmd[2] * 65536 +
2824b30d8bcaSHannes Reinecke 					  cmd[3] * 256 +
2825b30d8bcaSHannes Reinecke 					  cmd[4]);
28261da177e4SLinus Torvalds 			else
2827b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2828b30d8bcaSHannes Reinecke 					  "Writing %d setmarks.\n",
2829b30d8bcaSHannes Reinecke 					  cmd[2] * 65536 +
2830b30d8bcaSHannes Reinecke 					  cmd[3] * 256 +
2831b30d8bcaSHannes Reinecke 					  cmd[4]);
28321da177e4SLinus Torvalds 		)
28331da177e4SLinus Torvalds 		if (fileno >= 0)
28341da177e4SLinus Torvalds 			fileno += arg;
28351da177e4SLinus Torvalds 		blkno = 0;
28361da177e4SLinus Torvalds 		at_sm = (cmd_in == MTWSM);
28371da177e4SLinus Torvalds 		break;
28381da177e4SLinus Torvalds 	case MTREW:
28391da177e4SLinus Torvalds 		cmd[0] = REZERO_UNIT;
28401da177e4SLinus Torvalds 		if (STp->immediate) {
28411da177e4SLinus Torvalds 			cmd[1] = 1;	/* Don't wait for completion */
2842a02488edSJames Bottomley 			timeout = STp->device->request_queue->rq_timeout;
28431da177e4SLinus Torvalds 		}
2844b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Rewinding tape.\n");
28451da177e4SLinus Torvalds 		fileno = blkno = at_sm = 0;
28461da177e4SLinus Torvalds 		break;
28471da177e4SLinus Torvalds 	case MTNOP:
2848b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "No op on tape.\n");
28491da177e4SLinus Torvalds 		return 0;	/* Should do something ? */
28501da177e4SLinus Torvalds 	case MTRETEN:
28511da177e4SLinus Torvalds 		cmd[0] = START_STOP;
28521da177e4SLinus Torvalds 		if (STp->immediate) {
28531da177e4SLinus Torvalds 			cmd[1] = 1;	/* Don't wait for completion */
2854a02488edSJames Bottomley 			timeout = STp->device->request_queue->rq_timeout;
28551da177e4SLinus Torvalds 		}
28561da177e4SLinus Torvalds 		cmd[4] = 3;
2857b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Retensioning tape.\n");
28581da177e4SLinus Torvalds 		fileno = blkno = at_sm = 0;
28591da177e4SLinus Torvalds 		break;
28601da177e4SLinus Torvalds 	case MTEOM:
28611da177e4SLinus Torvalds 		if (!STp->fast_mteom) {
28621da177e4SLinus Torvalds 			/* space to the end of tape */
28631da177e4SLinus Torvalds 			ioctl_result = st_int_ioctl(STp, MTFSF, 0x7fffff);
28641da177e4SLinus Torvalds 			fileno = STps->drv_file;
28651da177e4SLinus Torvalds 			if (STps->eof >= ST_EOD_1)
28661da177e4SLinus Torvalds 				return 0;
28671da177e4SLinus Torvalds 			/* The next lines would hide the number of spaced FileMarks
28681da177e4SLinus Torvalds 			   That's why I inserted the previous lines. I had no luck
28691da177e4SLinus Torvalds 			   with detecting EOM with FSF, so we go now to EOM.
28701da177e4SLinus Torvalds 			   Joerg Weule */
28711da177e4SLinus Torvalds 		} else
28721da177e4SLinus Torvalds 			fileno = (-1);
28731da177e4SLinus Torvalds 		cmd[0] = SPACE;
28741da177e4SLinus Torvalds 		cmd[1] = 3;
2875b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Spacing to end of recorded medium.\n");
28761da177e4SLinus Torvalds 		blkno = -1;
28771da177e4SLinus Torvalds 		at_sm = 0;
28781da177e4SLinus Torvalds 		break;
28791da177e4SLinus Torvalds 	case MTERASE:
28801da177e4SLinus Torvalds 		if (STp->write_prot)
28811da177e4SLinus Torvalds 			return (-EACCES);
28821da177e4SLinus Torvalds 		cmd[0] = ERASE;
28831da177e4SLinus Torvalds 		cmd[1] = (arg ? 1 : 0);	/* Long erase with non-zero argument */
28841da177e4SLinus Torvalds 		if (STp->immediate) {
28851da177e4SLinus Torvalds 			cmd[1] |= 2;	/* Don't wait for completion */
2886a02488edSJames Bottomley 			timeout = STp->device->request_queue->rq_timeout;
28871da177e4SLinus Torvalds 		}
28881da177e4SLinus Torvalds 		else
28891da177e4SLinus Torvalds 			timeout = STp->long_timeout * 8;
28901da177e4SLinus Torvalds 
2891b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Erasing tape.\n");
28921da177e4SLinus Torvalds 		fileno = blkno = at_sm = 0;
28931da177e4SLinus Torvalds 		break;
28941da177e4SLinus Torvalds 	case MTSETBLK:		/* Set block length */
28951da177e4SLinus Torvalds 	case MTSETDENSITY:	/* Set tape density */
28961da177e4SLinus Torvalds 	case MTSETDRVBUFFER:	/* Set drive buffering */
28971da177e4SLinus Torvalds 	case SET_DENS_AND_BLK:	/* Set density and block size */
28981da177e4SLinus Torvalds 		chg_eof = 0;
28991da177e4SLinus Torvalds 		if (STp->dirty || (STp->buffer)->buffer_bytes != 0)
29001da177e4SLinus Torvalds 			return (-EIO);	/* Not allowed if data in buffer */
29011da177e4SLinus Torvalds 		if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) &&
29021da177e4SLinus Torvalds 		    (arg & MT_ST_BLKSIZE_MASK) != 0 &&
29031da177e4SLinus Torvalds 		    STp->max_block > 0 &&
29041da177e4SLinus Torvalds 		    ((arg & MT_ST_BLKSIZE_MASK) < STp->min_block ||
29051da177e4SLinus Torvalds 		     (arg & MT_ST_BLKSIZE_MASK) > STp->max_block)) {
2906b30d8bcaSHannes Reinecke 			st_printk(KERN_WARNING, STp, "Illegal block size.\n");
29071da177e4SLinus Torvalds 			return (-EINVAL);
29081da177e4SLinus Torvalds 		}
29091da177e4SLinus Torvalds 		cmd[0] = MODE_SELECT;
29101da177e4SLinus Torvalds 		if ((STp->use_pf & USE_PF))
29111da177e4SLinus Torvalds 			cmd[1] = MODE_SELECT_PAGE_FORMAT;
29121da177e4SLinus Torvalds 		cmd[4] = datalen = 12;
29131da177e4SLinus Torvalds 		direction = DMA_TO_DEVICE;
29141da177e4SLinus Torvalds 
29151da177e4SLinus Torvalds 		memset((STp->buffer)->b_data, 0, 12);
29161da177e4SLinus Torvalds 		if (cmd_in == MTSETDRVBUFFER)
29171da177e4SLinus Torvalds 			(STp->buffer)->b_data[2] = (arg & 7) << 4;
29181da177e4SLinus Torvalds 		else
29191da177e4SLinus Torvalds 			(STp->buffer)->b_data[2] =
29201da177e4SLinus Torvalds 			    STp->drv_buffer << 4;
29211da177e4SLinus Torvalds 		(STp->buffer)->b_data[3] = 8;	/* block descriptor length */
29221da177e4SLinus Torvalds 		if (cmd_in == MTSETDENSITY) {
29231da177e4SLinus Torvalds 			(STp->buffer)->b_data[4] = arg;
29241da177e4SLinus Torvalds 			STp->density_changed = 1;	/* At least we tried ;-) */
29251da177e4SLinus Torvalds 		} else if (cmd_in == SET_DENS_AND_BLK)
29261da177e4SLinus Torvalds 			(STp->buffer)->b_data[4] = arg >> 24;
29271da177e4SLinus Torvalds 		else
29281da177e4SLinus Torvalds 			(STp->buffer)->b_data[4] = STp->density;
29291da177e4SLinus Torvalds 		if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
29301da177e4SLinus Torvalds 			ltmp = arg & MT_ST_BLKSIZE_MASK;
29311da177e4SLinus Torvalds 			if (cmd_in == MTSETBLK)
29321da177e4SLinus Torvalds 				STp->blksize_changed = 1; /* At least we tried ;-) */
29331da177e4SLinus Torvalds 		} else
29341da177e4SLinus Torvalds 			ltmp = STp->block_size;
29351da177e4SLinus Torvalds 		(STp->buffer)->b_data[9] = (ltmp >> 16);
29361da177e4SLinus Torvalds 		(STp->buffer)->b_data[10] = (ltmp >> 8);
29371da177e4SLinus Torvalds 		(STp->buffer)->b_data[11] = ltmp;
2938a02488edSJames Bottomley 		timeout = STp->device->request_queue->rq_timeout;
29391da177e4SLinus Torvalds 		DEBC(
29401da177e4SLinus Torvalds 			if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK)
2941b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2942b30d8bcaSHannes Reinecke 					  "Setting block size to %d bytes.\n",
29431da177e4SLinus Torvalds 					  (STp->buffer)->b_data[9] * 65536 +
29441da177e4SLinus Torvalds 					  (STp->buffer)->b_data[10] * 256 +
29451da177e4SLinus Torvalds 					  (STp->buffer)->b_data[11]);
29461da177e4SLinus Torvalds 			if (cmd_in == MTSETDENSITY || cmd_in == SET_DENS_AND_BLK)
2947b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2948b30d8bcaSHannes Reinecke 					  "Setting density code to %x.\n",
29491da177e4SLinus Torvalds 					  (STp->buffer)->b_data[4]);
29501da177e4SLinus Torvalds 			if (cmd_in == MTSETDRVBUFFER)
2951b30d8bcaSHannes Reinecke 				st_printk(ST_DEB_MSG, STp,
2952b30d8bcaSHannes Reinecke 					  "Setting drive buffer code to %d.\n",
29531da177e4SLinus Torvalds 					  ((STp->buffer)->b_data[2] >> 4) & 7);
29541da177e4SLinus Torvalds 		)
29551da177e4SLinus Torvalds 		break;
29561da177e4SLinus Torvalds 	default:
29571da177e4SLinus Torvalds 		return (-ENOSYS);
29581da177e4SLinus Torvalds 	}
29591da177e4SLinus Torvalds 
296002ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction,
296102ae2c0eSKai Makisara 			   timeout, MAX_RETRIES, 1);
29621da177e4SLinus Torvalds 	if (!SRpnt)
29631da177e4SLinus Torvalds 		return (STp->buffer)->syscall_result;
29641da177e4SLinus Torvalds 
29651da177e4SLinus Torvalds 	ioctl_result = (STp->buffer)->syscall_result;
29661da177e4SLinus Torvalds 
29671da177e4SLinus Torvalds 	if (!ioctl_result) {	/* SCSI command successful */
29688b05b773SMike Christie 		st_release_request(SRpnt);
29691da177e4SLinus Torvalds 		SRpnt = NULL;
29701da177e4SLinus Torvalds 		STps->drv_block = blkno;
29711da177e4SLinus Torvalds 		STps->drv_file = fileno;
29721da177e4SLinus Torvalds 		STps->at_sm = at_sm;
29731da177e4SLinus Torvalds 
29741da177e4SLinus Torvalds 		if (cmd_in == MTBSFM)
29751da177e4SLinus Torvalds 			ioctl_result = st_int_ioctl(STp, MTFSF, 1);
29761da177e4SLinus Torvalds 		else if (cmd_in == MTFSFM)
29771da177e4SLinus Torvalds 			ioctl_result = st_int_ioctl(STp, MTBSF, 1);
29781da177e4SLinus Torvalds 
29791da177e4SLinus Torvalds 		if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) {
29801da177e4SLinus Torvalds 			STp->block_size = arg & MT_ST_BLKSIZE_MASK;
29811da177e4SLinus Torvalds 			if (STp->block_size != 0) {
29821da177e4SLinus Torvalds 				(STp->buffer)->buffer_blocks =
29831da177e4SLinus Torvalds 				    (STp->buffer)->buffer_size / STp->block_size;
29841da177e4SLinus Torvalds 			}
29851da177e4SLinus Torvalds 			(STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0;
29861da177e4SLinus Torvalds 			if (cmd_in == SET_DENS_AND_BLK)
29871da177e4SLinus Torvalds 				STp->density = arg >> MT_ST_DENSITY_SHIFT;
29881da177e4SLinus Torvalds 		} else if (cmd_in == MTSETDRVBUFFER)
29891da177e4SLinus Torvalds 			STp->drv_buffer = (arg & 7);
29901da177e4SLinus Torvalds 		else if (cmd_in == MTSETDENSITY)
29911da177e4SLinus Torvalds 			STp->density = arg;
29921da177e4SLinus Torvalds 
29931da177e4SLinus Torvalds 		if (cmd_in == MTEOM)
29941da177e4SLinus Torvalds 			STps->eof = ST_EOD;
29951da177e4SLinus Torvalds 		else if (cmd_in == MTFSF)
29961da177e4SLinus Torvalds 			STps->eof = ST_FM;
29971da177e4SLinus Torvalds 		else if (chg_eof)
29981da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
29991da177e4SLinus Torvalds 
30003e51d3c9SKai Makisara 		if (cmd_in == MTWEOF || cmd_in == MTWEOFI)
30013e51d3c9SKai Makisara 			STps->rw = ST_IDLE;  /* prevent automatic WEOF at close */
30021da177e4SLinus Torvalds 	} else { /* SCSI command was not completely successful. Don't return
30031da177e4SLinus Torvalds                     from this block without releasing the SCSI command block! */
30041da177e4SLinus Torvalds 		struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat;
30051da177e4SLinus Torvalds 
30061da177e4SLinus Torvalds 		if (cmdstatp->flags & SENSE_EOM) {
30071da177e4SLinus Torvalds 			if (cmd_in != MTBSF && cmd_in != MTBSFM &&
30081da177e4SLinus Torvalds 			    cmd_in != MTBSR && cmd_in != MTBSS)
30091da177e4SLinus Torvalds 				STps->eof = ST_EOM_OK;
30101da177e4SLinus Torvalds 			STps->drv_block = 0;
30111da177e4SLinus Torvalds 		}
30121da177e4SLinus Torvalds 
30131da177e4SLinus Torvalds 		if (cmdstatp->remainder_valid)
30141da177e4SLinus Torvalds 			undone = (int)cmdstatp->uremainder64;
30151da177e4SLinus Torvalds 		else
30161da177e4SLinus Torvalds 			undone = 0;
30171da177e4SLinus Torvalds 
30183e51d3c9SKai Makisara 		if ((cmd_in == MTWEOF || cmd_in == MTWEOFI) &&
30191da177e4SLinus Torvalds 		    cmdstatp->have_sense &&
302091614c05SKai Makisara 		    (cmdstatp->flags & SENSE_EOM)) {
302191614c05SKai Makisara 			if (cmdstatp->sense_hdr.sense_key == NO_SENSE ||
302291614c05SKai Makisara 			    cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) {
302391614c05SKai Makisara 				ioctl_result = 0;	/* EOF(s) written successfully at EOM */
30241da177e4SLinus Torvalds 				STps->eof = ST_NOEOF;
302591614c05SKai Makisara 			} else {  /* Writing EOF(s) failed */
302691614c05SKai Makisara 				if (fileno >= 0)
302791614c05SKai Makisara 					fileno -= undone;
302891614c05SKai Makisara 				if (undone < arg)
302991614c05SKai Makisara 					STps->eof = ST_NOEOF;
303091614c05SKai Makisara 			}
303191614c05SKai Makisara 			STps->drv_file = fileno;
30321da177e4SLinus Torvalds 		} else if ((cmd_in == MTFSF) || (cmd_in == MTFSFM)) {
30331da177e4SLinus Torvalds 			if (fileno >= 0)
30341da177e4SLinus Torvalds 				STps->drv_file = fileno - undone;
30351da177e4SLinus Torvalds 			else
30361da177e4SLinus Torvalds 				STps->drv_file = fileno;
30371da177e4SLinus Torvalds 			STps->drv_block = -1;
30381da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
30391da177e4SLinus Torvalds 		} else if ((cmd_in == MTBSF) || (cmd_in == MTBSFM)) {
30401da177e4SLinus Torvalds 			if (arg > 0 && undone < 0)  /* Some drives get this wrong */
30411da177e4SLinus Torvalds 				undone = (-undone);
30421da177e4SLinus Torvalds 			if (STps->drv_file >= 0)
30431da177e4SLinus Torvalds 				STps->drv_file = fileno + undone;
30441da177e4SLinus Torvalds 			STps->drv_block = 0;
30451da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
30461da177e4SLinus Torvalds 		} else if (cmd_in == MTFSR) {
30471da177e4SLinus Torvalds 			if (cmdstatp->flags & SENSE_FMK) {	/* Hit filemark */
30481da177e4SLinus Torvalds 				if (STps->drv_file >= 0)
30491da177e4SLinus Torvalds 					STps->drv_file++;
30501da177e4SLinus Torvalds 				STps->drv_block = 0;
30511da177e4SLinus Torvalds 				STps->eof = ST_FM;
30521da177e4SLinus Torvalds 			} else {
30531da177e4SLinus Torvalds 				if (blkno >= undone)
30541da177e4SLinus Torvalds 					STps->drv_block = blkno - undone;
30551da177e4SLinus Torvalds 				else
30561da177e4SLinus Torvalds 					STps->drv_block = (-1);
30571da177e4SLinus Torvalds 				STps->eof = ST_NOEOF;
30581da177e4SLinus Torvalds 			}
30591da177e4SLinus Torvalds 		} else if (cmd_in == MTBSR) {
30601da177e4SLinus Torvalds 			if (cmdstatp->flags & SENSE_FMK) {	/* Hit filemark */
30611da177e4SLinus Torvalds 				STps->drv_file--;
30621da177e4SLinus Torvalds 				STps->drv_block = (-1);
30631da177e4SLinus Torvalds 			} else {
30641da177e4SLinus Torvalds 				if (arg > 0 && undone < 0)  /* Some drives get this wrong */
30651da177e4SLinus Torvalds 					undone = (-undone);
30661da177e4SLinus Torvalds 				if (STps->drv_block >= 0)
30671da177e4SLinus Torvalds 					STps->drv_block = blkno + undone;
30681da177e4SLinus Torvalds 			}
30691da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
30701da177e4SLinus Torvalds 		} else if (cmd_in == MTEOM) {
30711da177e4SLinus Torvalds 			STps->drv_file = (-1);
30721da177e4SLinus Torvalds 			STps->drv_block = (-1);
30731da177e4SLinus Torvalds 			STps->eof = ST_EOD;
30741da177e4SLinus Torvalds 		} else if (cmd_in == MTSETBLK ||
30751da177e4SLinus Torvalds 			   cmd_in == MTSETDENSITY ||
30761da177e4SLinus Torvalds 			   cmd_in == MTSETDRVBUFFER ||
30771da177e4SLinus Torvalds 			   cmd_in == SET_DENS_AND_BLK) {
30781da177e4SLinus Torvalds 			if (cmdstatp->sense_hdr.sense_key == ILLEGAL_REQUEST &&
30791da177e4SLinus Torvalds 			    !(STp->use_pf & PF_TESTED)) {
30801da177e4SLinus Torvalds 				/* Try the other possible state of Page Format if not
30811da177e4SLinus Torvalds 				   already tried */
30821da2019fSKai Makisara 				STp->use_pf = (STp->use_pf ^ USE_PF) | PF_TESTED;
30838b05b773SMike Christie 				st_release_request(SRpnt);
30841da177e4SLinus Torvalds 				SRpnt = NULL;
30851da177e4SLinus Torvalds 				return st_int_ioctl(STp, cmd_in, arg);
30861da177e4SLinus Torvalds 			}
30871da177e4SLinus Torvalds 		} else if (chg_eof)
30881da177e4SLinus Torvalds 			STps->eof = ST_NOEOF;
30891da177e4SLinus Torvalds 
30901da177e4SLinus Torvalds 		if (cmdstatp->sense_hdr.sense_key == BLANK_CHECK)
30911da177e4SLinus Torvalds 			STps->eof = ST_EOD;
30921da177e4SLinus Torvalds 
30938b05b773SMike Christie 		st_release_request(SRpnt);
30941da177e4SLinus Torvalds 		SRpnt = NULL;
30951da177e4SLinus Torvalds 	}
30961da177e4SLinus Torvalds 
30971da177e4SLinus Torvalds 	return ioctl_result;
30981da177e4SLinus Torvalds }
30991da177e4SLinus Torvalds 
31001da177e4SLinus Torvalds 
31011da177e4SLinus Torvalds /* Get the tape position. If bt == 2, arg points into a kernel space mt_loc
31021da177e4SLinus Torvalds    structure. */
31031da177e4SLinus Torvalds 
get_location(struct scsi_tape * STp,unsigned int * block,int * partition,int logical)31041da177e4SLinus Torvalds static int get_location(struct scsi_tape *STp, unsigned int *block, int *partition,
31051da177e4SLinus Torvalds 			int logical)
31061da177e4SLinus Torvalds {
31071da177e4SLinus Torvalds 	int result;
31081da177e4SLinus Torvalds 	unsigned char scmd[MAX_COMMAND_SIZE];
31098b05b773SMike Christie 	struct st_request *SRpnt;
31101da177e4SLinus Torvalds 
31111da177e4SLinus Torvalds 	if (STp->ready != ST_READY)
31121da177e4SLinus Torvalds 		return (-EIO);
31131da177e4SLinus Torvalds 
31141da177e4SLinus Torvalds 	memset(scmd, 0, MAX_COMMAND_SIZE);
31151da177e4SLinus Torvalds 	if ((STp->device)->scsi_level < SCSI_2) {
31161da177e4SLinus Torvalds 		scmd[0] = QFA_REQUEST_BLOCK;
31171da177e4SLinus Torvalds 		scmd[4] = 3;
31181da177e4SLinus Torvalds 	} else {
31191da177e4SLinus Torvalds 		scmd[0] = READ_POSITION;
31201da177e4SLinus Torvalds 		if (!logical && !STp->scsi2_logical)
31211da177e4SLinus Torvalds 			scmd[1] = 1;
31221da177e4SLinus Torvalds 	}
312302ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE,
31247a31ec3cSFUJITA Tomonori 			   STp->device->request_queue->rq_timeout,
312502ae2c0eSKai Makisara 			   MAX_READY_RETRIES, 1);
312602ae2c0eSKai Makisara 	if (!SRpnt)
312702ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
31281da177e4SLinus Torvalds 
31291da177e4SLinus Torvalds 	if ((STp->buffer)->syscall_result != 0 ||
31301da177e4SLinus Torvalds 	    (STp->device->scsi_level >= SCSI_2 &&
31311da177e4SLinus Torvalds 	     ((STp->buffer)->b_data[0] & 4) != 0)) {
31321da177e4SLinus Torvalds 		*block = *partition = 0;
3133b30d8bcaSHannes Reinecke 		DEBC_printk(STp, " Can't read tape position.\n");
31341da177e4SLinus Torvalds 		result = (-EIO);
31351da177e4SLinus Torvalds 	} else {
31361da177e4SLinus Torvalds 		result = 0;
31371da177e4SLinus Torvalds 		if ((STp->device)->scsi_level < SCSI_2) {
31381da177e4SLinus Torvalds 			*block = ((STp->buffer)->b_data[0] << 16)
31391da177e4SLinus Torvalds 			    + ((STp->buffer)->b_data[1] << 8)
31401da177e4SLinus Torvalds 			    + (STp->buffer)->b_data[2];
31411da177e4SLinus Torvalds 			*partition = 0;
31421da177e4SLinus Torvalds 		} else {
31431da177e4SLinus Torvalds 			*block = ((STp->buffer)->b_data[4] << 24)
31441da177e4SLinus Torvalds 			    + ((STp->buffer)->b_data[5] << 16)
31451da177e4SLinus Torvalds 			    + ((STp->buffer)->b_data[6] << 8)
31461da177e4SLinus Torvalds 			    + (STp->buffer)->b_data[7];
31471da177e4SLinus Torvalds 			*partition = (STp->buffer)->b_data[1];
31481da177e4SLinus Torvalds 			if (((STp->buffer)->b_data[0] & 0x80) &&
31491da177e4SLinus Torvalds 			    (STp->buffer)->b_data[1] == 0)	/* BOP of partition 0 */
31501da177e4SLinus Torvalds 				STp->ps[0].drv_block = STp->ps[0].drv_file = 0;
31511da177e4SLinus Torvalds 		}
3152b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Got tape pos. blk %d part %d.\n",
3153b30d8bcaSHannes Reinecke 			    *block, *partition);
31541da177e4SLinus Torvalds 	}
31558b05b773SMike Christie 	st_release_request(SRpnt);
31561da177e4SLinus Torvalds 	SRpnt = NULL;
31571da177e4SLinus Torvalds 
31581da177e4SLinus Torvalds 	return result;
31591da177e4SLinus Torvalds }
31601da177e4SLinus Torvalds 
31611da177e4SLinus Torvalds 
31621da177e4SLinus Torvalds /* Set the tape block and partition. Negative partition means that only the
31631da177e4SLinus Torvalds    block should be set in vendor specific way. */
set_location(struct scsi_tape * STp,unsigned int block,int partition,int logical)31641da177e4SLinus Torvalds static int set_location(struct scsi_tape *STp, unsigned int block, int partition,
31651da177e4SLinus Torvalds 			int logical)
31661da177e4SLinus Torvalds {
31671da177e4SLinus Torvalds 	struct st_partstat *STps;
31681da177e4SLinus Torvalds 	int result, p;
31691da177e4SLinus Torvalds 	unsigned int blk;
31701da177e4SLinus Torvalds 	int timeout;
31711da177e4SLinus Torvalds 	unsigned char scmd[MAX_COMMAND_SIZE];
31728b05b773SMike Christie 	struct st_request *SRpnt;
31731da177e4SLinus Torvalds 
31741da177e4SLinus Torvalds 	if (STp->ready != ST_READY)
31751da177e4SLinus Torvalds 		return (-EIO);
31761da177e4SLinus Torvalds 	timeout = STp->long_timeout;
31771da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
31781da177e4SLinus Torvalds 
3179b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Setting block to %d and partition to %d.\n",
3180b30d8bcaSHannes Reinecke 		    block, partition);
31811da177e4SLinus Torvalds 	DEB(if (partition < 0)
31821da177e4SLinus Torvalds 		return (-EIO); )
31831da177e4SLinus Torvalds 
31841da177e4SLinus Torvalds 	/* Update the location at the partition we are leaving */
31851da177e4SLinus Torvalds 	if ((!STp->can_partitions && partition != 0) ||
31861da177e4SLinus Torvalds 	    partition >= ST_NBR_PARTITIONS)
31871da177e4SLinus Torvalds 		return (-EINVAL);
31881da177e4SLinus Torvalds 	if (partition != STp->partition) {
31891da177e4SLinus Torvalds 		if (get_location(STp, &blk, &p, 1))
31901da177e4SLinus Torvalds 			STps->last_block_valid = 0;
31911da177e4SLinus Torvalds 		else {
31921da177e4SLinus Torvalds 			STps->last_block_valid = 1;
31931da177e4SLinus Torvalds 			STps->last_block_visited = blk;
3194b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Visited block %d for "
3195b30d8bcaSHannes Reinecke 				    "partition %d saved.\n",
3196b30d8bcaSHannes Reinecke 				    blk, STp->partition);
31971da177e4SLinus Torvalds 		}
31981da177e4SLinus Torvalds 	}
31991da177e4SLinus Torvalds 
32001da177e4SLinus Torvalds 	memset(scmd, 0, MAX_COMMAND_SIZE);
32011da177e4SLinus Torvalds 	if ((STp->device)->scsi_level < SCSI_2) {
32021da177e4SLinus Torvalds 		scmd[0] = QFA_SEEK_BLOCK;
32031da177e4SLinus Torvalds 		scmd[2] = (block >> 16);
32041da177e4SLinus Torvalds 		scmd[3] = (block >> 8);
32051da177e4SLinus Torvalds 		scmd[4] = block;
32061da177e4SLinus Torvalds 		scmd[5] = 0;
32071da177e4SLinus Torvalds 	} else {
32081da177e4SLinus Torvalds 		scmd[0] = SEEK_10;
32091da177e4SLinus Torvalds 		scmd[3] = (block >> 24);
32101da177e4SLinus Torvalds 		scmd[4] = (block >> 16);
32111da177e4SLinus Torvalds 		scmd[5] = (block >> 8);
32121da177e4SLinus Torvalds 		scmd[6] = block;
32131da177e4SLinus Torvalds 		if (!logical && !STp->scsi2_logical)
32141da177e4SLinus Torvalds 			scmd[1] = 4;
32151da177e4SLinus Torvalds 		if (STp->partition != partition) {
32161da177e4SLinus Torvalds 			scmd[1] |= 2;
32171da177e4SLinus Torvalds 			scmd[8] = partition;
3218b30d8bcaSHannes Reinecke 			DEBC_printk(STp, "Trying to change partition "
3219b30d8bcaSHannes Reinecke 				    "from %d to %d\n", STp->partition,
3220b30d8bcaSHannes Reinecke 				    partition);
32211da177e4SLinus Torvalds 		}
32221da177e4SLinus Torvalds 	}
32231da177e4SLinus Torvalds 	if (STp->immediate) {
32241da177e4SLinus Torvalds 		scmd[1] |= 1;		/* Don't wait for completion */
3225a02488edSJames Bottomley 		timeout = STp->device->request_queue->rq_timeout;
32261da177e4SLinus Torvalds 	}
32271da177e4SLinus Torvalds 
322802ae2c0eSKai Makisara 	SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
322902ae2c0eSKai Makisara 			   timeout, MAX_READY_RETRIES, 1);
32301da177e4SLinus Torvalds 	if (!SRpnt)
323102ae2c0eSKai Makisara 		return (STp->buffer)->syscall_result;
32321da177e4SLinus Torvalds 
32331da177e4SLinus Torvalds 	STps->drv_block = STps->drv_file = (-1);
32341da177e4SLinus Torvalds 	STps->eof = ST_NOEOF;
32351da177e4SLinus Torvalds 	if ((STp->buffer)->syscall_result != 0) {
32361da177e4SLinus Torvalds 		result = (-EIO);
32371da177e4SLinus Torvalds 		if (STp->can_partitions &&
32381da177e4SLinus Torvalds 		    (STp->device)->scsi_level >= SCSI_2 &&
32391da177e4SLinus Torvalds 		    (p = find_partition(STp)) >= 0)
32401da177e4SLinus Torvalds 			STp->partition = p;
32411da177e4SLinus Torvalds 	} else {
32421da177e4SLinus Torvalds 		if (STp->can_partitions) {
32431da177e4SLinus Torvalds 			STp->partition = partition;
32441da177e4SLinus Torvalds 			STps = &(STp->ps[partition]);
32451da177e4SLinus Torvalds 			if (!STps->last_block_valid ||
32461da177e4SLinus Torvalds 			    STps->last_block_visited != block) {
32471da177e4SLinus Torvalds 				STps->at_sm = 0;
32481da177e4SLinus Torvalds 				STps->rw = ST_IDLE;
32491da177e4SLinus Torvalds 			}
32501da177e4SLinus Torvalds 		} else
32511da177e4SLinus Torvalds 			STps->at_sm = 0;
32521da177e4SLinus Torvalds 		if (block == 0)
32531da177e4SLinus Torvalds 			STps->drv_block = STps->drv_file = 0;
32541da177e4SLinus Torvalds 		result = 0;
32551da177e4SLinus Torvalds 	}
325602ae2c0eSKai Makisara 
32578b05b773SMike Christie 	st_release_request(SRpnt);
32581da177e4SLinus Torvalds 	SRpnt = NULL;
32591da177e4SLinus Torvalds 
32601da177e4SLinus Torvalds 	return result;
32611da177e4SLinus Torvalds }
32621da177e4SLinus Torvalds 
32631da177e4SLinus Torvalds 
32641da177e4SLinus Torvalds /* Find the current partition number for the drive status. Called from open and
32651da177e4SLinus Torvalds    returns either partition number of negative error code. */
find_partition(struct scsi_tape * STp)32661da177e4SLinus Torvalds static int find_partition(struct scsi_tape *STp)
32671da177e4SLinus Torvalds {
32681da177e4SLinus Torvalds 	int i, partition;
32691da177e4SLinus Torvalds 	unsigned int block;
32701da177e4SLinus Torvalds 
32711da177e4SLinus Torvalds 	if ((i = get_location(STp, &block, &partition, 1)) < 0)
32721da177e4SLinus Torvalds 		return i;
32731da177e4SLinus Torvalds 	if (partition >= ST_NBR_PARTITIONS)
32741da177e4SLinus Torvalds 		return (-EIO);
32751da177e4SLinus Torvalds 	return partition;
32761da177e4SLinus Torvalds }
32771da177e4SLinus Torvalds 
32781da177e4SLinus Torvalds 
32791da177e4SLinus Torvalds /* Change the partition if necessary */
switch_partition(struct scsi_tape * STp)32801da177e4SLinus Torvalds static int switch_partition(struct scsi_tape *STp)
32811da177e4SLinus Torvalds {
32821da177e4SLinus Torvalds 	struct st_partstat *STps;
32831da177e4SLinus Torvalds 
32841da177e4SLinus Torvalds 	if (STp->partition == STp->new_partition)
32851da177e4SLinus Torvalds 		return 0;
32861da177e4SLinus Torvalds 	STps = &(STp->ps[STp->new_partition]);
32871da177e4SLinus Torvalds 	if (!STps->last_block_valid)
32881da177e4SLinus Torvalds 		STps->last_block_visited = 0;
32891da177e4SLinus Torvalds 	return set_location(STp, STps->last_block_visited, STp->new_partition, 1);
32901da177e4SLinus Torvalds }
32911da177e4SLinus Torvalds 
32921da177e4SLinus Torvalds /* Functions for reading and writing the medium partition mode page. */
32931da177e4SLinus Torvalds 
32941da177e4SLinus Torvalds #define PART_PAGE   0x11
32951da177e4SLinus Torvalds #define PART_PAGE_FIXED_LENGTH 8
32961da177e4SLinus Torvalds 
32971da177e4SLinus Torvalds #define PP_OFF_MAX_ADD_PARTS   2
32981da177e4SLinus Torvalds #define PP_OFF_NBR_ADD_PARTS   3
32991da177e4SLinus Torvalds #define PP_OFF_FLAGS           4
33001da177e4SLinus Torvalds #define PP_OFF_PART_UNITS      6
33011da177e4SLinus Torvalds #define PP_OFF_RESERVED        7
33021da177e4SLinus Torvalds 
33031da177e4SLinus Torvalds #define PP_BIT_IDP             0x20
33048038e645SKai Makisara #define PP_BIT_FDP             0x80
33051da177e4SLinus Torvalds #define PP_MSK_PSUM_MB         0x10
33068038e645SKai Makisara #define PP_MSK_PSUM_UNITS      0x18
33078038e645SKai Makisara #define PP_MSK_POFM            0x04
33081da177e4SLinus Torvalds 
33091da177e4SLinus Torvalds /* Get the number of partitions on the tape. As a side effect reads the
33101da177e4SLinus Torvalds    mode page into the tape buffer. */
nbr_partitions(struct scsi_tape * STp)33111da177e4SLinus Torvalds static int nbr_partitions(struct scsi_tape *STp)
33121da177e4SLinus Torvalds {
33131da177e4SLinus Torvalds 	int result;
33141da177e4SLinus Torvalds 
33151da177e4SLinus Torvalds 	if (STp->ready != ST_READY)
33161da177e4SLinus Torvalds 		return (-EIO);
33171da177e4SLinus Torvalds 
33181da177e4SLinus Torvalds 	result = read_mode_page(STp, PART_PAGE, 1);
33191da177e4SLinus Torvalds 
33201da177e4SLinus Torvalds 	if (result) {
3321b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Can't read medium partition page.\n");
33221da177e4SLinus Torvalds 		result = (-EIO);
33231da177e4SLinus Torvalds 	} else {
33241da177e4SLinus Torvalds 		result = (STp->buffer)->b_data[MODE_HEADER_LENGTH +
33251da177e4SLinus Torvalds 					      PP_OFF_NBR_ADD_PARTS] + 1;
3326b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Number of partitions %d.\n", result);
33271da177e4SLinus Torvalds 	}
33281da177e4SLinus Torvalds 
33291da177e4SLinus Torvalds 	return result;
33301da177e4SLinus Torvalds }
33311da177e4SLinus Torvalds 
33321da177e4SLinus Torvalds 
format_medium(struct scsi_tape * STp,int format)33338038e645SKai Makisara static int format_medium(struct scsi_tape *STp, int format)
33348038e645SKai Makisara {
33358038e645SKai Makisara 	int result = 0;
33368038e645SKai Makisara 	int timeout = STp->long_timeout;
33378038e645SKai Makisara 	unsigned char scmd[MAX_COMMAND_SIZE];
33388038e645SKai Makisara 	struct st_request *SRpnt;
33398038e645SKai Makisara 
33408038e645SKai Makisara 	memset(scmd, 0, MAX_COMMAND_SIZE);
33418038e645SKai Makisara 	scmd[0] = FORMAT_UNIT;
33428038e645SKai Makisara 	scmd[2] = format;
33438038e645SKai Makisara 	if (STp->immediate) {
33448038e645SKai Makisara 		scmd[1] |= 1;		/* Don't wait for completion */
33458038e645SKai Makisara 		timeout = STp->device->request_queue->rq_timeout;
33468038e645SKai Makisara 	}
33478038e645SKai Makisara 	DEBC_printk(STp, "Sending FORMAT MEDIUM\n");
33488038e645SKai Makisara 	SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE,
33498038e645SKai Makisara 			   timeout, MAX_RETRIES, 1);
33508038e645SKai Makisara 	if (!SRpnt)
33518038e645SKai Makisara 		result = STp->buffer->syscall_result;
33528038e645SKai Makisara 	return result;
33538038e645SKai Makisara }
33548038e645SKai Makisara 
33558038e645SKai Makisara 
33561da177e4SLinus Torvalds /* Partition the tape into two partitions if size > 0 or one partition if
33571da177e4SLinus Torvalds    size == 0.
33581da177e4SLinus Torvalds 
33591da177e4SLinus Torvalds    The block descriptors are read and written because Sony SDT-7000 does not
33601da177e4SLinus Torvalds    work without this (suggestion from Michael Schaefer <Michael.Schaefer@dlr.de>).
33611da177e4SLinus Torvalds 
33621da177e4SLinus Torvalds    My HP C1533A drive returns only one partition size field. This is used to
33631da177e4SLinus Torvalds    set the size of partition 1. There is no size field for the default partition.
33641da177e4SLinus Torvalds    Michael Schaefer's Sony SDT-7000 returns two descriptors and the second is
33651da177e4SLinus Torvalds    used to set the size of partition 1 (this is what the SCSI-3 standard specifies).
33661da177e4SLinus Torvalds    The following algorithm is used to accommodate both drives: if the number of
33671da177e4SLinus Torvalds    partition size fields is greater than the maximum number of additional partitions
33681da177e4SLinus Torvalds    in the mode page, the second field is used. Otherwise the first field is used.
33691da177e4SLinus Torvalds 
33701da177e4SLinus Torvalds    For Seagate DDS drives the page length must be 8 when no partitions is defined
33711da177e4SLinus Torvalds    and 10 when 1 partition is defined (information from Eric Lee Green). This is
33721da177e4SLinus Torvalds    is acceptable also to some other old drives and enforced if the first partition
33731da177e4SLinus Torvalds    size field is used for the first additional partition size.
33748038e645SKai Makisara 
33758038e645SKai Makisara    For drives that advertize SCSI-3 or newer, use the SSC-3 methods.
33761da177e4SLinus Torvalds  */
partition_tape(struct scsi_tape * STp,int size)33771da177e4SLinus Torvalds static int partition_tape(struct scsi_tape *STp, int size)
33781da177e4SLinus Torvalds {
33791da177e4SLinus Torvalds 	int result;
33808038e645SKai Makisara 	int target_partition;
33818038e645SKai Makisara 	bool scsi3 = STp->device->scsi_level >= SCSI_3, needs_format = false;
33821da177e4SLinus Torvalds 	int pgo, psd_cnt, psdo;
33838038e645SKai Makisara 	int psum = PP_MSK_PSUM_MB, units = 0;
33841da177e4SLinus Torvalds 	unsigned char *bp;
33851da177e4SLinus Torvalds 
33861da177e4SLinus Torvalds 	result = read_mode_page(STp, PART_PAGE, 0);
33871da177e4SLinus Torvalds 	if (result) {
3388b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Can't read partition mode page.\n");
33891da177e4SLinus Torvalds 		return result;
33901da177e4SLinus Torvalds 	}
33918038e645SKai Makisara 	target_partition = 1;
33928038e645SKai Makisara 	if (size < 0) {
33938038e645SKai Makisara 		target_partition = 0;
33948038e645SKai Makisara 		size = -size;
33958038e645SKai Makisara 	}
33968038e645SKai Makisara 
33971da177e4SLinus Torvalds 	/* The mode page is in the buffer. Let's modify it and write it. */
33981da177e4SLinus Torvalds 	bp = (STp->buffer)->b_data;
33991da177e4SLinus Torvalds 	pgo = MODE_HEADER_LENGTH + bp[MH_OFF_BDESCS_LENGTH];
3400b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "Partition page length is %d bytes.\n",
3401b30d8bcaSHannes Reinecke 		    bp[pgo + MP_OFF_PAGE_LENGTH] + 2);
34021da177e4SLinus Torvalds 
34031da177e4SLinus Torvalds 	psd_cnt = (bp[pgo + MP_OFF_PAGE_LENGTH] + 2 - PART_PAGE_FIXED_LENGTH) / 2;
34048038e645SKai Makisara 
34058038e645SKai Makisara 	if (scsi3) {
34068038e645SKai Makisara 		needs_format = (bp[pgo + PP_OFF_FLAGS] & PP_MSK_POFM) != 0;
34078038e645SKai Makisara 		if (needs_format && size == 0) {
34088038e645SKai Makisara 			/* No need to write the mode page when clearing
34098038e645SKai Makisara 			 *  partitioning
34108038e645SKai Makisara 			 */
34118038e645SKai Makisara 			DEBC_printk(STp, "Formatting tape with one partition.\n");
34128038e645SKai Makisara 			result = format_medium(STp, 0);
34138038e645SKai Makisara 			goto out;
34148038e645SKai Makisara 		}
34158038e645SKai Makisara 		if (needs_format)  /* Leave the old value for HP DATs claiming SCSI_3 */
34168038e645SKai Makisara 			psd_cnt = 2;
34178038e645SKai Makisara 		if ((bp[pgo + PP_OFF_FLAGS] & PP_MSK_PSUM_UNITS) == PP_MSK_PSUM_UNITS) {
34188038e645SKai Makisara 			/* Use units scaling for large partitions if the device
34198038e645SKai Makisara 			 * suggests it and no precision lost. Required for IBM
34208038e645SKai Makisara 			 * TS1140/50 drives that don't support MB units.
34218038e645SKai Makisara 			 */
34228038e645SKai Makisara 			if (size >= 1000 && (size % 1000) == 0) {
34238038e645SKai Makisara 				size /= 1000;
34248038e645SKai Makisara 				psum = PP_MSK_PSUM_UNITS;
34258038e645SKai Makisara 				units = 9; /* GB */
34268038e645SKai Makisara 			}
34278038e645SKai Makisara 		}
34288038e645SKai Makisara 		/* Try it anyway if too large to specify in MB */
34298038e645SKai Makisara 		if (psum == PP_MSK_PSUM_MB && size >= 65534) {
34308038e645SKai Makisara 			size /= 1000;
34318038e645SKai Makisara 			psum = PP_MSK_PSUM_UNITS;
34328038e645SKai Makisara 			units = 9;  /* GB */
34338038e645SKai Makisara 		}
34348038e645SKai Makisara 	}
34358038e645SKai Makisara 
34368038e645SKai Makisara 	if (size >= 65535 ||  /* Does not fit into two bytes */
34378038e645SKai Makisara 	    (target_partition == 0 && psd_cnt < 2)) {
34388038e645SKai Makisara 		result = -EINVAL;
34398038e645SKai Makisara 		goto out;
34408038e645SKai Makisara 	}
34418038e645SKai Makisara 
34421da177e4SLinus Torvalds 	psdo = pgo + PART_PAGE_FIXED_LENGTH;
34438038e645SKai Makisara 	/* The second condition is for HP DDS which use only one partition size
34448038e645SKai Makisara 	 * descriptor
34458038e645SKai Makisara 	 */
34468038e645SKai Makisara 	if (target_partition > 0 &&
34478038e645SKai Makisara 	    (psd_cnt > bp[pgo + PP_OFF_MAX_ADD_PARTS] ||
34488038e645SKai Makisara 	     bp[pgo + PP_OFF_MAX_ADD_PARTS] != 1)) {
34498038e645SKai Makisara 		bp[psdo] = bp[psdo + 1] = 0xff;  /* Rest to partition 0 */
34501da177e4SLinus Torvalds 		psdo += 2;
34511da177e4SLinus Torvalds 	}
34521da177e4SLinus Torvalds 	memset(bp + psdo, 0, bp[pgo + PP_OFF_NBR_ADD_PARTS] * 2);
34531da177e4SLinus Torvalds 
3454b30d8bcaSHannes Reinecke 	DEBC_printk(STp, "psd_cnt %d, max.parts %d, nbr_parts %d\n",
34551da177e4SLinus Torvalds 		    psd_cnt, bp[pgo + PP_OFF_MAX_ADD_PARTS],
3456b30d8bcaSHannes Reinecke 		    bp[pgo + PP_OFF_NBR_ADD_PARTS]);
34571da177e4SLinus Torvalds 
34588038e645SKai Makisara 	if (size == 0) {
34591da177e4SLinus Torvalds 		bp[pgo + PP_OFF_NBR_ADD_PARTS] = 0;
34601da177e4SLinus Torvalds 		if (psd_cnt <= bp[pgo + PP_OFF_MAX_ADD_PARTS])
34611da177e4SLinus Torvalds 		    bp[pgo + MP_OFF_PAGE_LENGTH] = 6;
3462b30d8bcaSHannes Reinecke 		DEBC_printk(STp, "Formatting tape with one partition.\n");
34631da177e4SLinus Torvalds 	} else {
34641da177e4SLinus Torvalds 		bp[psdo] = (size >> 8) & 0xff;
34651da177e4SLinus Torvalds 		bp[psdo + 1] = size & 0xff;
34668038e645SKai Makisara 		if (target_partition == 0)
34678038e645SKai Makisara 			bp[psdo + 2] = bp[psdo + 3] = 0xff;
34681da177e4SLinus Torvalds 		bp[pgo + 3] = 1;
34691da177e4SLinus Torvalds 		if (bp[pgo + MP_OFF_PAGE_LENGTH] < 8)
34701da177e4SLinus Torvalds 		    bp[pgo + MP_OFF_PAGE_LENGTH] = 8;
34718038e645SKai Makisara 		DEBC_printk(STp,
34728038e645SKai Makisara 			    "Formatting tape with two partitions (%i = %d MB).\n",
34738038e645SKai Makisara 			    target_partition, units > 0 ? size * 1000 : size);
34741da177e4SLinus Torvalds 	}
34751da177e4SLinus Torvalds 	bp[pgo + PP_OFF_PART_UNITS] = 0;
34761da177e4SLinus Torvalds 	bp[pgo + PP_OFF_RESERVED] = 0;
34778038e645SKai Makisara 	if (size != 1 || units != 0) {
34788038e645SKai Makisara 		bp[pgo + PP_OFF_FLAGS] = PP_BIT_IDP | psum |
34798038e645SKai Makisara 			(bp[pgo + PP_OFF_FLAGS] & 0x07);
34808038e645SKai Makisara 		bp[pgo + PP_OFF_PART_UNITS] = units;
34818038e645SKai Makisara 	} else
34828038e645SKai Makisara 		bp[pgo + PP_OFF_FLAGS] = PP_BIT_FDP |
34838038e645SKai Makisara 			(bp[pgo + PP_OFF_FLAGS] & 0x1f);
34848038e645SKai Makisara 	bp[pgo + MP_OFF_PAGE_LENGTH] = 6 + psd_cnt * 2;
34851da177e4SLinus Torvalds 
34861da177e4SLinus Torvalds 	result = write_mode_page(STp, PART_PAGE, 1);
34878038e645SKai Makisara 
34888038e645SKai Makisara 	if (!result && needs_format)
34898038e645SKai Makisara 		result = format_medium(STp, 1);
34908038e645SKai Makisara 
34911da177e4SLinus Torvalds 	if (result) {
3492b30d8bcaSHannes Reinecke 		st_printk(KERN_INFO, STp, "Partitioning of tape failed.\n");
34931da177e4SLinus Torvalds 		result = (-EIO);
34941da177e4SLinus Torvalds 	}
34951da177e4SLinus Torvalds 
34968038e645SKai Makisara out:
34971da177e4SLinus Torvalds 	return result;
34981da177e4SLinus Torvalds }
34991da177e4SLinus Torvalds 
35001da177e4SLinus Torvalds 
35011da177e4SLinus Torvalds 
35021da177e4SLinus Torvalds /* The ioctl command */
st_ioctl(struct file * file,unsigned int cmd_in,unsigned long arg)3503dba7688fSChristoph Hellwig static long st_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
35041da177e4SLinus Torvalds {
3505dba7688fSChristoph Hellwig 	void __user *p = (void __user *)arg;
35061da177e4SLinus Torvalds 	int i, cmd_nr, cmd_type, bt;
35071da177e4SLinus Torvalds 	int retval = 0;
35081da177e4SLinus Torvalds 	unsigned int blk;
35091da177e4SLinus Torvalds 	struct scsi_tape *STp = file->private_data;
35101da177e4SLinus Torvalds 	struct st_modedef *STm;
35111da177e4SLinus Torvalds 	struct st_partstat *STps;
35121da177e4SLinus Torvalds 
351328f85009SMatthias Kaehlcke 	if (mutex_lock_interruptible(&STp->lock))
35141da177e4SLinus Torvalds 		return -ERESTARTSYS;
35151da177e4SLinus Torvalds 
35161da177e4SLinus Torvalds 	DEB(
35171da177e4SLinus Torvalds 	if (debugging && !STp->in_use) {
3518b30d8bcaSHannes Reinecke 		st_printk(ST_DEB_MSG, STp, "Incorrect device.\n");
35191da177e4SLinus Torvalds 		retval = (-EIO);
35201da177e4SLinus Torvalds 		goto out;
35211da177e4SLinus Torvalds 	} ) /* end DEB */
35221da177e4SLinus Torvalds 
35231da177e4SLinus Torvalds 	STm = &(STp->modes[STp->current_mode]);
35241da177e4SLinus Torvalds 	STps = &(STp->ps[STp->partition]);
35251da177e4SLinus Torvalds 
35261da177e4SLinus Torvalds 	/*
35271da177e4SLinus Torvalds 	 * If we are in the middle of error recovery, don't let anyone
35281da177e4SLinus Torvalds 	 * else try and use this device.  Also, if error recovery fails, it
35291da177e4SLinus Torvalds 	 * may try and take the device offline, in which case all further
35301da177e4SLinus Torvalds 	 * access to the device is prohibited.
35311da177e4SLinus Torvalds 	 */
3532906d15fbSChristoph Hellwig 	retval = scsi_ioctl_block_when_processing_errors(STp->device, cmd_in,
353383ff6fe8SAl Viro 			file->f_flags & O_NDELAY);
3534906d15fbSChristoph Hellwig 	if (retval)
35351da177e4SLinus Torvalds 		goto out;
35361da177e4SLinus Torvalds 
35371da177e4SLinus Torvalds 	cmd_type = _IOC_TYPE(cmd_in);
35381da177e4SLinus Torvalds 	cmd_nr = _IOC_NR(cmd_in);
35391da177e4SLinus Torvalds 
35401da177e4SLinus Torvalds 	if (cmd_type == _IOC_TYPE(MTIOCTOP) && cmd_nr == _IOC_NR(MTIOCTOP)) {
35411da177e4SLinus Torvalds 		struct mtop mtc;
35421da177e4SLinus Torvalds 
35431da177e4SLinus Torvalds 		if (_IOC_SIZE(cmd_in) != sizeof(mtc)) {
35441da177e4SLinus Torvalds 			retval = (-EINVAL);
35451da177e4SLinus Torvalds 			goto out;
35461da177e4SLinus Torvalds 		}
35471da177e4SLinus Torvalds 
35481da177e4SLinus Torvalds 		i = copy_from_user(&mtc, p, sizeof(struct mtop));
35491da177e4SLinus Torvalds 		if (i) {
35501da177e4SLinus Torvalds 			retval = (-EFAULT);
35511da177e4SLinus Torvalds 			goto out;
35521da177e4SLinus Torvalds 		}
35531da177e4SLinus Torvalds 
35541da177e4SLinus Torvalds 		if (mtc.mt_op == MTSETDRVBUFFER && !capable(CAP_SYS_ADMIN)) {
3555b30d8bcaSHannes Reinecke 			st_printk(KERN_WARNING, STp,
3556b30d8bcaSHannes Reinecke 				  "MTSETDRVBUFFER only allowed for root.\n");
35571da177e4SLinus Torvalds 			retval = (-EPERM);
35581da177e4SLinus Torvalds 			goto out;
35591da177e4SLinus Torvalds 		}
35601da177e4SLinus Torvalds 		if (!STm->defined &&
35611da177e4SLinus Torvalds 		    (mtc.mt_op != MTSETDRVBUFFER &&
35621da177e4SLinus Torvalds 		     (mtc.mt_count & MT_ST_OPTIONS) == 0)) {
35631da177e4SLinus Torvalds 			retval = (-ENXIO);
35641da177e4SLinus Torvalds 			goto out;
35651da177e4SLinus Torvalds 		}
35661da177e4SLinus Torvalds 
35671da177e4SLinus Torvalds 		if (!STp->pos_unknown) {
35681da177e4SLinus Torvalds 
35691da177e4SLinus Torvalds 			if (STps->eof == ST_FM_HIT) {
35701da177e4SLinus Torvalds 				if (mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
35711da177e4SLinus Torvalds                                     mtc.mt_op == MTEOM) {
35721da177e4SLinus Torvalds 					mtc.mt_count -= 1;
35731da177e4SLinus Torvalds 					if (STps->drv_file >= 0)
35741da177e4SLinus Torvalds 						STps->drv_file += 1;
35751da177e4SLinus Torvalds 				} else if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM) {
35761da177e4SLinus Torvalds 					mtc.mt_count += 1;
35771da177e4SLinus Torvalds 					if (STps->drv_file >= 0)
35781da177e4SLinus Torvalds 						STps->drv_file += 1;
35791da177e4SLinus Torvalds 				}
35801da177e4SLinus Torvalds 			}
35811da177e4SLinus Torvalds 
35821da177e4SLinus Torvalds 			if (mtc.mt_op == MTSEEK) {
35831da177e4SLinus Torvalds 				/* Old position must be restored if partition will be
35841da177e4SLinus Torvalds                                    changed */
35851da177e4SLinus Torvalds 				i = !STp->can_partitions ||
35861da177e4SLinus Torvalds 				    (STp->new_partition != STp->partition);
35871da177e4SLinus Torvalds 			} else {
35881da177e4SLinus Torvalds 				i = mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
35891da177e4SLinus Torvalds 				    mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM ||
35901da177e4SLinus Torvalds 				    mtc.mt_op == MTLOCK || mtc.mt_op == MTLOAD ||
35911da177e4SLinus Torvalds 				    mtc.mt_op == MTFSF || mtc.mt_op == MTFSFM ||
35921da177e4SLinus Torvalds 				    mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM ||
35931da177e4SLinus Torvalds 				    mtc.mt_op == MTCOMPRESSION;
35941da177e4SLinus Torvalds 			}
35951da177e4SLinus Torvalds 			i = flush_buffer(STp, i);
35961da177e4SLinus Torvalds 			if (i < 0) {
35971da177e4SLinus Torvalds 				retval = i;
35981da177e4SLinus Torvalds 				goto out;
35991da177e4SLinus Torvalds 			}
36001da177e4SLinus Torvalds 			if (STps->rw == ST_WRITING &&
36011da177e4SLinus Torvalds 			    (mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
36021da177e4SLinus Torvalds 			     mtc.mt_op == MTSEEK ||
36031da177e4SLinus Torvalds 			     mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)) {
36041da177e4SLinus Torvalds 				i = st_int_ioctl(STp, MTWEOF, 1);
36051da177e4SLinus Torvalds 				if (i < 0) {
36061da177e4SLinus Torvalds 					retval = i;
36071da177e4SLinus Torvalds 					goto out;
36081da177e4SLinus Torvalds 				}
36091da177e4SLinus Torvalds 				if (mtc.mt_op == MTBSF || mtc.mt_op == MTBSFM)
36101da177e4SLinus Torvalds 					mtc.mt_count++;
36111da177e4SLinus Torvalds 				STps->rw = ST_IDLE;
36121da177e4SLinus Torvalds 			     }
36131da177e4SLinus Torvalds 
36141da177e4SLinus Torvalds 		} else {
36151da177e4SLinus Torvalds 			/*
36161da177e4SLinus Torvalds 			 * If there was a bus reset, block further access
36171da177e4SLinus Torvalds 			 * to this device.  If the user wants to rewind the tape,
36181da177e4SLinus Torvalds 			 * then reset the flag and allow access again.
36191da177e4SLinus Torvalds 			 */
36201da177e4SLinus Torvalds 			if (mtc.mt_op != MTREW &&
36211da177e4SLinus Torvalds 			    mtc.mt_op != MTOFFL &&
36221da177e4SLinus Torvalds 			    mtc.mt_op != MTRETEN &&
36231da177e4SLinus Torvalds 			    mtc.mt_op != MTERASE &&
36241da177e4SLinus Torvalds 			    mtc.mt_op != MTSEEK &&
36251da177e4SLinus Torvalds 			    mtc.mt_op != MTEOM) {
36261da177e4SLinus Torvalds 				retval = (-EIO);
36271da177e4SLinus Torvalds 				goto out;
36281da177e4SLinus Torvalds 			}
36291da177e4SLinus Torvalds 			reset_state(STp);
36301da177e4SLinus Torvalds 			/* remove this when the midlevel properly clears was_reset */
36311da177e4SLinus Torvalds 			STp->device->was_reset = 0;
36321da177e4SLinus Torvalds 		}
36331da177e4SLinus Torvalds 
36341da177e4SLinus Torvalds 		if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
36351da177e4SLinus Torvalds 		    mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
36361da177e4SLinus Torvalds 		    mtc.mt_op != MTSETDRVBUFFER && mtc.mt_op != MTSETPART)
36371da177e4SLinus Torvalds 			STps->rw = ST_IDLE;	/* Prevent automatic WEOF and fsf */
36381da177e4SLinus Torvalds 
36391da177e4SLinus Torvalds 		if (mtc.mt_op == MTOFFL && STp->door_locked != ST_UNLOCKED)
36401da177e4SLinus Torvalds 			do_door_lock(STp, 0);	/* Ignore result! */
36411da177e4SLinus Torvalds 
36421da177e4SLinus Torvalds 		if (mtc.mt_op == MTSETDRVBUFFER &&
36431da177e4SLinus Torvalds 		    (mtc.mt_count & MT_ST_OPTIONS) != 0) {
36441da177e4SLinus Torvalds 			retval = st_set_options(STp, mtc.mt_count);
36451da177e4SLinus Torvalds 			goto out;
36461da177e4SLinus Torvalds 		}
36471da177e4SLinus Torvalds 
36481da177e4SLinus Torvalds 		if (mtc.mt_op == MTSETPART) {
36491da177e4SLinus Torvalds 			if (!STp->can_partitions ||
36501da177e4SLinus Torvalds 			    mtc.mt_count < 0 || mtc.mt_count >= ST_NBR_PARTITIONS) {
36511da177e4SLinus Torvalds 				retval = (-EINVAL);
36521da177e4SLinus Torvalds 				goto out;
36531da177e4SLinus Torvalds 			}
36541da177e4SLinus Torvalds 			if (mtc.mt_count >= STp->nbr_partitions &&
36551da177e4SLinus Torvalds 			    (STp->nbr_partitions = nbr_partitions(STp)) < 0) {
36561da177e4SLinus Torvalds 				retval = (-EIO);
36571da177e4SLinus Torvalds 				goto out;
36581da177e4SLinus Torvalds 			}
36591da177e4SLinus Torvalds 			if (mtc.mt_count >= STp->nbr_partitions) {
36601da177e4SLinus Torvalds 				retval = (-EINVAL);
36611da177e4SLinus Torvalds 				goto out;
36621da177e4SLinus Torvalds 			}
36631da177e4SLinus Torvalds 			STp->new_partition = mtc.mt_count;
36641da177e4SLinus Torvalds 			retval = 0;
36651da177e4SLinus Torvalds 			goto out;
36661da177e4SLinus Torvalds 		}
36671da177e4SLinus Torvalds 
36681da177e4SLinus Torvalds 		if (mtc.mt_op == MTMKPART) {
36691da177e4SLinus Torvalds 			if (!STp->can_partitions) {
36701da177e4SLinus Torvalds 				retval = (-EINVAL);
36711da177e4SLinus Torvalds 				goto out;
36721da177e4SLinus Torvalds 			}
36738038e645SKai Makisara 			i = do_load_unload(STp, file, 1);
36748038e645SKai Makisara 			if (i < 0) {
36758038e645SKai Makisara 				retval = i;
36768038e645SKai Makisara 				goto out;
36778038e645SKai Makisara 			}
36788038e645SKai Makisara 			i = partition_tape(STp, mtc.mt_count);
36798038e645SKai Makisara 			if (i < 0) {
36801da177e4SLinus Torvalds 				retval = i;
36811da177e4SLinus Torvalds 				goto out;
36821da177e4SLinus Torvalds 			}
36831da177e4SLinus Torvalds 			for (i = 0; i < ST_NBR_PARTITIONS; i++) {
36841da177e4SLinus Torvalds 				STp->ps[i].rw = ST_IDLE;
36851da177e4SLinus Torvalds 				STp->ps[i].at_sm = 0;
36861da177e4SLinus Torvalds 				STp->ps[i].last_block_valid = 0;
36871da177e4SLinus Torvalds 			}
36881da177e4SLinus Torvalds 			STp->partition = STp->new_partition = 0;
36898038e645SKai Makisara 			STp->nbr_partitions = mtc.mt_count != 0 ? 2 : 1;
36901da177e4SLinus Torvalds 			STps->drv_block = STps->drv_file = 0;
36911da177e4SLinus Torvalds 			retval = 0;
36921da177e4SLinus Torvalds 			goto out;
36931da177e4SLinus Torvalds 		}
36941da177e4SLinus Torvalds 
36951da177e4SLinus Torvalds 		if (mtc.mt_op == MTSEEK) {
36961da177e4SLinus Torvalds 			i = set_location(STp, mtc.mt_count, STp->new_partition, 0);
36971da177e4SLinus Torvalds 			if (!STp->can_partitions)
36981da177e4SLinus Torvalds 				STp->ps[0].rw = ST_IDLE;
36991da177e4SLinus Torvalds 			retval = i;
37001da177e4SLinus Torvalds 			goto out;
37011da177e4SLinus Torvalds 		}
37021da177e4SLinus Torvalds 
37031da177e4SLinus Torvalds 		if (mtc.mt_op == MTUNLOAD || mtc.mt_op == MTOFFL) {
37041da177e4SLinus Torvalds 			retval = do_load_unload(STp, file, 0);
37051da177e4SLinus Torvalds 			goto out;
37061da177e4SLinus Torvalds 		}
37071da177e4SLinus Torvalds 
37081da177e4SLinus Torvalds 		if (mtc.mt_op == MTLOAD) {
37091da177e4SLinus Torvalds 			retval = do_load_unload(STp, file, max(1, mtc.mt_count));
37101da177e4SLinus Torvalds 			goto out;
37111da177e4SLinus Torvalds 		}
37121da177e4SLinus Torvalds 
37131da177e4SLinus Torvalds 		if (mtc.mt_op == MTLOCK || mtc.mt_op == MTUNLOCK) {
37141da177e4SLinus Torvalds 			retval = do_door_lock(STp, (mtc.mt_op == MTLOCK));
37151da177e4SLinus Torvalds 			goto out;
37161da177e4SLinus Torvalds 		}
37171da177e4SLinus Torvalds 
37181da177e4SLinus Torvalds 		if (STp->can_partitions && STp->ready == ST_READY &&
37191da177e4SLinus Torvalds 		    (i = switch_partition(STp)) < 0) {
37201da177e4SLinus Torvalds 			retval = i;
37211da177e4SLinus Torvalds 			goto out;
37221da177e4SLinus Torvalds 		}
37231da177e4SLinus Torvalds 
37241da177e4SLinus Torvalds 		if (mtc.mt_op == MTCOMPRESSION)
37251da177e4SLinus Torvalds 			retval = st_compression(STp, (mtc.mt_count & 1));
37261da177e4SLinus Torvalds 		else
37271da177e4SLinus Torvalds 			retval = st_int_ioctl(STp, mtc.mt_op, mtc.mt_count);
37281da177e4SLinus Torvalds 		goto out;
37291da177e4SLinus Torvalds 	}
37301da177e4SLinus Torvalds 	if (!STm->defined) {
37311da177e4SLinus Torvalds 		retval = (-ENXIO);
37321da177e4SLinus Torvalds 		goto out;
37331da177e4SLinus Torvalds 	}
37341da177e4SLinus Torvalds 
37351da177e4SLinus Torvalds 	if ((i = flush_buffer(STp, 0)) < 0) {
37361da177e4SLinus Torvalds 		retval = i;
37371da177e4SLinus Torvalds 		goto out;
37381da177e4SLinus Torvalds 	}
37391da177e4SLinus Torvalds 	if (STp->can_partitions &&
37401da177e4SLinus Torvalds 	    (i = switch_partition(STp)) < 0) {
37411da177e4SLinus Torvalds 		retval = i;
37421da177e4SLinus Torvalds 		goto out;
37431da177e4SLinus Torvalds 	}
37441da177e4SLinus Torvalds 
37451da177e4SLinus Torvalds 	if (cmd_type == _IOC_TYPE(MTIOCGET) && cmd_nr == _IOC_NR(MTIOCGET)) {
37461da177e4SLinus Torvalds 		struct mtget mt_status;
37471da177e4SLinus Torvalds 
37481da177e4SLinus Torvalds 		if (_IOC_SIZE(cmd_in) != sizeof(struct mtget)) {
37491da177e4SLinus Torvalds 			 retval = (-EINVAL);
37501da177e4SLinus Torvalds 			 goto out;
37511da177e4SLinus Torvalds 		}
37521da177e4SLinus Torvalds 
37531da177e4SLinus Torvalds 		mt_status.mt_type = STp->tape_type;
37541da177e4SLinus Torvalds 		mt_status.mt_dsreg =
37551da177e4SLinus Torvalds 		    ((STp->block_size << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK) |
37561da177e4SLinus Torvalds 		    ((STp->density << MT_ST_DENSITY_SHIFT) & MT_ST_DENSITY_MASK);
37571da177e4SLinus Torvalds 		mt_status.mt_blkno = STps->drv_block;
37581da177e4SLinus Torvalds 		mt_status.mt_fileno = STps->drv_file;
37591da177e4SLinus Torvalds 		if (STp->block_size != 0) {
37601da177e4SLinus Torvalds 			if (STps->rw == ST_WRITING)
37611da177e4SLinus Torvalds 				mt_status.mt_blkno +=
37621da177e4SLinus Torvalds 				    (STp->buffer)->buffer_bytes / STp->block_size;
37631da177e4SLinus Torvalds 			else if (STps->rw == ST_READING)
37641da177e4SLinus Torvalds 				mt_status.mt_blkno -=
37651da177e4SLinus Torvalds                                         ((STp->buffer)->buffer_bytes +
37661da177e4SLinus Torvalds                                          STp->block_size - 1) / STp->block_size;
37671da177e4SLinus Torvalds 		}
37681da177e4SLinus Torvalds 
37691da177e4SLinus Torvalds 		mt_status.mt_gstat = 0;
37701da177e4SLinus Torvalds 		if (STp->drv_write_prot)
37711da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_WR_PROT(0xffffffff);
37721da177e4SLinus Torvalds 		if (mt_status.mt_blkno == 0) {
37731da177e4SLinus Torvalds 			if (mt_status.mt_fileno == 0)
37741da177e4SLinus Torvalds 				mt_status.mt_gstat |= GMT_BOT(0xffffffff);
37751da177e4SLinus Torvalds 			else
37761da177e4SLinus Torvalds 				mt_status.mt_gstat |= GMT_EOF(0xffffffff);
37771da177e4SLinus Torvalds 		}
37781da177e4SLinus Torvalds 		mt_status.mt_erreg = (STp->recover_reg << MT_ST_SOFTERR_SHIFT);
37791da177e4SLinus Torvalds 		mt_status.mt_resid = STp->partition;
37801da177e4SLinus Torvalds 		if (STps->eof == ST_EOM_OK || STps->eof == ST_EOM_ERROR)
37811da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_EOT(0xffffffff);
37821da177e4SLinus Torvalds 		else if (STps->eof >= ST_EOM_OK)
37831da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_EOD(0xffffffff);
37841da177e4SLinus Torvalds 		if (STp->density == 1)
37851da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_D_800(0xffffffff);
37861da177e4SLinus Torvalds 		else if (STp->density == 2)
37871da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_D_1600(0xffffffff);
37881da177e4SLinus Torvalds 		else if (STp->density == 3)
37891da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_D_6250(0xffffffff);
37901da177e4SLinus Torvalds 		if (STp->ready == ST_READY)
37911da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_ONLINE(0xffffffff);
37921da177e4SLinus Torvalds 		if (STp->ready == ST_NO_TAPE)
37931da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_DR_OPEN(0xffffffff);
37941da177e4SLinus Torvalds 		if (STps->at_sm)
37951da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_SM(0xffffffff);
37961da177e4SLinus Torvalds 		if (STm->do_async_writes ||
37971da177e4SLinus Torvalds                     (STm->do_buffer_writes && STp->block_size != 0) ||
37981da177e4SLinus Torvalds 		    STp->drv_buffer != 0)
37991da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_IM_REP_EN(0xffffffff);
38001da177e4SLinus Torvalds 		if (STp->cleaning_req)
38011da177e4SLinus Torvalds 			mt_status.mt_gstat |= GMT_CLN(0xffffffff);
38021da177e4SLinus Torvalds 
38031207045dSArnd Bergmann 		retval = put_user_mtget(p, &mt_status);
38041207045dSArnd Bergmann 		if (retval)
38051da177e4SLinus Torvalds 			goto out;
38061da177e4SLinus Torvalds 
38071da177e4SLinus Torvalds 		STp->recover_reg = 0;		/* Clear after read */
38081da177e4SLinus Torvalds 		goto out;
38091da177e4SLinus Torvalds 	}			/* End of MTIOCGET */
38101da177e4SLinus Torvalds 	if (cmd_type == _IOC_TYPE(MTIOCPOS) && cmd_nr == _IOC_NR(MTIOCPOS)) {
38111da177e4SLinus Torvalds 		struct mtpos mt_pos;
38121da177e4SLinus Torvalds 		if (_IOC_SIZE(cmd_in) != sizeof(struct mtpos)) {
38131da177e4SLinus Torvalds 			 retval = (-EINVAL);
38141da177e4SLinus Torvalds 			 goto out;
38151da177e4SLinus Torvalds 		}
38161da177e4SLinus Torvalds 		if ((i = get_location(STp, &blk, &bt, 0)) < 0) {
38171da177e4SLinus Torvalds 			retval = i;
38181da177e4SLinus Torvalds 			goto out;
38191da177e4SLinus Torvalds 		}
38201da177e4SLinus Torvalds 		mt_pos.mt_blkno = blk;
38211207045dSArnd Bergmann 		retval = put_user_mtpos(p, &mt_pos);
38221da177e4SLinus Torvalds 		goto out;
38231da177e4SLinus Torvalds 	}
382428f85009SMatthias Kaehlcke 	mutex_unlock(&STp->lock);
3825dba7688fSChristoph Hellwig 
38261da177e4SLinus Torvalds 	switch (cmd_in) {
3827dba7688fSChristoph Hellwig 	case SG_IO:
3828dba7688fSChristoph Hellwig 	case SCSI_IOCTL_SEND_COMMAND:
3829dba7688fSChristoph Hellwig 	case CDROM_SEND_PACKET:
3830dba7688fSChristoph Hellwig 		if (!capable(CAP_SYS_RAWIO))
3831dba7688fSChristoph Hellwig 			return -EPERM;
38326a2ea0d3SNathan Chancellor 		break;
3833dba7688fSChristoph Hellwig 	default:
3834dba7688fSChristoph Hellwig 		break;
3835dba7688fSChristoph Hellwig 	}
3836dba7688fSChristoph Hellwig 
38372e80089cSChristoph Hellwig 	retval = scsi_ioctl(STp->device, file->f_mode & FMODE_WRITE, cmd_in, p);
3838dba7688fSChristoph Hellwig 	if (!retval && cmd_in == SCSI_IOCTL_STOP_UNIT) {
3839d320a955SArnd Bergmann 		/* unload */
3840d320a955SArnd Bergmann 		STp->rew_at_close = 0;
3841d320a955SArnd Bergmann 		STp->ready = ST_NO_TAPE;
3842d320a955SArnd Bergmann 	}
3843d320a955SArnd Bergmann 	return retval;
3844d320a955SArnd Bergmann 
38451da177e4SLinus Torvalds  out:
384628f85009SMatthias Kaehlcke 	mutex_unlock(&STp->lock);
38471da177e4SLinus Torvalds 	return retval;
38481da177e4SLinus Torvalds }
38491da177e4SLinus Torvalds 
38501da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
st_compat_ioctl(struct file * file,unsigned int cmd_in,unsigned long arg)38511207045dSArnd Bergmann static long st_compat_ioctl(struct file *file, unsigned int cmd_in, unsigned long arg)
38521da177e4SLinus Torvalds {
38531207045dSArnd Bergmann 	/* argument conversion is handled using put_user_mtpos/put_user_mtget */
38541207045dSArnd Bergmann 	switch (cmd_in) {
38551207045dSArnd Bergmann 	case MTIOCPOS32:
3856dba7688fSChristoph Hellwig 		cmd_in = MTIOCPOS;
3857dba7688fSChristoph Hellwig 		break;
38581207045dSArnd Bergmann 	case MTIOCGET32:
3859dba7688fSChristoph Hellwig 		cmd_in = MTIOCGET;
3860dba7688fSChristoph Hellwig 		break;
38611207045dSArnd Bergmann 	}
38621207045dSArnd Bergmann 
3863dba7688fSChristoph Hellwig 	return st_ioctl(file, cmd_in, arg);
38641da177e4SLinus Torvalds }
38651da177e4SLinus Torvalds #endif
38661da177e4SLinus Torvalds 
38671da177e4SLinus Torvalds 
38681da177e4SLinus Torvalds 
38691da177e4SLinus Torvalds /* Try to allocate a new tape buffer. Calling function must not hold
38701da177e4SLinus Torvalds    dev_arr_lock. */
new_tape_buffer(int max_sg)3871aaff5ebaSChristoph Hellwig static struct st_buffer *new_tape_buffer(int max_sg)
38721da177e4SLinus Torvalds {
38731da177e4SLinus Torvalds 	struct st_buffer *tb;
38741da177e4SLinus Torvalds 
38754011f076SJia-Ju Bai 	tb = kzalloc(sizeof(struct st_buffer), GFP_KERNEL);
38761da177e4SLinus Torvalds 	if (!tb) {
38771da177e4SLinus Torvalds 		printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n");
38781da177e4SLinus Torvalds 		return NULL;
38791da177e4SLinus Torvalds 	}
38801ac63cf5SFUJITA Tomonori 	tb->frp_segs = 0;
38811da177e4SLinus Torvalds 	tb->use_sg = max_sg;
3882f409d6ccSFUJITA Tomonori 	tb->buffer_size = 0;
38831da177e4SLinus Torvalds 
38846396bb22SKees Cook 	tb->reserved_pages = kcalloc(max_sg, sizeof(struct page *),
38854011f076SJia-Ju Bai 				     GFP_KERNEL);
3886d0e1ae31SFUJITA Tomonori 	if (!tb->reserved_pages) {
3887d0e1ae31SFUJITA Tomonori 		kfree(tb);
3888d0e1ae31SFUJITA Tomonori 		return NULL;
3889d0e1ae31SFUJITA Tomonori 	}
3890d0e1ae31SFUJITA Tomonori 
38911da177e4SLinus Torvalds 	return tb;
38921da177e4SLinus Torvalds }
38931da177e4SLinus Torvalds 
38941da177e4SLinus Torvalds 
38951da177e4SLinus Torvalds /* Try to allocate enough space in the tape buffer */
38968f78fc5eSKai Makisara #define ST_MAX_ORDER 6
38978f78fc5eSKai Makisara 
enlarge_buffer(struct st_buffer * STbuffer,int new_size)3898aaff5ebaSChristoph Hellwig static int enlarge_buffer(struct st_buffer * STbuffer, int new_size)
38991da177e4SLinus Torvalds {
3900769989a4SBodo Stroesser 	int segs, max_segs, b_size, order, got;
3901c53033f6SAl Viro 	gfp_t priority;
39021da177e4SLinus Torvalds 
39031da177e4SLinus Torvalds 	if (new_size <= STbuffer->buffer_size)
39041da177e4SLinus Torvalds 		return 1;
39051da177e4SLinus Torvalds 
39061da177e4SLinus Torvalds 	if (STbuffer->buffer_size <= PAGE_SIZE)
39071da177e4SLinus Torvalds 		normalize_buffer(STbuffer);  /* Avoid extra segment */
39081da177e4SLinus Torvalds 
39091da177e4SLinus Torvalds 	max_segs = STbuffer->use_sg;
39101da177e4SLinus Torvalds 
39111da177e4SLinus Torvalds 	priority = GFP_KERNEL | __GFP_NOWARN;
39129c905966SFUJITA Tomonori 
391308c95832SFUJITA Tomonori 	if (STbuffer->cleared)
391408c95832SFUJITA Tomonori 		priority |= __GFP_ZERO;
391508c95832SFUJITA Tomonori 
39169c905966SFUJITA Tomonori 	if (STbuffer->frp_segs) {
3917c982c368SFUJITA Tomonori 		order = STbuffer->reserved_page_order;
391808c95832SFUJITA Tomonori 		b_size = PAGE_SIZE << order;
39199c905966SFUJITA Tomonori 	} else {
39209c905966SFUJITA Tomonori 		for (b_size = PAGE_SIZE, order = 0;
392146081b16SFUJITA Tomonori 		     order < ST_MAX_ORDER &&
392246081b16SFUJITA Tomonori 			     max_segs * (PAGE_SIZE << order) < new_size;
39238f78fc5eSKai Makisara 		     order++, b_size *= 2)
39241da177e4SLinus Torvalds 			;  /* empty */
3925373daacfSKai Makisara 		STbuffer->reserved_page_order = order;
39269c905966SFUJITA Tomonori 	}
39278f78fc5eSKai Makisara 	if (max_segs * (PAGE_SIZE << order) < new_size) {
39288f78fc5eSKai Makisara 		if (order == ST_MAX_ORDER)
39298f78fc5eSKai Makisara 			return 0;
39308f78fc5eSKai Makisara 		normalize_buffer(STbuffer);
3931aaff5ebaSChristoph Hellwig 		return enlarge_buffer(STbuffer, new_size);
39328f78fc5eSKai Makisara 	}
39331da177e4SLinus Torvalds 
39341da177e4SLinus Torvalds 	for (segs = STbuffer->frp_segs, got = STbuffer->buffer_size;
39351da177e4SLinus Torvalds 	     segs < max_segs && got < new_size;) {
393608c95832SFUJITA Tomonori 		struct page *page;
393708c95832SFUJITA Tomonori 
393808c95832SFUJITA Tomonori 		page = alloc_pages(priority, order);
393908c95832SFUJITA Tomonori 		if (!page) {
39401da177e4SLinus Torvalds 			DEB(STbuffer->buffer_size = got);
39411da177e4SLinus Torvalds 			normalize_buffer(STbuffer);
39421da177e4SLinus Torvalds 			return 0;
39431da177e4SLinus Torvalds 		}
394408c95832SFUJITA Tomonori 
39451da177e4SLinus Torvalds 		STbuffer->frp_segs += 1;
39461da177e4SLinus Torvalds 		got += b_size;
39471da177e4SLinus Torvalds 		STbuffer->buffer_size = got;
394808c95832SFUJITA Tomonori 		STbuffer->reserved_pages[segs] = page;
39491da177e4SLinus Torvalds 		segs++;
39501da177e4SLinus Torvalds 	}
395108c95832SFUJITA Tomonori 	STbuffer->b_data = page_address(STbuffer->reserved_pages[0]);
39521da177e4SLinus Torvalds 
39531da177e4SLinus Torvalds 	return 1;
39541da177e4SLinus Torvalds }
39551da177e4SLinus Torvalds 
39561da177e4SLinus Torvalds 
395740f6b36cSKai Makisara /* Make sure that no data from previous user is in the internal buffer */
clear_buffer(struct st_buffer * st_bp)395840f6b36cSKai Makisara static void clear_buffer(struct st_buffer * st_bp)
395940f6b36cSKai Makisara {
396040f6b36cSKai Makisara 	int i;
396140f6b36cSKai Makisara 
396240f6b36cSKai Makisara 	for (i=0; i < st_bp->frp_segs; i++)
396308c95832SFUJITA Tomonori 		memset(page_address(st_bp->reserved_pages[i]), 0,
3964c982c368SFUJITA Tomonori 		       PAGE_SIZE << st_bp->reserved_page_order);
396540f6b36cSKai Makisara 	st_bp->cleared = 1;
396640f6b36cSKai Makisara }
396740f6b36cSKai Makisara 
396840f6b36cSKai Makisara 
39691da177e4SLinus Torvalds /* Release the extra buffer */
normalize_buffer(struct st_buffer * STbuffer)39701da177e4SLinus Torvalds static void normalize_buffer(struct st_buffer * STbuffer)
39711da177e4SLinus Torvalds {
3972c982c368SFUJITA Tomonori 	int i, order = STbuffer->reserved_page_order;
39731da177e4SLinus Torvalds 
39741ac63cf5SFUJITA Tomonori 	for (i = 0; i < STbuffer->frp_segs; i++) {
397508c95832SFUJITA Tomonori 		__free_pages(STbuffer->reserved_pages[i], order);
397608c95832SFUJITA Tomonori 		STbuffer->buffer_size -= (PAGE_SIZE << order);
39771da177e4SLinus Torvalds 	}
39781ac63cf5SFUJITA Tomonori 	STbuffer->frp_segs = 0;
39798b05b773SMike Christie 	STbuffer->sg_segs = 0;
3980c982c368SFUJITA Tomonori 	STbuffer->reserved_page_order = 0;
3981d0e1ae31SFUJITA Tomonori 	STbuffer->map_data.offset = 0;
39821da177e4SLinus Torvalds }
39831da177e4SLinus Torvalds 
39841da177e4SLinus Torvalds 
39851da177e4SLinus Torvalds /* Move data from the user buffer to the tape buffer. Returns zero (success) or
39861da177e4SLinus Torvalds    negative error code. */
append_to_buffer(const char __user * ubp,struct st_buffer * st_bp,int do_count)39871da177e4SLinus Torvalds static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count)
39881da177e4SLinus Torvalds {
39891da177e4SLinus Torvalds 	int i, cnt, res, offset;
3990c982c368SFUJITA Tomonori 	int length = PAGE_SIZE << st_bp->reserved_page_order;
39911da177e4SLinus Torvalds 
39921da177e4SLinus Torvalds 	for (i = 0, offset = st_bp->buffer_bytes;
399308c95832SFUJITA Tomonori 	     i < st_bp->frp_segs && offset >= length; i++)
399408c95832SFUJITA Tomonori 		offset -= length;
39951da177e4SLinus Torvalds 	if (i == st_bp->frp_segs) {	/* Should never happen */
39961da177e4SLinus Torvalds 		printk(KERN_WARNING "st: append_to_buffer offset overflow.\n");
39971da177e4SLinus Torvalds 		return (-EIO);
39981da177e4SLinus Torvalds 	}
39991da177e4SLinus Torvalds 	for (; i < st_bp->frp_segs && do_count > 0; i++) {
400008c95832SFUJITA Tomonori 		struct page *page = st_bp->reserved_pages[i];
400108c95832SFUJITA Tomonori 		cnt = length - offset < do_count ? length - offset : do_count;
400208c95832SFUJITA Tomonori 		res = copy_from_user(page_address(page) + offset, ubp, cnt);
40031da177e4SLinus Torvalds 		if (res)
40041da177e4SLinus Torvalds 			return (-EFAULT);
40051da177e4SLinus Torvalds 		do_count -= cnt;
40061da177e4SLinus Torvalds 		st_bp->buffer_bytes += cnt;
40071da177e4SLinus Torvalds 		ubp += cnt;
40081da177e4SLinus Torvalds 		offset = 0;
40091da177e4SLinus Torvalds 	}
40101da177e4SLinus Torvalds 	if (do_count) /* Should never happen */
40111da177e4SLinus Torvalds 		return (-EIO);
40121da177e4SLinus Torvalds 
40131da177e4SLinus Torvalds 	return 0;
40141da177e4SLinus Torvalds }
40151da177e4SLinus Torvalds 
40161da177e4SLinus Torvalds 
40171da177e4SLinus Torvalds /* Move data from the tape buffer to the user buffer. Returns zero (success) or
40181da177e4SLinus Torvalds    negative error code. */
from_buffer(struct st_buffer * st_bp,char __user * ubp,int do_count)40191da177e4SLinus Torvalds static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
40201da177e4SLinus Torvalds {
40211da177e4SLinus Torvalds 	int i, cnt, res, offset;
4022c982c368SFUJITA Tomonori 	int length = PAGE_SIZE << st_bp->reserved_page_order;
40231da177e4SLinus Torvalds 
40241da177e4SLinus Torvalds 	for (i = 0, offset = st_bp->read_pointer;
402508c95832SFUJITA Tomonori 	     i < st_bp->frp_segs && offset >= length; i++)
402608c95832SFUJITA Tomonori 		offset -= length;
40271da177e4SLinus Torvalds 	if (i == st_bp->frp_segs) {	/* Should never happen */
40281da177e4SLinus Torvalds 		printk(KERN_WARNING "st: from_buffer offset overflow.\n");
40291da177e4SLinus Torvalds 		return (-EIO);
40301da177e4SLinus Torvalds 	}
40311da177e4SLinus Torvalds 	for (; i < st_bp->frp_segs && do_count > 0; i++) {
403208c95832SFUJITA Tomonori 		struct page *page = st_bp->reserved_pages[i];
403308c95832SFUJITA Tomonori 		cnt = length - offset < do_count ? length - offset : do_count;
403408c95832SFUJITA Tomonori 		res = copy_to_user(ubp, page_address(page) + offset, cnt);
40351da177e4SLinus Torvalds 		if (res)
40361da177e4SLinus Torvalds 			return (-EFAULT);
40371da177e4SLinus Torvalds 		do_count -= cnt;
40381da177e4SLinus Torvalds 		st_bp->buffer_bytes -= cnt;
40391da177e4SLinus Torvalds 		st_bp->read_pointer += cnt;
40401da177e4SLinus Torvalds 		ubp += cnt;
40411da177e4SLinus Torvalds 		offset = 0;
40421da177e4SLinus Torvalds 	}
40431da177e4SLinus Torvalds 	if (do_count) /* Should never happen */
40441da177e4SLinus Torvalds 		return (-EIO);
40451da177e4SLinus Torvalds 
40461da177e4SLinus Torvalds 	return 0;
40471da177e4SLinus Torvalds }
40481da177e4SLinus Torvalds 
40491da177e4SLinus Torvalds 
40501da177e4SLinus Torvalds /* Move data towards start of buffer */
move_buffer_data(struct st_buffer * st_bp,int offset)40511da177e4SLinus Torvalds static void move_buffer_data(struct st_buffer * st_bp, int offset)
40521da177e4SLinus Torvalds {
40531da177e4SLinus Torvalds 	int src_seg, dst_seg, src_offset = 0, dst_offset;
40541da177e4SLinus Torvalds 	int count, total;
4055c982c368SFUJITA Tomonori 	int length = PAGE_SIZE << st_bp->reserved_page_order;
40561da177e4SLinus Torvalds 
40571da177e4SLinus Torvalds 	if (offset == 0)
40581da177e4SLinus Torvalds 		return;
40591da177e4SLinus Torvalds 
40601da177e4SLinus Torvalds 	total=st_bp->buffer_bytes - offset;
40611da177e4SLinus Torvalds 	for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) {
40621da177e4SLinus Torvalds 		src_offset = offset;
406308c95832SFUJITA Tomonori 		if (src_offset < length)
40641da177e4SLinus Torvalds 			break;
406508c95832SFUJITA Tomonori 		offset -= length;
40661da177e4SLinus Torvalds 	}
40671da177e4SLinus Torvalds 
40681da177e4SLinus Torvalds 	st_bp->buffer_bytes = st_bp->read_pointer = total;
40691da177e4SLinus Torvalds 	for (dst_seg=dst_offset=0; total > 0; ) {
407008c95832SFUJITA Tomonori 		struct page *dpage = st_bp->reserved_pages[dst_seg];
407108c95832SFUJITA Tomonori 		struct page *spage = st_bp->reserved_pages[src_seg];
407208c95832SFUJITA Tomonori 
407308c95832SFUJITA Tomonori 		count = min(length - dst_offset, length - src_offset);
407408c95832SFUJITA Tomonori 		memmove(page_address(dpage) + dst_offset,
407508c95832SFUJITA Tomonori 			page_address(spage) + src_offset, count);
40761da177e4SLinus Torvalds 		src_offset += count;
407708c95832SFUJITA Tomonori 		if (src_offset >= length) {
40781da177e4SLinus Torvalds 			src_seg++;
40791da177e4SLinus Torvalds 			src_offset = 0;
40801da177e4SLinus Torvalds 		}
40811da177e4SLinus Torvalds 		dst_offset += count;
408208c95832SFUJITA Tomonori 		if (dst_offset >= length) {
40831da177e4SLinus Torvalds 			dst_seg++;
40841da177e4SLinus Torvalds 			dst_offset = 0;
40851da177e4SLinus Torvalds 		}
40861da177e4SLinus Torvalds 		total -= count;
40871da177e4SLinus Torvalds 	}
40881da177e4SLinus Torvalds }
40891da177e4SLinus Torvalds 
40901da177e4SLinus Torvalds /* Validate the options from command line or module parameters */
validate_options(void)40911da177e4SLinus Torvalds static void validate_options(void)
40921da177e4SLinus Torvalds {
40931da177e4SLinus Torvalds 	if (buffer_kbs > 0)
40941da177e4SLinus Torvalds 		st_fixed_buffer_size = buffer_kbs * ST_KILOBYTE;
40951da177e4SLinus Torvalds 	if (max_sg_segs >= ST_FIRST_SG)
40961da177e4SLinus Torvalds 		st_max_sg_segs = max_sg_segs;
40971da177e4SLinus Torvalds }
40981da177e4SLinus Torvalds 
40991da177e4SLinus Torvalds #ifndef MODULE
41001da177e4SLinus Torvalds /* Set the boot options. Syntax is defined in Documenation/scsi/st.txt.
41011da177e4SLinus Torvalds  */
st_setup(char * str)41021da177e4SLinus Torvalds static int __init st_setup(char *str)
41031da177e4SLinus Torvalds {
41041da177e4SLinus Torvalds 	int i, len, ints[5];
41051da177e4SLinus Torvalds 	char *stp;
41061da177e4SLinus Torvalds 
41071da177e4SLinus Torvalds 	stp = get_options(str, ARRAY_SIZE(ints), ints);
41081da177e4SLinus Torvalds 
41091da177e4SLinus Torvalds 	if (ints[0] > 0) {
41101da177e4SLinus Torvalds 		for (i = 0; i < ints[0] && i < ARRAY_SIZE(parms); i++)
41111da177e4SLinus Torvalds 			if (parms[i].val)
41121da177e4SLinus Torvalds 				*parms[i].val = ints[i + 1];
41131da177e4SLinus Torvalds 	} else {
41141da177e4SLinus Torvalds 		while (stp != NULL) {
41151da177e4SLinus Torvalds 			for (i = 0; i < ARRAY_SIZE(parms); i++) {
41161da177e4SLinus Torvalds 				len = strlen(parms[i].name);
41171da177e4SLinus Torvalds 				if (!strncmp(stp, parms[i].name, len) &&
41181da177e4SLinus Torvalds 				    (*(stp + len) == ':' || *(stp + len) == '=')) {
41191da177e4SLinus Torvalds 					if (parms[i].val)
41201da177e4SLinus Torvalds 						*parms[i].val =
41211da177e4SLinus Torvalds 							simple_strtoul(stp + len + 1, NULL, 0);
41221da177e4SLinus Torvalds 					else
41231da177e4SLinus Torvalds 						printk(KERN_WARNING "st: Obsolete parameter %s\n",
41241da177e4SLinus Torvalds 						       parms[i].name);
41251da177e4SLinus Torvalds 					break;
41261da177e4SLinus Torvalds 				}
41271da177e4SLinus Torvalds 			}
41286391a113STobias Klauser 			if (i >= ARRAY_SIZE(parms))
41291da177e4SLinus Torvalds 				 printk(KERN_WARNING "st: invalid parameter in '%s'\n",
41301da177e4SLinus Torvalds 					stp);
41311da177e4SLinus Torvalds 			stp = strchr(stp, ',');
41321da177e4SLinus Torvalds 			if (stp)
41331da177e4SLinus Torvalds 				stp++;
41341da177e4SLinus Torvalds 		}
41351da177e4SLinus Torvalds 	}
41361da177e4SLinus Torvalds 
41371da177e4SLinus Torvalds 	validate_options();
41381da177e4SLinus Torvalds 
41391da177e4SLinus Torvalds 	return 1;
41401da177e4SLinus Torvalds }
41411da177e4SLinus Torvalds 
41421da177e4SLinus Torvalds __setup("st=", st_setup);
41431da177e4SLinus Torvalds 
41441da177e4SLinus Torvalds #endif
41451da177e4SLinus Torvalds 
414600977a59SArjan van de Ven static const struct file_operations st_fops =
41471da177e4SLinus Torvalds {
41481da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
41491da177e4SLinus Torvalds 	.read =		st_read,
41501da177e4SLinus Torvalds 	.write =	st_write,
4151fd66c1b4SKai Makisara 	.unlocked_ioctl = st_ioctl,
41521da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
41531da177e4SLinus Torvalds 	.compat_ioctl = st_compat_ioctl,
41541da177e4SLinus Torvalds #endif
41551da177e4SLinus Torvalds 	.open =		st_open,
41561da177e4SLinus Torvalds 	.flush =	st_flush,
41571da177e4SLinus Torvalds 	.release =	st_release,
4158b4d878e2SJan Blunck 	.llseek =	noop_llseek,
41591da177e4SLinus Torvalds };
41601da177e4SLinus Torvalds 
create_one_cdev(struct scsi_tape * tape,int mode,int rew)416126898afdSJeff Mahoney static int create_one_cdev(struct scsi_tape *tape, int mode, int rew)
416226898afdSJeff Mahoney {
416326898afdSJeff Mahoney 	int i, error;
416426898afdSJeff Mahoney 	dev_t cdev_devno;
416526898afdSJeff Mahoney 	struct cdev *cdev;
416626898afdSJeff Mahoney 	struct device *dev;
416726898afdSJeff Mahoney 	struct st_modedef *STm = &(tape->modes[mode]);
416826898afdSJeff Mahoney 	char name[10];
416926898afdSJeff Mahoney 	int dev_num = tape->index;
417026898afdSJeff Mahoney 
417126898afdSJeff Mahoney 	cdev_devno = MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, rew));
417226898afdSJeff Mahoney 
417326898afdSJeff Mahoney 	cdev = cdev_alloc();
417426898afdSJeff Mahoney 	if (!cdev) {
417526898afdSJeff Mahoney 		pr_err("st%d: out of memory. Device not attached.\n", dev_num);
417626898afdSJeff Mahoney 		error = -ENOMEM;
417726898afdSJeff Mahoney 		goto out;
417826898afdSJeff Mahoney 	}
417926898afdSJeff Mahoney 	cdev->owner = THIS_MODULE;
418026898afdSJeff Mahoney 	cdev->ops = &st_fops;
4181ab08ee14SMaurizio Lombardi 	STm->cdevs[rew] = cdev;
418226898afdSJeff Mahoney 
418326898afdSJeff Mahoney 	error = cdev_add(cdev, cdev_devno, 1);
418426898afdSJeff Mahoney 	if (error) {
418526898afdSJeff Mahoney 		pr_err("st%d: Can't add %s-rewind mode %d\n", dev_num,
418626898afdSJeff Mahoney 		       rew ? "non" : "auto", mode);
418726898afdSJeff Mahoney 		pr_err("st%d: Device not attached.\n", dev_num);
418826898afdSJeff Mahoney 		goto out_free;
418926898afdSJeff Mahoney 	}
419026898afdSJeff Mahoney 
419126898afdSJeff Mahoney 	i = mode << (4 - ST_NBR_MODE_BITS);
419226898afdSJeff Mahoney 	snprintf(name, 10, "%s%s%s", rew ? "n" : "",
419345938335SChristoph Hellwig 		 tape->name, st_formats[i]);
419426898afdSJeff Mahoney 
419526898afdSJeff Mahoney 	dev = device_create(&st_sysfs_class, &tape->device->sdev_gendev,
419626898afdSJeff Mahoney 			    cdev_devno, &tape->modes[mode], "%s", name);
419726898afdSJeff Mahoney 	if (IS_ERR(dev)) {
419826898afdSJeff Mahoney 		pr_err("st%d: device_create failed\n", dev_num);
419926898afdSJeff Mahoney 		error = PTR_ERR(dev);
420026898afdSJeff Mahoney 		goto out_free;
420126898afdSJeff Mahoney 	}
420226898afdSJeff Mahoney 
420326898afdSJeff Mahoney 	STm->devs[rew] = dev;
420426898afdSJeff Mahoney 
420526898afdSJeff Mahoney 	return 0;
420626898afdSJeff Mahoney out_free:
420726898afdSJeff Mahoney 	cdev_del(STm->cdevs[rew]);
420826898afdSJeff Mahoney out:
4209ab08ee14SMaurizio Lombardi 	STm->cdevs[rew] = NULL;
4210ab08ee14SMaurizio Lombardi 	STm->devs[rew] = NULL;
421126898afdSJeff Mahoney 	return error;
421226898afdSJeff Mahoney }
421326898afdSJeff Mahoney 
create_cdevs(struct scsi_tape * tape)421426898afdSJeff Mahoney static int create_cdevs(struct scsi_tape *tape)
421526898afdSJeff Mahoney {
421626898afdSJeff Mahoney 	int mode, error;
421726898afdSJeff Mahoney 	for (mode = 0; mode < ST_NBR_MODES; ++mode) {
421826898afdSJeff Mahoney 		error = create_one_cdev(tape, mode, 0);
421926898afdSJeff Mahoney 		if (error)
422026898afdSJeff Mahoney 			return error;
422126898afdSJeff Mahoney 		error = create_one_cdev(tape, mode, 1);
422226898afdSJeff Mahoney 		if (error)
422326898afdSJeff Mahoney 			return error;
422426898afdSJeff Mahoney 	}
422526898afdSJeff Mahoney 
422626898afdSJeff Mahoney 	return sysfs_create_link(&tape->device->sdev_gendev.kobj,
422726898afdSJeff Mahoney 				 &tape->modes[0].devs[0]->kobj, "tape");
422826898afdSJeff Mahoney }
422926898afdSJeff Mahoney 
remove_cdevs(struct scsi_tape * tape)423026898afdSJeff Mahoney static void remove_cdevs(struct scsi_tape *tape)
423126898afdSJeff Mahoney {
423226898afdSJeff Mahoney 	int mode, rew;
423326898afdSJeff Mahoney 	sysfs_remove_link(&tape->device->sdev_gendev.kobj, "tape");
423426898afdSJeff Mahoney 	for (mode = 0; mode < ST_NBR_MODES; mode++) {
423526898afdSJeff Mahoney 		struct st_modedef *STm = &(tape->modes[mode]);
423626898afdSJeff Mahoney 		for (rew = 0; rew < 2; rew++) {
423726898afdSJeff Mahoney 			if (STm->cdevs[rew])
423826898afdSJeff Mahoney 				cdev_del(STm->cdevs[rew]);
423926898afdSJeff Mahoney 			if (STm->devs[rew])
424026898afdSJeff Mahoney 				device_unregister(STm->devs[rew]);
424126898afdSJeff Mahoney 		}
424226898afdSJeff Mahoney 	}
424326898afdSJeff Mahoney }
424426898afdSJeff Mahoney 
st_probe(struct device * dev)42451da177e4SLinus Torvalds static int st_probe(struct device *dev)
42461da177e4SLinus Torvalds {
42471da177e4SLinus Torvalds 	struct scsi_device *SDp = to_scsi_device(dev);
42481da177e4SLinus Torvalds 	struct scsi_tape *tpnt = NULL;
42491da177e4SLinus Torvalds 	struct st_modedef *STm;
42501da177e4SLinus Torvalds 	struct st_partstat *STps;
42511da177e4SLinus Torvalds 	struct st_buffer *buffer;
4252b98c52b5STejun Heo 	int i, error;
42531da177e4SLinus Torvalds 
42541da177e4SLinus Torvalds 	if (SDp->type != TYPE_TAPE)
42551da177e4SLinus Torvalds 		return -ENODEV;
4256f0ee639aSColin Ian King 	if (st_incompatible(SDp)) {
4257b30d8bcaSHannes Reinecke 		sdev_printk(KERN_INFO, SDp,
42584e3ea141SHannes Reinecke 			    "OnStream tapes are no longer supported;\n");
42594e3ea141SHannes Reinecke 		sdev_printk(KERN_INFO, SDp,
42604e3ea141SHannes Reinecke 			    "please mail to linux-scsi@vger.kernel.org.\n");
42611da177e4SLinus Torvalds 		return -ENODEV;
42621da177e4SLinus Torvalds 	}
42631da177e4SLinus Torvalds 
42646fe8c1dbSSubhash Jadavani 	scsi_autopm_get_device(SDp);
42658a78362cSMartin K. Petersen 	i = queue_max_segments(SDp->request_queue);
42661da177e4SLinus Torvalds 	if (st_max_sg_segs < i)
42671da177e4SLinus Torvalds 		i = st_max_sg_segs;
4268aaff5ebaSChristoph Hellwig 	buffer = new_tape_buffer(i);
42691da177e4SLinus Torvalds 	if (buffer == NULL) {
4270b30d8bcaSHannes Reinecke 		sdev_printk(KERN_ERR, SDp,
4271b30d8bcaSHannes Reinecke 			    "st: Can't allocate new tape buffer. "
4272b30d8bcaSHannes Reinecke 			    "Device not attached.\n");
42731da177e4SLinus Torvalds 		goto out;
42741da177e4SLinus Torvalds 	}
42751da177e4SLinus Torvalds 
42761f618aacSJia-Ju Bai 	tpnt = kzalloc(sizeof(struct scsi_tape), GFP_KERNEL);
42771da177e4SLinus Torvalds 	if (tpnt == NULL) {
4278b30d8bcaSHannes Reinecke 		sdev_printk(KERN_ERR, SDp,
4279b30d8bcaSHannes Reinecke 			    "st: Can't allocate device descriptor.\n");
428045938335SChristoph Hellwig 		goto out_buffer_free;
42811da177e4SLinus Torvalds 	}
4282f03a5670SKai Makisara 	kref_init(&tpnt->kref);
42831da177e4SLinus Torvalds 
42841da177e4SLinus Torvalds 	tpnt->device = SDp;
42851da177e4SLinus Torvalds 	if (SDp->scsi_level <= 2)
42861da177e4SLinus Torvalds 		tpnt->tape_type = MT_ISSCSI1;
42871da177e4SLinus Torvalds 	else
42881da177e4SLinus Torvalds 		tpnt->tape_type = MT_ISSCSI2;
42891da177e4SLinus Torvalds 
42901da177e4SLinus Torvalds 	tpnt->buffer = buffer;
4291f03a5670SKai Makisara 	tpnt->buffer->last_SRpnt = NULL;
42921da177e4SLinus Torvalds 
42931da177e4SLinus Torvalds 	tpnt->inited = 0;
42941da177e4SLinus Torvalds 	tpnt->dirty = 0;
42951da177e4SLinus Torvalds 	tpnt->in_use = 0;
42961da177e4SLinus Torvalds 	tpnt->drv_buffer = 1;	/* Try buffering if no mode sense */
42971da177e4SLinus Torvalds 	tpnt->use_pf = (SDp->scsi_level >= SCSI_2);
42981da177e4SLinus Torvalds 	tpnt->density = 0;
42991da177e4SLinus Torvalds 	tpnt->do_auto_lock = ST_AUTO_LOCK;
43001da177e4SLinus Torvalds 	tpnt->can_bsr = (SDp->scsi_level > 2 ? 1 : ST_IN_FILE_POS); /* BSR mandatory in SCSI3 */
43011da177e4SLinus Torvalds 	tpnt->can_partitions = 0;
43021da177e4SLinus Torvalds 	tpnt->two_fm = ST_TWO_FM;
43031da177e4SLinus Torvalds 	tpnt->fast_mteom = ST_FAST_MTEOM;
43041da177e4SLinus Torvalds 	tpnt->scsi2_logical = ST_SCSI2LOGICAL;
430540f6b36cSKai Makisara 	tpnt->sili = ST_SILI;
43061da177e4SLinus Torvalds 	tpnt->immediate = ST_NOWAIT;
4307c743e44fSLee Duncan 	tpnt->immediate_filemark = 0;
43081da177e4SLinus Torvalds 	tpnt->default_drvbuffer = 0xff;		/* No forced buffering */
43091da177e4SLinus Torvalds 	tpnt->partition = 0;
43101da177e4SLinus Torvalds 	tpnt->new_partition = 0;
43111da177e4SLinus Torvalds 	tpnt->nbr_partitions = 0;
4312a02488edSJames Bottomley 	blk_queue_rq_timeout(tpnt->device->request_queue, ST_TIMEOUT);
43131da177e4SLinus Torvalds 	tpnt->long_timeout = ST_LONG_TIMEOUT;
4314aaff5ebaSChristoph Hellwig 	tpnt->try_dio = try_direct_io;
43151da177e4SLinus Torvalds 
43161da177e4SLinus Torvalds 	for (i = 0; i < ST_NBR_MODES; i++) {
43171da177e4SLinus Torvalds 		STm = &(tpnt->modes[i]);
43181da177e4SLinus Torvalds 		STm->defined = 0;
43191da177e4SLinus Torvalds 		STm->sysv = ST_SYSV;
43201da177e4SLinus Torvalds 		STm->defaults_for_writes = 0;
43211da177e4SLinus Torvalds 		STm->do_async_writes = ST_ASYNC_WRITES;
43221da177e4SLinus Torvalds 		STm->do_buffer_writes = ST_BUFFER_WRITES;
43231da177e4SLinus Torvalds 		STm->do_read_ahead = ST_READ_AHEAD;
43241da177e4SLinus Torvalds 		STm->default_compression = ST_DONT_TOUCH;
43251da177e4SLinus Torvalds 		STm->default_blksize = (-1);	/* No forced size */
43261da177e4SLinus Torvalds 		STm->default_density = (-1);	/* No forced density */
43276c648d95SJeff Mahoney 		STm->tape = tpnt;
43281da177e4SLinus Torvalds 	}
43291da177e4SLinus Torvalds 
43301da177e4SLinus Torvalds 	for (i = 0; i < ST_NBR_PARTITIONS; i++) {
43311da177e4SLinus Torvalds 		STps = &(tpnt->ps[i]);
43321da177e4SLinus Torvalds 		STps->rw = ST_IDLE;
43331da177e4SLinus Torvalds 		STps->eof = ST_NOEOF;
43341da177e4SLinus Torvalds 		STps->at_sm = 0;
43351da177e4SLinus Torvalds 		STps->last_block_valid = 0;
43361da177e4SLinus Torvalds 		STps->drv_block = (-1);
43371da177e4SLinus Torvalds 		STps->drv_file = (-1);
43381da177e4SLinus Torvalds 	}
43391da177e4SLinus Torvalds 
43401da177e4SLinus Torvalds 	tpnt->current_mode = 0;
43411da177e4SLinus Torvalds 	tpnt->modes[0].defined = 1;
43421da177e4SLinus Torvalds 
43431da177e4SLinus Torvalds 	tpnt->density_changed = tpnt->compression_changed =
43441da177e4SLinus Torvalds 	    tpnt->blksize_changed = 0;
434528f85009SMatthias Kaehlcke 	mutex_init(&tpnt->lock);
43461da177e4SLinus Torvalds 
4347b98c52b5STejun Heo 	idr_preload(GFP_KERNEL);
43486c648d95SJeff Mahoney 	spin_lock(&st_index_lock);
4349b98c52b5STejun Heo 	error = idr_alloc(&st_index_idr, tpnt, 0, ST_MAX_TAPES + 1, GFP_NOWAIT);
43506c648d95SJeff Mahoney 	spin_unlock(&st_index_lock);
4351b98c52b5STejun Heo 	idr_preload_end();
4352b98c52b5STejun Heo 	if (error < 0) {
43536c648d95SJeff Mahoney 		pr_warn("st: idr allocation failed: %d\n", error);
435445938335SChristoph Hellwig 		goto out_free_tape;
43556c648d95SJeff Mahoney 	}
4356b98c52b5STejun Heo 	tpnt->index = error;
435745938335SChristoph Hellwig 	sprintf(tpnt->name, "st%d", tpnt->index);
435805545c92SSeymour, Shane M 	tpnt->stats = kzalloc(sizeof(struct scsi_tape_stats), GFP_KERNEL);
435905545c92SSeymour, Shane M 	if (tpnt->stats == NULL) {
436005545c92SSeymour, Shane M 		sdev_printk(KERN_ERR, SDp,
436105545c92SSeymour, Shane M 			    "st: Can't allocate statistics.\n");
436205545c92SSeymour, Shane M 		goto out_idr_remove;
436305545c92SSeymour, Shane M 	}
43646c648d95SJeff Mahoney 
43656c648d95SJeff Mahoney 	dev_set_drvdata(dev, tpnt);
43661da177e4SLinus Torvalds 
43671da177e4SLinus Torvalds 
436826898afdSJeff Mahoney 	error = create_cdevs(tpnt);
436913026a6bSJeff Garzik 	if (error)
437026898afdSJeff Mahoney 		goto out_remove_devs;
437146a243f7SOliver Neukum 	scsi_autopm_put_device(SDp);
43721da177e4SLinus Torvalds 
437342252854SKai Makisara 	sdev_printk(KERN_NOTICE, SDp,
437445938335SChristoph Hellwig 		    "Attached scsi tape %s\n", tpnt->name);
437542252854SKai Makisara 	sdev_printk(KERN_INFO, SDp, "%s: try direct i/o: %s (alignment %d B)\n",
437645938335SChristoph Hellwig 		    tpnt->name, tpnt->try_dio ? "yes" : "no",
43778b05b773SMike Christie 		    queue_dma_alignment(SDp->request_queue) + 1);
43781da177e4SLinus Torvalds 
43791da177e4SLinus Torvalds 	return 0;
43801da177e4SLinus Torvalds 
438126898afdSJeff Mahoney out_remove_devs:
438226898afdSJeff Mahoney 	remove_cdevs(tpnt);
438305545c92SSeymour, Shane M 	kfree(tpnt->stats);
438405545c92SSeymour, Shane M out_idr_remove:
43856c648d95SJeff Mahoney 	spin_lock(&st_index_lock);
4386b98c52b5STejun Heo 	idr_remove(&st_index_idr, tpnt->index);
43876c648d95SJeff Mahoney 	spin_unlock(&st_index_lock);
438845938335SChristoph Hellwig out_free_tape:
43891da177e4SLinus Torvalds 	kfree(tpnt);
43901da177e4SLinus Torvalds out_buffer_free:
43911da177e4SLinus Torvalds 	kfree(buffer);
43921da177e4SLinus Torvalds out:
43936fe8c1dbSSubhash Jadavani 	scsi_autopm_put_device(SDp);
43941da177e4SLinus Torvalds 	return -ENODEV;
43951da177e4SLinus Torvalds };
43961da177e4SLinus Torvalds 
43971da177e4SLinus Torvalds 
st_remove(struct device * dev)43981da177e4SLinus Torvalds static int st_remove(struct device *dev)
43991da177e4SLinus Torvalds {
44006c648d95SJeff Mahoney 	struct scsi_tape *tpnt = dev_get_drvdata(dev);
44016c648d95SJeff Mahoney 	int index = tpnt->index;
44021da177e4SLinus Torvalds 
44036c648d95SJeff Mahoney 	scsi_autopm_get_device(to_scsi_device(dev));
440426898afdSJeff Mahoney 	remove_cdevs(tpnt);
4405f03a5670SKai Makisara 
44060b950672SArjan van de Ven 	mutex_lock(&st_ref_mutex);
4407f03a5670SKai Makisara 	kref_put(&tpnt->kref, scsi_tape_release);
44080b950672SArjan van de Ven 	mutex_unlock(&st_ref_mutex);
44096c648d95SJeff Mahoney 	spin_lock(&st_index_lock);
44106c648d95SJeff Mahoney 	idr_remove(&st_index_idr, index);
44116c648d95SJeff Mahoney 	spin_unlock(&st_index_lock);
4412f03a5670SKai Makisara 	return 0;
4413f03a5670SKai Makisara }
4414f03a5670SKai Makisara 
4415f03a5670SKai Makisara /**
4416f03a5670SKai Makisara  *      scsi_tape_release - Called to free the Scsi_Tape structure
4417f03a5670SKai Makisara  *      @kref: pointer to embedded kref
4418f03a5670SKai Makisara  *
44190b950672SArjan van de Ven  *      st_ref_mutex must be held entering this routine.  Because it is
4420f03a5670SKai Makisara  *      called on last put, you should always use the scsi_tape_get()
4421f03a5670SKai Makisara  *      scsi_tape_put() helpers which manipulate the semaphore directly
4422f03a5670SKai Makisara  *      and never do a direct kref_put().
4423f03a5670SKai Makisara  **/
scsi_tape_release(struct kref * kref)4424f03a5670SKai Makisara static void scsi_tape_release(struct kref *kref)
4425f03a5670SKai Makisara {
4426f03a5670SKai Makisara 	struct scsi_tape *tpnt = to_scsi_tape(kref);
4427f03a5670SKai Makisara 
44281da177e4SLinus Torvalds 	tpnt->device = NULL;
44291da177e4SLinus Torvalds 
44301da177e4SLinus Torvalds 	if (tpnt->buffer) {
44311da177e4SLinus Torvalds 		normalize_buffer(tpnt->buffer);
4432d0e1ae31SFUJITA Tomonori 		kfree(tpnt->buffer->reserved_pages);
44331da177e4SLinus Torvalds 		kfree(tpnt->buffer);
44341da177e4SLinus Torvalds 	}
44351da177e4SLinus Torvalds 
443605545c92SSeymour, Shane M 	kfree(tpnt->stats);
4437f03a5670SKai Makisara 	kfree(tpnt);
4438f03a5670SKai Makisara 	return;
44391da177e4SLinus Torvalds }
44401da177e4SLinus Torvalds 
4441af23782bSJeff Mahoney static struct class st_sysfs_class = {
4442af23782bSJeff Mahoney 	.name = "scsi_tape",
4443c69c6be5SGreg Kroah-Hartman 	.dev_groups = st_dev_groups,
4444af23782bSJeff Mahoney };
4445af23782bSJeff Mahoney 
init_st(void)44461da177e4SLinus Torvalds static int __init init_st(void)
44471da177e4SLinus Torvalds {
444813026a6bSJeff Garzik 	int err;
444913026a6bSJeff Garzik 
44501da177e4SLinus Torvalds 	validate_options();
44511da177e4SLinus Torvalds 
445213026a6bSJeff Garzik 	printk(KERN_INFO "st: Version %s, fixed bufsize %d, s/g segs %d\n",
44531da177e4SLinus Torvalds 		verstr, st_fixed_buffer_size, st_max_sg_segs);
44541da177e4SLinus Torvalds 
44552bec708aSLaurence Oberman 	debugging = (debug_flag > 0) ? debug_flag : NO_DEBUG;
44562bec708aSLaurence Oberman 	if (debugging) {
44572bec708aSLaurence Oberman 		printk(KERN_INFO "st: Debugging enabled debug_flag = %d\n",
44582bec708aSLaurence Oberman 			debugging);
44592bec708aSLaurence Oberman 	}
44602bec708aSLaurence Oberman 
4461af23782bSJeff Mahoney 	err = class_register(&st_sysfs_class);
4462af23782bSJeff Mahoney 	if (err) {
4463af23782bSJeff Mahoney 		pr_err("Unable register sysfs class for SCSI tapes\n");
4464af23782bSJeff Mahoney 		return err;
44651da177e4SLinus Torvalds 	}
44661da177e4SLinus Torvalds 
446713026a6bSJeff Garzik 	err = register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
446813026a6bSJeff Garzik 				     ST_MAX_TAPE_ENTRIES, "st");
446913026a6bSJeff Garzik 	if (err) {
447013026a6bSJeff Garzik 		printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
447113026a6bSJeff Garzik 		       SCSI_TAPE_MAJOR);
447213026a6bSJeff Garzik 		goto err_class;
44731da177e4SLinus Torvalds 	}
447413026a6bSJeff Garzik 
447513026a6bSJeff Garzik 	err = scsi_register_driver(&st_template.gendrv);
447613026a6bSJeff Garzik 	if (err)
447713026a6bSJeff Garzik 		goto err_chrdev;
447813026a6bSJeff Garzik 
447913026a6bSJeff Garzik 	return 0;
448013026a6bSJeff Garzik 
448113026a6bSJeff Garzik err_chrdev:
44821da177e4SLinus Torvalds 	unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
44831da177e4SLinus Torvalds 				 ST_MAX_TAPE_ENTRIES);
448413026a6bSJeff Garzik err_class:
4485af23782bSJeff Mahoney 	class_unregister(&st_sysfs_class);
448613026a6bSJeff Garzik 	return err;
44871da177e4SLinus Torvalds }
44881da177e4SLinus Torvalds 
exit_st(void)44891da177e4SLinus Torvalds static void __exit exit_st(void)
44901da177e4SLinus Torvalds {
44911da177e4SLinus Torvalds 	scsi_unregister_driver(&st_template.gendrv);
44921da177e4SLinus Torvalds 	unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0),
44931da177e4SLinus Torvalds 				 ST_MAX_TAPE_ENTRIES);
4494af23782bSJeff Mahoney 	class_unregister(&st_sysfs_class);
44952d3a5d21SJohannes Thumshirn 	idr_destroy(&st_index_idr);
44961da177e4SLinus Torvalds 	printk(KERN_INFO "st: Unloaded.\n");
44971da177e4SLinus Torvalds }
44981da177e4SLinus Torvalds 
44991da177e4SLinus Torvalds module_init(init_st);
45001da177e4SLinus Torvalds module_exit(exit_st);
45011da177e4SLinus Torvalds 
45021da177e4SLinus Torvalds 
45031da177e4SLinus Torvalds /* The sysfs driver interface. Read-only at the moment */
try_direct_io_show(struct device_driver * ddp,char * buf)450410978e48SSeymour, Shane M static ssize_t try_direct_io_show(struct device_driver *ddp, char *buf)
45051da177e4SLinus Torvalds {
450610978e48SSeymour, Shane M 	return scnprintf(buf, PAGE_SIZE, "%d\n", try_direct_io);
45071da177e4SLinus Torvalds }
450810978e48SSeymour, Shane M static DRIVER_ATTR_RO(try_direct_io);
45091da177e4SLinus Torvalds 
fixed_buffer_size_show(struct device_driver * ddp,char * buf)451010978e48SSeymour, Shane M static ssize_t fixed_buffer_size_show(struct device_driver *ddp, char *buf)
45111da177e4SLinus Torvalds {
451210978e48SSeymour, Shane M 	return scnprintf(buf, PAGE_SIZE, "%d\n", st_fixed_buffer_size);
45131da177e4SLinus Torvalds }
451410978e48SSeymour, Shane M static DRIVER_ATTR_RO(fixed_buffer_size);
45151da177e4SLinus Torvalds 
max_sg_segs_show(struct device_driver * ddp,char * buf)451610978e48SSeymour, Shane M static ssize_t max_sg_segs_show(struct device_driver *ddp, char *buf)
45171da177e4SLinus Torvalds {
451810978e48SSeymour, Shane M 	return scnprintf(buf, PAGE_SIZE, "%d\n", st_max_sg_segs);
45191da177e4SLinus Torvalds }
452010978e48SSeymour, Shane M static DRIVER_ATTR_RO(max_sg_segs);
45211da177e4SLinus Torvalds 
version_show(struct device_driver * ddd,char * buf)452210978e48SSeymour, Shane M static ssize_t version_show(struct device_driver *ddd, char *buf)
45231da177e4SLinus Torvalds {
452410978e48SSeymour, Shane M 	return scnprintf(buf, PAGE_SIZE, "[%s]\n", verstr);
45251da177e4SLinus Torvalds }
452610978e48SSeymour, Shane M static DRIVER_ATTR_RO(version);
45271da177e4SLinus Torvalds 
4528d9b43a10SSeymour, Shane M #if DEBUG
debug_flag_store(struct device_driver * ddp,const char * buf,size_t count)4529d9b43a10SSeymour, Shane M static ssize_t debug_flag_store(struct device_driver *ddp,
4530d9b43a10SSeymour, Shane M 	const char *buf, size_t count)
4531d9b43a10SSeymour, Shane M {
4532d9b43a10SSeymour, Shane M /* We only care what the first byte of the data is the rest is unused.
4533d9b43a10SSeymour, Shane M  * if it's a '1' we turn on debug and if it's a '0' we disable it. All
4534d9b43a10SSeymour, Shane M  * other values have -EINVAL returned if they are passed in.
4535d9b43a10SSeymour, Shane M  */
4536d9b43a10SSeymour, Shane M 	if (count > 0) {
4537d9b43a10SSeymour, Shane M 		if (buf[0] == '0') {
4538d9b43a10SSeymour, Shane M 			debugging = NO_DEBUG;
4539d9b43a10SSeymour, Shane M 			return count;
4540d9b43a10SSeymour, Shane M 		} else if (buf[0] == '1') {
4541d9b43a10SSeymour, Shane M 			debugging = 1;
4542d9b43a10SSeymour, Shane M 			return count;
4543d9b43a10SSeymour, Shane M 		}
4544d9b43a10SSeymour, Shane M 	}
4545d9b43a10SSeymour, Shane M 	return -EINVAL;
4546d9b43a10SSeymour, Shane M }
4547d9b43a10SSeymour, Shane M 
debug_flag_show(struct device_driver * ddp,char * buf)4548d9b43a10SSeymour, Shane M static ssize_t debug_flag_show(struct device_driver *ddp, char *buf)
4549d9b43a10SSeymour, Shane M {
4550d9b43a10SSeymour, Shane M 	return scnprintf(buf, PAGE_SIZE, "%d\n", debugging);
4551d9b43a10SSeymour, Shane M }
4552d9b43a10SSeymour, Shane M static DRIVER_ATTR_RW(debug_flag);
4553d9b43a10SSeymour, Shane M #endif
4554d9b43a10SSeymour, Shane M 
4555442d7562SSeymour, Shane M static struct attribute *st_drv_attrs[] = {
4556442d7562SSeymour, Shane M 	&driver_attr_try_direct_io.attr,
4557442d7562SSeymour, Shane M 	&driver_attr_fixed_buffer_size.attr,
4558442d7562SSeymour, Shane M 	&driver_attr_max_sg_segs.attr,
4559442d7562SSeymour, Shane M 	&driver_attr_version.attr,
4560d9b43a10SSeymour, Shane M #if DEBUG
4561d9b43a10SSeymour, Shane M 	&driver_attr_debug_flag.attr,
4562d9b43a10SSeymour, Shane M #endif
4563442d7562SSeymour, Shane M 	NULL,
4564442d7562SSeymour, Shane M };
4565442d7562SSeymour, Shane M ATTRIBUTE_GROUPS(st_drv);
45661da177e4SLinus Torvalds 
45671da177e4SLinus Torvalds /* The sysfs simple class interface */
4568ee959b00STony Jones static ssize_t
defined_show(struct device * dev,struct device_attribute * attr,char * buf)4569af23782bSJeff Mahoney defined_show(struct device *dev, struct device_attribute *attr, char *buf)
45701da177e4SLinus Torvalds {
45717d15d6a4SJames Bottomley 	struct st_modedef *STm = dev_get_drvdata(dev);
45721da177e4SLinus Torvalds 	ssize_t l = 0;
45731da177e4SLinus Torvalds 
45741da177e4SLinus Torvalds 	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->defined);
45751da177e4SLinus Torvalds 	return l;
45761da177e4SLinus Torvalds }
4577c69c6be5SGreg Kroah-Hartman static DEVICE_ATTR_RO(defined);
45781da177e4SLinus Torvalds 
4579ee959b00STony Jones static ssize_t
default_blksize_show(struct device * dev,struct device_attribute * attr,char * buf)4580af23782bSJeff Mahoney default_blksize_show(struct device *dev, struct device_attribute *attr,
4581af23782bSJeff Mahoney 		     char *buf)
45821da177e4SLinus Torvalds {
45837d15d6a4SJames Bottomley 	struct st_modedef *STm = dev_get_drvdata(dev);
45841da177e4SLinus Torvalds 	ssize_t l = 0;
45851da177e4SLinus Torvalds 
45861da177e4SLinus Torvalds 	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_blksize);
45871da177e4SLinus Torvalds 	return l;
45881da177e4SLinus Torvalds }
4589c69c6be5SGreg Kroah-Hartman static DEVICE_ATTR_RO(default_blksize);
45901da177e4SLinus Torvalds 
4591ee959b00STony Jones static ssize_t
default_density_show(struct device * dev,struct device_attribute * attr,char * buf)4592af23782bSJeff Mahoney default_density_show(struct device *dev, struct device_attribute *attr,
4593af23782bSJeff Mahoney 		     char *buf)
45941da177e4SLinus Torvalds {
45957d15d6a4SJames Bottomley 	struct st_modedef *STm = dev_get_drvdata(dev);
45961da177e4SLinus Torvalds 	ssize_t l = 0;
45971da177e4SLinus Torvalds 	char *fmt;
45981da177e4SLinus Torvalds 
45991da177e4SLinus Torvalds 	fmt = STm->default_density >= 0 ? "0x%02x\n" : "%d\n";
46001da177e4SLinus Torvalds 	l = snprintf(buf, PAGE_SIZE, fmt, STm->default_density);
46011da177e4SLinus Torvalds 	return l;
46021da177e4SLinus Torvalds }
4603c69c6be5SGreg Kroah-Hartman static DEVICE_ATTR_RO(default_density);
46041da177e4SLinus Torvalds 
4605ee959b00STony Jones static ssize_t
default_compression_show(struct device * dev,struct device_attribute * attr,char * buf)4606af23782bSJeff Mahoney default_compression_show(struct device *dev, struct device_attribute *attr,
4607ee959b00STony Jones 			 char *buf)
46081da177e4SLinus Torvalds {
46097d15d6a4SJames Bottomley 	struct st_modedef *STm = dev_get_drvdata(dev);
46101da177e4SLinus Torvalds 	ssize_t l = 0;
46111da177e4SLinus Torvalds 
46121da177e4SLinus Torvalds 	l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_compression - 1);
46131da177e4SLinus Torvalds 	return l;
46141da177e4SLinus Torvalds }
4615c69c6be5SGreg Kroah-Hartman static DEVICE_ATTR_RO(default_compression);
46161da177e4SLinus Torvalds 
4617ee959b00STony Jones static ssize_t
options_show(struct device * dev,struct device_attribute * attr,char * buf)4618af23782bSJeff Mahoney options_show(struct device *dev, struct device_attribute *attr, char *buf)
4619b174be02SKai Makisara {
46207d15d6a4SJames Bottomley 	struct st_modedef *STm = dev_get_drvdata(dev);
46216c648d95SJeff Mahoney 	struct scsi_tape *STp = STm->tape;
46226c648d95SJeff Mahoney 	int options;
4623b174be02SKai Makisara 	ssize_t l = 0;
4624b174be02SKai Makisara 
4625b174be02SKai Makisara 	options = STm->do_buffer_writes ? MT_ST_BUFFER_WRITES : 0;
4626b174be02SKai Makisara 	options |= STm->do_async_writes ? MT_ST_ASYNC_WRITES : 0;
4627b174be02SKai Makisara 	options |= STm->do_read_ahead ? MT_ST_READ_AHEAD : 0;
4628b174be02SKai Makisara 	DEB( options |= debugging ? MT_ST_DEBUGGING : 0 );
4629b174be02SKai Makisara 	options |= STp->two_fm ? MT_ST_TWO_FM : 0;
4630b174be02SKai Makisara 	options |= STp->fast_mteom ? MT_ST_FAST_MTEOM : 0;
4631b174be02SKai Makisara 	options |= STm->defaults_for_writes ? MT_ST_DEF_WRITES : 0;
4632b174be02SKai Makisara 	options |= STp->can_bsr ? MT_ST_CAN_BSR : 0;
4633b174be02SKai Makisara 	options |= STp->omit_blklims ? MT_ST_NO_BLKLIMS : 0;
4634b174be02SKai Makisara 	options |= STp->can_partitions ? MT_ST_CAN_PARTITIONS : 0;
4635b174be02SKai Makisara 	options |= STp->scsi2_logical ? MT_ST_SCSI2LOGICAL : 0;
4636b174be02SKai Makisara 	options |= STm->sysv ? MT_ST_SYSV : 0;
4637b174be02SKai Makisara 	options |= STp->immediate ? MT_ST_NOWAIT : 0;
4638c743e44fSLee Duncan 	options |= STp->immediate_filemark ? MT_ST_NOWAIT_EOF : 0;
4639b174be02SKai Makisara 	options |= STp->sili ? MT_ST_SILI : 0;
4640b174be02SKai Makisara 
4641b174be02SKai Makisara 	l = snprintf(buf, PAGE_SIZE, "0x%08x\n", options);
4642b174be02SKai Makisara 	return l;
4643b174be02SKai Makisara }
4644c69c6be5SGreg Kroah-Hartman static DEVICE_ATTR_RO(options);
4645b174be02SKai Makisara 
464605545c92SSeymour, Shane M /* Support for tape stats */
464705545c92SSeymour, Shane M 
464805545c92SSeymour, Shane M /**
464905545c92SSeymour, Shane M  * read_cnt_show - return read count - count of reads made from tape drive
465005545c92SSeymour, Shane M  * @dev: struct device
465105545c92SSeymour, Shane M  * @attr: attribute structure
465205545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
465305545c92SSeymour, Shane M  */
read_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)465405545c92SSeymour, Shane M static ssize_t read_cnt_show(struct device *dev,
465505545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
465605545c92SSeymour, Shane M {
465705545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
465805545c92SSeymour, Shane M 
465905545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
466005545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->read_cnt));
466105545c92SSeymour, Shane M }
466205545c92SSeymour, Shane M static DEVICE_ATTR_RO(read_cnt);
466305545c92SSeymour, Shane M 
466405545c92SSeymour, Shane M /**
466505545c92SSeymour, Shane M  * read_byte_cnt_show - return read byte count - tape drives
466605545c92SSeymour, Shane M  * may use blocks less than 512 bytes this gives the raw byte count of
466705545c92SSeymour, Shane M  * of data read from the tape drive.
466805545c92SSeymour, Shane M  * @dev: struct device
466905545c92SSeymour, Shane M  * @attr: attribute structure
467005545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
467105545c92SSeymour, Shane M  */
read_byte_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)467205545c92SSeymour, Shane M static ssize_t read_byte_cnt_show(struct device *dev,
467305545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
467405545c92SSeymour, Shane M {
467505545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
467605545c92SSeymour, Shane M 
467705545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
467805545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->read_byte_cnt));
467905545c92SSeymour, Shane M }
468005545c92SSeymour, Shane M static DEVICE_ATTR_RO(read_byte_cnt);
468105545c92SSeymour, Shane M 
468205545c92SSeymour, Shane M /**
4683e2dca2a2SRandy Dunlap  * read_ns_show - return read ns - overall time spent waiting on reads in ns.
468405545c92SSeymour, Shane M  * @dev: struct device
468505545c92SSeymour, Shane M  * @attr: attribute structure
468605545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
468705545c92SSeymour, Shane M  */
read_ns_show(struct device * dev,struct device_attribute * attr,char * buf)468805545c92SSeymour, Shane M static ssize_t read_ns_show(struct device *dev,
468905545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
469005545c92SSeymour, Shane M {
469105545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
469205545c92SSeymour, Shane M 
469305545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
469405545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->tot_read_time));
469505545c92SSeymour, Shane M }
469605545c92SSeymour, Shane M static DEVICE_ATTR_RO(read_ns);
469705545c92SSeymour, Shane M 
469805545c92SSeymour, Shane M /**
469905545c92SSeymour, Shane M  * write_cnt_show - write count - number of user calls
470005545c92SSeymour, Shane M  * to write(2) that have written data to tape.
470105545c92SSeymour, Shane M  * @dev: struct device
470205545c92SSeymour, Shane M  * @attr: attribute structure
470305545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
470405545c92SSeymour, Shane M  */
write_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)470505545c92SSeymour, Shane M static ssize_t write_cnt_show(struct device *dev,
470605545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
470705545c92SSeymour, Shane M {
470805545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
470905545c92SSeymour, Shane M 
471005545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
471105545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->write_cnt));
471205545c92SSeymour, Shane M }
471305545c92SSeymour, Shane M static DEVICE_ATTR_RO(write_cnt);
471405545c92SSeymour, Shane M 
471505545c92SSeymour, Shane M /**
471605545c92SSeymour, Shane M  * write_byte_cnt_show - write byte count - raw count of
471705545c92SSeymour, Shane M  * bytes written to tape.
471805545c92SSeymour, Shane M  * @dev: struct device
471905545c92SSeymour, Shane M  * @attr: attribute structure
472005545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
472105545c92SSeymour, Shane M  */
write_byte_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)472205545c92SSeymour, Shane M static ssize_t write_byte_cnt_show(struct device *dev,
472305545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
472405545c92SSeymour, Shane M {
472505545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
472605545c92SSeymour, Shane M 
472705545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
472805545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->write_byte_cnt));
472905545c92SSeymour, Shane M }
473005545c92SSeymour, Shane M static DEVICE_ATTR_RO(write_byte_cnt);
473105545c92SSeymour, Shane M 
473205545c92SSeymour, Shane M /**
473305545c92SSeymour, Shane M  * write_ns_show - write ns - number of nanoseconds waiting on write
473405545c92SSeymour, Shane M  * requests to complete.
473505545c92SSeymour, Shane M  * @dev: struct device
473605545c92SSeymour, Shane M  * @attr: attribute structure
473705545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
473805545c92SSeymour, Shane M  */
write_ns_show(struct device * dev,struct device_attribute * attr,char * buf)473905545c92SSeymour, Shane M static ssize_t write_ns_show(struct device *dev,
474005545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
474105545c92SSeymour, Shane M {
474205545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
474305545c92SSeymour, Shane M 
474405545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
474505545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->tot_write_time));
474605545c92SSeymour, Shane M }
474705545c92SSeymour, Shane M static DEVICE_ATTR_RO(write_ns);
474805545c92SSeymour, Shane M 
474905545c92SSeymour, Shane M /**
475005545c92SSeymour, Shane M  * in_flight_show - number of I/Os currently in flight -
475105545c92SSeymour, Shane M  * in most cases this will be either 0 or 1. It may be higher if someone
475205545c92SSeymour, Shane M  * has also issued other SCSI commands such as via an ioctl.
475305545c92SSeymour, Shane M  * @dev: struct device
475405545c92SSeymour, Shane M  * @attr: attribute structure
475505545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
475605545c92SSeymour, Shane M  */
in_flight_show(struct device * dev,struct device_attribute * attr,char * buf)475705545c92SSeymour, Shane M static ssize_t in_flight_show(struct device *dev,
475805545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
475905545c92SSeymour, Shane M {
476005545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
476105545c92SSeymour, Shane M 
476205545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
476305545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->in_flight));
476405545c92SSeymour, Shane M }
476505545c92SSeymour, Shane M static DEVICE_ATTR_RO(in_flight);
476605545c92SSeymour, Shane M 
476705545c92SSeymour, Shane M /**
476805545c92SSeymour, Shane M  * io_ns_show - io wait ns - this is the number of ns spent
476905545c92SSeymour, Shane M  * waiting on all I/O to complete. This includes tape movement commands
477005545c92SSeymour, Shane M  * such as rewinding, seeking to end of file or tape, it also includes
477105545c92SSeymour, Shane M  * read and write. To determine the time spent on tape movement
477205545c92SSeymour, Shane M  * subtract the read and write ns from this value.
477305545c92SSeymour, Shane M  * @dev: struct device
477405545c92SSeymour, Shane M  * @attr: attribute structure
477505545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
477605545c92SSeymour, Shane M  */
io_ns_show(struct device * dev,struct device_attribute * attr,char * buf)477705545c92SSeymour, Shane M static ssize_t io_ns_show(struct device *dev,
477805545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
477905545c92SSeymour, Shane M {
478005545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
478105545c92SSeymour, Shane M 
478205545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
478305545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->tot_io_time));
478405545c92SSeymour, Shane M }
478505545c92SSeymour, Shane M static DEVICE_ATTR_RO(io_ns);
478605545c92SSeymour, Shane M 
478705545c92SSeymour, Shane M /**
478805545c92SSeymour, Shane M  * other_cnt_show - other io count - this is the number of
478905545c92SSeymour, Shane M  * I/O requests other than read and write requests.
479005545c92SSeymour, Shane M  * Typically these are tape movement requests but will include driver
479105545c92SSeymour, Shane M  * tape movement. This includes only requests issued by the st driver.
479205545c92SSeymour, Shane M  * @dev: struct device
479305545c92SSeymour, Shane M  * @attr: attribute structure
479405545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
479505545c92SSeymour, Shane M  */
other_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)479605545c92SSeymour, Shane M static ssize_t other_cnt_show(struct device *dev,
479705545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
479805545c92SSeymour, Shane M {
479905545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
480005545c92SSeymour, Shane M 
480105545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
480205545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->other_cnt));
480305545c92SSeymour, Shane M }
480405545c92SSeymour, Shane M static DEVICE_ATTR_RO(other_cnt);
480505545c92SSeymour, Shane M 
480605545c92SSeymour, Shane M /**
480705545c92SSeymour, Shane M  * resid_cnt_show - A count of the number of times we get a residual
480805545c92SSeymour, Shane M  * count - this should indicate someone issuing reads larger than the
480905545c92SSeymour, Shane M  * block size on tape.
481005545c92SSeymour, Shane M  * @dev: struct device
481105545c92SSeymour, Shane M  * @attr: attribute structure
481205545c92SSeymour, Shane M  * @buf: buffer to return formatted data in
481305545c92SSeymour, Shane M  */
resid_cnt_show(struct device * dev,struct device_attribute * attr,char * buf)481405545c92SSeymour, Shane M static ssize_t resid_cnt_show(struct device *dev,
481505545c92SSeymour, Shane M 	struct device_attribute *attr, char *buf)
481605545c92SSeymour, Shane M {
481705545c92SSeymour, Shane M 	struct st_modedef *STm = dev_get_drvdata(dev);
481805545c92SSeymour, Shane M 
481905545c92SSeymour, Shane M 	return sprintf(buf, "%lld",
482005545c92SSeymour, Shane M 		       (long long)atomic64_read(&STm->tape->stats->resid_cnt));
482105545c92SSeymour, Shane M }
482205545c92SSeymour, Shane M static DEVICE_ATTR_RO(resid_cnt);
482305545c92SSeymour, Shane M 
4824c69c6be5SGreg Kroah-Hartman static struct attribute *st_dev_attrs[] = {
4825c69c6be5SGreg Kroah-Hartman 	&dev_attr_defined.attr,
4826c69c6be5SGreg Kroah-Hartman 	&dev_attr_default_blksize.attr,
4827c69c6be5SGreg Kroah-Hartman 	&dev_attr_default_density.attr,
4828c69c6be5SGreg Kroah-Hartman 	&dev_attr_default_compression.attr,
4829c69c6be5SGreg Kroah-Hartman 	&dev_attr_options.attr,
4830c69c6be5SGreg Kroah-Hartman 	NULL,
4831af23782bSJeff Mahoney };
483205545c92SSeymour, Shane M 
483305545c92SSeymour, Shane M static struct attribute *st_stats_attrs[] = {
483405545c92SSeymour, Shane M 	&dev_attr_read_cnt.attr,
483505545c92SSeymour, Shane M 	&dev_attr_read_byte_cnt.attr,
483605545c92SSeymour, Shane M 	&dev_attr_read_ns.attr,
483705545c92SSeymour, Shane M 	&dev_attr_write_cnt.attr,
483805545c92SSeymour, Shane M 	&dev_attr_write_byte_cnt.attr,
483905545c92SSeymour, Shane M 	&dev_attr_write_ns.attr,
484005545c92SSeymour, Shane M 	&dev_attr_in_flight.attr,
484105545c92SSeymour, Shane M 	&dev_attr_io_ns.attr,
484205545c92SSeymour, Shane M 	&dev_attr_other_cnt.attr,
484305545c92SSeymour, Shane M 	&dev_attr_resid_cnt.attr,
484405545c92SSeymour, Shane M 	NULL,
484505545c92SSeymour, Shane M };
484605545c92SSeymour, Shane M 
484705545c92SSeymour, Shane M static struct attribute_group stats_group = {
484805545c92SSeymour, Shane M 	.name = "stats",
484905545c92SSeymour, Shane M 	.attrs = st_stats_attrs,
485005545c92SSeymour, Shane M };
485105545c92SSeymour, Shane M 
485205545c92SSeymour, Shane M static struct attribute_group st_group = {
485305545c92SSeymour, Shane M 	.attrs = st_dev_attrs,
485405545c92SSeymour, Shane M };
485505545c92SSeymour, Shane M 
485605545c92SSeymour, Shane M static const struct attribute_group *st_dev_groups[] = {
485705545c92SSeymour, Shane M 	&st_group,
485805545c92SSeymour, Shane M 	&stats_group,
485905545c92SSeymour, Shane M 	NULL,
486005545c92SSeymour, Shane M };
4861b174be02SKai Makisara 
48621da177e4SLinus Torvalds /* The following functions may be useful for a larger audience. */
sgl_map_user_pages(struct st_buffer * STbp,const unsigned int max_pages,unsigned long uaddr,size_t count,int rw)48636620742fSFUJITA Tomonori static int sgl_map_user_pages(struct st_buffer *STbp,
48646620742fSFUJITA Tomonori 			      const unsigned int max_pages, unsigned long uaddr,
48656620742fSFUJITA Tomonori 			      size_t count, int rw)
48661da177e4SLinus Torvalds {
486707542b83SJames Bottomley 	unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
486807542b83SJames Bottomley 	unsigned long start = uaddr >> PAGE_SHIFT;
486907542b83SJames Bottomley 	const int nr_pages = end - start;
487008e9cbe7SJohn Hubbard 	int res, i;
48711da177e4SLinus Torvalds 	struct page **pages;
48726620742fSFUJITA Tomonori 	struct rq_map_data *mdata = &STbp->map_data;
48731da177e4SLinus Torvalds 
48741da177e4SLinus Torvalds 	/* User attempted Overflow! */
48751da177e4SLinus Torvalds 	if ((uaddr + count) < uaddr)
48761da177e4SLinus Torvalds 		return -EINVAL;
48771da177e4SLinus Torvalds 
48781da177e4SLinus Torvalds 	/* Too big */
48791da177e4SLinus Torvalds         if (nr_pages > max_pages)
48801da177e4SLinus Torvalds 		return -ENOMEM;
48811da177e4SLinus Torvalds 
48821da177e4SLinus Torvalds 	/* Hmm? */
48831da177e4SLinus Torvalds 	if (count == 0)
48841da177e4SLinus Torvalds 		return 0;
48851da177e4SLinus Torvalds 
48866da2ec56SKees Cook 	pages = kmalloc_array(max_pages, sizeof(*pages), GFP_KERNEL);
48876da2ec56SKees Cook 	if (pages == NULL)
48881da177e4SLinus Torvalds 		return -ENOMEM;
48891da177e4SLinus Torvalds 
48901da177e4SLinus Torvalds         /* Try to fault in all of the necessary pages */
48911da177e4SLinus Torvalds         /* rw==READ means read from drive, write into memory area */
489208e9cbe7SJohn Hubbard 	res = pin_user_pages_fast(uaddr, nr_pages, rw == READ ? FOLL_WRITE : 0,
489373b0140bSIra Weiny 				  pages);
48941da177e4SLinus Torvalds 
48951da177e4SLinus Torvalds 	/* Errors and no page mapped should return here */
48961da177e4SLinus Torvalds 	if (res < nr_pages)
48971da177e4SLinus Torvalds 		goto out_unmap;
48981da177e4SLinus Torvalds 
48991da177e4SLinus Torvalds         for (i=0; i < nr_pages; i++) {
49001da177e4SLinus Torvalds                 /* FIXME: flush superflous for rw==READ,
49011da177e4SLinus Torvalds                  * probably wrong function for rw==WRITE
49021da177e4SLinus Torvalds                  */
49031da177e4SLinus Torvalds 		flush_dcache_page(pages[i]);
49041da177e4SLinus Torvalds         }
49051da177e4SLinus Torvalds 
49066620742fSFUJITA Tomonori 	mdata->offset = uaddr & ~PAGE_MASK;
49076620742fSFUJITA Tomonori 	STbp->mapped_pages = pages;
49081da177e4SLinus Torvalds 
49091da177e4SLinus Torvalds 	return nr_pages;
49101da177e4SLinus Torvalds  out_unmap:
49111da177e4SLinus Torvalds 	if (res > 0) {
491208e9cbe7SJohn Hubbard 		unpin_user_pages(pages, res);
49136bc733e9SHugh Dickins 		res = 0;
49141da177e4SLinus Torvalds 	}
49151da177e4SLinus Torvalds 	kfree(pages);
49161da177e4SLinus Torvalds 	return res;
49171da177e4SLinus Torvalds }
49181da177e4SLinus Torvalds 
49191da177e4SLinus Torvalds 
49201da177e4SLinus Torvalds /* And unmap them... */
sgl_unmap_user_pages(struct st_buffer * STbp,const unsigned int nr_pages,int dirtied)49216620742fSFUJITA Tomonori static int sgl_unmap_user_pages(struct st_buffer *STbp,
49226620742fSFUJITA Tomonori 				const unsigned int nr_pages, int dirtied)
49231da177e4SLinus Torvalds {
492408e9cbe7SJohn Hubbard 	/* FIXME: cache flush missing for rw==READ */
492508e9cbe7SJohn Hubbard 	unpin_user_pages_dirty_lock(STbp->mapped_pages, nr_pages, dirtied);
49261da177e4SLinus Torvalds 
49276620742fSFUJITA Tomonori 	kfree(STbp->mapped_pages);
49286620742fSFUJITA Tomonori 	STbp->mapped_pages = NULL;
49291da177e4SLinus Torvalds 
49301da177e4SLinus Torvalds 	return 0;
49311da177e4SLinus Torvalds }
4932