18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    tape device discipline for 3480/3490 tapes.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2001, 2009
68c2ecf20Sopenharmony_ci *    Author(s): Carsten Otte <cotte@de.ibm.com>
78c2ecf20Sopenharmony_ci *		 Tuan Ngo-Anh <ngoanh@de.ibm.com>
88c2ecf20Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "tape_34xx"
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/bio.h>
178c2ecf20Sopenharmony_ci#include <linux/workqueue.h>
188c2ecf20Sopenharmony_ci#include <linux/slab.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define TAPE_DBF_AREA	tape_34xx_dbf
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "tape.h"
238c2ecf20Sopenharmony_ci#include "tape_std.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Pointer to debug area.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cidebug_info_t *TAPE_DBF_AREA = NULL;
298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(TAPE_DBF_AREA);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480	0
328c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480_2_XF	1
338c2ecf20Sopenharmony_ci#define TAPE34XX_FMT_3480_XF	2
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct tape_34xx_block_id {
368c2ecf20Sopenharmony_ci	unsigned int	wrap		: 1;
378c2ecf20Sopenharmony_ci	unsigned int	segment		: 7;
388c2ecf20Sopenharmony_ci	unsigned int	format		: 2;
398c2ecf20Sopenharmony_ci	unsigned int	block		: 22;
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * A list of block ID's is used to faster seek blocks.
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistruct tape_34xx_sbid {
468c2ecf20Sopenharmony_ci	struct list_head		list;
478c2ecf20Sopenharmony_ci	struct tape_34xx_block_id	bid;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic void tape_34xx_delete_sbid_from(struct tape_device *, int);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/*
538c2ecf20Sopenharmony_ci * Medium sense for 34xx tapes. There is no 'real' medium sense call.
548c2ecf20Sopenharmony_ci * So we just do a normal sense.
558c2ecf20Sopenharmony_ci */
568c2ecf20Sopenharmony_cistatic void __tape_34xx_medium_sense(struct tape_request *request)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct tape_device *device = request->device;
598c2ecf20Sopenharmony_ci	unsigned char *sense;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (request->rc == 0) {
628c2ecf20Sopenharmony_ci		sense = request->cpdata;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		/*
658c2ecf20Sopenharmony_ci		 * This isn't quite correct. But since INTERVENTION_REQUIRED
668c2ecf20Sopenharmony_ci		 * means that the drive is 'neither ready nor on-line' it is
678c2ecf20Sopenharmony_ci		 * only slightly inaccurate to say there is no tape loaded if
688c2ecf20Sopenharmony_ci		 * the drive isn't online...
698c2ecf20Sopenharmony_ci		 */
708c2ecf20Sopenharmony_ci		if (sense[0] & SENSE_INTERVENTION_REQUIRED)
718c2ecf20Sopenharmony_ci			tape_med_state_set(device, MS_UNLOADED);
728c2ecf20Sopenharmony_ci		else
738c2ecf20Sopenharmony_ci			tape_med_state_set(device, MS_LOADED);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci		if (sense[1] & SENSE_WRITE_PROTECT)
768c2ecf20Sopenharmony_ci			device->tape_generic_status |= GMT_WR_PROT(~0);
778c2ecf20Sopenharmony_ci		else
788c2ecf20Sopenharmony_ci			device->tape_generic_status &= ~GMT_WR_PROT(~0);
798c2ecf20Sopenharmony_ci	} else
808c2ecf20Sopenharmony_ci		DBF_EVENT(4, "tape_34xx: medium sense failed with rc=%d\n",
818c2ecf20Sopenharmony_ci			request->rc);
828c2ecf20Sopenharmony_ci	tape_free_request(request);
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int tape_34xx_medium_sense(struct tape_device *device)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	struct tape_request *request;
888c2ecf20Sopenharmony_ci	int rc;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, 32);
918c2ecf20Sopenharmony_ci	if (IS_ERR(request)) {
928c2ecf20Sopenharmony_ci		DBF_EXCEPTION(6, "MSEN fail\n");
938c2ecf20Sopenharmony_ci		return PTR_ERR(request);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	request->op = TO_MSEN;
978c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
988c2ecf20Sopenharmony_ci	rc = tape_do_io_interruptible(device, request);
998c2ecf20Sopenharmony_ci	__tape_34xx_medium_sense(request);
1008c2ecf20Sopenharmony_ci	return rc;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void tape_34xx_medium_sense_async(struct tape_device *device)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	struct tape_request *request;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, 32);
1088c2ecf20Sopenharmony_ci	if (IS_ERR(request)) {
1098c2ecf20Sopenharmony_ci		DBF_EXCEPTION(6, "MSEN fail\n");
1108c2ecf20Sopenharmony_ci		return;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	request->op = TO_MSEN;
1148c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, SENSE, 32, request->cpdata);
1158c2ecf20Sopenharmony_ci	request->callback = (void *) __tape_34xx_medium_sense;
1168c2ecf20Sopenharmony_ci	request->callback_data = NULL;
1178c2ecf20Sopenharmony_ci	tape_do_io_async(device, request);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistruct tape_34xx_work {
1218c2ecf20Sopenharmony_ci	struct tape_device	*device;
1228c2ecf20Sopenharmony_ci	enum tape_op		 op;
1238c2ecf20Sopenharmony_ci	struct work_struct	 work;
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/*
1278c2ecf20Sopenharmony_ci * These functions are currently used only to schedule a medium_sense for
1288c2ecf20Sopenharmony_ci * later execution. This is because we get an interrupt whenever a medium
1298c2ecf20Sopenharmony_ci * is inserted but cannot call tape_do_io* from an interrupt context.
1308c2ecf20Sopenharmony_ci * Maybe that's useful for other actions we want to start from the
1318c2ecf20Sopenharmony_ci * interrupt handler.
1328c2ecf20Sopenharmony_ci * Note: the work handler is called by the system work queue. The tape
1338c2ecf20Sopenharmony_ci * commands started by the handler need to be asynchrounous, otherwise
1348c2ecf20Sopenharmony_ci * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
1358c2ecf20Sopenharmony_ci */
1368c2ecf20Sopenharmony_cistatic void
1378c2ecf20Sopenharmony_citape_34xx_work_handler(struct work_struct *work)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct tape_34xx_work *p =
1408c2ecf20Sopenharmony_ci		container_of(work, struct tape_34xx_work, work);
1418c2ecf20Sopenharmony_ci	struct tape_device *device = p->device;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	switch(p->op) {
1448c2ecf20Sopenharmony_ci		case TO_MSEN:
1458c2ecf20Sopenharmony_ci			tape_34xx_medium_sense_async(device);
1468c2ecf20Sopenharmony_ci			break;
1478c2ecf20Sopenharmony_ci		default:
1488c2ecf20Sopenharmony_ci			DBF_EVENT(3, "T34XX: internal error: unknown work\n");
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	tape_put_device(device);
1518c2ecf20Sopenharmony_ci	kfree(p);
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int
1558c2ecf20Sopenharmony_citape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct tape_34xx_work *p;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
1608c2ecf20Sopenharmony_ci		return -ENOMEM;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	INIT_WORK(&p->work, tape_34xx_work_handler);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	p->device = tape_get_device(device);
1658c2ecf20Sopenharmony_ci	p->op     = op;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	schedule_work(&p->work);
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * Done Handler is called when dev stat = DEVICE-END (successful operation)
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cistatic inline int
1758c2ecf20Sopenharmony_citape_34xx_done(struct tape_request *request)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	switch (request->op) {
1808c2ecf20Sopenharmony_ci		case TO_DSE:
1818c2ecf20Sopenharmony_ci		case TO_RUN:
1828c2ecf20Sopenharmony_ci		case TO_WRI:
1838c2ecf20Sopenharmony_ci		case TO_WTM:
1848c2ecf20Sopenharmony_ci		case TO_ASSIGN:
1858c2ecf20Sopenharmony_ci		case TO_UNASSIGN:
1868c2ecf20Sopenharmony_ci			tape_34xx_delete_sbid_from(request->device, 0);
1878c2ecf20Sopenharmony_ci			break;
1888c2ecf20Sopenharmony_ci		default:
1898c2ecf20Sopenharmony_ci			;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	return TAPE_IO_SUCCESS;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic inline int
1958c2ecf20Sopenharmony_citape_34xx_erp_failed(struct tape_request *request, int rc)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	DBF_EVENT(3, "Error recovery failed for %s (RC=%d)\n",
1988c2ecf20Sopenharmony_ci		  tape_op_verbose[request->op], rc);
1998c2ecf20Sopenharmony_ci	return rc;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic inline int
2038c2ecf20Sopenharmony_citape_34xx_erp_succeeded(struct tape_request *request)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	DBF_EVENT(3, "Error Recovery successful for %s\n",
2068c2ecf20Sopenharmony_ci		  tape_op_verbose[request->op]);
2078c2ecf20Sopenharmony_ci	return tape_34xx_done(request);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic inline int
2118c2ecf20Sopenharmony_citape_34xx_erp_retry(struct tape_request *request)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	DBF_EVENT(3, "xerp retr %s\n", tape_op_verbose[request->op]);
2148c2ecf20Sopenharmony_ci	return TAPE_IO_RETRY;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci/*
2188c2ecf20Sopenharmony_ci * This function is called, when no request is outstanding and we get an
2198c2ecf20Sopenharmony_ci * interrupt
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic int
2228c2ecf20Sopenharmony_citape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat == 0x85) { /* READY */
2258c2ecf20Sopenharmony_ci		/* A medium was inserted in the drive. */
2268c2ecf20Sopenharmony_ci		DBF_EVENT(6, "xuud med\n");
2278c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
2288c2ecf20Sopenharmony_ci		tape_34xx_schedule_work(device, TO_MSEN);
2298c2ecf20Sopenharmony_ci	} else {
2308c2ecf20Sopenharmony_ci		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
2318c2ecf20Sopenharmony_ci		tape_dump_sense_dbf(device, NULL, irb);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci	return TAPE_IO_SUCCESS;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * Read Opposite Error Recovery Function:
2388c2ecf20Sopenharmony_ci * Used, when Read Forward does not work
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_cistatic int
2418c2ecf20Sopenharmony_citape_34xx_erp_read_opposite(struct tape_device *device,
2428c2ecf20Sopenharmony_ci			    struct tape_request *request)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	if (request->op == TO_RFO) {
2458c2ecf20Sopenharmony_ci		/*
2468c2ecf20Sopenharmony_ci		 * We did read forward, but the data could not be read
2478c2ecf20Sopenharmony_ci		 * *correctly*. We transform the request to a read backward
2488c2ecf20Sopenharmony_ci		 * and try again.
2498c2ecf20Sopenharmony_ci		 */
2508c2ecf20Sopenharmony_ci		tape_std_read_backward(device, request);
2518c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * We tried to read forward and backward, but hat no
2568c2ecf20Sopenharmony_ci	 * success -> failed.
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	return tape_34xx_erp_failed(request, -EIO);
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cistatic int
2628c2ecf20Sopenharmony_citape_34xx_erp_bug(struct tape_device *device, struct tape_request *request,
2638c2ecf20Sopenharmony_ci		  struct irb *irb, int no)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	if (request->op != TO_ASSIGN) {
2668c2ecf20Sopenharmony_ci		dev_err(&device->cdev->dev, "An unexpected condition %d "
2678c2ecf20Sopenharmony_ci			"occurred in tape error recovery\n", no);
2688c2ecf20Sopenharmony_ci		tape_dump_sense_dbf(device, request, irb);
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci	return tape_34xx_erp_failed(request, -EIO);
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * Handle data overrun between cu and drive. The channel speed might
2758c2ecf20Sopenharmony_ci * be too slow.
2768c2ecf20Sopenharmony_ci */
2778c2ecf20Sopenharmony_cistatic int
2788c2ecf20Sopenharmony_citape_34xx_erp_overrun(struct tape_device *device, struct tape_request *request,
2798c2ecf20Sopenharmony_ci		      struct irb *irb)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	if (irb->ecw[3] == 0x40) {
2828c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A data overrun occurred between"
2838c2ecf20Sopenharmony_ci			" the control unit and tape unit\n");
2848c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	return tape_34xx_erp_bug(device, request, irb, -1);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/*
2908c2ecf20Sopenharmony_ci * Handle record sequence error.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_cistatic int
2938c2ecf20Sopenharmony_citape_34xx_erp_sequence(struct tape_device *device,
2948c2ecf20Sopenharmony_ci		       struct tape_request *request, struct irb *irb)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	if (irb->ecw[3] == 0x41) {
2978c2ecf20Sopenharmony_ci		/*
2988c2ecf20Sopenharmony_ci		 * cu detected incorrect block-id sequence on tape.
2998c2ecf20Sopenharmony_ci		 */
3008c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The block ID sequence on the "
3018c2ecf20Sopenharmony_ci			"tape is incorrect\n");
3028c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
3038c2ecf20Sopenharmony_ci	}
3048c2ecf20Sopenharmony_ci	/*
3058c2ecf20Sopenharmony_ci	 * Record sequence error bit is set, but erpa does not
3068c2ecf20Sopenharmony_ci	 * show record sequence error.
3078c2ecf20Sopenharmony_ci	 */
3088c2ecf20Sopenharmony_ci	return tape_34xx_erp_bug(device, request, irb, -2);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/*
3128c2ecf20Sopenharmony_ci * This function analyses the tape's sense-data in case of a unit-check.
3138c2ecf20Sopenharmony_ci * If possible, it tries to recover from the error. Else the user is
3148c2ecf20Sopenharmony_ci * informed about the problem.
3158c2ecf20Sopenharmony_ci */
3168c2ecf20Sopenharmony_cistatic int
3178c2ecf20Sopenharmony_citape_34xx_unit_check(struct tape_device *device, struct tape_request *request,
3188c2ecf20Sopenharmony_ci		     struct irb *irb)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int inhibit_cu_recovery;
3218c2ecf20Sopenharmony_ci	__u8* sense;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	inhibit_cu_recovery = (*device->modeset_byte & 0x80) ? 1 : 0;
3248c2ecf20Sopenharmony_ci	sense = irb->ecw;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (
3278c2ecf20Sopenharmony_ci		sense[0] & SENSE_COMMAND_REJECT &&
3288c2ecf20Sopenharmony_ci		sense[1] & SENSE_WRITE_PROTECT
3298c2ecf20Sopenharmony_ci	) {
3308c2ecf20Sopenharmony_ci		if (
3318c2ecf20Sopenharmony_ci			request->op == TO_DSE ||
3328c2ecf20Sopenharmony_ci			request->op == TO_WRI ||
3338c2ecf20Sopenharmony_ci			request->op == TO_WTM
3348c2ecf20Sopenharmony_ci		) {
3358c2ecf20Sopenharmony_ci			/* medium is write protected */
3368c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EACCES);
3378c2ecf20Sopenharmony_ci		} else {
3388c2ecf20Sopenharmony_ci			return tape_34xx_erp_bug(device, request, irb, -3);
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/*
3438c2ecf20Sopenharmony_ci	 * Special cases for various tape-states when reaching
3448c2ecf20Sopenharmony_ci	 * end of recorded area
3458c2ecf20Sopenharmony_ci	 *
3468c2ecf20Sopenharmony_ci	 * FIXME: Maybe a special case of the special case:
3478c2ecf20Sopenharmony_ci	 *        sense[0] == SENSE_EQUIPMENT_CHECK &&
3488c2ecf20Sopenharmony_ci	 *        sense[1] == SENSE_DRIVE_ONLINE    &&
3498c2ecf20Sopenharmony_ci	 *        sense[3] == 0x47 (Volume Fenced)
3508c2ecf20Sopenharmony_ci	 *
3518c2ecf20Sopenharmony_ci	 *        This was caused by continued FSF or FSR after an
3528c2ecf20Sopenharmony_ci	 *        'End Of Data'.
3538c2ecf20Sopenharmony_ci	 */
3548c2ecf20Sopenharmony_ci	if ((
3558c2ecf20Sopenharmony_ci		sense[0] == SENSE_DATA_CHECK      ||
3568c2ecf20Sopenharmony_ci		sense[0] == SENSE_EQUIPMENT_CHECK ||
3578c2ecf20Sopenharmony_ci		sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK
3588c2ecf20Sopenharmony_ci	) && (
3598c2ecf20Sopenharmony_ci		sense[1] == SENSE_DRIVE_ONLINE ||
3608c2ecf20Sopenharmony_ci		sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE
3618c2ecf20Sopenharmony_ci	)) {
3628c2ecf20Sopenharmony_ci		switch (request->op) {
3638c2ecf20Sopenharmony_ci		/*
3648c2ecf20Sopenharmony_ci		 * sense[0] == SENSE_DATA_CHECK   &&
3658c2ecf20Sopenharmony_ci		 * sense[1] == SENSE_DRIVE_ONLINE
3668c2ecf20Sopenharmony_ci		 * sense[3] == 0x36 (End Of Data)
3678c2ecf20Sopenharmony_ci		 *
3688c2ecf20Sopenharmony_ci		 * Further seeks might return a 'Volume Fenced'.
3698c2ecf20Sopenharmony_ci		 */
3708c2ecf20Sopenharmony_ci		case TO_FSF:
3718c2ecf20Sopenharmony_ci		case TO_FSB:
3728c2ecf20Sopenharmony_ci			/* Trying to seek beyond end of recorded area */
3738c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -ENOSPC);
3748c2ecf20Sopenharmony_ci		case TO_BSB:
3758c2ecf20Sopenharmony_ci			return tape_34xx_erp_retry(request);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		/*
3788c2ecf20Sopenharmony_ci		 * sense[0] == SENSE_DATA_CHECK   &&
3798c2ecf20Sopenharmony_ci		 * sense[1] == SENSE_DRIVE_ONLINE &&
3808c2ecf20Sopenharmony_ci		 * sense[3] == 0x36 (End Of Data)
3818c2ecf20Sopenharmony_ci		 */
3828c2ecf20Sopenharmony_ci		case TO_LBL:
3838c2ecf20Sopenharmony_ci			/* Block could not be located. */
3848c2ecf20Sopenharmony_ci			tape_34xx_delete_sbid_from(device, 0);
3858c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		case TO_RFO:
3888c2ecf20Sopenharmony_ci			/* Read beyond end of recorded area -> 0 bytes read */
3898c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, 0);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci		/*
3928c2ecf20Sopenharmony_ci		 * sense[0] == SENSE_EQUIPMENT_CHECK &&
3938c2ecf20Sopenharmony_ci		 * sense[1] == SENSE_DRIVE_ONLINE    &&
3948c2ecf20Sopenharmony_ci		 * sense[3] == 0x38 (Physical End Of Volume)
3958c2ecf20Sopenharmony_ci		 */
3968c2ecf20Sopenharmony_ci		case TO_WRI:
3978c2ecf20Sopenharmony_ci			/* Writing at physical end of volume */
3988c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -ENOSPC);
3998c2ecf20Sopenharmony_ci		default:
4008c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, 0);
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	/* Sensing special bits */
4058c2ecf20Sopenharmony_ci	if (sense[0] & SENSE_BUS_OUT_CHECK)
4068c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (sense[0] & SENSE_DATA_CHECK) {
4098c2ecf20Sopenharmony_ci		/*
4108c2ecf20Sopenharmony_ci		 * hardware failure, damaged tape or improper
4118c2ecf20Sopenharmony_ci		 * operating conditions
4128c2ecf20Sopenharmony_ci		 */
4138c2ecf20Sopenharmony_ci		switch (sense[3]) {
4148c2ecf20Sopenharmony_ci		case 0x23:
4158c2ecf20Sopenharmony_ci			/* a read data check occurred */
4168c2ecf20Sopenharmony_ci			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
4178c2ecf20Sopenharmony_ci			    inhibit_cu_recovery)
4188c2ecf20Sopenharmony_ci				// data check is not permanent, may be
4198c2ecf20Sopenharmony_ci				// recovered. We always use async-mode with
4208c2ecf20Sopenharmony_ci				// cu-recovery, so this should *never* happen.
4218c2ecf20Sopenharmony_ci				return tape_34xx_erp_bug(device, request,
4228c2ecf20Sopenharmony_ci							 irb, -4);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci			/* data check is permanent, CU recovery has failed */
4258c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "A read error occurred "
4268c2ecf20Sopenharmony_ci				"that cannot be recovered\n");
4278c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
4288c2ecf20Sopenharmony_ci		case 0x25:
4298c2ecf20Sopenharmony_ci			// a write data check occurred
4308c2ecf20Sopenharmony_ci			if ((sense[2] & SENSE_TAPE_SYNC_MODE) ||
4318c2ecf20Sopenharmony_ci			    inhibit_cu_recovery)
4328c2ecf20Sopenharmony_ci				// data check is not permanent, may be
4338c2ecf20Sopenharmony_ci				// recovered. We always use async-mode with
4348c2ecf20Sopenharmony_ci				// cu-recovery, so this should *never* happen.
4358c2ecf20Sopenharmony_ci				return tape_34xx_erp_bug(device, request,
4368c2ecf20Sopenharmony_ci							 irb, -5);
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci			// data check is permanent, cu-recovery has failed
4398c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "A write error on the "
4408c2ecf20Sopenharmony_ci				"tape cannot be recovered\n");
4418c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
4428c2ecf20Sopenharmony_ci		case 0x26:
4438c2ecf20Sopenharmony_ci			/* Data Check (read opposite) occurred. */
4448c2ecf20Sopenharmony_ci			return tape_34xx_erp_read_opposite(device, request);
4458c2ecf20Sopenharmony_ci		case 0x28:
4468c2ecf20Sopenharmony_ci			/* ID-Mark at tape start couldn't be written */
4478c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "Writing the ID-mark "
4488c2ecf20Sopenharmony_ci				"failed\n");
4498c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
4508c2ecf20Sopenharmony_ci		case 0x31:
4518c2ecf20Sopenharmony_ci			/* Tape void. Tried to read beyond end of device. */
4528c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "Reading the tape beyond"
4538c2ecf20Sopenharmony_ci				" the end of the recorded area failed\n");
4548c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -ENOSPC);
4558c2ecf20Sopenharmony_ci		case 0x41:
4568c2ecf20Sopenharmony_ci			/* Record sequence error. */
4578c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "The tape contains an "
4588c2ecf20Sopenharmony_ci				"incorrect block ID sequence\n");
4598c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
4608c2ecf20Sopenharmony_ci		default:
4618c2ecf20Sopenharmony_ci			/* all data checks for 3480 should result in one of
4628c2ecf20Sopenharmony_ci			 * the above erpa-codes. For 3490, other data-check
4638c2ecf20Sopenharmony_ci			 * conditions do exist. */
4648c2ecf20Sopenharmony_ci			if (device->cdev->id.driver_info == tape_3480)
4658c2ecf20Sopenharmony_ci				return tape_34xx_erp_bug(device, request,
4668c2ecf20Sopenharmony_ci							 irb, -6);
4678c2ecf20Sopenharmony_ci		}
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (sense[0] & SENSE_OVERRUN)
4718c2ecf20Sopenharmony_ci		return tape_34xx_erp_overrun(device, request, irb);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (sense[1] & SENSE_RECORD_SEQUENCE_ERR)
4748c2ecf20Sopenharmony_ci		return tape_34xx_erp_sequence(device, request, irb);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	/* Sensing erpa codes */
4778c2ecf20Sopenharmony_ci	switch (sense[3]) {
4788c2ecf20Sopenharmony_ci	case 0x00:
4798c2ecf20Sopenharmony_ci		/* Unit check with erpa code 0. Report and ignore. */
4808c2ecf20Sopenharmony_ci		return TAPE_IO_SUCCESS;
4818c2ecf20Sopenharmony_ci	case 0x21:
4828c2ecf20Sopenharmony_ci		/*
4838c2ecf20Sopenharmony_ci		 * Data streaming not operational. CU will switch to
4848c2ecf20Sopenharmony_ci		 * interlock mode. Reissue the command.
4858c2ecf20Sopenharmony_ci		 */
4868c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
4878c2ecf20Sopenharmony_ci	case 0x22:
4888c2ecf20Sopenharmony_ci		/*
4898c2ecf20Sopenharmony_ci		 * Path equipment check. Might be drive adapter error, buffer
4908c2ecf20Sopenharmony_ci		 * error on the lower interface, internal path not usable,
4918c2ecf20Sopenharmony_ci		 * or error during cartridge load.
4928c2ecf20Sopenharmony_ci		 */
4938c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A path equipment check occurred"
4948c2ecf20Sopenharmony_ci			" for the tape device\n");
4958c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
4968c2ecf20Sopenharmony_ci	case 0x24:
4978c2ecf20Sopenharmony_ci		/*
4988c2ecf20Sopenharmony_ci		 * Load display check. Load display was command was issued,
4998c2ecf20Sopenharmony_ci		 * but the drive is displaying a drive check message. Can
5008c2ecf20Sopenharmony_ci		 * be threated as "device end".
5018c2ecf20Sopenharmony_ci		 */
5028c2ecf20Sopenharmony_ci		return tape_34xx_erp_succeeded(request);
5038c2ecf20Sopenharmony_ci	case 0x27:
5048c2ecf20Sopenharmony_ci		/*
5058c2ecf20Sopenharmony_ci		 * Command reject. May indicate illegal channel program or
5068c2ecf20Sopenharmony_ci		 * buffer over/underrun. Since all channel programs are
5078c2ecf20Sopenharmony_ci		 * issued by this driver and ought be correct, we assume a
5088c2ecf20Sopenharmony_ci		 * over/underrun situation and retry the channel program.
5098c2ecf20Sopenharmony_ci		 */
5108c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
5118c2ecf20Sopenharmony_ci	case 0x29:
5128c2ecf20Sopenharmony_ci		/*
5138c2ecf20Sopenharmony_ci		 * Function incompatible. Either the tape is idrc compressed
5148c2ecf20Sopenharmony_ci		 * but the hardware isn't capable to do idrc, or a perform
5158c2ecf20Sopenharmony_ci		 * subsystem func is issued and the CU is not on-line.
5168c2ecf20Sopenharmony_ci		 */
5178c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
5188c2ecf20Sopenharmony_ci	case 0x2a:
5198c2ecf20Sopenharmony_ci		/*
5208c2ecf20Sopenharmony_ci		 * Unsolicited environmental data. An internal counter
5218c2ecf20Sopenharmony_ci		 * overflows, we can ignore this and reissue the cmd.
5228c2ecf20Sopenharmony_ci		 */
5238c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
5248c2ecf20Sopenharmony_ci	case 0x2b:
5258c2ecf20Sopenharmony_ci		/*
5268c2ecf20Sopenharmony_ci		 * Environmental data present. Indicates either unload
5278c2ecf20Sopenharmony_ci		 * completed ok or read buffered log command completed ok.
5288c2ecf20Sopenharmony_ci		 */
5298c2ecf20Sopenharmony_ci		if (request->op == TO_RUN) {
5308c2ecf20Sopenharmony_ci			/* Rewind unload completed ok. */
5318c2ecf20Sopenharmony_ci			tape_med_state_set(device, MS_UNLOADED);
5328c2ecf20Sopenharmony_ci			return tape_34xx_erp_succeeded(request);
5338c2ecf20Sopenharmony_ci		}
5348c2ecf20Sopenharmony_ci		/* tape_34xx doesn't use read buffered log commands. */
5358c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
5368c2ecf20Sopenharmony_ci	case 0x2c:
5378c2ecf20Sopenharmony_ci		/*
5388c2ecf20Sopenharmony_ci		 * Permanent equipment check. CU has tried recovery, but
5398c2ecf20Sopenharmony_ci		 * did not succeed.
5408c2ecf20Sopenharmony_ci		 */
5418c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
5428c2ecf20Sopenharmony_ci	case 0x2d:
5438c2ecf20Sopenharmony_ci		/* Data security erase failure. */
5448c2ecf20Sopenharmony_ci		if (request->op == TO_DSE)
5458c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
5468c2ecf20Sopenharmony_ci		/* Data security erase failure, but no such command issued. */
5478c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
5488c2ecf20Sopenharmony_ci	case 0x2e:
5498c2ecf20Sopenharmony_ci		/*
5508c2ecf20Sopenharmony_ci		 * Not capable. This indicates either that the drive fails
5518c2ecf20Sopenharmony_ci		 * reading the format id mark or that that format specified
5528c2ecf20Sopenharmony_ci		 * is not supported by the drive.
5538c2ecf20Sopenharmony_ci		 */
5548c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit cannot process "
5558c2ecf20Sopenharmony_ci			"the tape format\n");
5568c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
5578c2ecf20Sopenharmony_ci	case 0x30:
5588c2ecf20Sopenharmony_ci		/* The medium is write protected. */
5598c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape medium is write-"
5608c2ecf20Sopenharmony_ci			"protected\n");
5618c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EACCES);
5628c2ecf20Sopenharmony_ci	case 0x32:
5638c2ecf20Sopenharmony_ci		// Tension loss. We cannot recover this, it's an I/O error.
5648c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape does not have the "
5658c2ecf20Sopenharmony_ci			"required tape tension\n");
5668c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
5678c2ecf20Sopenharmony_ci	case 0x33:
5688c2ecf20Sopenharmony_ci		/*
5698c2ecf20Sopenharmony_ci		 * Load Failure. The cartridge was not inserted correctly or
5708c2ecf20Sopenharmony_ci		 * the tape is not threaded correctly.
5718c2ecf20Sopenharmony_ci		 */
5728c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit failed to load"
5738c2ecf20Sopenharmony_ci			" the cartridge\n");
5748c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
5758c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
5768c2ecf20Sopenharmony_ci	case 0x34:
5778c2ecf20Sopenharmony_ci		/*
5788c2ecf20Sopenharmony_ci		 * Unload failure. The drive cannot maintain tape tension
5798c2ecf20Sopenharmony_ci		 * and control tape movement during an unload operation.
5808c2ecf20Sopenharmony_ci		 */
5818c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "Automatic unloading of the tape"
5828c2ecf20Sopenharmony_ci			" cartridge failed\n");
5838c2ecf20Sopenharmony_ci		if (request->op == TO_RUN)
5848c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
5858c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
5868c2ecf20Sopenharmony_ci	case 0x35:
5878c2ecf20Sopenharmony_ci		/*
5888c2ecf20Sopenharmony_ci		 * Drive equipment check. One of the following:
5898c2ecf20Sopenharmony_ci		 * - cu cannot recover from a drive detected error
5908c2ecf20Sopenharmony_ci		 * - a check code message is shown on drive display
5918c2ecf20Sopenharmony_ci		 * - the cartridge loader does not respond correctly
5928c2ecf20Sopenharmony_ci		 * - a failure occurs during an index, load, or unload cycle
5938c2ecf20Sopenharmony_ci		 */
5948c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "An equipment check has occurred"
5958c2ecf20Sopenharmony_ci			" on the tape unit\n");
5968c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
5978c2ecf20Sopenharmony_ci	case 0x36:
5988c2ecf20Sopenharmony_ci		if (device->cdev->id.driver_info == tape_3490)
5998c2ecf20Sopenharmony_ci			/* End of data. */
6008c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -EIO);
6018c2ecf20Sopenharmony_ci		/* This erpa is reserved for 3480 */
6028c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
6038c2ecf20Sopenharmony_ci	case 0x37:
6048c2ecf20Sopenharmony_ci		/*
6058c2ecf20Sopenharmony_ci		 * Tape length error. The tape is shorter than reported in
6068c2ecf20Sopenharmony_ci		 * the beginning-of-tape data.
6078c2ecf20Sopenharmony_ci		 */
6088c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape information states an"
6098c2ecf20Sopenharmony_ci			" incorrect length\n");
6108c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6118c2ecf20Sopenharmony_ci	case 0x38:
6128c2ecf20Sopenharmony_ci		/*
6138c2ecf20Sopenharmony_ci		 * Physical end of tape. A read/write operation reached
6148c2ecf20Sopenharmony_ci		 * the physical end of tape.
6158c2ecf20Sopenharmony_ci		 */
6168c2ecf20Sopenharmony_ci		if (request->op==TO_WRI ||
6178c2ecf20Sopenharmony_ci		    request->op==TO_DSE ||
6188c2ecf20Sopenharmony_ci		    request->op==TO_WTM)
6198c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -ENOSPC);
6208c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6218c2ecf20Sopenharmony_ci	case 0x39:
6228c2ecf20Sopenharmony_ci		/* Backward at Beginning of tape. */
6238c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6248c2ecf20Sopenharmony_ci	case 0x3a:
6258c2ecf20Sopenharmony_ci		/* Drive switched to not ready. */
6268c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit is not ready\n");
6278c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6288c2ecf20Sopenharmony_ci	case 0x3b:
6298c2ecf20Sopenharmony_ci		/* Manual rewind or unload. This causes an I/O error. */
6308c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape medium has been "
6318c2ecf20Sopenharmony_ci			"rewound or unloaded manually\n");
6328c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
6338c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6348c2ecf20Sopenharmony_ci	case 0x42:
6358c2ecf20Sopenharmony_ci		/*
6368c2ecf20Sopenharmony_ci		 * Degraded mode. A condition that can cause degraded
6378c2ecf20Sopenharmony_ci		 * performance is detected.
6388c2ecf20Sopenharmony_ci		 */
6398c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape subsystem is running "
6408c2ecf20Sopenharmony_ci			"in degraded mode\n");
6418c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
6428c2ecf20Sopenharmony_ci	case 0x43:
6438c2ecf20Sopenharmony_ci		/* Drive not ready. */
6448c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
6458c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
6468c2ecf20Sopenharmony_ci		/* Some commands commands are successful even in this case */
6478c2ecf20Sopenharmony_ci		if (sense[1] & SENSE_DRIVE_ONLINE) {
6488c2ecf20Sopenharmony_ci			switch(request->op) {
6498c2ecf20Sopenharmony_ci				case TO_ASSIGN:
6508c2ecf20Sopenharmony_ci				case TO_UNASSIGN:
6518c2ecf20Sopenharmony_ci				case TO_DIS:
6528c2ecf20Sopenharmony_ci				case TO_NOP:
6538c2ecf20Sopenharmony_ci					return tape_34xx_done(request);
6548c2ecf20Sopenharmony_ci					break;
6558c2ecf20Sopenharmony_ci				default:
6568c2ecf20Sopenharmony_ci					break;
6578c2ecf20Sopenharmony_ci			}
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -ENOMEDIUM);
6608c2ecf20Sopenharmony_ci	case 0x44:
6618c2ecf20Sopenharmony_ci		/* Locate Block unsuccessful. */
6628c2ecf20Sopenharmony_ci		if (request->op != TO_BLOCK && request->op != TO_LBL)
6638c2ecf20Sopenharmony_ci			/* No locate block was issued. */
6648c2ecf20Sopenharmony_ci			return tape_34xx_erp_bug(device, request,
6658c2ecf20Sopenharmony_ci						 irb, sense[3]);
6668c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6678c2ecf20Sopenharmony_ci	case 0x45:
6688c2ecf20Sopenharmony_ci		/* The drive is assigned to a different channel path. */
6698c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit is already "
6708c2ecf20Sopenharmony_ci			"assigned\n");
6718c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6728c2ecf20Sopenharmony_ci	case 0x46:
6738c2ecf20Sopenharmony_ci		/*
6748c2ecf20Sopenharmony_ci		 * Drive not on-line. Drive may be switched offline,
6758c2ecf20Sopenharmony_ci		 * the power supply may be switched off or
6768c2ecf20Sopenharmony_ci		 * the drive address may not be set correctly.
6778c2ecf20Sopenharmony_ci		 */
6788c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit is not online\n");
6798c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6808c2ecf20Sopenharmony_ci	case 0x47:
6818c2ecf20Sopenharmony_ci		/* Volume fenced. CU reports volume integrity is lost. */
6828c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The control unit has fenced "
6838c2ecf20Sopenharmony_ci			"access to the tape volume\n");
6848c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
6858c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6868c2ecf20Sopenharmony_ci	case 0x48:
6878c2ecf20Sopenharmony_ci		/* Log sense data and retry request. */
6888c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
6898c2ecf20Sopenharmony_ci	case 0x49:
6908c2ecf20Sopenharmony_ci		/* Bus out check. A parity check error on the bus was found. */
6918c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A parity error occurred on the "
6928c2ecf20Sopenharmony_ci			"tape bus\n");
6938c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6948c2ecf20Sopenharmony_ci	case 0x4a:
6958c2ecf20Sopenharmony_ci		/* Control unit erp failed. */
6968c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "I/O error recovery failed on "
6978c2ecf20Sopenharmony_ci			"the tape control unit\n");
6988c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
6998c2ecf20Sopenharmony_ci	case 0x4b:
7008c2ecf20Sopenharmony_ci		/*
7018c2ecf20Sopenharmony_ci		 * CU and drive incompatible. The drive requests micro-program
7028c2ecf20Sopenharmony_ci		 * patches, which are not available on the CU.
7038c2ecf20Sopenharmony_ci		 */
7048c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit requires a "
7058c2ecf20Sopenharmony_ci			"firmware update\n");
7068c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
7078c2ecf20Sopenharmony_ci	case 0x4c:
7088c2ecf20Sopenharmony_ci		/*
7098c2ecf20Sopenharmony_ci		 * Recovered Check-One failure. Cu develops a hardware error,
7108c2ecf20Sopenharmony_ci		 * but is able to recover.
7118c2ecf20Sopenharmony_ci		 */
7128c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7138c2ecf20Sopenharmony_ci	case 0x4d:
7148c2ecf20Sopenharmony_ci		if (device->cdev->id.driver_info == tape_3490)
7158c2ecf20Sopenharmony_ci			/*
7168c2ecf20Sopenharmony_ci			 * Resetting event received. Since the driver does
7178c2ecf20Sopenharmony_ci			 * not support resetting event recovery (which has to
7188c2ecf20Sopenharmony_ci			 * be handled by the I/O Layer), retry our command.
7198c2ecf20Sopenharmony_ci			 */
7208c2ecf20Sopenharmony_ci			return tape_34xx_erp_retry(request);
7218c2ecf20Sopenharmony_ci		/* This erpa is reserved for 3480. */
7228c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
7238c2ecf20Sopenharmony_ci	case 0x4e:
7248c2ecf20Sopenharmony_ci		if (device->cdev->id.driver_info == tape_3490) {
7258c2ecf20Sopenharmony_ci			/*
7268c2ecf20Sopenharmony_ci			 * Maximum block size exceeded. This indicates, that
7278c2ecf20Sopenharmony_ci			 * the block to be written is larger than allowed for
7288c2ecf20Sopenharmony_ci			 * buffered mode.
7298c2ecf20Sopenharmony_ci			 */
7308c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "The maximum block size"
7318c2ecf20Sopenharmony_ci				" for buffered mode is exceeded\n");
7328c2ecf20Sopenharmony_ci			return tape_34xx_erp_failed(request, -ENOBUFS);
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci		/* This erpa is reserved for 3480. */
7358c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
7368c2ecf20Sopenharmony_ci	case 0x50:
7378c2ecf20Sopenharmony_ci		/*
7388c2ecf20Sopenharmony_ci		 * Read buffered log (Overflow). CU is running in extended
7398c2ecf20Sopenharmony_ci		 * buffered log mode, and a counter overflows. This should
7408c2ecf20Sopenharmony_ci		 * never happen, since we're never running in extended
7418c2ecf20Sopenharmony_ci		 * buffered log mode.
7428c2ecf20Sopenharmony_ci		 */
7438c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7448c2ecf20Sopenharmony_ci	case 0x51:
7458c2ecf20Sopenharmony_ci		/*
7468c2ecf20Sopenharmony_ci		 * Read buffered log (EOV). EOF processing occurs while the
7478c2ecf20Sopenharmony_ci		 * CU is in extended buffered log mode. This should never
7488c2ecf20Sopenharmony_ci		 * happen, since we're never running in extended buffered
7498c2ecf20Sopenharmony_ci		 * log mode.
7508c2ecf20Sopenharmony_ci		 */
7518c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7528c2ecf20Sopenharmony_ci	case 0x52:
7538c2ecf20Sopenharmony_ci		/* End of Volume complete. Rewind unload completed ok. */
7548c2ecf20Sopenharmony_ci		if (request->op == TO_RUN) {
7558c2ecf20Sopenharmony_ci			tape_med_state_set(device, MS_UNLOADED);
7568c2ecf20Sopenharmony_ci			tape_34xx_delete_sbid_from(device, 0);
7578c2ecf20Sopenharmony_ci			return tape_34xx_erp_succeeded(request);
7588c2ecf20Sopenharmony_ci		}
7598c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
7608c2ecf20Sopenharmony_ci	case 0x53:
7618c2ecf20Sopenharmony_ci		/* Global command intercept. */
7628c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7638c2ecf20Sopenharmony_ci	case 0x54:
7648c2ecf20Sopenharmony_ci		/* Channel interface recovery (temporary). */
7658c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7668c2ecf20Sopenharmony_ci	case 0x55:
7678c2ecf20Sopenharmony_ci		/* Channel interface recovery (permanent). */
7688c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A channel interface error cannot be"
7698c2ecf20Sopenharmony_ci			" recovered\n");
7708c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
7718c2ecf20Sopenharmony_ci	case 0x56:
7728c2ecf20Sopenharmony_ci		/* Channel protocol error. */
7738c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A channel protocol error "
7748c2ecf20Sopenharmony_ci			"occurred\n");
7758c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
7768c2ecf20Sopenharmony_ci	case 0x57:
7778c2ecf20Sopenharmony_ci		/*
7788c2ecf20Sopenharmony_ci		 * 3480: Attention intercept.
7798c2ecf20Sopenharmony_ci		 * 3490: Global status intercept.
7808c2ecf20Sopenharmony_ci		 */
7818c2ecf20Sopenharmony_ci		return tape_34xx_erp_retry(request);
7828c2ecf20Sopenharmony_ci	case 0x5a:
7838c2ecf20Sopenharmony_ci		/*
7848c2ecf20Sopenharmony_ci		 * Tape length incompatible. The tape inserted is too long,
7858c2ecf20Sopenharmony_ci		 * which could cause damage to the tape or the drive.
7868c2ecf20Sopenharmony_ci		 */
7878c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit does not support "
7888c2ecf20Sopenharmony_ci			"the tape length\n");
7898c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
7908c2ecf20Sopenharmony_ci	case 0x5b:
7918c2ecf20Sopenharmony_ci		/* Format 3480 XF incompatible */
7928c2ecf20Sopenharmony_ci		if (sense[1] & SENSE_BEGINNING_OF_TAPE)
7938c2ecf20Sopenharmony_ci			/* The tape will get overwritten. */
7948c2ecf20Sopenharmony_ci			return tape_34xx_erp_retry(request);
7958c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit does not support"
7968c2ecf20Sopenharmony_ci			" format 3480 XF\n");
7978c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
7988c2ecf20Sopenharmony_ci	case 0x5c:
7998c2ecf20Sopenharmony_ci		/* Format 3480-2 XF incompatible */
8008c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit does not support tape "
8018c2ecf20Sopenharmony_ci			"format 3480-2 XF\n");
8028c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EIO);
8038c2ecf20Sopenharmony_ci	case 0x5d:
8048c2ecf20Sopenharmony_ci		/* Tape length violation. */
8058c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit does not support"
8068c2ecf20Sopenharmony_ci			" the current tape length\n");
8078c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
8088c2ecf20Sopenharmony_ci	case 0x5e:
8098c2ecf20Sopenharmony_ci		/* Compaction algorithm incompatible. */
8108c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "The tape unit does not support"
8118c2ecf20Sopenharmony_ci			" the compaction algorithm\n");
8128c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -EMEDIUMTYPE);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci		/* The following erpas should have been covered earlier. */
8158c2ecf20Sopenharmony_ci	case 0x23: /* Read data check. */
8168c2ecf20Sopenharmony_ci	case 0x25: /* Write data check. */
8178c2ecf20Sopenharmony_ci	case 0x26: /* Data check (read opposite). */
8188c2ecf20Sopenharmony_ci	case 0x28: /* Write id mark check. */
8198c2ecf20Sopenharmony_ci	case 0x31: /* Tape void. */
8208c2ecf20Sopenharmony_ci	case 0x40: /* Overrun error. */
8218c2ecf20Sopenharmony_ci	case 0x41: /* Record sequence error. */
8228c2ecf20Sopenharmony_ci		/* All other erpas are reserved for future use. */
8238c2ecf20Sopenharmony_ci	default:
8248c2ecf20Sopenharmony_ci		return tape_34xx_erp_bug(device, request, irb, sense[3]);
8258c2ecf20Sopenharmony_ci	}
8268c2ecf20Sopenharmony_ci}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci/*
8298c2ecf20Sopenharmony_ci * 3480/3490 interrupt handler
8308c2ecf20Sopenharmony_ci */
8318c2ecf20Sopenharmony_cistatic int
8328c2ecf20Sopenharmony_citape_34xx_irq(struct tape_device *device, struct tape_request *request,
8338c2ecf20Sopenharmony_ci	      struct irb *irb)
8348c2ecf20Sopenharmony_ci{
8358c2ecf20Sopenharmony_ci	if (request == NULL)
8368c2ecf20Sopenharmony_ci		return tape_34xx_unsolicited_irq(device, irb);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
8398c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
8408c2ecf20Sopenharmony_ci	    (request->op == TO_WRI)) {
8418c2ecf20Sopenharmony_ci		/* Write at end of volume */
8428c2ecf20Sopenharmony_ci		return tape_34xx_erp_failed(request, -ENOSPC);
8438c2ecf20Sopenharmony_ci	}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
8468c2ecf20Sopenharmony_ci		return tape_34xx_unit_check(device, request, irb);
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
8498c2ecf20Sopenharmony_ci		/*
8508c2ecf20Sopenharmony_ci		 * A unit exception occurs on skipping over a tapemark block.
8518c2ecf20Sopenharmony_ci		 */
8528c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
8538c2ecf20Sopenharmony_ci			if (request->op == TO_BSB || request->op == TO_FSB)
8548c2ecf20Sopenharmony_ci				request->rescnt++;
8558c2ecf20Sopenharmony_ci			else
8568c2ecf20Sopenharmony_ci				DBF_EVENT(5, "Unit Exception!\n");
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci		return tape_34xx_done(request);
8598c2ecf20Sopenharmony_ci	}
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	DBF_EVENT(6, "xunknownirq\n");
8628c2ecf20Sopenharmony_ci	tape_dump_sense_dbf(device, request, irb);
8638c2ecf20Sopenharmony_ci	return TAPE_IO_STOP;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci/*
8678c2ecf20Sopenharmony_ci * ioctl_overload
8688c2ecf20Sopenharmony_ci */
8698c2ecf20Sopenharmony_cistatic int
8708c2ecf20Sopenharmony_citape_34xx_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
8718c2ecf20Sopenharmony_ci{
8728c2ecf20Sopenharmony_ci	if (cmd == TAPE390_DISPLAY) {
8738c2ecf20Sopenharmony_ci		struct display_struct disp;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci		if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)) != 0)
8768c2ecf20Sopenharmony_ci			return -EFAULT;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci		return tape_std_display(device, &disp);
8798c2ecf20Sopenharmony_ci	} else
8808c2ecf20Sopenharmony_ci		return -EINVAL;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic inline void
8848c2ecf20Sopenharmony_citape_34xx_append_new_sbid(struct tape_34xx_block_id bid, struct list_head *l)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	struct tape_34xx_sbid *	new_sbid;
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	new_sbid = kmalloc(sizeof(*new_sbid), GFP_ATOMIC);
8898c2ecf20Sopenharmony_ci	if (!new_sbid)
8908c2ecf20Sopenharmony_ci		return;
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	new_sbid->bid = bid;
8938c2ecf20Sopenharmony_ci	list_add(&new_sbid->list, l);
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci/*
8978c2ecf20Sopenharmony_ci * Build up the search block ID list. The block ID consists of a logical
8988c2ecf20Sopenharmony_ci * block number and a hardware specific part. The hardware specific part
8998c2ecf20Sopenharmony_ci * helps the tape drive to speed up searching for a specific block.
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic void
9028c2ecf20Sopenharmony_citape_34xx_add_sbid(struct tape_device *device, struct tape_34xx_block_id bid)
9038c2ecf20Sopenharmony_ci{
9048c2ecf20Sopenharmony_ci	struct list_head *	sbid_list;
9058c2ecf20Sopenharmony_ci	struct tape_34xx_sbid *	sbid;
9068c2ecf20Sopenharmony_ci	struct list_head *	l;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	/*
9098c2ecf20Sopenharmony_ci	 * immediately return if there is no list at all or the block to add
9108c2ecf20Sopenharmony_ci	 * is located in segment 1 of wrap 0 because this position is used
9118c2ecf20Sopenharmony_ci	 * if no hardware position data is supplied.
9128c2ecf20Sopenharmony_ci	 */
9138c2ecf20Sopenharmony_ci	sbid_list = (struct list_head *) device->discdata;
9148c2ecf20Sopenharmony_ci	if (!sbid_list || (bid.segment < 2 && bid.wrap == 0))
9158c2ecf20Sopenharmony_ci		return;
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	/*
9188c2ecf20Sopenharmony_ci	 * Search the position where to insert the new entry. Hardware
9198c2ecf20Sopenharmony_ci	 * acceleration uses only the segment and wrap number. So we
9208c2ecf20Sopenharmony_ci	 * need only one entry for a specific wrap/segment combination.
9218c2ecf20Sopenharmony_ci	 * If there is a block with a lower number but the same hard-
9228c2ecf20Sopenharmony_ci	 * ware position data we just update the block number in the
9238c2ecf20Sopenharmony_ci	 * existing entry.
9248c2ecf20Sopenharmony_ci	 */
9258c2ecf20Sopenharmony_ci	list_for_each(l, sbid_list) {
9268c2ecf20Sopenharmony_ci		sbid = list_entry(l, struct tape_34xx_sbid, list);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci		if (
9298c2ecf20Sopenharmony_ci			(sbid->bid.segment == bid.segment) &&
9308c2ecf20Sopenharmony_ci			(sbid->bid.wrap    == bid.wrap)
9318c2ecf20Sopenharmony_ci		) {
9328c2ecf20Sopenharmony_ci			if (bid.block < sbid->bid.block)
9338c2ecf20Sopenharmony_ci				sbid->bid = bid;
9348c2ecf20Sopenharmony_ci			else return;
9358c2ecf20Sopenharmony_ci			break;
9368c2ecf20Sopenharmony_ci		}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		/* Sort in according to logical block number. */
9398c2ecf20Sopenharmony_ci		if (bid.block < sbid->bid.block) {
9408c2ecf20Sopenharmony_ci			tape_34xx_append_new_sbid(bid, l->prev);
9418c2ecf20Sopenharmony_ci			break;
9428c2ecf20Sopenharmony_ci		}
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci	/* List empty or new block bigger than last entry. */
9458c2ecf20Sopenharmony_ci	if (l == sbid_list)
9468c2ecf20Sopenharmony_ci		tape_34xx_append_new_sbid(bid, l->prev);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	DBF_LH(4, "Current list is:\n");
9498c2ecf20Sopenharmony_ci	list_for_each(l, sbid_list) {
9508c2ecf20Sopenharmony_ci		sbid = list_entry(l, struct tape_34xx_sbid, list);
9518c2ecf20Sopenharmony_ci		DBF_LH(4, "%d:%03d@%05d\n",
9528c2ecf20Sopenharmony_ci			sbid->bid.wrap,
9538c2ecf20Sopenharmony_ci			sbid->bid.segment,
9548c2ecf20Sopenharmony_ci			sbid->bid.block
9558c2ecf20Sopenharmony_ci		);
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci}
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci/*
9608c2ecf20Sopenharmony_ci * Delete all entries from the search block ID list that belong to tape blocks
9618c2ecf20Sopenharmony_ci * equal or higher than the given number.
9628c2ecf20Sopenharmony_ci */
9638c2ecf20Sopenharmony_cistatic void
9648c2ecf20Sopenharmony_citape_34xx_delete_sbid_from(struct tape_device *device, int from)
9658c2ecf20Sopenharmony_ci{
9668c2ecf20Sopenharmony_ci	struct list_head *	sbid_list;
9678c2ecf20Sopenharmony_ci	struct tape_34xx_sbid *	sbid;
9688c2ecf20Sopenharmony_ci	struct list_head *	l;
9698c2ecf20Sopenharmony_ci	struct list_head *	n;
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	sbid_list = (struct list_head *) device->discdata;
9728c2ecf20Sopenharmony_ci	if (!sbid_list)
9738c2ecf20Sopenharmony_ci		return;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	list_for_each_safe(l, n, sbid_list) {
9768c2ecf20Sopenharmony_ci		sbid = list_entry(l, struct tape_34xx_sbid, list);
9778c2ecf20Sopenharmony_ci		if (sbid->bid.block >= from) {
9788c2ecf20Sopenharmony_ci			DBF_LH(4, "Delete sbid %d:%03d@%05d\n",
9798c2ecf20Sopenharmony_ci				sbid->bid.wrap,
9808c2ecf20Sopenharmony_ci				sbid->bid.segment,
9818c2ecf20Sopenharmony_ci				sbid->bid.block
9828c2ecf20Sopenharmony_ci			);
9838c2ecf20Sopenharmony_ci			list_del(l);
9848c2ecf20Sopenharmony_ci			kfree(sbid);
9858c2ecf20Sopenharmony_ci		}
9868c2ecf20Sopenharmony_ci	}
9878c2ecf20Sopenharmony_ci}
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci/*
9908c2ecf20Sopenharmony_ci * Merge hardware position data into a block id.
9918c2ecf20Sopenharmony_ci */
9928c2ecf20Sopenharmony_cistatic void
9938c2ecf20Sopenharmony_citape_34xx_merge_sbid(
9948c2ecf20Sopenharmony_ci	struct tape_device *		device,
9958c2ecf20Sopenharmony_ci	struct tape_34xx_block_id *	bid
9968c2ecf20Sopenharmony_ci) {
9978c2ecf20Sopenharmony_ci	struct tape_34xx_sbid *	sbid;
9988c2ecf20Sopenharmony_ci	struct tape_34xx_sbid *	sbid_to_use;
9998c2ecf20Sopenharmony_ci	struct list_head *	sbid_list;
10008c2ecf20Sopenharmony_ci	struct list_head *	l;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	sbid_list = (struct list_head *) device->discdata;
10038c2ecf20Sopenharmony_ci	bid->wrap    = 0;
10048c2ecf20Sopenharmony_ci	bid->segment = 1;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	if (!sbid_list || list_empty(sbid_list))
10078c2ecf20Sopenharmony_ci		return;
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	sbid_to_use = NULL;
10108c2ecf20Sopenharmony_ci	list_for_each(l, sbid_list) {
10118c2ecf20Sopenharmony_ci		sbid = list_entry(l, struct tape_34xx_sbid, list);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci		if (sbid->bid.block >= bid->block)
10148c2ecf20Sopenharmony_ci			break;
10158c2ecf20Sopenharmony_ci		sbid_to_use = sbid;
10168c2ecf20Sopenharmony_ci	}
10178c2ecf20Sopenharmony_ci	if (sbid_to_use) {
10188c2ecf20Sopenharmony_ci		bid->wrap    = sbid_to_use->bid.wrap;
10198c2ecf20Sopenharmony_ci		bid->segment = sbid_to_use->bid.segment;
10208c2ecf20Sopenharmony_ci		DBF_LH(4, "Use %d:%03d@%05d for %05d\n",
10218c2ecf20Sopenharmony_ci			sbid_to_use->bid.wrap,
10228c2ecf20Sopenharmony_ci			sbid_to_use->bid.segment,
10238c2ecf20Sopenharmony_ci			sbid_to_use->bid.block,
10248c2ecf20Sopenharmony_ci			bid->block
10258c2ecf20Sopenharmony_ci		);
10268c2ecf20Sopenharmony_ci	}
10278c2ecf20Sopenharmony_ci}
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_cistatic int
10308c2ecf20Sopenharmony_citape_34xx_setup_device(struct tape_device * device)
10318c2ecf20Sopenharmony_ci{
10328c2ecf20Sopenharmony_ci	int			rc;
10338c2ecf20Sopenharmony_ci	struct list_head *	discdata;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	DBF_EVENT(6, "34xx device setup\n");
10368c2ecf20Sopenharmony_ci	if ((rc = tape_std_assign(device)) == 0) {
10378c2ecf20Sopenharmony_ci		if ((rc = tape_34xx_medium_sense(device)) != 0) {
10388c2ecf20Sopenharmony_ci			DBF_LH(3, "34xx medium sense returned %d\n", rc);
10398c2ecf20Sopenharmony_ci		}
10408c2ecf20Sopenharmony_ci	}
10418c2ecf20Sopenharmony_ci	discdata = kmalloc(sizeof(struct list_head), GFP_KERNEL);
10428c2ecf20Sopenharmony_ci	if (discdata) {
10438c2ecf20Sopenharmony_ci			INIT_LIST_HEAD(discdata);
10448c2ecf20Sopenharmony_ci			device->discdata = discdata;
10458c2ecf20Sopenharmony_ci	}
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	return rc;
10488c2ecf20Sopenharmony_ci}
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_cistatic void
10518c2ecf20Sopenharmony_citape_34xx_cleanup_device(struct tape_device *device)
10528c2ecf20Sopenharmony_ci{
10538c2ecf20Sopenharmony_ci	tape_std_unassign(device);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	if (device->discdata) {
10568c2ecf20Sopenharmony_ci		tape_34xx_delete_sbid_from(device, 0);
10578c2ecf20Sopenharmony_ci		kfree(device->discdata);
10588c2ecf20Sopenharmony_ci		device->discdata = NULL;
10598c2ecf20Sopenharmony_ci	}
10608c2ecf20Sopenharmony_ci}
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci/*
10648c2ecf20Sopenharmony_ci * MTTELL: Tell block. Return the number of block relative to current file.
10658c2ecf20Sopenharmony_ci */
10668c2ecf20Sopenharmony_cistatic int
10678c2ecf20Sopenharmony_citape_34xx_mttell(struct tape_device *device, int mt_count)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct {
10708c2ecf20Sopenharmony_ci		struct tape_34xx_block_id	cbid;
10718c2ecf20Sopenharmony_ci		struct tape_34xx_block_id	dbid;
10728c2ecf20Sopenharmony_ci	} __attribute__ ((packed)) block_id;
10738c2ecf20Sopenharmony_ci	int rc;
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_ci	rc = tape_std_read_block_id(device, (__u64 *) &block_id);
10768c2ecf20Sopenharmony_ci	if (rc)
10778c2ecf20Sopenharmony_ci		return rc;
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	tape_34xx_add_sbid(device, block_id.cbid);
10808c2ecf20Sopenharmony_ci	return block_id.cbid.block;
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_ci/*
10848c2ecf20Sopenharmony_ci * MTSEEK: seek to the specified block.
10858c2ecf20Sopenharmony_ci */
10868c2ecf20Sopenharmony_cistatic int
10878c2ecf20Sopenharmony_citape_34xx_mtseek(struct tape_device *device, int mt_count)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct tape_request *request;
10908c2ecf20Sopenharmony_ci	struct tape_34xx_block_id *	bid;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	if (mt_count > 0x3fffff) {
10938c2ecf20Sopenharmony_ci		DBF_EXCEPTION(6, "xsee parm\n");
10948c2ecf20Sopenharmony_ci		return -EINVAL;
10958c2ecf20Sopenharmony_ci	}
10968c2ecf20Sopenharmony_ci	request = tape_alloc_request(3, 4);
10978c2ecf20Sopenharmony_ci	if (IS_ERR(request))
10988c2ecf20Sopenharmony_ci		return PTR_ERR(request);
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	/* setup ccws */
11018c2ecf20Sopenharmony_ci	request->op = TO_LBL;
11028c2ecf20Sopenharmony_ci	bid         = (struct tape_34xx_block_id *) request->cpdata;
11038c2ecf20Sopenharmony_ci	bid->format = (*device->modeset_byte & 0x08) ?
11048c2ecf20Sopenharmony_ci			TAPE34XX_FMT_3480_XF : TAPE34XX_FMT_3480;
11058c2ecf20Sopenharmony_ci	bid->block  = mt_count;
11068c2ecf20Sopenharmony_ci	tape_34xx_merge_sbid(device, bid);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
11098c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
11108c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	/* execute it */
11138c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
11148c2ecf20Sopenharmony_ci}
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci/*
11178c2ecf20Sopenharmony_ci * List of 3480/3490 magnetic tape commands.
11188c2ecf20Sopenharmony_ci */
11198c2ecf20Sopenharmony_cistatic tape_mtop_fn tape_34xx_mtop[TAPE_NR_MTOPS] = {
11208c2ecf20Sopenharmony_ci	[MTRESET]	 = tape_std_mtreset,
11218c2ecf20Sopenharmony_ci	[MTFSF]		 = tape_std_mtfsf,
11228c2ecf20Sopenharmony_ci	[MTBSF]		 = tape_std_mtbsf,
11238c2ecf20Sopenharmony_ci	[MTFSR]		 = tape_std_mtfsr,
11248c2ecf20Sopenharmony_ci	[MTBSR]		 = tape_std_mtbsr,
11258c2ecf20Sopenharmony_ci	[MTWEOF]	 = tape_std_mtweof,
11268c2ecf20Sopenharmony_ci	[MTREW]		 = tape_std_mtrew,
11278c2ecf20Sopenharmony_ci	[MTOFFL]	 = tape_std_mtoffl,
11288c2ecf20Sopenharmony_ci	[MTNOP]		 = tape_std_mtnop,
11298c2ecf20Sopenharmony_ci	[MTRETEN]	 = tape_std_mtreten,
11308c2ecf20Sopenharmony_ci	[MTBSFM]	 = tape_std_mtbsfm,
11318c2ecf20Sopenharmony_ci	[MTFSFM]	 = tape_std_mtfsfm,
11328c2ecf20Sopenharmony_ci	[MTEOM]		 = tape_std_mteom,
11338c2ecf20Sopenharmony_ci	[MTERASE]	 = tape_std_mterase,
11348c2ecf20Sopenharmony_ci	[MTRAS1]	 = NULL,
11358c2ecf20Sopenharmony_ci	[MTRAS2]	 = NULL,
11368c2ecf20Sopenharmony_ci	[MTRAS3]	 = NULL,
11378c2ecf20Sopenharmony_ci	[MTSETBLK]	 = tape_std_mtsetblk,
11388c2ecf20Sopenharmony_ci	[MTSETDENSITY]	 = NULL,
11398c2ecf20Sopenharmony_ci	[MTSEEK]	 = tape_34xx_mtseek,
11408c2ecf20Sopenharmony_ci	[MTTELL]	 = tape_34xx_mttell,
11418c2ecf20Sopenharmony_ci	[MTSETDRVBUFFER] = NULL,
11428c2ecf20Sopenharmony_ci	[MTFSS]		 = NULL,
11438c2ecf20Sopenharmony_ci	[MTBSS]		 = NULL,
11448c2ecf20Sopenharmony_ci	[MTWSM]		 = NULL,
11458c2ecf20Sopenharmony_ci	[MTLOCK]	 = NULL,
11468c2ecf20Sopenharmony_ci	[MTUNLOCK]	 = NULL,
11478c2ecf20Sopenharmony_ci	[MTLOAD]	 = tape_std_mtload,
11488c2ecf20Sopenharmony_ci	[MTUNLOAD]	 = tape_std_mtunload,
11498c2ecf20Sopenharmony_ci	[MTCOMPRESSION]	 = tape_std_mtcompression,
11508c2ecf20Sopenharmony_ci	[MTSETPART]	 = NULL,
11518c2ecf20Sopenharmony_ci	[MTMKPART]	 = NULL
11528c2ecf20Sopenharmony_ci};
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci/*
11558c2ecf20Sopenharmony_ci * Tape discipline structure for 3480 and 3490.
11568c2ecf20Sopenharmony_ci */
11578c2ecf20Sopenharmony_cistatic struct tape_discipline tape_discipline_34xx = {
11588c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
11598c2ecf20Sopenharmony_ci	.setup_device = tape_34xx_setup_device,
11608c2ecf20Sopenharmony_ci	.cleanup_device = tape_34xx_cleanup_device,
11618c2ecf20Sopenharmony_ci	.process_eov = tape_std_process_eov,
11628c2ecf20Sopenharmony_ci	.irq = tape_34xx_irq,
11638c2ecf20Sopenharmony_ci	.read_block = tape_std_read_block,
11648c2ecf20Sopenharmony_ci	.write_block = tape_std_write_block,
11658c2ecf20Sopenharmony_ci	.ioctl_fn = tape_34xx_ioctl,
11668c2ecf20Sopenharmony_ci	.mtop_array = tape_34xx_mtop
11678c2ecf20Sopenharmony_ci};
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_cistatic struct ccw_device_id tape_34xx_ids[] = {
11708c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE(0x3480, 0, 0x3480, 0), .driver_info = tape_3480},
11718c2ecf20Sopenharmony_ci	{ CCW_DEVICE_DEVTYPE(0x3490, 0, 0x3490, 0), .driver_info = tape_3490},
11728c2ecf20Sopenharmony_ci	{ /* end of list */ },
11738c2ecf20Sopenharmony_ci};
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_cistatic int
11768c2ecf20Sopenharmony_citape_34xx_online(struct ccw_device *cdev)
11778c2ecf20Sopenharmony_ci{
11788c2ecf20Sopenharmony_ci	return tape_generic_online(
11798c2ecf20Sopenharmony_ci		dev_get_drvdata(&cdev->dev),
11808c2ecf20Sopenharmony_ci		&tape_discipline_34xx
11818c2ecf20Sopenharmony_ci	);
11828c2ecf20Sopenharmony_ci}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic struct ccw_driver tape_34xx_driver = {
11858c2ecf20Sopenharmony_ci	.driver = {
11868c2ecf20Sopenharmony_ci		.name = "tape_34xx",
11878c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
11888c2ecf20Sopenharmony_ci	},
11898c2ecf20Sopenharmony_ci	.ids = tape_34xx_ids,
11908c2ecf20Sopenharmony_ci	.probe = tape_generic_probe,
11918c2ecf20Sopenharmony_ci	.remove = tape_generic_remove,
11928c2ecf20Sopenharmony_ci	.set_online = tape_34xx_online,
11938c2ecf20Sopenharmony_ci	.set_offline = tape_generic_offline,
11948c2ecf20Sopenharmony_ci	.freeze = tape_generic_pm_suspend,
11958c2ecf20Sopenharmony_ci	.int_class = IRQIO_TAP,
11968c2ecf20Sopenharmony_ci};
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_cistatic int
11998c2ecf20Sopenharmony_citape_34xx_init (void)
12008c2ecf20Sopenharmony_ci{
12018c2ecf20Sopenharmony_ci	int rc;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	TAPE_DBF_AREA = debug_register ( "tape_34xx", 2, 2, 4*sizeof(long));
12048c2ecf20Sopenharmony_ci	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
12058c2ecf20Sopenharmony_ci#ifdef DBF_LIKE_HELL
12068c2ecf20Sopenharmony_ci	debug_set_level(TAPE_DBF_AREA, 6);
12078c2ecf20Sopenharmony_ci#endif
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	DBF_EVENT(3, "34xx init\n");
12108c2ecf20Sopenharmony_ci	/* Register driver for 3480/3490 tapes. */
12118c2ecf20Sopenharmony_ci	rc = ccw_driver_register(&tape_34xx_driver);
12128c2ecf20Sopenharmony_ci	if (rc)
12138c2ecf20Sopenharmony_ci		DBF_EVENT(3, "34xx init failed\n");
12148c2ecf20Sopenharmony_ci	else
12158c2ecf20Sopenharmony_ci		DBF_EVENT(3, "34xx registered\n");
12168c2ecf20Sopenharmony_ci	return rc;
12178c2ecf20Sopenharmony_ci}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_cistatic void
12208c2ecf20Sopenharmony_citape_34xx_exit(void)
12218c2ecf20Sopenharmony_ci{
12228c2ecf20Sopenharmony_ci	ccw_driver_unregister(&tape_34xx_driver);
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	debug_unregister(TAPE_DBF_AREA);
12258c2ecf20Sopenharmony_ci}
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, tape_34xx_ids);
12288c2ecf20Sopenharmony_ciMODULE_AUTHOR("(C) 2001-2002 IBM Deutschland Entwicklung GmbH");
12298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux on zSeries channel attached 3480 tape device driver");
12308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_cimodule_init(tape_34xx_init);
12338c2ecf20Sopenharmony_cimodule_exit(tape_34xx_exit);
1234