xref: /linux/drivers/s390/char/tape_3490.c (revision 8934827db5403eae57d4537114a9ff88b0a8460f)
19872dae6SJan Höppner // SPDX-License-Identifier: GPL-2.0
29872dae6SJan Höppner /*
39872dae6SJan Höppner  *    tape device discipline for 3490 tapes.
49872dae6SJan Höppner  *
59872dae6SJan Höppner  *    Copyright IBM Corp. 2001, 2009
69872dae6SJan Höppner  *    Author(s): Carsten Otte <cotte@de.ibm.com>
79872dae6SJan Höppner  *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
89872dae6SJan Höppner  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
99872dae6SJan Höppner  */
109872dae6SJan Höppner 
119872dae6SJan Höppner #define pr_fmt(fmt) "tape_3490: " fmt
129872dae6SJan Höppner 
139872dae6SJan Höppner #include <linux/export.h>
149872dae6SJan Höppner #include <linux/module.h>
159872dae6SJan Höppner #include <linux/init.h>
169872dae6SJan Höppner #include <linux/bio.h>
179872dae6SJan Höppner #include <linux/workqueue.h>
189872dae6SJan Höppner #include <linux/slab.h>
199872dae6SJan Höppner 
209872dae6SJan Höppner #define TAPE_DBF_AREA	tape_3490_dbf
219872dae6SJan Höppner 
229872dae6SJan Höppner #include "tape.h"
239872dae6SJan Höppner #include "tape_std.h"
249872dae6SJan Höppner 
259872dae6SJan Höppner /*
269872dae6SJan Höppner  * Pointer to debug area.
279872dae6SJan Höppner  */
289872dae6SJan Höppner debug_info_t *TAPE_DBF_AREA = NULL;
299872dae6SJan Höppner EXPORT_SYMBOL(TAPE_DBF_AREA);
309872dae6SJan Höppner 
319872dae6SJan Höppner struct tape_3490_block_id {
329872dae6SJan Höppner 	unsigned int	unused		: 10;
339872dae6SJan Höppner 	unsigned int	block		: 22;
349872dae6SJan Höppner };
359872dae6SJan Höppner 
369872dae6SJan Höppner /*
379872dae6SJan Höppner  * Medium sense for 3490 tapes. There is no 'real' medium sense call.
389872dae6SJan Höppner  * So we just do a normal sense.
399872dae6SJan Höppner  */
__tape_3490_medium_sense(struct tape_request * request)409872dae6SJan Höppner static void __tape_3490_medium_sense(struct tape_request *request)
419872dae6SJan Höppner {
429872dae6SJan Höppner 	struct tape_device *device = request->device;
439872dae6SJan Höppner 	unsigned char *sense;
449872dae6SJan Höppner 
459872dae6SJan Höppner 	if (request->rc == 0) {
469872dae6SJan Höppner 		sense = request->cpdata;
479872dae6SJan Höppner 
489872dae6SJan Höppner 		/*
499872dae6SJan Höppner 		 * This isn't quite correct. But since INTERVENTION_REQUIRED
509872dae6SJan Höppner 		 * means that the drive is 'neither ready nor on-line' it is
519872dae6SJan Höppner 		 * only slightly inaccurate to say there is no tape loaded if
529872dae6SJan Höppner 		 * the drive isn't online...
539872dae6SJan Höppner 		 */
549872dae6SJan Höppner 		if (sense[0] & SENSE_INTERVENTION_REQUIRED)
559872dae6SJan Höppner 			tape_med_state_set(device, MS_UNLOADED);
569872dae6SJan Höppner 		else
579872dae6SJan Höppner 			tape_med_state_set(device, MS_LOADED);
589872dae6SJan Höppner 
599872dae6SJan Höppner 		if (sense[1] & SENSE_WRITE_PROTECT)
609872dae6SJan Höppner 			device->tape_generic_status |= GMT_WR_PROT(~0);
619872dae6SJan Höppner 		else
629872dae6SJan Höppner 			device->tape_generic_status &= ~GMT_WR_PROT(~0);
639872dae6SJan Höppner 	} else
649872dae6SJan Höppner 		DBF_EVENT(4, "tape_3490: medium sense failed with rc=%d\n",
659872dae6SJan Höppner 			request->rc);
669872dae6SJan Höppner 	tape_free_request(request);
679872dae6SJan Höppner }
689872dae6SJan Höppner 
tape_3490_medium_sense(struct tape_device * device)699872dae6SJan Höppner static int tape_3490_medium_sense(struct tape_device *device)
709872dae6SJan Höppner {
719872dae6SJan Höppner 	struct tape_request *request;
729872dae6SJan Höppner 	int rc;
739872dae6SJan Höppner 
749872dae6SJan Höppner 	request = tape_alloc_request(1, 32);
759872dae6SJan Höppner 	if (IS_ERR(request)) {
769872dae6SJan Höppner 		DBF_EXCEPTION(6, "MSEN fail\n");
779872dae6SJan Höppner 		return PTR_ERR(request);
789872dae6SJan Höppner 	}
799872dae6SJan Höppner 
809872dae6SJan Höppner 	request->op = TO_MSEN;
819872dae6SJan Höppner 	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
829872dae6SJan Höppner 	rc = tape_do_io_interruptible(device, request);
839872dae6SJan Höppner 	__tape_3490_medium_sense(request);
849872dae6SJan Höppner 	return rc;
859872dae6SJan Höppner }
869872dae6SJan Höppner 
tape_3490_medium_sense_async(struct tape_device * device)879872dae6SJan Höppner static void tape_3490_medium_sense_async(struct tape_device *device)
889872dae6SJan Höppner {
899872dae6SJan Höppner 	struct tape_request *request;
909872dae6SJan Höppner 
919872dae6SJan Höppner 	request = tape_alloc_request(1, 32);
929872dae6SJan Höppner 	if (IS_ERR(request)) {
939872dae6SJan Höppner 		DBF_EXCEPTION(6, "MSEN fail\n");
949872dae6SJan Höppner 		return;
959872dae6SJan Höppner 	}
969872dae6SJan Höppner 
979872dae6SJan Höppner 	request->op = TO_MSEN;
989872dae6SJan Höppner 	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
999872dae6SJan Höppner 	request->callback = (void *) __tape_3490_medium_sense;
1009872dae6SJan Höppner 	request->callback_data = NULL;
1019872dae6SJan Höppner 	tape_do_io_async(device, request);
1029872dae6SJan Höppner }
1039872dae6SJan Höppner 
1049872dae6SJan Höppner struct tape_3490_work {
1059872dae6SJan Höppner 	struct tape_device	*device;
1069872dae6SJan Höppner 	enum tape_op		 op;
1079872dae6SJan Höppner 	struct work_struct	 work;
1089872dae6SJan Höppner };
1099872dae6SJan Höppner 
1109872dae6SJan Höppner /*
1119872dae6SJan Höppner  * These functions are currently used only to schedule a medium_sense for
1129872dae6SJan Höppner  * later execution. This is because we get an interrupt whenever a medium
1139872dae6SJan Höppner  * is inserted but cannot call tape_do_io* from an interrupt context.
1149872dae6SJan Höppner  * Maybe that's useful for other actions we want to start from the
1159872dae6SJan Höppner  * interrupt handler.
1169872dae6SJan Höppner  * Note: the work handler is called by the system work queue. The tape
1179872dae6SJan Höppner  * commands started by the handler need to be asynchrounous, otherwise
1189872dae6SJan Höppner  * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
1199872dae6SJan Höppner  */
1209872dae6SJan Höppner static void
tape_3490_work_handler(struct work_struct * work)1219872dae6SJan Höppner tape_3490_work_handler(struct work_struct *work)
1229872dae6SJan Höppner {
1239872dae6SJan Höppner 	struct tape_3490_work *p =
1249872dae6SJan Höppner 		container_of(work, struct tape_3490_work, work);
1259872dae6SJan Höppner 	struct tape_device *device = p->device;
1269872dae6SJan Höppner 
1279872dae6SJan Höppner 	switch(p->op) {
1289872dae6SJan Höppner 		case TO_MSEN:
1299872dae6SJan Höppner 			tape_3490_medium_sense_async(device);
1309872dae6SJan Höppner 			break;
1319872dae6SJan Höppner 		default:
1329872dae6SJan Höppner 			DBF_EVENT(3, "T3490: internal error: unknown work\n");
1339872dae6SJan Höppner 	}
1349872dae6SJan Höppner 	tape_put_device(device);
1359872dae6SJan Höppner 	kfree(p);
1369872dae6SJan Höppner }
1379872dae6SJan Höppner 
1389872dae6SJan Höppner static int
tape_3490_schedule_work(struct tape_device * device,enum tape_op op)1399872dae6SJan Höppner tape_3490_schedule_work(struct tape_device *device, enum tape_op op)
1409872dae6SJan Höppner {
1419872dae6SJan Höppner 	struct tape_3490_work *p;
1429872dae6SJan Höppner 
143*69050f8dSKees Cook 	if ((p = kzalloc_obj(*p, GFP_ATOMIC)) == NULL)
1449872dae6SJan Höppner 		return -ENOMEM;
1459872dae6SJan Höppner 
1469872dae6SJan Höppner 	INIT_WORK(&p->work, tape_3490_work_handler);
1479872dae6SJan Höppner 
1489872dae6SJan Höppner 	p->device = tape_get_device(device);
1499872dae6SJan Höppner 	p->op     = op;
1509872dae6SJan Höppner 
1519872dae6SJan Höppner 	schedule_work(&p->work);
1529872dae6SJan Höppner 	return 0;
1539872dae6SJan Höppner }
1549872dae6SJan Höppner 
1559872dae6SJan Höppner /*
1569872dae6SJan Höppner  * Done Handler is called when dev stat = DEVICE-END (successful operation)
1579872dae6SJan Höppner  */
1589872dae6SJan Höppner static inline int
tape_3490_done(struct tape_request * request)1599872dae6SJan Höppner tape_3490_done(struct tape_request *request)
1609872dae6SJan Höppner {
1619872dae6SJan Höppner 	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
1629872dae6SJan Höppner 	return TAPE_IO_SUCCESS;
1639872dae6SJan Höppner }
1649872dae6SJan Höppner 
1659872dae6SJan Höppner static inline int
tape_3490_erp_failed(struct tape_request * request,int rc)1669872dae6SJan Höppner tape_3490_erp_failed(struct tape_request *request, int rc)
1679872dae6SJan Höppner {
1689872dae6SJan Höppner 	DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n",
1699872dae6SJan Höppner 		  tape_op_verbose[request->op], rc);
1709872dae6SJan Höppner 	return rc;
1719872dae6SJan Höppner }
1729872dae6SJan Höppner 
1739872dae6SJan Höppner static inline int
tape_3490_erp_succeeded(struct tape_request * request)1749872dae6SJan Höppner tape_3490_erp_succeeded(struct tape_request *request)
1759872dae6SJan Höppner {
1769872dae6SJan Höppner 	DBF_EVENT(3, "Error Recovery successful for %s\n",
1779872dae6SJan Höppner 		  tape_op_verbose[request->op]);
1789872dae6SJan Höppner 	return tape_3490_done(request);
1799872dae6SJan Höppner }
1809872dae6SJan Höppner 
1819872dae6SJan Höppner static inline int
tape_3490_erp_retry(struct tape_request * request)1829872dae6SJan Höppner tape_3490_erp_retry(struct tape_request *request)
1839872dae6SJan Höppner {
1849872dae6SJan Höppner 	DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]);
1859872dae6SJan Höppner 	return TAPE_IO_RETRY;
1869872dae6SJan Höppner }
1879872dae6SJan Höppner 
1889872dae6SJan Höppner /*
1899872dae6SJan Höppner  * This function is called, when no request is outstanding and we get an
1909872dae6SJan Höppner  * interrupt
1919872dae6SJan Höppner  */
1929872dae6SJan Höppner static int
tape_3490_unsolicited_irq(struct tape_device * device,struct irb * irb)1939872dae6SJan Höppner tape_3490_unsolicited_irq(struct tape_device *device, struct irb *irb)
1949872dae6SJan Höppner {
1959872dae6SJan Höppner 	if (irb->scsw.cmd.dstat == 0x85) { /* READY */
1969872dae6SJan Höppner 		/* A medium was inserted in the drive. */
1979872dae6SJan Höppner 		DBF_EVENT(6, "xuud med\n");
1989872dae6SJan Höppner 		tape_3490_schedule_work(device, TO_MSEN);
1999872dae6SJan Höppner 	} else {
2009872dae6SJan Höppner 		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
2019872dae6SJan Höppner 		tape_dump_sense_dbf(device, NULL, irb);
2029872dae6SJan Höppner 	}
2039872dae6SJan Höppner 	return TAPE_IO_SUCCESS;
2049872dae6SJan Höppner }
2059872dae6SJan Höppner 
2069872dae6SJan Höppner static int
tape_3490_erp_bug(struct tape_device * device,struct tape_request * request,struct irb * irb,int no)2079872dae6SJan Höppner tape_3490_erp_bug(struct tape_device *device, struct tape_request *request,
2089872dae6SJan Höppner 		  struct irb *irb, int no)
2099872dae6SJan Höppner {
2109872dae6SJan Höppner 	if (request->op != TO_ASSIGN) {
2119872dae6SJan Höppner 		dev_err(&device->cdev->dev, "An unexpected condition %d "
2129872dae6SJan Höppner 			"occurred in tape error recovery\n", no);
2139872dae6SJan Höppner 		tape_dump_sense_dbf(device, request, irb);
2149872dae6SJan Höppner 	}
2159872dae6SJan Höppner 	return tape_3490_erp_failed(request, -EIO);
2169872dae6SJan Höppner }
2179872dae6SJan Höppner 
2189872dae6SJan Höppner /*
2199872dae6SJan Höppner  * Handle data overrun between cu and drive. The channel speed might
2209872dae6SJan Höppner  * be too slow.
2219872dae6SJan Höppner  */
2229872dae6SJan Höppner static int
tape_3490_erp_overrun(struct tape_device * device,struct tape_request * request,struct irb * irb)2239872dae6SJan Höppner tape_3490_erp_overrun(struct tape_device *device, struct tape_request *request,
2249872dae6SJan Höppner 		      struct irb *irb)
2259872dae6SJan Höppner {
2269872dae6SJan Höppner 	if (irb->ecw[3] == 0x40) {
2279872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "A data overrun occurred between"
2289872dae6SJan Höppner 			" the control unit and tape unit\n");
2299872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
2309872dae6SJan Höppner 	}
2319872dae6SJan Höppner 	return tape_3490_erp_bug(device, request, irb, -1);
2329872dae6SJan Höppner }
2339872dae6SJan Höppner 
2349872dae6SJan Höppner /*
2359872dae6SJan Höppner  * Handle record sequence error.
2369872dae6SJan Höppner  */
2379872dae6SJan Höppner static int
tape_3490_erp_sequence(struct tape_device * device,struct tape_request * request,struct irb * irb)2389872dae6SJan Höppner tape_3490_erp_sequence(struct tape_device *device,
2399872dae6SJan Höppner 		       struct tape_request *request, struct irb *irb)
2409872dae6SJan Höppner {
2419872dae6SJan Höppner 	if (irb->ecw[3] == 0x41) {
2429872dae6SJan Höppner 		/*
2439872dae6SJan Höppner 		 * cu detected incorrect block-id sequence on tape.
2449872dae6SJan Höppner 		 */
2459872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The block ID sequence on the "
2469872dae6SJan Höppner 			"tape is incorrect\n");
2479872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
2489872dae6SJan Höppner 	}
2499872dae6SJan Höppner 	/*
2509872dae6SJan Höppner 	 * Record sequence error bit is set, but erpa does not
2519872dae6SJan Höppner 	 * show record sequence error.
2529872dae6SJan Höppner 	 */
2539872dae6SJan Höppner 	return tape_3490_erp_bug(device, request, irb, -2);
2549872dae6SJan Höppner }
2559872dae6SJan Höppner 
2569872dae6SJan Höppner /*
2579872dae6SJan Höppner  * This function analyses the tape's sense-data in case of a unit-check.
2589872dae6SJan Höppner  * If possible, it tries to recover from the error. Else the user is
2599872dae6SJan Höppner  * informed about the problem.
2609872dae6SJan Höppner  */
2619872dae6SJan Höppner static int
tape_3490_unit_check(struct tape_device * device,struct tape_request * request,struct irb * irb)2629872dae6SJan Höppner tape_3490_unit_check(struct tape_device *device, struct tape_request *request,
2639872dae6SJan Höppner 		     struct irb *irb)
2649872dae6SJan Höppner {
2659872dae6SJan Höppner 	int inhibit_cu_recovery;
2669872dae6SJan Höppner 	__u8* sense;
2679872dae6SJan Höppner 
2689872dae6SJan Höppner 	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
2699872dae6SJan Höppner 	sense = irb->ecw;
2709872dae6SJan Höppner 
2719872dae6SJan Höppner 	if (
2729872dae6SJan Höppner 		sense[0] & SENSE_COMMAND_REJECT &&
2739872dae6SJan Höppner 		sense[1] & SENSE_WRITE_PROTECT
2749872dae6SJan Höppner 	) {
2759872dae6SJan Höppner 		if (
2769872dae6SJan Höppner 			request->op == TO_DSE ||
2779872dae6SJan Höppner 			request->op == TO_WRI ||
2789872dae6SJan Höppner 			request->op == TO_WTM
2799872dae6SJan Höppner 		) {
2809872dae6SJan Höppner 			/* medium is write protected */
2819872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EACCES);
2829872dae6SJan Höppner 		} else {
2839872dae6SJan Höppner 			return tape_3490_erp_bug(device, request, irb, -3);
2849872dae6SJan Höppner 		}
2859872dae6SJan Höppner 	}
2869872dae6SJan Höppner 
2879872dae6SJan Höppner 	/*
2889872dae6SJan Höppner 	 * Special cases for various tape-states when reaching
2899872dae6SJan Höppner 	 * end of recorded area
2909872dae6SJan Höppner 	 *
2919872dae6SJan Höppner 	 * FIXME: Maybe a special case of the special case:
2929872dae6SJan Höppner 	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
2939872dae6SJan Höppner 	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
2949872dae6SJan Höppner 	 *        sense[3] == 0x47 (Volume Fenced)
2959872dae6SJan Höppner 	 *
2969872dae6SJan Höppner 	 *        This was caused by continued FSF or FSR after an
2979872dae6SJan Höppner 	 *        'End Of Data'.
2989872dae6SJan Höppner 	 */
2999872dae6SJan Höppner 	if ((
3009872dae6SJan Höppner 		sense[0] == SENSE_DATA_CHECK      ||
3019872dae6SJan Höppner 		sense[0] == SENSE_EQUIPMENT_CHECK ||
3029872dae6SJan Höppner 		sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK)
3039872dae6SJan Höppner 	) && (
3049872dae6SJan Höppner 		sense[1] == SENSE_DRIVE_ONLINE ||
3059872dae6SJan Höppner 		sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE)
3069872dae6SJan Höppner 	)) {
3079872dae6SJan Höppner 		switch (request->op) {
3089872dae6SJan Höppner 		/*
3099872dae6SJan Höppner 		 * sense[0] == SENSE_DATA_CHECK   &&
3109872dae6SJan Höppner 		 * sense[1] == SENSE_DRIVE_ONLINE
3119872dae6SJan Höppner 		 * sense[3] == 0x36 (End Of Data)
3129872dae6SJan Höppner 		 *
3139872dae6SJan Höppner 		 * Further seeks might return a 'Volume Fenced'.
3149872dae6SJan Höppner 		 */
3159872dae6SJan Höppner 		case TO_FSF:
3169872dae6SJan Höppner 		case TO_FSB:
3179872dae6SJan Höppner 			/* Trying to seek beyond end of recorded area */
3189872dae6SJan Höppner 			return tape_3490_erp_failed(request, -ENOSPC);
3199872dae6SJan Höppner 		case TO_BSB:
3209872dae6SJan Höppner 			return tape_3490_erp_retry(request);
3219872dae6SJan Höppner 
3229872dae6SJan Höppner 		/*
3239872dae6SJan Höppner 		 * sense[0] == SENSE_DATA_CHECK   &&
3249872dae6SJan Höppner 		 * sense[1] == SENSE_DRIVE_ONLINE &&
3259872dae6SJan Höppner 		 * sense[3] == 0x36 (End Of Data)
3269872dae6SJan Höppner 		 */
3279872dae6SJan Höppner 		case TO_LBL:
3289872dae6SJan Höppner 			/* Block could not be located. */
3299872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
3309872dae6SJan Höppner 
3319872dae6SJan Höppner 		case TO_RFO:
3329872dae6SJan Höppner 			/* Read beyond end of recorded area -> 0 bytes read */
3339872dae6SJan Höppner 			return tape_3490_erp_failed(request, 0);
3349872dae6SJan Höppner 
3359872dae6SJan Höppner 		/*
3369872dae6SJan Höppner 		 * sense[0] == SENSE_EQUIPMENT_CHECK &&
3379872dae6SJan Höppner 		 * sense[1] == SENSE_DRIVE_ONLINE    &&
3389872dae6SJan Höppner 		 * sense[3] == 0x38 (Physical End Of Volume)
3399872dae6SJan Höppner 		 */
3409872dae6SJan Höppner 		case TO_WRI:
3419872dae6SJan Höppner 			/* Writing at physical end of volume */
3429872dae6SJan Höppner 			return tape_3490_erp_failed(request, -ENOSPC);
3439872dae6SJan Höppner 		default:
3449872dae6SJan Höppner 			return tape_3490_erp_failed(request, 0);
3459872dae6SJan Höppner 		}
3469872dae6SJan Höppner 	}
3479872dae6SJan Höppner 
3489872dae6SJan Höppner 	/* Sensing special bits */
3499872dae6SJan Höppner 	if (sense[0] & SENSE_BUS_OUT_CHECK)
3509872dae6SJan Höppner 		return tape_3490_erp_retry(request);
3519872dae6SJan Höppner 
3529872dae6SJan Höppner 	if (sense[0] & SENSE_DATA_CHECK) {
3539872dae6SJan Höppner 		/*
3549872dae6SJan Höppner 		 * hardware failure, damaged tape or improper
3559872dae6SJan Höppner 		 * operating conditions
3569872dae6SJan Höppner 		 */
3579872dae6SJan Höppner 		switch (sense[3]) {
3589872dae6SJan Höppner 		case 0x23:
3599872dae6SJan Höppner 			/* a read data check occurred */
3609872dae6SJan Höppner 			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
3619872dae6SJan Höppner 			    inhibit_cu_recovery)
3629872dae6SJan Höppner 				// data check is not permanent, may be
3639872dae6SJan Höppner 				// recovered. We always use async-mode with
3649872dae6SJan Höppner 				// cu-recovery, so this should *never* happen.
3659872dae6SJan Höppner 				return tape_3490_erp_bug(device, request,
3669872dae6SJan Höppner 							 irb, -4);
3679872dae6SJan Höppner 
3689872dae6SJan Höppner 			/* data check is permanent, CU recovery has failed */
3699872dae6SJan Höppner 			dev_warn (&device->cdev->dev, "A read error occurred "
3709872dae6SJan Höppner 				"that cannot be recovered\n");
3719872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
3729872dae6SJan Höppner 		case 0x25:
3739872dae6SJan Höppner 			// a write data check occurred
3749872dae6SJan Höppner 			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
3759872dae6SJan Höppner 			    inhibit_cu_recovery)
3769872dae6SJan Höppner 				// data check is not permanent, may be
3779872dae6SJan Höppner 				// recovered. We always use async-mode with
3789872dae6SJan Höppner 				// cu-recovery, so this should *never* happen.
3799872dae6SJan Höppner 				return tape_3490_erp_bug(device, request,
3809872dae6SJan Höppner 							 irb, -5);
3819872dae6SJan Höppner 
3829872dae6SJan Höppner 			// data check is permanent, cu-recovery has failed
3839872dae6SJan Höppner 			dev_warn (&device->cdev->dev, "A write error on the "
3849872dae6SJan Höppner 				"tape cannot be recovered\n");
3859872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
3869872dae6SJan Höppner 		case 0x28:
3879872dae6SJan Höppner 			/* ID-Mark at tape start couldn't be written */
3889872dae6SJan Höppner 			dev_warn (&device->cdev->dev, "Writing the ID-mark "
3899872dae6SJan Höppner 				"failed\n");
3909872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
3919872dae6SJan Höppner 		case 0x31:
3929872dae6SJan Höppner 			/* Tape void. Tried to read beyond end of device. */
3939872dae6SJan Höppner 			dev_warn (&device->cdev->dev, "Reading the tape beyond"
3949872dae6SJan Höppner 				" the end of the recorded area failed\n");
3959872dae6SJan Höppner 			return tape_3490_erp_failed(request, -ENOSPC);
3969872dae6SJan Höppner 		case 0x41:
3979872dae6SJan Höppner 			/* Record sequence error. */
3989872dae6SJan Höppner 			dev_warn (&device->cdev->dev, "The tape contains an "
3999872dae6SJan Höppner 				"incorrect block ID sequence\n");
4009872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
4019872dae6SJan Höppner 		}
4029872dae6SJan Höppner 	}
4039872dae6SJan Höppner 
4049872dae6SJan Höppner 	if (sense[0] & SENSE_OVERRUN)
4059872dae6SJan Höppner 		return tape_3490_erp_overrun(device, request, irb);
4069872dae6SJan Höppner 
4079872dae6SJan Höppner 	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
4089872dae6SJan Höppner 		return tape_3490_erp_sequence(device, request, irb);
4099872dae6SJan Höppner 
4109872dae6SJan Höppner 	/* Sensing erpa codes */
4119872dae6SJan Höppner 	switch (sense[3]) {
4129872dae6SJan Höppner 	case 0x00:
4139872dae6SJan Höppner 		/* Unit check with erpa code 0. Report and ignore. */
4149872dae6SJan Höppner 		return TAPE_IO_SUCCESS;
4159872dae6SJan Höppner 	case 0x27:
4169872dae6SJan Höppner 		/*
4179872dae6SJan Höppner 		 * Command reject. May indicate illegal channel program or
4189872dae6SJan Höppner 		 * buffer over/underrun. Since all channel programs are
4199872dae6SJan Höppner 		 * issued by this driver and ought be correct, we assume a
4209872dae6SJan Höppner 		 * over/underrun situation and retry the channel program.
4219872dae6SJan Höppner 		 */
4229872dae6SJan Höppner 		return tape_3490_erp_retry(request);
4239872dae6SJan Höppner 	case 0x29:
4249872dae6SJan Höppner 		/*
4259872dae6SJan Höppner 		 * Function incompatible. Either the tape is idrc compressed
4269872dae6SJan Höppner 		 * but the hardware isn't capable to do idrc, or a perform
4279872dae6SJan Höppner 		 * subsystem func is issued and the CU is not on-line.
4289872dae6SJan Höppner 		 */
4299872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4309872dae6SJan Höppner 	case 0x2b:
4319872dae6SJan Höppner 		/*
4329872dae6SJan Höppner 		 * Environmental data present. Indicates either unload
4339872dae6SJan Höppner 		 * completed ok or read buffered log command completed ok.
4349872dae6SJan Höppner 		 */
4359872dae6SJan Höppner 		if (request->op == TO_RUN) {
4369872dae6SJan Höppner 			/* Rewind unload completed ok. */
4379872dae6SJan Höppner 			tape_med_state_set(device, MS_UNLOADED);
4389872dae6SJan Höppner 			return tape_3490_erp_succeeded(request);
4399872dae6SJan Höppner 		}
4409872dae6SJan Höppner 		/* tape_3490 doesn't use read buffered log commands. */
4419872dae6SJan Höppner 		return tape_3490_erp_bug(device, request, irb, sense[3]);
4429872dae6SJan Höppner 	case 0x2c:
4439872dae6SJan Höppner 		/*
4449872dae6SJan Höppner 		 * Permanent equipment check. CU has tried recovery, but
4459872dae6SJan Höppner 		 * did not succeed.
4469872dae6SJan Höppner 		 */
4479872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4489872dae6SJan Höppner 	case 0x2d:
4499872dae6SJan Höppner 		/* Data security erase failure. */
4509872dae6SJan Höppner 		if (request->op == TO_DSE)
4519872dae6SJan Höppner 			return tape_3490_erp_failed(request, -EIO);
4529872dae6SJan Höppner 		/* Data security erase failure, but no such command issued. */
4539872dae6SJan Höppner 		return tape_3490_erp_bug(device, request, irb, sense[3]);
4549872dae6SJan Höppner 	case 0x2e:
4559872dae6SJan Höppner 		/*
4569872dae6SJan Höppner 		 * Not capable. This indicates either that the drive fails
4579872dae6SJan Höppner 		 * reading the format id mark or that format specified
4589872dae6SJan Höppner 		 * is not supported by the drive.
4599872dae6SJan Höppner 		 */
4609872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The tape unit cannot process "
4619872dae6SJan Höppner 			"the tape format\n");
4629872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EMEDIUMTYPE);
4639872dae6SJan Höppner 	case 0x30:
4649872dae6SJan Höppner 		/* The medium is write protected. */
4659872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The tape medium is write-"
4669872dae6SJan Höppner 			"protected\n");
4679872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EACCES);
4689872dae6SJan Höppner 	case 0x35:
4699872dae6SJan Höppner 		/*
4709872dae6SJan Höppner 		 * Drive equipment check. One of the following:
4719872dae6SJan Höppner 		 * - cu cannot recover from a drive detected error
4729872dae6SJan Höppner 		 * - a check code message is shown on drive display
4739872dae6SJan Höppner 		 * - the cartridge loader does not respond correctly
4749872dae6SJan Höppner 		 * - a failure occurs during an index, load, or unload cycle
4759872dae6SJan Höppner 		 */
4769872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "An equipment check has occurred"
4779872dae6SJan Höppner 			" on the tape unit\n");
4789872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4799872dae6SJan Höppner 	case 0x36:
4809872dae6SJan Höppner 		/* End of data. */
4819872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4829872dae6SJan Höppner 	case 0x38:
4839872dae6SJan Höppner 		/*
4849872dae6SJan Höppner 		 * Physical end of tape. A read/write operation reached
4859872dae6SJan Höppner 		 * the physical end of tape.
4869872dae6SJan Höppner 		 */
4879872dae6SJan Höppner 		if (request->op==TO_WRI ||
4889872dae6SJan Höppner 		    request->op==TO_DSE ||
4899872dae6SJan Höppner 		    request->op==TO_WTM)
4909872dae6SJan Höppner 			return tape_3490_erp_failed(request, -ENOSPC);
4919872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4929872dae6SJan Höppner 	case 0x39:
4939872dae6SJan Höppner 		/* Backward at Beginning of tape. */
4949872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
4959872dae6SJan Höppner 	case 0x42:
4969872dae6SJan Höppner 		/*
4979872dae6SJan Höppner 		 * Degraded mode. A condition that can cause degraded
4989872dae6SJan Höppner 		 * performance is detected.
4999872dae6SJan Höppner 		 */
5009872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The tape subsystem is running "
5019872dae6SJan Höppner 			"in degraded mode\n");
5029872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5039872dae6SJan Höppner 	case 0x43:
5049872dae6SJan Höppner 		/* Drive not ready. */
5059872dae6SJan Höppner 		tape_med_state_set(device, MS_UNLOADED);
5069872dae6SJan Höppner 		/* Some commands commands are successful even in this case */
5079872dae6SJan Höppner 		if (sense[1] & SENSE_DRIVE_ONLINE) {
5089872dae6SJan Höppner 			switch(request->op) {
5099872dae6SJan Höppner 				case TO_ASSIGN:
5109872dae6SJan Höppner 				case TO_UNASSIGN:
5119872dae6SJan Höppner 				case TO_DIS:
5129872dae6SJan Höppner 				case TO_NOP:
5139872dae6SJan Höppner 					return tape_3490_done(request);
5149872dae6SJan Höppner 					break;
5159872dae6SJan Höppner 				default:
5169872dae6SJan Höppner 					break;
5179872dae6SJan Höppner 			}
5189872dae6SJan Höppner 		}
5199872dae6SJan Höppner 		return tape_3490_erp_failed(request, -ENOMEDIUM);
5209872dae6SJan Höppner 	case 0x44:
5219872dae6SJan Höppner 		/* Locate Block unsuccessful. */
5229872dae6SJan Höppner 		if (request->op != TO_BLOCK && request->op != TO_LBL)
5239872dae6SJan Höppner 			/* No locate block was issued. */
5249872dae6SJan Höppner 			return tape_3490_erp_bug(device, request,
5259872dae6SJan Höppner 						 irb, sense[3]);
5269872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
5279872dae6SJan Höppner 	case 0x45:
5289872dae6SJan Höppner 		/* The drive is assigned to a different channel path. */
5299872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The tape unit is already "
5309872dae6SJan Höppner 			"assigned\n");
5319872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
5329872dae6SJan Höppner 	case 0x47:
5339872dae6SJan Höppner 		/* Volume fenced. CU reports volume integrity is lost. */
5349872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "The control unit has fenced "
5359872dae6SJan Höppner 			"access to the tape volume\n");
5369872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
5379872dae6SJan Höppner 	case 0x48:
5389872dae6SJan Höppner 		/* Log sense data and retry request. */
5399872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5409872dae6SJan Höppner 	case 0x4d:
5419872dae6SJan Höppner 		/*
5429872dae6SJan Höppner 		 * Resetting event received. Since the driver does
5439872dae6SJan Höppner 		 * not support resetting event recovery (which has to
5449872dae6SJan Höppner 		 * be handled by the I/O Layer), retry our command.
5459872dae6SJan Höppner 		 */
5469872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5479872dae6SJan Höppner 	case 0x4e:
5489872dae6SJan Höppner 		/*
5499872dae6SJan Höppner 		 * Maximum block size exceeded. This indicates, that
5509872dae6SJan Höppner 		 * the block to be written is larger than allowed for
5519872dae6SJan Höppner 		 * buffered mode.
5529872dae6SJan Höppner 		 */
5539872dae6SJan Höppner 		dev_warn (&device->cdev->dev,
5549872dae6SJan Höppner 			  "The maximum block size for buffered mode is exceeded\n");
5559872dae6SJan Höppner 		return tape_3490_erp_failed(request, -ENOBUFS);
5569872dae6SJan Höppner 	case 0x50:
5579872dae6SJan Höppner 		/*
5589872dae6SJan Höppner 		 * Read buffered log (Overflow). CU is running in extended
5599872dae6SJan Höppner 		 * buffered log mode, and a counter overflows. This should
5609872dae6SJan Höppner 		 * never happen, since we're never running in extended
5619872dae6SJan Höppner 		 * buffered log mode.
5629872dae6SJan Höppner 		 */
5639872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5649872dae6SJan Höppner 	case 0x51:
5659872dae6SJan Höppner 		/*
5669872dae6SJan Höppner 		 * Read buffered log (EOV). EOF processing occurs while the
5679872dae6SJan Höppner 		 * CU is in extended buffered log mode. This should never
5689872dae6SJan Höppner 		 * happen, since we're never running in extended buffered
5699872dae6SJan Höppner 		 * log mode.
5709872dae6SJan Höppner 		 */
5719872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5729872dae6SJan Höppner 	case 0x52:
5739872dae6SJan Höppner 		/* End of Volume complete. Rewind unload completed ok. */
5749872dae6SJan Höppner 		if (request->op == TO_RUN) {
5759872dae6SJan Höppner 			tape_med_state_set(device, MS_UNLOADED);
5769872dae6SJan Höppner 			return tape_3490_erp_succeeded(request);
5779872dae6SJan Höppner 		}
5789872dae6SJan Höppner 		return tape_3490_erp_bug(device, request, irb, sense[3]);
5799872dae6SJan Höppner 	case 0x53:
5809872dae6SJan Höppner 		/* Global command intercept. */
5819872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5829872dae6SJan Höppner 	case 0x54:
5839872dae6SJan Höppner 		/* Channel interface recovery (temporary). */
5849872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5859872dae6SJan Höppner 	case 0x55:
5869872dae6SJan Höppner 		/* Channel interface recovery (permanent). */
5879872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "A channel interface error cannot be"
5889872dae6SJan Höppner 			" recovered\n");
5899872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
5909872dae6SJan Höppner 	case 0x56:
5919872dae6SJan Höppner 		/* Channel protocol error. */
5929872dae6SJan Höppner 		dev_warn (&device->cdev->dev, "A channel protocol error "
5939872dae6SJan Höppner 			"occurred\n");
5949872dae6SJan Höppner 		return tape_3490_erp_failed(request, -EIO);
5959872dae6SJan Höppner 	case 0x57:
5969872dae6SJan Höppner 		/* Global status intercept. */
5979872dae6SJan Höppner 		return tape_3490_erp_retry(request);
5989872dae6SJan Höppner 		/* The following erpas should have been covered earlier. */
5999872dae6SJan Höppner 	case 0x23: /* Read data check. */
6009872dae6SJan Höppner 	case 0x25: /* Write data check. */
6019872dae6SJan Höppner 	case 0x28: /* Write id mark check. */
6029872dae6SJan Höppner 	case 0x31: /* Tape void. */
6039872dae6SJan Höppner 	case 0x40: /* Overrun error. */
6049872dae6SJan Höppner 	case 0x41: /* Record sequence error. */
6059872dae6SJan Höppner 		/* All other erpas are reserved for future use. */
6069872dae6SJan Höppner 	default:
6079872dae6SJan Höppner 		return tape_3490_erp_bug(device, request, irb, sense[3]);
6089872dae6SJan Höppner 	}
6099872dae6SJan Höppner }
6109872dae6SJan Höppner 
6119872dae6SJan Höppner /*
6129872dae6SJan Höppner  * 3490 interrupt handler
6139872dae6SJan Höppner  */
6149872dae6SJan Höppner static int
tape_3490_irq(struct tape_device * device,struct tape_request * request,struct irb * irb)6159872dae6SJan Höppner tape_3490_irq(struct tape_device *device, struct tape_request *request,
6169872dae6SJan Höppner 	      struct irb *irb)
6179872dae6SJan Höppner {
6189872dae6SJan Höppner 	if (request == NULL)
6199872dae6SJan Höppner 		return tape_3490_unsolicited_irq(device, irb);
6209872dae6SJan Höppner 
6219872dae6SJan Höppner 	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
6229872dae6SJan Höppner 	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
6239872dae6SJan Höppner 	    (request->op == TO_WRI)) {
6249872dae6SJan Höppner 		/* Write at end of volume */
6259872dae6SJan Höppner 		return tape_3490_erp_failed(request, -ENOSPC);
6269872dae6SJan Höppner 	}
6279872dae6SJan Höppner 
6289872dae6SJan Höppner 	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
6299872dae6SJan Höppner 		return tape_3490_unit_check(device, request, irb);
6309872dae6SJan Höppner 
6319872dae6SJan Höppner 	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
6329872dae6SJan Höppner 		/*
6339872dae6SJan Höppner 		 * A unit exception occurs on skipping over a tapemark block.
6349872dae6SJan Höppner 		 */
6359872dae6SJan Höppner 		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
6369872dae6SJan Höppner 			if (request->op == TO_BSB || request->op == TO_FSB)
6379872dae6SJan Höppner 				request->rescnt++;
6389872dae6SJan Höppner 			else
6399872dae6SJan Höppner 				DBF_EVENT(5, "Unit Exception!\n");
6409872dae6SJan Höppner 		}
6419872dae6SJan Höppner 		return tape_3490_done(request);
6429872dae6SJan Höppner 	}
6439872dae6SJan Höppner 
6449872dae6SJan Höppner 	DBF_EVENT(6, "xunknownirq\n");
6459872dae6SJan Höppner 	tape_dump_sense_dbf(device, request, irb);
6469872dae6SJan Höppner 	return TAPE_IO_STOP;
6479872dae6SJan Höppner }
6489872dae6SJan Höppner 
6499872dae6SJan Höppner static int
tape_3490_setup_device(struct tape_device * device)6509872dae6SJan Höppner tape_3490_setup_device(struct tape_device * device)
6519872dae6SJan Höppner {
6529872dae6SJan Höppner 	int rc;
6539872dae6SJan Höppner 
6549872dae6SJan Höppner 	DBF_EVENT(6, "3490 device setup\n");
6559872dae6SJan Höppner 	if ((rc = tape_std_assign(device)) == 0) {
6569872dae6SJan Höppner 		if ((rc = tape_3490_medium_sense(device)) != 0) {
6579872dae6SJan Höppner 			DBF_LH(3, "3490 medium sense returned %d\n", rc);
6589872dae6SJan Höppner 		}
6599872dae6SJan Höppner 	}
6609872dae6SJan Höppner 	return rc;
6619872dae6SJan Höppner }
6629872dae6SJan Höppner 
6639872dae6SJan Höppner static void
tape_3490_cleanup_device(struct tape_device * device)6649872dae6SJan Höppner tape_3490_cleanup_device(struct tape_device *device)
6659872dae6SJan Höppner {
6669872dae6SJan Höppner 	tape_std_unassign(device);
6679872dae6SJan Höppner }
6689872dae6SJan Höppner 
6699872dae6SJan Höppner 
6709872dae6SJan Höppner /*
6719872dae6SJan Höppner  * MTTELL: Tell block. Return the number of block relative to current file.
6729872dae6SJan Höppner  */
6739872dae6SJan Höppner static int
tape_3490_mttell(struct tape_device * device,int mt_count)6749872dae6SJan Höppner tape_3490_mttell(struct tape_device *device, int mt_count)
6759872dae6SJan Höppner {
6769872dae6SJan Höppner 	struct {
6779872dae6SJan Höppner 		struct tape_3490_block_id	cbid;
6789872dae6SJan Höppner 		struct tape_3490_block_id	dbid;
6799872dae6SJan Höppner 	} __attribute__ ((packed)) block_id;
6809872dae6SJan Höppner 	int rc;
6819872dae6SJan Höppner 
6829872dae6SJan Höppner 	rc = tape_std_read_block_id(device, (__u64 *) &block_id);
6839872dae6SJan Höppner 	if (rc)
6849872dae6SJan Höppner 		return rc;
6859872dae6SJan Höppner 
6869872dae6SJan Höppner 	return block_id.cbid.block;
6879872dae6SJan Höppner }
6889872dae6SJan Höppner 
6899872dae6SJan Höppner /*
6909872dae6SJan Höppner  * MTSEEK: seek to the specified block.
6919872dae6SJan Höppner  */
6929872dae6SJan Höppner static int
tape_3490_mtseek(struct tape_device * device,int mt_count)6939872dae6SJan Höppner tape_3490_mtseek(struct tape_device *device, int mt_count)
6949872dae6SJan Höppner {
6959872dae6SJan Höppner 	struct tape_request *request;
6969872dae6SJan Höppner 	struct tape_3490_block_id *	bid;
6979872dae6SJan Höppner 
6989872dae6SJan Höppner 	if (mt_count > 0x3fffff) {
6999872dae6SJan Höppner 		DBF_EXCEPTION(6, "xsee parm\n");
7009872dae6SJan Höppner 		return -EINVAL;
7019872dae6SJan Höppner 	}
7029872dae6SJan Höppner 	request = tape_alloc_request(3, 4);
7039872dae6SJan Höppner 	if (IS_ERR(request))
7049872dae6SJan Höppner 		return PTR_ERR(request);
7059872dae6SJan Höppner 
7069872dae6SJan Höppner 	/* setup ccws */
7079872dae6SJan Höppner 	request->op = TO_LBL;
7089872dae6SJan Höppner 	bid         = (struct tape_3490_block_id *) request->cpdata;
7099872dae6SJan Höppner 	bid->block  = mt_count;
7109872dae6SJan Höppner 
7119872dae6SJan Höppner 	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
7129872dae6SJan Höppner 	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
7139872dae6SJan Höppner 	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
7149872dae6SJan Höppner 
7159872dae6SJan Höppner 	/* execute it */
7169872dae6SJan Höppner 	return tape_do_io_free(device, request);
7179872dae6SJan Höppner }
7189872dae6SJan Höppner 
7199872dae6SJan Höppner /*
7209872dae6SJan Höppner  * List of 3490 tape commands.
7219872dae6SJan Höppner  */
7229872dae6SJan Höppner static tape_mtop_fn tape_3490_mtop[TAPE_NR_MTOPS] = {
7239872dae6SJan Höppner 	[MTRESET]	 = tape_std_mtreset,
7249872dae6SJan Höppner 	[MTFSF]		 = tape_std_mtfsf,
7259872dae6SJan Höppner 	[MTBSF]		 = tape_std_mtbsf,
7269872dae6SJan Höppner 	[MTFSR]		 = tape_std_mtfsr,
7279872dae6SJan Höppner 	[MTBSR]		 = tape_std_mtbsr,
7289872dae6SJan Höppner 	[MTWEOF]	 = tape_std_mtweof,
7299872dae6SJan Höppner 	[MTREW]		 = tape_std_mtrew,
7309872dae6SJan Höppner 	[MTOFFL]	 = tape_std_mtoffl,
7319872dae6SJan Höppner 	[MTNOP]		 = tape_std_mtnop,
7329872dae6SJan Höppner 	[MTRETEN]	 = tape_std_mtreten,
7339872dae6SJan Höppner 	[MTBSFM]	 = tape_std_mtbsfm,
7349872dae6SJan Höppner 	[MTFSFM]	 = tape_std_mtfsfm,
7359872dae6SJan Höppner 	[MTEOM]		 = tape_std_mteom,
7369872dae6SJan Höppner 	[MTERASE]	 = tape_std_mterase,
7379872dae6SJan Höppner 	[MTRAS1]	 = NULL,
7389872dae6SJan Höppner 	[MTRAS2]	 = NULL,
7399872dae6SJan Höppner 	[MTRAS3]	 = NULL,
7409872dae6SJan Höppner 	[MTSETBLK]	 = tape_std_mtsetblk,
7419872dae6SJan Höppner 	[MTSETDENSITY]	 = NULL,
7429872dae6SJan Höppner 	[MTSEEK]	 = tape_3490_mtseek,
7439872dae6SJan Höppner 	[MTTELL]	 = tape_3490_mttell,
7449872dae6SJan Höppner 	[MTSETDRVBUFFER] = NULL,
7459872dae6SJan Höppner 	[MTFSS]		 = NULL,
7469872dae6SJan Höppner 	[MTBSS]		 = NULL,
7479872dae6SJan Höppner 	[MTWSM]		 = NULL,
7489872dae6SJan Höppner 	[MTLOCK]	 = NULL,
7499872dae6SJan Höppner 	[MTUNLOCK]	 = NULL,
7509872dae6SJan Höppner 	[MTLOAD]	 = tape_std_mtload,
7519872dae6SJan Höppner 	[MTUNLOAD]	 = tape_std_mtunload,
7529872dae6SJan Höppner 	[MTCOMPRESSION]	 = tape_std_mtcompression,
7539872dae6SJan Höppner 	[MTSETPART]	 = NULL,
7549872dae6SJan Höppner 	[MTMKPART]	 = NULL
7559872dae6SJan Höppner };
7569872dae6SJan Höppner 
7579872dae6SJan Höppner /*
7589872dae6SJan Höppner  * Tape discipline structure for 3490.
7599872dae6SJan Höppner  */
7609872dae6SJan Höppner static struct tape_discipline tape_discipline_3490 = {
7619872dae6SJan Höppner 	.owner = THIS_MODULE,
7629872dae6SJan Höppner 	.setup_device = tape_3490_setup_device,
7639872dae6SJan Höppner 	.cleanup_device = tape_3490_cleanup_device,
7649872dae6SJan Höppner 	.process_eov = tape_std_process_eov,
7659872dae6SJan Höppner 	.irq = tape_3490_irq,
7669872dae6SJan Höppner 	.read_block = tape_std_read_block,
7679872dae6SJan Höppner 	.write_block = tape_std_write_block,
7689872dae6SJan Höppner 	.mtop_array = tape_3490_mtop
7699872dae6SJan Höppner };
7709872dae6SJan Höppner 
7719872dae6SJan Höppner static struct ccw_device_id tape_3490_ids[] = {
7729872dae6SJan Höppner 	{ CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490},
7739872dae6SJan Höppner 	{ /* end of list */ },
7749872dae6SJan Höppner };
7759872dae6SJan Höppner 
7769872dae6SJan Höppner static int
tape_3490_online(struct ccw_device * cdev)7779872dae6SJan Höppner tape_3490_online(struct ccw_device *cdev)
7789872dae6SJan Höppner {
7799872dae6SJan Höppner 	return tape_generic_online(
7809872dae6SJan Höppner 		dev_get_drvdata(&cdev->dev),
7819872dae6SJan Höppner 		&tape_discipline_3490
7829872dae6SJan Höppner 	);
7839872dae6SJan Höppner }
7849872dae6SJan Höppner 
7859872dae6SJan Höppner static struct ccw_driver tape_3490_driver = {
7869872dae6SJan Höppner 	.driver = {
787123d2e75SJan Höppner 		.name = "tape_34xx",
7889872dae6SJan Höppner 		.owner = THIS_MODULE,
7899872dae6SJan Höppner 	},
7909872dae6SJan Höppner 	.ids = tape_3490_ids,
7919872dae6SJan Höppner 	.probe = tape_generic_probe,
7929872dae6SJan Höppner 	.remove = tape_generic_remove,
7939872dae6SJan Höppner 	.set_online = tape_3490_online,
7949872dae6SJan Höppner 	.set_offline = tape_generic_offline,
7959872dae6SJan Höppner 	.int_class = IRQIO_TAP,
7969872dae6SJan Höppner };
7979872dae6SJan Höppner 
tape_3490_init(void)7985ae76830SJan Höppner int tape_3490_init(void)
7999872dae6SJan Höppner {
8009872dae6SJan Höppner 	int rc;
8019872dae6SJan Höppner 
8029872dae6SJan Höppner 	TAPE_DBF_AREA = debug_register ( "tape_3490", 2, 2, 4*sizeof(long));
8039872dae6SJan Höppner 	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
8049872dae6SJan Höppner #ifdef DBF_LIKE_HELL
8059872dae6SJan Höppner 	debug_set_level(TAPE_DBF_AREA, 6);
8069872dae6SJan Höppner #endif
8079872dae6SJan Höppner 
8089872dae6SJan Höppner 	DBF_EVENT(3, "3490 init\n");
8099872dae6SJan Höppner 	/* Register driver for 3490 tapes. */
8109872dae6SJan Höppner 	rc = ccw_driver_register(&tape_3490_driver);
8119872dae6SJan Höppner 	if (rc)
8129872dae6SJan Höppner 		DBF_EVENT(3, "3490 init failed\n");
8139872dae6SJan Höppner 	else
8149872dae6SJan Höppner 		DBF_EVENT(3, "3490 registered\n");
8159872dae6SJan Höppner 	return rc;
8169872dae6SJan Höppner }
8179872dae6SJan Höppner 
tape_3490_exit(void)8185ae76830SJan Höppner void tape_3490_exit(void)
8199872dae6SJan Höppner {
8209872dae6SJan Höppner 	ccw_driver_unregister(&tape_3490_driver);
8219872dae6SJan Höppner 
8229872dae6SJan Höppner 	debug_unregister(TAPE_DBF_AREA);
8239872dae6SJan Höppner }
8249872dae6SJan Höppner 
8259872dae6SJan Höppner MODULE_DEVICE_TABLE(ccw, tape_3490_ids);
826