18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    tape device discipline for 3590 tapes.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2001, 2009
68c2ecf20Sopenharmony_ci *    Author(s): Stefan Bader <shbader@de.ibm.com>
78c2ecf20Sopenharmony_ci *		 Michael Holzheu <holzheu@de.ibm.com>
88c2ecf20Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "tape_3590"
128c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/bio.h>
188c2ecf20Sopenharmony_ci#include <asm/ebcdic.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define TAPE_DBF_AREA	tape_3590_dbf
218c2ecf20Sopenharmony_ci#define BUFSIZE 512	/* size of buffers for dynamic generated messages */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "tape.h"
248c2ecf20Sopenharmony_ci#include "tape_std.h"
258c2ecf20Sopenharmony_ci#include "tape_3590.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct workqueue_struct *tape_3590_wq;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/*
308c2ecf20Sopenharmony_ci * Pointer to debug area.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cidebug_info_t *TAPE_DBF_AREA = NULL;
338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(TAPE_DBF_AREA);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*******************************************************************
368c2ecf20Sopenharmony_ci * Error Recovery functions:
378c2ecf20Sopenharmony_ci * - Read Opposite:		 implemented
388c2ecf20Sopenharmony_ci * - Read Device (buffered) log: BRA
398c2ecf20Sopenharmony_ci * - Read Library log:		 BRA
408c2ecf20Sopenharmony_ci * - Swap Devices:		 BRA
418c2ecf20Sopenharmony_ci * - Long Busy:			 implemented
428c2ecf20Sopenharmony_ci * - Special Intercept:		 BRA
438c2ecf20Sopenharmony_ci * - Read Alternate:		 implemented
448c2ecf20Sopenharmony_ci *******************************************************************/
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic const char *tape_3590_msg[TAPE_3590_MAX_MSG] = {
478c2ecf20Sopenharmony_ci	[0x00] = "",
488c2ecf20Sopenharmony_ci	[0x10] = "Lost Sense",
498c2ecf20Sopenharmony_ci	[0x11] = "Assigned Elsewhere",
508c2ecf20Sopenharmony_ci	[0x12] = "Allegiance Reset",
518c2ecf20Sopenharmony_ci	[0x13] = "Shared Access Violation",
528c2ecf20Sopenharmony_ci	[0x20] = "Command Reject",
538c2ecf20Sopenharmony_ci	[0x21] = "Configuration Error",
548c2ecf20Sopenharmony_ci	[0x22] = "Protection Exception",
558c2ecf20Sopenharmony_ci	[0x23] = "Write Protect",
568c2ecf20Sopenharmony_ci	[0x24] = "Write Length",
578c2ecf20Sopenharmony_ci	[0x25] = "Read-Only Format",
588c2ecf20Sopenharmony_ci	[0x31] = "Beginning of Partition",
598c2ecf20Sopenharmony_ci	[0x33] = "End of Partition",
608c2ecf20Sopenharmony_ci	[0x34] = "End of Data",
618c2ecf20Sopenharmony_ci	[0x35] = "Block not found",
628c2ecf20Sopenharmony_ci	[0x40] = "Device Intervention",
638c2ecf20Sopenharmony_ci	[0x41] = "Loader Intervention",
648c2ecf20Sopenharmony_ci	[0x42] = "Library Intervention",
658c2ecf20Sopenharmony_ci	[0x50] = "Write Error",
668c2ecf20Sopenharmony_ci	[0x51] = "Erase Error",
678c2ecf20Sopenharmony_ci	[0x52] = "Formatting Error",
688c2ecf20Sopenharmony_ci	[0x53] = "Read Error",
698c2ecf20Sopenharmony_ci	[0x54] = "Unsupported Format",
708c2ecf20Sopenharmony_ci	[0x55] = "No Formatting",
718c2ecf20Sopenharmony_ci	[0x56] = "Positioning lost",
728c2ecf20Sopenharmony_ci	[0x57] = "Read Length",
738c2ecf20Sopenharmony_ci	[0x60] = "Unsupported Medium",
748c2ecf20Sopenharmony_ci	[0x61] = "Medium Length Error",
758c2ecf20Sopenharmony_ci	[0x62] = "Medium removed",
768c2ecf20Sopenharmony_ci	[0x64] = "Load Check",
778c2ecf20Sopenharmony_ci	[0x65] = "Unload Check",
788c2ecf20Sopenharmony_ci	[0x70] = "Equipment Check",
798c2ecf20Sopenharmony_ci	[0x71] = "Bus out Check",
808c2ecf20Sopenharmony_ci	[0x72] = "Protocol Error",
818c2ecf20Sopenharmony_ci	[0x73] = "Interface Error",
828c2ecf20Sopenharmony_ci	[0x74] = "Overrun",
838c2ecf20Sopenharmony_ci	[0x75] = "Halt Signal",
848c2ecf20Sopenharmony_ci	[0x90] = "Device fenced",
858c2ecf20Sopenharmony_ci	[0x91] = "Device Path fenced",
868c2ecf20Sopenharmony_ci	[0xa0] = "Volume misplaced",
878c2ecf20Sopenharmony_ci	[0xa1] = "Volume inaccessible",
888c2ecf20Sopenharmony_ci	[0xa2] = "Volume in input",
898c2ecf20Sopenharmony_ci	[0xa3] = "Volume ejected",
908c2ecf20Sopenharmony_ci	[0xa4] = "All categories reserved",
918c2ecf20Sopenharmony_ci	[0xa5] = "Duplicate Volume",
928c2ecf20Sopenharmony_ci	[0xa6] = "Library Manager Offline",
938c2ecf20Sopenharmony_ci	[0xa7] = "Library Output Station full",
948c2ecf20Sopenharmony_ci	[0xa8] = "Vision System non-operational",
958c2ecf20Sopenharmony_ci	[0xa9] = "Library Manager Equipment Check",
968c2ecf20Sopenharmony_ci	[0xaa] = "Library Equipment Check",
978c2ecf20Sopenharmony_ci	[0xab] = "All Library Cells full",
988c2ecf20Sopenharmony_ci	[0xac] = "No Cleaner Volumes in Library",
998c2ecf20Sopenharmony_ci	[0xad] = "I/O Station door open",
1008c2ecf20Sopenharmony_ci	[0xae] = "Subsystem environmental alert",
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int crypt_supported(struct tape_device *device)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	return TAPE390_CRYPT_SUPPORTED(TAPE_3590_CRYPT_INFO(device));
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int crypt_enabled(struct tape_device *device)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	return TAPE390_CRYPT_ON(TAPE_3590_CRYPT_INFO(device));
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic void ext_to_int_kekl(struct tape390_kekl *in,
1148c2ecf20Sopenharmony_ci			    struct tape3592_kekl *out)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int len;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	memset(out, 0, sizeof(*out));
1198c2ecf20Sopenharmony_ci	if (in->type == TAPE390_KEKL_TYPE_HASH)
1208c2ecf20Sopenharmony_ci		out->flags |= 0x40;
1218c2ecf20Sopenharmony_ci	if (in->type_on_tape == TAPE390_KEKL_TYPE_HASH)
1228c2ecf20Sopenharmony_ci		out->flags |= 0x80;
1238c2ecf20Sopenharmony_ci	len = min(sizeof(out->label), strlen(in->label));
1248c2ecf20Sopenharmony_ci	memcpy(out->label, in->label, len);
1258c2ecf20Sopenharmony_ci	memset(out->label + len, ' ', sizeof(out->label) - len);
1268c2ecf20Sopenharmony_ci	ASCEBC(out->label, sizeof(out->label));
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void int_to_ext_kekl(struct tape3592_kekl *in,
1308c2ecf20Sopenharmony_ci			    struct tape390_kekl *out)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	memset(out, 0, sizeof(*out));
1338c2ecf20Sopenharmony_ci	if(in->flags & 0x40)
1348c2ecf20Sopenharmony_ci		out->type = TAPE390_KEKL_TYPE_HASH;
1358c2ecf20Sopenharmony_ci	else
1368c2ecf20Sopenharmony_ci		out->type = TAPE390_KEKL_TYPE_LABEL;
1378c2ecf20Sopenharmony_ci	if(in->flags & 0x80)
1388c2ecf20Sopenharmony_ci		out->type_on_tape = TAPE390_KEKL_TYPE_HASH;
1398c2ecf20Sopenharmony_ci	else
1408c2ecf20Sopenharmony_ci		out->type_on_tape = TAPE390_KEKL_TYPE_LABEL;
1418c2ecf20Sopenharmony_ci	memcpy(out->label, in->label, sizeof(in->label));
1428c2ecf20Sopenharmony_ci	EBCASC(out->label, sizeof(in->label));
1438c2ecf20Sopenharmony_ci	strim(out->label);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void int_to_ext_kekl_pair(struct tape3592_kekl_pair *in,
1478c2ecf20Sopenharmony_ci				 struct tape390_kekl_pair *out)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	if (in->count == 0) {
1508c2ecf20Sopenharmony_ci		out->kekl[0].type = TAPE390_KEKL_TYPE_NONE;
1518c2ecf20Sopenharmony_ci		out->kekl[0].type_on_tape = TAPE390_KEKL_TYPE_NONE;
1528c2ecf20Sopenharmony_ci		out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
1538c2ecf20Sopenharmony_ci		out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
1548c2ecf20Sopenharmony_ci	} else if (in->count == 1) {
1558c2ecf20Sopenharmony_ci		int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
1568c2ecf20Sopenharmony_ci		out->kekl[1].type = TAPE390_KEKL_TYPE_NONE;
1578c2ecf20Sopenharmony_ci		out->kekl[1].type_on_tape = TAPE390_KEKL_TYPE_NONE;
1588c2ecf20Sopenharmony_ci	} else if (in->count == 2) {
1598c2ecf20Sopenharmony_ci		int_to_ext_kekl(&in->kekl[0], &out->kekl[0]);
1608c2ecf20Sopenharmony_ci		int_to_ext_kekl(&in->kekl[1], &out->kekl[1]);
1618c2ecf20Sopenharmony_ci	} else {
1628c2ecf20Sopenharmony_ci		printk("Invalid KEKL number: %d\n", in->count);
1638c2ecf20Sopenharmony_ci		BUG();
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int check_ext_kekl(struct tape390_kekl *kekl)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	if (kekl->type == TAPE390_KEKL_TYPE_NONE)
1708c2ecf20Sopenharmony_ci		goto invalid;
1718c2ecf20Sopenharmony_ci	if (kekl->type > TAPE390_KEKL_TYPE_HASH)
1728c2ecf20Sopenharmony_ci		goto invalid;
1738c2ecf20Sopenharmony_ci	if (kekl->type_on_tape == TAPE390_KEKL_TYPE_NONE)
1748c2ecf20Sopenharmony_ci		goto invalid;
1758c2ecf20Sopenharmony_ci	if (kekl->type_on_tape > TAPE390_KEKL_TYPE_HASH)
1768c2ecf20Sopenharmony_ci		goto invalid;
1778c2ecf20Sopenharmony_ci	if ((kekl->type == TAPE390_KEKL_TYPE_HASH) &&
1788c2ecf20Sopenharmony_ci	    (kekl->type_on_tape == TAPE390_KEKL_TYPE_LABEL))
1798c2ecf20Sopenharmony_ci		goto invalid;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return 0;
1828c2ecf20Sopenharmony_ciinvalid:
1838c2ecf20Sopenharmony_ci	return -EINVAL;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int check_ext_kekl_pair(struct tape390_kekl_pair *kekls)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	if (check_ext_kekl(&kekls->kekl[0]))
1898c2ecf20Sopenharmony_ci		goto invalid;
1908c2ecf20Sopenharmony_ci	if (check_ext_kekl(&kekls->kekl[1]))
1918c2ecf20Sopenharmony_ci		goto invalid;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return 0;
1948c2ecf20Sopenharmony_ciinvalid:
1958c2ecf20Sopenharmony_ci	return -EINVAL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/*
1998c2ecf20Sopenharmony_ci * Query KEKLs
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cistatic int tape_3592_kekl_query(struct tape_device *device,
2028c2ecf20Sopenharmony_ci				struct tape390_kekl_pair *ext_kekls)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct tape_request *request;
2058c2ecf20Sopenharmony_ci	struct tape3592_kekl_query_order *order;
2068c2ecf20Sopenharmony_ci	struct tape3592_kekl_query_data *int_kekls;
2078c2ecf20Sopenharmony_ci	int rc;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape3592_kekl_query\n");
2108c2ecf20Sopenharmony_ci	int_kekls = kmalloc(sizeof(*int_kekls), GFP_KERNEL|GFP_DMA);
2118c2ecf20Sopenharmony_ci	if (!int_kekls)
2128c2ecf20Sopenharmony_ci		return -ENOMEM;
2138c2ecf20Sopenharmony_ci	request = tape_alloc_request(2, sizeof(*order));
2148c2ecf20Sopenharmony_ci	if (IS_ERR(request)) {
2158c2ecf20Sopenharmony_ci		rc = PTR_ERR(request);
2168c2ecf20Sopenharmony_ci		goto fail_malloc;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci	order = request->cpdata;
2198c2ecf20Sopenharmony_ci	memset(order,0,sizeof(*order));
2208c2ecf20Sopenharmony_ci	order->code = 0xe2;
2218c2ecf20Sopenharmony_ci	order->max_count = 2;
2228c2ecf20Sopenharmony_ci	request->op = TO_KEKL_QUERY;
2238c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
2248c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 1, READ_SS_DATA, sizeof(*int_kekls),
2258c2ecf20Sopenharmony_ci		     int_kekls);
2268c2ecf20Sopenharmony_ci	rc = tape_do_io(device, request);
2278c2ecf20Sopenharmony_ci	if (rc)
2288c2ecf20Sopenharmony_ci		goto fail_request;
2298c2ecf20Sopenharmony_ci	int_to_ext_kekl_pair(&int_kekls->kekls, ext_kekls);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	rc = 0;
2328c2ecf20Sopenharmony_cifail_request:
2338c2ecf20Sopenharmony_ci	tape_free_request(request);
2348c2ecf20Sopenharmony_cifail_malloc:
2358c2ecf20Sopenharmony_ci	kfree(int_kekls);
2368c2ecf20Sopenharmony_ci	return rc;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci/*
2408c2ecf20Sopenharmony_ci * IOCTL: Query KEKLs
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_cistatic int tape_3592_ioctl_kekl_query(struct tape_device *device,
2438c2ecf20Sopenharmony_ci				      unsigned long arg)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int rc;
2468c2ecf20Sopenharmony_ci	struct tape390_kekl_pair *ext_kekls;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_ioctl_kekl_query\n");
2498c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
2508c2ecf20Sopenharmony_ci		return -ENOSYS;
2518c2ecf20Sopenharmony_ci	if (!crypt_enabled(device))
2528c2ecf20Sopenharmony_ci		return -EUNATCH;
2538c2ecf20Sopenharmony_ci	ext_kekls = kmalloc(sizeof(*ext_kekls), GFP_KERNEL);
2548c2ecf20Sopenharmony_ci	if (!ext_kekls)
2558c2ecf20Sopenharmony_ci		return -ENOMEM;
2568c2ecf20Sopenharmony_ci	rc = tape_3592_kekl_query(device, ext_kekls);
2578c2ecf20Sopenharmony_ci	if (rc != 0)
2588c2ecf20Sopenharmony_ci		goto fail;
2598c2ecf20Sopenharmony_ci	if (copy_to_user((char __user *) arg, ext_kekls, sizeof(*ext_kekls))) {
2608c2ecf20Sopenharmony_ci		rc = -EFAULT;
2618c2ecf20Sopenharmony_ci		goto fail;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci	rc = 0;
2648c2ecf20Sopenharmony_cifail:
2658c2ecf20Sopenharmony_ci	kfree(ext_kekls);
2668c2ecf20Sopenharmony_ci	return rc;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int tape_3590_mttell(struct tape_device *device, int mt_count);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci/*
2728c2ecf20Sopenharmony_ci * Set KEKLs
2738c2ecf20Sopenharmony_ci */
2748c2ecf20Sopenharmony_cistatic int tape_3592_kekl_set(struct tape_device *device,
2758c2ecf20Sopenharmony_ci			      struct tape390_kekl_pair *ext_kekls)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct tape_request *request;
2788c2ecf20Sopenharmony_ci	struct tape3592_kekl_set_order *order;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape3592_kekl_set\n");
2818c2ecf20Sopenharmony_ci	if (check_ext_kekl_pair(ext_kekls)) {
2828c2ecf20Sopenharmony_ci		DBF_EVENT(6, "invalid kekls\n");
2838c2ecf20Sopenharmony_ci		return -EINVAL;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci	if (tape_3590_mttell(device, 0) != 0)
2868c2ecf20Sopenharmony_ci		return -EBADSLT;
2878c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, sizeof(*order));
2888c2ecf20Sopenharmony_ci	if (IS_ERR(request))
2898c2ecf20Sopenharmony_ci		return PTR_ERR(request);
2908c2ecf20Sopenharmony_ci	order = request->cpdata;
2918c2ecf20Sopenharmony_ci	memset(order, 0, sizeof(*order));
2928c2ecf20Sopenharmony_ci	order->code = 0xe3;
2938c2ecf20Sopenharmony_ci	order->kekls.count = 2;
2948c2ecf20Sopenharmony_ci	ext_to_int_kekl(&ext_kekls->kekl[0], &order->kekls.kekl[0]);
2958c2ecf20Sopenharmony_ci	ext_to_int_kekl(&ext_kekls->kekl[1], &order->kekls.kekl[1]);
2968c2ecf20Sopenharmony_ci	request->op = TO_KEKL_SET;
2978c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, PERF_SUBSYS_FUNC, sizeof(*order), order);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/*
3038c2ecf20Sopenharmony_ci * IOCTL: Set KEKLs
3048c2ecf20Sopenharmony_ci */
3058c2ecf20Sopenharmony_cistatic int tape_3592_ioctl_kekl_set(struct tape_device *device,
3068c2ecf20Sopenharmony_ci				    unsigned long arg)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int rc;
3098c2ecf20Sopenharmony_ci	struct tape390_kekl_pair *ext_kekls;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_ioctl_kekl_set\n");
3128c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
3138c2ecf20Sopenharmony_ci		return -ENOSYS;
3148c2ecf20Sopenharmony_ci	if (!crypt_enabled(device))
3158c2ecf20Sopenharmony_ci		return -EUNATCH;
3168c2ecf20Sopenharmony_ci	ext_kekls = memdup_user((char __user *)arg, sizeof(*ext_kekls));
3178c2ecf20Sopenharmony_ci	if (IS_ERR(ext_kekls))
3188c2ecf20Sopenharmony_ci		return PTR_ERR(ext_kekls);
3198c2ecf20Sopenharmony_ci	rc = tape_3592_kekl_set(device, ext_kekls);
3208c2ecf20Sopenharmony_ci	kfree(ext_kekls);
3218c2ecf20Sopenharmony_ci	return rc;
3228c2ecf20Sopenharmony_ci}
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/*
3258c2ecf20Sopenharmony_ci * Enable encryption
3268c2ecf20Sopenharmony_ci */
3278c2ecf20Sopenharmony_cistatic struct tape_request *__tape_3592_enable_crypt(struct tape_device *device)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct tape_request *request;
3308c2ecf20Sopenharmony_ci	char *data;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_enable_crypt\n");
3338c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
3348c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOSYS);
3358c2ecf20Sopenharmony_ci	request = tape_alloc_request(2, 72);
3368c2ecf20Sopenharmony_ci	if (IS_ERR(request))
3378c2ecf20Sopenharmony_ci		return request;
3388c2ecf20Sopenharmony_ci	data = request->cpdata;
3398c2ecf20Sopenharmony_ci	memset(data,0,72);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	data[0]       = 0x05;
3428c2ecf20Sopenharmony_ci	data[36 + 0]  = 0x03;
3438c2ecf20Sopenharmony_ci	data[36 + 1]  = 0x03;
3448c2ecf20Sopenharmony_ci	data[36 + 4]  = 0x40;
3458c2ecf20Sopenharmony_ci	data[36 + 6]  = 0x01;
3468c2ecf20Sopenharmony_ci	data[36 + 14] = 0x2f;
3478c2ecf20Sopenharmony_ci	data[36 + 18] = 0xc3;
3488c2ecf20Sopenharmony_ci	data[36 + 35] = 0x72;
3498c2ecf20Sopenharmony_ci	request->op = TO_CRYPT_ON;
3508c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
3518c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
3528c2ecf20Sopenharmony_ci	return request;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int tape_3592_enable_crypt(struct tape_device *device)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct tape_request *request;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	request = __tape_3592_enable_crypt(device);
3608c2ecf20Sopenharmony_ci	if (IS_ERR(request))
3618c2ecf20Sopenharmony_ci		return PTR_ERR(request);
3628c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic void tape_3592_enable_crypt_async(struct tape_device *device)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct tape_request *request;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	request = __tape_3592_enable_crypt(device);
3708c2ecf20Sopenharmony_ci	if (!IS_ERR(request))
3718c2ecf20Sopenharmony_ci		tape_do_io_async_free(device, request);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci/*
3758c2ecf20Sopenharmony_ci * Disable encryption
3768c2ecf20Sopenharmony_ci */
3778c2ecf20Sopenharmony_cistatic struct tape_request *__tape_3592_disable_crypt(struct tape_device *device)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct tape_request *request;
3808c2ecf20Sopenharmony_ci	char *data;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_disable_crypt\n");
3838c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
3848c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOSYS);
3858c2ecf20Sopenharmony_ci	request = tape_alloc_request(2, 72);
3868c2ecf20Sopenharmony_ci	if (IS_ERR(request))
3878c2ecf20Sopenharmony_ci		return request;
3888c2ecf20Sopenharmony_ci	data = request->cpdata;
3898c2ecf20Sopenharmony_ci	memset(data,0,72);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	data[0]       = 0x05;
3928c2ecf20Sopenharmony_ci	data[36 + 0]  = 0x03;
3938c2ecf20Sopenharmony_ci	data[36 + 1]  = 0x03;
3948c2ecf20Sopenharmony_ci	data[36 + 35] = 0x32;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	request->op = TO_CRYPT_OFF;
3978c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, MODE_SET_CB, 36, data);
3988c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 1, MODE_SET_CB, 36, data + 36);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return request;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int tape_3592_disable_crypt(struct tape_device *device)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	struct tape_request *request;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	request = __tape_3592_disable_crypt(device);
4088c2ecf20Sopenharmony_ci	if (IS_ERR(request))
4098c2ecf20Sopenharmony_ci		return PTR_ERR(request);
4108c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void tape_3592_disable_crypt_async(struct tape_device *device)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct tape_request *request;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	request = __tape_3592_disable_crypt(device);
4188c2ecf20Sopenharmony_ci	if (!IS_ERR(request))
4198c2ecf20Sopenharmony_ci		tape_do_io_async_free(device, request);
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/*
4238c2ecf20Sopenharmony_ci * IOCTL: Set encryption status
4248c2ecf20Sopenharmony_ci */
4258c2ecf20Sopenharmony_cistatic int tape_3592_ioctl_crypt_set(struct tape_device *device,
4268c2ecf20Sopenharmony_ci				     unsigned long arg)
4278c2ecf20Sopenharmony_ci{
4288c2ecf20Sopenharmony_ci	struct tape390_crypt_info info;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_ioctl_crypt_set\n");
4318c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
4328c2ecf20Sopenharmony_ci		return -ENOSYS;
4338c2ecf20Sopenharmony_ci	if (copy_from_user(&info, (char __user *)arg, sizeof(info)))
4348c2ecf20Sopenharmony_ci		return -EFAULT;
4358c2ecf20Sopenharmony_ci	if (info.status & ~TAPE390_CRYPT_ON_MASK)
4368c2ecf20Sopenharmony_ci		return -EINVAL;
4378c2ecf20Sopenharmony_ci	if (info.status & TAPE390_CRYPT_ON_MASK)
4388c2ecf20Sopenharmony_ci		return tape_3592_enable_crypt(device);
4398c2ecf20Sopenharmony_ci	else
4408c2ecf20Sopenharmony_ci		return tape_3592_disable_crypt(device);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int tape_3590_sense_medium(struct tape_device *device);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci/*
4468c2ecf20Sopenharmony_ci * IOCTL: Query enryption status
4478c2ecf20Sopenharmony_ci */
4488c2ecf20Sopenharmony_cistatic int tape_3592_ioctl_crypt_query(struct tape_device *device,
4498c2ecf20Sopenharmony_ci				       unsigned long arg)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	DBF_EVENT(6, "tape_3592_ioctl_crypt_query\n");
4528c2ecf20Sopenharmony_ci	if (!crypt_supported(device))
4538c2ecf20Sopenharmony_ci		return -ENOSYS;
4548c2ecf20Sopenharmony_ci	tape_3590_sense_medium(device);
4558c2ecf20Sopenharmony_ci	if (copy_to_user((char __user *) arg, &TAPE_3590_CRYPT_INFO(device),
4568c2ecf20Sopenharmony_ci		sizeof(TAPE_3590_CRYPT_INFO(device))))
4578c2ecf20Sopenharmony_ci		return -EFAULT;
4588c2ecf20Sopenharmony_ci	else
4598c2ecf20Sopenharmony_ci		return 0;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci/*
4638c2ecf20Sopenharmony_ci * 3590 IOCTL Overload
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_cistatic int
4668c2ecf20Sopenharmony_citape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
4678c2ecf20Sopenharmony_ci{
4688c2ecf20Sopenharmony_ci	switch (cmd) {
4698c2ecf20Sopenharmony_ci	case TAPE390_DISPLAY: {
4708c2ecf20Sopenharmony_ci		struct display_struct disp;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci		if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)))
4738c2ecf20Sopenharmony_ci			return -EFAULT;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci		return tape_std_display(device, &disp);
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	case TAPE390_KEKL_SET:
4788c2ecf20Sopenharmony_ci		return tape_3592_ioctl_kekl_set(device, arg);
4798c2ecf20Sopenharmony_ci	case TAPE390_KEKL_QUERY:
4808c2ecf20Sopenharmony_ci		return tape_3592_ioctl_kekl_query(device, arg);
4818c2ecf20Sopenharmony_ci	case TAPE390_CRYPT_SET:
4828c2ecf20Sopenharmony_ci		return tape_3592_ioctl_crypt_set(device, arg);
4838c2ecf20Sopenharmony_ci	case TAPE390_CRYPT_QUERY:
4848c2ecf20Sopenharmony_ci		return tape_3592_ioctl_crypt_query(device, arg);
4858c2ecf20Sopenharmony_ci	default:
4868c2ecf20Sopenharmony_ci		return -EINVAL;	/* no additional ioctls */
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci/*
4918c2ecf20Sopenharmony_ci * SENSE Medium: Get Sense data about medium state
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_cistatic int tape_3590_sense_medium(struct tape_device *device)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	struct tape_request *request;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, 128);
4988c2ecf20Sopenharmony_ci	if (IS_ERR(request))
4998c2ecf20Sopenharmony_ci		return PTR_ERR(request);
5008c2ecf20Sopenharmony_ci	request->op = TO_MSEN;
5018c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata);
5028c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic void tape_3590_sense_medium_async(struct tape_device *device)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct tape_request *request;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, 128);
5108c2ecf20Sopenharmony_ci	if (IS_ERR(request))
5118c2ecf20Sopenharmony_ci		return;
5128c2ecf20Sopenharmony_ci	request->op = TO_MSEN;
5138c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata);
5148c2ecf20Sopenharmony_ci	tape_do_io_async_free(device, request);
5158c2ecf20Sopenharmony_ci}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci/*
5188c2ecf20Sopenharmony_ci * MTTELL: Tell block. Return the number of block relative to current file.
5198c2ecf20Sopenharmony_ci */
5208c2ecf20Sopenharmony_cistatic int
5218c2ecf20Sopenharmony_citape_3590_mttell(struct tape_device *device, int mt_count)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	__u64 block_id;
5248c2ecf20Sopenharmony_ci	int rc;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	rc = tape_std_read_block_id(device, &block_id);
5278c2ecf20Sopenharmony_ci	if (rc)
5288c2ecf20Sopenharmony_ci		return rc;
5298c2ecf20Sopenharmony_ci	return block_id >> 32;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci/*
5338c2ecf20Sopenharmony_ci * MTSEEK: seek to the specified block.
5348c2ecf20Sopenharmony_ci */
5358c2ecf20Sopenharmony_cistatic int
5368c2ecf20Sopenharmony_citape_3590_mtseek(struct tape_device *device, int count)
5378c2ecf20Sopenharmony_ci{
5388c2ecf20Sopenharmony_ci	struct tape_request *request;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	DBF_EVENT(6, "xsee id: %x\n", count);
5418c2ecf20Sopenharmony_ci	request = tape_alloc_request(3, 4);
5428c2ecf20Sopenharmony_ci	if (IS_ERR(request))
5438c2ecf20Sopenharmony_ci		return PTR_ERR(request);
5448c2ecf20Sopenharmony_ci	request->op = TO_LBL;
5458c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
5468c2ecf20Sopenharmony_ci	*(__u32 *) request->cpdata = count;
5478c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
5488c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
5498c2ecf20Sopenharmony_ci	return tape_do_io_free(device, request);
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci/*
5538c2ecf20Sopenharmony_ci * Read Opposite Error Recovery Function:
5548c2ecf20Sopenharmony_ci * Used, when Read Forward does not work
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_cistatic void
5578c2ecf20Sopenharmony_citape_3590_read_opposite(struct tape_device *device,
5588c2ecf20Sopenharmony_ci			struct tape_request *request)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct tape_3590_disc_data *data;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	/*
5638c2ecf20Sopenharmony_ci	 * We have allocated 4 ccws in tape_std_read, so we can now
5648c2ecf20Sopenharmony_ci	 * transform the request to a read backward, followed by a
5658c2ecf20Sopenharmony_ci	 * forward space block.
5668c2ecf20Sopenharmony_ci	 */
5678c2ecf20Sopenharmony_ci	request->op = TO_RBA;
5688c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
5698c2ecf20Sopenharmony_ci	data = device->discdata;
5708c2ecf20Sopenharmony_ci	tape_ccw_cc_idal(request->cpaddr + 1, data->read_back_op,
5718c2ecf20Sopenharmony_ci			 device->char_data.idal_buf);
5728c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL);
5738c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL);
5748c2ecf20Sopenharmony_ci	DBF_EVENT(6, "xrop ccwg\n");
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/*
5788c2ecf20Sopenharmony_ci * Read Attention Msg
5798c2ecf20Sopenharmony_ci * This should be done after an interrupt with attention bit (0x80)
5808c2ecf20Sopenharmony_ci * in device state.
5818c2ecf20Sopenharmony_ci *
5828c2ecf20Sopenharmony_ci * After a "read attention message" request there are two possible
5838c2ecf20Sopenharmony_ci * results:
5848c2ecf20Sopenharmony_ci *
5858c2ecf20Sopenharmony_ci * 1. A unit check is presented, when attention sense is present (e.g. when
5868c2ecf20Sopenharmony_ci * a medium has been unloaded). The attention sense comes then
5878c2ecf20Sopenharmony_ci * together with the unit check. The recovery action is either "retry"
5888c2ecf20Sopenharmony_ci * (in case there is an attention message pending) or "permanent error".
5898c2ecf20Sopenharmony_ci *
5908c2ecf20Sopenharmony_ci * 2. The attention msg is written to the "read subsystem data" buffer.
5918c2ecf20Sopenharmony_ci * In this case we probably should print it to the console.
5928c2ecf20Sopenharmony_ci */
5938c2ecf20Sopenharmony_cistatic void tape_3590_read_attmsg_async(struct tape_device *device)
5948c2ecf20Sopenharmony_ci{
5958c2ecf20Sopenharmony_ci	struct tape_request *request;
5968c2ecf20Sopenharmony_ci	char *buf;
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	request = tape_alloc_request(3, 4096);
5998c2ecf20Sopenharmony_ci	if (IS_ERR(request))
6008c2ecf20Sopenharmony_ci		return;
6018c2ecf20Sopenharmony_ci	request->op = TO_READ_ATTMSG;
6028c2ecf20Sopenharmony_ci	buf = request->cpdata;
6038c2ecf20Sopenharmony_ci	buf[0] = PREP_RD_SS_DATA;
6048c2ecf20Sopenharmony_ci	buf[6] = RD_ATTMSG;	/* read att msg */
6058c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr, PERFORM_SS_FUNC, 12, buf);
6068c2ecf20Sopenharmony_ci	tape_ccw_cc(request->cpaddr + 1, READ_SS_DATA, 4096 - 12, buf + 12);
6078c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
6088c2ecf20Sopenharmony_ci	tape_do_io_async_free(device, request);
6098c2ecf20Sopenharmony_ci}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci/*
6128c2ecf20Sopenharmony_ci * These functions are used to schedule follow-up actions from within an
6138c2ecf20Sopenharmony_ci * interrupt context (like unsolicited interrupts).
6148c2ecf20Sopenharmony_ci * Note: the work handler is called by the system work queue. The tape
6158c2ecf20Sopenharmony_ci * commands started by the handler need to be asynchrounous, otherwise
6168c2ecf20Sopenharmony_ci * a deadlock can occur e.g. in case of a deferred cc=1 (see __tape_do_irq).
6178c2ecf20Sopenharmony_ci */
6188c2ecf20Sopenharmony_cistruct work_handler_data {
6198c2ecf20Sopenharmony_ci	struct tape_device *device;
6208c2ecf20Sopenharmony_ci	enum tape_op        op;
6218c2ecf20Sopenharmony_ci	struct work_struct  work;
6228c2ecf20Sopenharmony_ci};
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic void
6258c2ecf20Sopenharmony_citape_3590_work_handler(struct work_struct *work)
6268c2ecf20Sopenharmony_ci{
6278c2ecf20Sopenharmony_ci	struct work_handler_data *p =
6288c2ecf20Sopenharmony_ci		container_of(work, struct work_handler_data, work);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	switch (p->op) {
6318c2ecf20Sopenharmony_ci	case TO_MSEN:
6328c2ecf20Sopenharmony_ci		tape_3590_sense_medium_async(p->device);
6338c2ecf20Sopenharmony_ci		break;
6348c2ecf20Sopenharmony_ci	case TO_READ_ATTMSG:
6358c2ecf20Sopenharmony_ci		tape_3590_read_attmsg_async(p->device);
6368c2ecf20Sopenharmony_ci		break;
6378c2ecf20Sopenharmony_ci	case TO_CRYPT_ON:
6388c2ecf20Sopenharmony_ci		tape_3592_enable_crypt_async(p->device);
6398c2ecf20Sopenharmony_ci		break;
6408c2ecf20Sopenharmony_ci	case TO_CRYPT_OFF:
6418c2ecf20Sopenharmony_ci		tape_3592_disable_crypt_async(p->device);
6428c2ecf20Sopenharmony_ci		break;
6438c2ecf20Sopenharmony_ci	default:
6448c2ecf20Sopenharmony_ci		DBF_EVENT(3, "T3590: work handler undefined for "
6458c2ecf20Sopenharmony_ci			  "operation 0x%02x\n", p->op);
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci	tape_put_device(p->device);
6488c2ecf20Sopenharmony_ci	kfree(p);
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic int
6528c2ecf20Sopenharmony_citape_3590_schedule_work(struct tape_device *device, enum tape_op op)
6538c2ecf20Sopenharmony_ci{
6548c2ecf20Sopenharmony_ci	struct work_handler_data *p;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
6578c2ecf20Sopenharmony_ci		return -ENOMEM;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	INIT_WORK(&p->work, tape_3590_work_handler);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	p->device = tape_get_device(device);
6628c2ecf20Sopenharmony_ci	p->op = op;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	queue_work(tape_3590_wq, &p->work);
6658c2ecf20Sopenharmony_ci	return 0;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic void tape_3590_med_state_set(struct tape_device *device,
6698c2ecf20Sopenharmony_ci				    struct tape_3590_med_sense *sense)
6708c2ecf20Sopenharmony_ci{
6718c2ecf20Sopenharmony_ci	struct tape390_crypt_info *c_info;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	c_info = &TAPE_3590_CRYPT_INFO(device);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	DBF_EVENT(6, "medium state: %x:%x\n", sense->macst, sense->masst);
6768c2ecf20Sopenharmony_ci	switch (sense->macst) {
6778c2ecf20Sopenharmony_ci	case 0x04:
6788c2ecf20Sopenharmony_ci	case 0x05:
6798c2ecf20Sopenharmony_ci	case 0x06:
6808c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
6818c2ecf20Sopenharmony_ci		TAPE_3590_CRYPT_INFO(device).medium_status = 0;
6828c2ecf20Sopenharmony_ci		return;
6838c2ecf20Sopenharmony_ci	case 0x08:
6848c2ecf20Sopenharmony_ci	case 0x09:
6858c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_LOADED);
6868c2ecf20Sopenharmony_ci		break;
6878c2ecf20Sopenharmony_ci	default:
6888c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNKNOWN);
6898c2ecf20Sopenharmony_ci		return;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci	c_info->medium_status |= TAPE390_MEDIUM_LOADED_MASK;
6928c2ecf20Sopenharmony_ci	if (sense->flags & MSENSE_CRYPT_MASK) {
6938c2ecf20Sopenharmony_ci		DBF_EVENT(6, "Medium is encrypted (%04x)\n", sense->flags);
6948c2ecf20Sopenharmony_ci		c_info->medium_status |= TAPE390_MEDIUM_ENCRYPTED_MASK;
6958c2ecf20Sopenharmony_ci	} else	{
6968c2ecf20Sopenharmony_ci		DBF_EVENT(6, "Medium is not encrypted %04x\n", sense->flags);
6978c2ecf20Sopenharmony_ci		c_info->medium_status &= ~TAPE390_MEDIUM_ENCRYPTED_MASK;
6988c2ecf20Sopenharmony_ci	}
6998c2ecf20Sopenharmony_ci}
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci/*
7028c2ecf20Sopenharmony_ci * The done handler is called at device/channel end and wakes up the sleeping
7038c2ecf20Sopenharmony_ci * process
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_cistatic int
7068c2ecf20Sopenharmony_citape_3590_done(struct tape_device *device, struct tape_request *request)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	switch (request->op) {
7128c2ecf20Sopenharmony_ci	case TO_BSB:
7138c2ecf20Sopenharmony_ci	case TO_BSF:
7148c2ecf20Sopenharmony_ci	case TO_DSE:
7158c2ecf20Sopenharmony_ci	case TO_FSB:
7168c2ecf20Sopenharmony_ci	case TO_FSF:
7178c2ecf20Sopenharmony_ci	case TO_LBL:
7188c2ecf20Sopenharmony_ci	case TO_RFO:
7198c2ecf20Sopenharmony_ci	case TO_RBA:
7208c2ecf20Sopenharmony_ci	case TO_REW:
7218c2ecf20Sopenharmony_ci	case TO_WRI:
7228c2ecf20Sopenharmony_ci	case TO_WTM:
7238c2ecf20Sopenharmony_ci	case TO_BLOCK:
7248c2ecf20Sopenharmony_ci	case TO_LOAD:
7258c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_LOADED);
7268c2ecf20Sopenharmony_ci		break;
7278c2ecf20Sopenharmony_ci	case TO_RUN:
7288c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
7298c2ecf20Sopenharmony_ci		tape_3590_schedule_work(device, TO_CRYPT_OFF);
7308c2ecf20Sopenharmony_ci		break;
7318c2ecf20Sopenharmony_ci	case TO_MSEN:
7328c2ecf20Sopenharmony_ci		tape_3590_med_state_set(device, request->cpdata);
7338c2ecf20Sopenharmony_ci		break;
7348c2ecf20Sopenharmony_ci	case TO_CRYPT_ON:
7358c2ecf20Sopenharmony_ci		TAPE_3590_CRYPT_INFO(device).status
7368c2ecf20Sopenharmony_ci			|= TAPE390_CRYPT_ON_MASK;
7378c2ecf20Sopenharmony_ci		*(device->modeset_byte) |= 0x03;
7388c2ecf20Sopenharmony_ci		break;
7398c2ecf20Sopenharmony_ci	case TO_CRYPT_OFF:
7408c2ecf20Sopenharmony_ci		TAPE_3590_CRYPT_INFO(device).status
7418c2ecf20Sopenharmony_ci			&= ~TAPE390_CRYPT_ON_MASK;
7428c2ecf20Sopenharmony_ci		*(device->modeset_byte) &= ~0x03;
7438c2ecf20Sopenharmony_ci		break;
7448c2ecf20Sopenharmony_ci	case TO_RBI:	/* RBI seems to succeed even without medium loaded. */
7458c2ecf20Sopenharmony_ci	case TO_NOP:	/* Same to NOP. */
7468c2ecf20Sopenharmony_ci	case TO_READ_CONFIG:
7478c2ecf20Sopenharmony_ci	case TO_READ_ATTMSG:
7488c2ecf20Sopenharmony_ci	case TO_DIS:
7498c2ecf20Sopenharmony_ci	case TO_ASSIGN:
7508c2ecf20Sopenharmony_ci	case TO_UNASSIGN:
7518c2ecf20Sopenharmony_ci	case TO_SIZE:
7528c2ecf20Sopenharmony_ci	case TO_KEKL_SET:
7538c2ecf20Sopenharmony_ci	case TO_KEKL_QUERY:
7548c2ecf20Sopenharmony_ci	case TO_RDC:
7558c2ecf20Sopenharmony_ci		break;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci	return TAPE_IO_SUCCESS;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci/*
7618c2ecf20Sopenharmony_ci * This function is called, when error recovery was successful
7628c2ecf20Sopenharmony_ci */
7638c2ecf20Sopenharmony_cistatic inline int
7648c2ecf20Sopenharmony_citape_3590_erp_succeded(struct tape_device *device, struct tape_request *request)
7658c2ecf20Sopenharmony_ci{
7668c2ecf20Sopenharmony_ci	DBF_EVENT(3, "Error Recovery successful for %s\n",
7678c2ecf20Sopenharmony_ci		  tape_op_verbose[request->op]);
7688c2ecf20Sopenharmony_ci	return tape_3590_done(device, request);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci/*
7728c2ecf20Sopenharmony_ci * This function is called, when error recovery was not successful
7738c2ecf20Sopenharmony_ci */
7748c2ecf20Sopenharmony_cistatic inline int
7758c2ecf20Sopenharmony_citape_3590_erp_failed(struct tape_device *device, struct tape_request *request,
7768c2ecf20Sopenharmony_ci		     struct irb *irb, int rc)
7778c2ecf20Sopenharmony_ci{
7788c2ecf20Sopenharmony_ci	DBF_EVENT(3, "Error Recovery failed for %s\n",
7798c2ecf20Sopenharmony_ci		  tape_op_verbose[request->op]);
7808c2ecf20Sopenharmony_ci	tape_dump_sense_dbf(device, request, irb);
7818c2ecf20Sopenharmony_ci	return rc;
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci/*
7858c2ecf20Sopenharmony_ci * Error Recovery do retry
7868c2ecf20Sopenharmony_ci */
7878c2ecf20Sopenharmony_cistatic inline int
7888c2ecf20Sopenharmony_citape_3590_erp_retry(struct tape_device *device, struct tape_request *request,
7898c2ecf20Sopenharmony_ci		    struct irb *irb)
7908c2ecf20Sopenharmony_ci{
7918c2ecf20Sopenharmony_ci	DBF_EVENT(2, "Retry: %s\n", tape_op_verbose[request->op]);
7928c2ecf20Sopenharmony_ci	tape_dump_sense_dbf(device, request, irb);
7938c2ecf20Sopenharmony_ci	return TAPE_IO_RETRY;
7948c2ecf20Sopenharmony_ci}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/*
7978c2ecf20Sopenharmony_ci * Handle unsolicited interrupts
7988c2ecf20Sopenharmony_ci */
7998c2ecf20Sopenharmony_cistatic int
8008c2ecf20Sopenharmony_citape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END)
8038c2ecf20Sopenharmony_ci		/* Probably result of halt ssch */
8048c2ecf20Sopenharmony_ci		return TAPE_IO_PENDING;
8058c2ecf20Sopenharmony_ci	else if (irb->scsw.cmd.dstat == 0x85)
8068c2ecf20Sopenharmony_ci		/* Device Ready */
8078c2ecf20Sopenharmony_ci		DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id);
8088c2ecf20Sopenharmony_ci	else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
8098c2ecf20Sopenharmony_ci		tape_3590_schedule_work(device, TO_READ_ATTMSG);
8108c2ecf20Sopenharmony_ci	} else {
8118c2ecf20Sopenharmony_ci		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
8128c2ecf20Sopenharmony_ci		tape_dump_sense_dbf(device, NULL, irb);
8138c2ecf20Sopenharmony_ci	}
8148c2ecf20Sopenharmony_ci	/* check medium state */
8158c2ecf20Sopenharmony_ci	tape_3590_schedule_work(device, TO_MSEN);
8168c2ecf20Sopenharmony_ci	return TAPE_IO_SUCCESS;
8178c2ecf20Sopenharmony_ci}
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci/*
8208c2ecf20Sopenharmony_ci * Basic Recovery routine
8218c2ecf20Sopenharmony_ci */
8228c2ecf20Sopenharmony_cistatic int
8238c2ecf20Sopenharmony_citape_3590_erp_basic(struct tape_device *device, struct tape_request *request,
8248c2ecf20Sopenharmony_ci		    struct irb *irb, int rc)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	switch (sense->bra) {
8318c2ecf20Sopenharmony_ci	case SENSE_BRA_PER:
8328c2ecf20Sopenharmony_ci		return tape_3590_erp_failed(device, request, irb, rc);
8338c2ecf20Sopenharmony_ci	case SENSE_BRA_CONT:
8348c2ecf20Sopenharmony_ci		return tape_3590_erp_succeded(device, request);
8358c2ecf20Sopenharmony_ci	case SENSE_BRA_RE:
8368c2ecf20Sopenharmony_ci		return tape_3590_erp_retry(device, request, irb);
8378c2ecf20Sopenharmony_ci	case SENSE_BRA_DRE:
8388c2ecf20Sopenharmony_ci		return tape_3590_erp_failed(device, request, irb, rc);
8398c2ecf20Sopenharmony_ci	default:
8408c2ecf20Sopenharmony_ci		BUG();
8418c2ecf20Sopenharmony_ci		return TAPE_IO_STOP;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci}
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci/*
8468c2ecf20Sopenharmony_ci *  RDL: Read Device (buffered) log
8478c2ecf20Sopenharmony_ci */
8488c2ecf20Sopenharmony_cistatic int
8498c2ecf20Sopenharmony_citape_3590_erp_read_buf_log(struct tape_device *device,
8508c2ecf20Sopenharmony_ci			   struct tape_request *request, struct irb *irb)
8518c2ecf20Sopenharmony_ci{
8528c2ecf20Sopenharmony_ci	/*
8538c2ecf20Sopenharmony_ci	 * We just do the basic error recovery at the moment (retry).
8548c2ecf20Sopenharmony_ci	 * Perhaps in the future, we read the log and dump it somewhere...
8558c2ecf20Sopenharmony_ci	 */
8568c2ecf20Sopenharmony_ci	return tape_3590_erp_basic(device, request, irb, -EIO);
8578c2ecf20Sopenharmony_ci}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci/*
8608c2ecf20Sopenharmony_ci *  SWAP: Swap Devices
8618c2ecf20Sopenharmony_ci */
8628c2ecf20Sopenharmony_cistatic int
8638c2ecf20Sopenharmony_citape_3590_erp_swap(struct tape_device *device, struct tape_request *request,
8648c2ecf20Sopenharmony_ci		   struct irb *irb)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	/*
8678c2ecf20Sopenharmony_ci	 * This error recovery should swap the tapes
8688c2ecf20Sopenharmony_ci	 * if the original has a problem. The operation
8698c2ecf20Sopenharmony_ci	 * should proceed with the new tape... this
8708c2ecf20Sopenharmony_ci	 * should probably be done in user space!
8718c2ecf20Sopenharmony_ci	 */
8728c2ecf20Sopenharmony_ci	dev_warn (&device->cdev->dev, "The tape medium must be loaded into a "
8738c2ecf20Sopenharmony_ci		"different tape unit\n");
8748c2ecf20Sopenharmony_ci	return tape_3590_erp_basic(device, request, irb, -EIO);
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci/*
8788c2ecf20Sopenharmony_ci *  LBY: Long Busy
8798c2ecf20Sopenharmony_ci */
8808c2ecf20Sopenharmony_cistatic int
8818c2ecf20Sopenharmony_citape_3590_erp_long_busy(struct tape_device *device,
8828c2ecf20Sopenharmony_ci			struct tape_request *request, struct irb *irb)
8838c2ecf20Sopenharmony_ci{
8848c2ecf20Sopenharmony_ci	DBF_EVENT(6, "Device is busy\n");
8858c2ecf20Sopenharmony_ci	return TAPE_IO_LONG_BUSY;
8868c2ecf20Sopenharmony_ci}
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci/*
8898c2ecf20Sopenharmony_ci *  SPI: Special Intercept
8908c2ecf20Sopenharmony_ci */
8918c2ecf20Sopenharmony_cistatic int
8928c2ecf20Sopenharmony_citape_3590_erp_special_interrupt(struct tape_device *device,
8938c2ecf20Sopenharmony_ci				struct tape_request *request, struct irb *irb)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	return tape_3590_erp_basic(device, request, irb, -EIO);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci/*
8998c2ecf20Sopenharmony_ci *  RDA: Read Alternate
9008c2ecf20Sopenharmony_ci */
9018c2ecf20Sopenharmony_cistatic int
9028c2ecf20Sopenharmony_citape_3590_erp_read_alternate(struct tape_device *device,
9038c2ecf20Sopenharmony_ci			     struct tape_request *request, struct irb *irb)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct tape_3590_disc_data *data;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/*
9088c2ecf20Sopenharmony_ci	 * The issued Read Backward or Read Previous command is not
9098c2ecf20Sopenharmony_ci	 * supported by the device
9108c2ecf20Sopenharmony_ci	 * The recovery action should be to issue another command:
9118c2ecf20Sopenharmony_ci	 * Read Revious: if Read Backward is not supported
9128c2ecf20Sopenharmony_ci	 * Read Backward: if Read Previous is not supported
9138c2ecf20Sopenharmony_ci	 */
9148c2ecf20Sopenharmony_ci	data = device->discdata;
9158c2ecf20Sopenharmony_ci	if (data->read_back_op == READ_PREVIOUS) {
9168c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): No support for READ_PREVIOUS command\n",
9178c2ecf20Sopenharmony_ci			  device->cdev_id);
9188c2ecf20Sopenharmony_ci		data->read_back_op = READ_BACKWARD;
9198c2ecf20Sopenharmony_ci	} else {
9208c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): No support for READ_BACKWARD command\n",
9218c2ecf20Sopenharmony_ci			  device->cdev_id);
9228c2ecf20Sopenharmony_ci		data->read_back_op = READ_PREVIOUS;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	tape_3590_read_opposite(device, request);
9258c2ecf20Sopenharmony_ci	return tape_3590_erp_retry(device, request, irb);
9268c2ecf20Sopenharmony_ci}
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci/*
9298c2ecf20Sopenharmony_ci * Error Recovery read opposite
9308c2ecf20Sopenharmony_ci */
9318c2ecf20Sopenharmony_cistatic int
9328c2ecf20Sopenharmony_citape_3590_erp_read_opposite(struct tape_device *device,
9338c2ecf20Sopenharmony_ci			    struct tape_request *request, struct irb *irb)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	switch (request->op) {
9368c2ecf20Sopenharmony_ci	case TO_RFO:
9378c2ecf20Sopenharmony_ci		/*
9388c2ecf20Sopenharmony_ci		 * We did read forward, but the data could not be read.
9398c2ecf20Sopenharmony_ci		 * We will read backward and then skip forward again.
9408c2ecf20Sopenharmony_ci		 */
9418c2ecf20Sopenharmony_ci		tape_3590_read_opposite(device, request);
9428c2ecf20Sopenharmony_ci		return tape_3590_erp_retry(device, request, irb);
9438c2ecf20Sopenharmony_ci	case TO_RBA:
9448c2ecf20Sopenharmony_ci		/* We tried to read forward and backward, but hat no success */
9458c2ecf20Sopenharmony_ci		return tape_3590_erp_failed(device, request, irb, -EIO);
9468c2ecf20Sopenharmony_ci		break;
9478c2ecf20Sopenharmony_ci	default:
9488c2ecf20Sopenharmony_ci		return tape_3590_erp_failed(device, request, irb, -EIO);
9498c2ecf20Sopenharmony_ci	}
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_ci/*
9538c2ecf20Sopenharmony_ci * Print an MIM (Media Information  Message) (message code f0)
9548c2ecf20Sopenharmony_ci */
9558c2ecf20Sopenharmony_cistatic void
9568c2ecf20Sopenharmony_citape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb)
9578c2ecf20Sopenharmony_ci{
9588c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
9598c2ecf20Sopenharmony_ci	char *exception, *service;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
9628c2ecf20Sopenharmony_ci	service = kmalloc(BUFSIZE, GFP_ATOMIC);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	if (!exception || !service)
9658c2ecf20Sopenharmony_ci		goto out_nomem;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
9688c2ecf20Sopenharmony_ci	/* Exception Message */
9698c2ecf20Sopenharmony_ci	switch (sense->fmt.f70.emc) {
9708c2ecf20Sopenharmony_ci	case 0x02:
9718c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Data degraded");
9728c2ecf20Sopenharmony_ci		break;
9738c2ecf20Sopenharmony_ci	case 0x03:
9748c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Data degraded in partition %i",
9758c2ecf20Sopenharmony_ci			sense->fmt.f70.mp);
9768c2ecf20Sopenharmony_ci		break;
9778c2ecf20Sopenharmony_ci	case 0x04:
9788c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Medium degraded");
9798c2ecf20Sopenharmony_ci		break;
9808c2ecf20Sopenharmony_ci	case 0x05:
9818c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Medium degraded in partition %i",
9828c2ecf20Sopenharmony_ci			sense->fmt.f70.mp);
9838c2ecf20Sopenharmony_ci		break;
9848c2ecf20Sopenharmony_ci	case 0x06:
9858c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Block 0 Error");
9868c2ecf20Sopenharmony_ci		break;
9878c2ecf20Sopenharmony_ci	case 0x07:
9888c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Medium Exception 0x%02x",
9898c2ecf20Sopenharmony_ci			sense->fmt.f70.md);
9908c2ecf20Sopenharmony_ci		break;
9918c2ecf20Sopenharmony_ci	default:
9928c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "0x%02x",
9938c2ecf20Sopenharmony_ci			sense->fmt.f70.emc);
9948c2ecf20Sopenharmony_ci		break;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci	/* Service Message */
9978c2ecf20Sopenharmony_ci	switch (sense->fmt.f70.smc) {
9988c2ecf20Sopenharmony_ci	case 0x02:
9998c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Reference Media maintenance "
10008c2ecf20Sopenharmony_ci			"procedure %i", sense->fmt.f70.md);
10018c2ecf20Sopenharmony_ci		break;
10028c2ecf20Sopenharmony_ci	default:
10038c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "0x%02x",
10048c2ecf20Sopenharmony_ci			sense->fmt.f70.smc);
10058c2ecf20Sopenharmony_ci		break;
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	dev_warn (&device->cdev->dev, "Tape media information: exception %s, "
10098c2ecf20Sopenharmony_ci		"service %s\n", exception, service);
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ciout_nomem:
10128c2ecf20Sopenharmony_ci	kfree(exception);
10138c2ecf20Sopenharmony_ci	kfree(service);
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_ci/*
10178c2ecf20Sopenharmony_ci * Print an I/O Subsystem Service Information Message (message code f1)
10188c2ecf20Sopenharmony_ci */
10198c2ecf20Sopenharmony_cistatic void
10208c2ecf20Sopenharmony_citape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
10238c2ecf20Sopenharmony_ci	char *exception, *service;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
10268c2ecf20Sopenharmony_ci	service = kmalloc(BUFSIZE, GFP_ATOMIC);
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	if (!exception || !service)
10298c2ecf20Sopenharmony_ci		goto out_nomem;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
10328c2ecf20Sopenharmony_ci	/* Exception Message */
10338c2ecf20Sopenharmony_ci	switch (sense->fmt.f71.emc) {
10348c2ecf20Sopenharmony_ci	case 0x01:
10358c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Effect of failure is unknown");
10368c2ecf20Sopenharmony_ci		break;
10378c2ecf20Sopenharmony_ci	case 0x02:
10388c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception - no performance "
10398c2ecf20Sopenharmony_ci			"impact");
10408c2ecf20Sopenharmony_ci		break;
10418c2ecf20Sopenharmony_ci	case 0x03:
10428c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception on channel "
10438c2ecf20Sopenharmony_ci			"interface 0x%02x", sense->fmt.f71.md[0]);
10448c2ecf20Sopenharmony_ci		break;
10458c2ecf20Sopenharmony_ci	case 0x04:
10468c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception on device path "
10478c2ecf20Sopenharmony_ci			"0x%02x", sense->fmt.f71.md[0]);
10488c2ecf20Sopenharmony_ci		break;
10498c2ecf20Sopenharmony_ci	case 0x05:
10508c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception on library path "
10518c2ecf20Sopenharmony_ci			"0x%02x", sense->fmt.f71.md[0]);
10528c2ecf20Sopenharmony_ci		break;
10538c2ecf20Sopenharmony_ci	case 0x06:
10548c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception on node 0x%02x",
10558c2ecf20Sopenharmony_ci			sense->fmt.f71.md[0]);
10568c2ecf20Sopenharmony_ci		break;
10578c2ecf20Sopenharmony_ci	case 0x07:
10588c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "CU Exception on partition "
10598c2ecf20Sopenharmony_ci			"0x%02x", sense->fmt.f71.md[0]);
10608c2ecf20Sopenharmony_ci		break;
10618c2ecf20Sopenharmony_ci	default:
10628c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "0x%02x",
10638c2ecf20Sopenharmony_ci			sense->fmt.f71.emc);
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci	/* Service Message */
10668c2ecf20Sopenharmony_ci	switch (sense->fmt.f71.smc) {
10678c2ecf20Sopenharmony_ci	case 0x01:
10688c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair impact is unknown");
10698c2ecf20Sopenharmony_ci		break;
10708c2ecf20Sopenharmony_ci	case 0x02:
10718c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair will not impact cu "
10728c2ecf20Sopenharmony_ci			"performance");
10738c2ecf20Sopenharmony_ci		break;
10748c2ecf20Sopenharmony_ci	case 0x03:
10758c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
10768c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable node "
10778c2ecf20Sopenharmony_ci				"0x%x on CU", sense->fmt.f71.md[1]);
10788c2ecf20Sopenharmony_ci		else
10798c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
10808c2ecf20Sopenharmony_ci				"nodes (0x%x-0x%x) on CU", sense->fmt.f71.md[1],
10818c2ecf20Sopenharmony_ci				sense->fmt.f71.md[2]);
10828c2ecf20Sopenharmony_ci		break;
10838c2ecf20Sopenharmony_ci	case 0x04:
10848c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
10858c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
10868c2ecf20Sopenharmony_ci				"channel path 0x%x on CU",
10878c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1]);
10888c2ecf20Sopenharmony_ci		else
10898c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable channel"
10908c2ecf20Sopenharmony_ci				" paths (0x%x-0x%x) on CU",
10918c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
10928c2ecf20Sopenharmony_ci		break;
10938c2ecf20Sopenharmony_ci	case 0x05:
10948c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
10958c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable device"
10968c2ecf20Sopenharmony_ci				" path 0x%x on CU", sense->fmt.f71.md[1]);
10978c2ecf20Sopenharmony_ci		else
10988c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable device"
10998c2ecf20Sopenharmony_ci				" paths (0x%x-0x%x) on CU",
11008c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
11018c2ecf20Sopenharmony_ci		break;
11028c2ecf20Sopenharmony_ci	case 0x06:
11038c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
11048c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11058c2ecf20Sopenharmony_ci				"library path 0x%x on CU",
11068c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1]);
11078c2ecf20Sopenharmony_ci		else
11088c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11098c2ecf20Sopenharmony_ci				"library paths (0x%x-0x%x) on CU",
11108c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
11118c2ecf20Sopenharmony_ci		break;
11128c2ecf20Sopenharmony_ci	case 0x07:
11138c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair will disable access to CU");
11148c2ecf20Sopenharmony_ci		break;
11158c2ecf20Sopenharmony_ci	default:
11168c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "0x%02x",
11178c2ecf20Sopenharmony_ci			sense->fmt.f71.smc);
11188c2ecf20Sopenharmony_ci	}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	dev_warn (&device->cdev->dev, "I/O subsystem information: exception"
11218c2ecf20Sopenharmony_ci		" %s, service %s\n", exception, service);
11228c2ecf20Sopenharmony_ciout_nomem:
11238c2ecf20Sopenharmony_ci	kfree(exception);
11248c2ecf20Sopenharmony_ci	kfree(service);
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_ci/*
11288c2ecf20Sopenharmony_ci * Print an Device Subsystem Service Information Message (message code f2)
11298c2ecf20Sopenharmony_ci */
11308c2ecf20Sopenharmony_cistatic void
11318c2ecf20Sopenharmony_citape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
11348c2ecf20Sopenharmony_ci	char *exception, *service;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	exception = kmalloc(BUFSIZE, GFP_ATOMIC);
11378c2ecf20Sopenharmony_ci	service = kmalloc(BUFSIZE, GFP_ATOMIC);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	if (!exception || !service)
11408c2ecf20Sopenharmony_ci		goto out_nomem;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
11438c2ecf20Sopenharmony_ci	/* Exception Message */
11448c2ecf20Sopenharmony_ci	switch (sense->fmt.f71.emc) {
11458c2ecf20Sopenharmony_ci	case 0x01:
11468c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "Effect of failure is unknown");
11478c2ecf20Sopenharmony_ci		break;
11488c2ecf20Sopenharmony_ci	case 0x02:
11498c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception - no performance"
11508c2ecf20Sopenharmony_ci			" impact");
11518c2ecf20Sopenharmony_ci		break;
11528c2ecf20Sopenharmony_ci	case 0x03:
11538c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception on channel "
11548c2ecf20Sopenharmony_ci			"interface 0x%02x", sense->fmt.f71.md[0]);
11558c2ecf20Sopenharmony_ci		break;
11568c2ecf20Sopenharmony_ci	case 0x04:
11578c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception on loader 0x%02x",
11588c2ecf20Sopenharmony_ci			sense->fmt.f71.md[0]);
11598c2ecf20Sopenharmony_ci		break;
11608c2ecf20Sopenharmony_ci	case 0x05:
11618c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception on message display"
11628c2ecf20Sopenharmony_ci			" 0x%02x", sense->fmt.f71.md[0]);
11638c2ecf20Sopenharmony_ci		break;
11648c2ecf20Sopenharmony_ci	case 0x06:
11658c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception in tape path");
11668c2ecf20Sopenharmony_ci		break;
11678c2ecf20Sopenharmony_ci	case 0x07:
11688c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "DV Exception in drive");
11698c2ecf20Sopenharmony_ci		break;
11708c2ecf20Sopenharmony_ci	default:
11718c2ecf20Sopenharmony_ci		snprintf(exception, BUFSIZE, "0x%02x",
11728c2ecf20Sopenharmony_ci			sense->fmt.f71.emc);
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci	/* Service Message */
11758c2ecf20Sopenharmony_ci	switch (sense->fmt.f71.smc) {
11768c2ecf20Sopenharmony_ci	case 0x01:
11778c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair impact is unknown");
11788c2ecf20Sopenharmony_ci		break;
11798c2ecf20Sopenharmony_ci	case 0x02:
11808c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair will not impact device "
11818c2ecf20Sopenharmony_ci			"performance");
11828c2ecf20Sopenharmony_ci		break;
11838c2ecf20Sopenharmony_ci	case 0x03:
11848c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
11858c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11868c2ecf20Sopenharmony_ci				"channel path 0x%x on DV",
11878c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1]);
11888c2ecf20Sopenharmony_ci		else
11898c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11908c2ecf20Sopenharmony_ci				"channel path (0x%x-0x%x) on DV",
11918c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
11928c2ecf20Sopenharmony_ci		break;
11938c2ecf20Sopenharmony_ci	case 0x04:
11948c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
11958c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11968c2ecf20Sopenharmony_ci				"interface 0x%x on DV", sense->fmt.f71.md[1]);
11978c2ecf20Sopenharmony_ci		else
11988c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
11998c2ecf20Sopenharmony_ci				"interfaces (0x%x-0x%x) on DV",
12008c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
12018c2ecf20Sopenharmony_ci		break;
12028c2ecf20Sopenharmony_ci	case 0x05:
12038c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
12048c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable loader"
12058c2ecf20Sopenharmony_ci				" 0x%x on DV", sense->fmt.f71.md[1]);
12068c2ecf20Sopenharmony_ci		else
12078c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable loader"
12088c2ecf20Sopenharmony_ci				" (0x%x-0x%x) on DV",
12098c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
12108c2ecf20Sopenharmony_ci		break;
12118c2ecf20Sopenharmony_ci	case 0x07:
12128c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Repair will disable access to DV");
12138c2ecf20Sopenharmony_ci		break;
12148c2ecf20Sopenharmony_ci	case 0x08:
12158c2ecf20Sopenharmony_ci		if (sense->fmt.f71.mdf == 0)
12168c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
12178c2ecf20Sopenharmony_ci				"message display 0x%x on DV",
12188c2ecf20Sopenharmony_ci				sense->fmt.f71.md[1]);
12198c2ecf20Sopenharmony_ci		else
12208c2ecf20Sopenharmony_ci			snprintf(service, BUFSIZE, "Repair will disable "
12218c2ecf20Sopenharmony_ci				"message displays (0x%x-0x%x) on DV",
12228c2ecf20Sopenharmony_ci				 sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
12238c2ecf20Sopenharmony_ci		break;
12248c2ecf20Sopenharmony_ci	case 0x09:
12258c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "Clean DV");
12268c2ecf20Sopenharmony_ci		break;
12278c2ecf20Sopenharmony_ci	default:
12288c2ecf20Sopenharmony_ci		snprintf(service, BUFSIZE, "0x%02x",
12298c2ecf20Sopenharmony_ci			sense->fmt.f71.smc);
12308c2ecf20Sopenharmony_ci	}
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	dev_warn (&device->cdev->dev, "Device subsystem information: exception"
12338c2ecf20Sopenharmony_ci		" %s, service %s\n", exception, service);
12348c2ecf20Sopenharmony_ciout_nomem:
12358c2ecf20Sopenharmony_ci	kfree(exception);
12368c2ecf20Sopenharmony_ci	kfree(service);
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_ci/*
12408c2ecf20Sopenharmony_ci * Print standard ERA Message
12418c2ecf20Sopenharmony_ci */
12428c2ecf20Sopenharmony_cistatic void
12438c2ecf20Sopenharmony_citape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
12448c2ecf20Sopenharmony_ci{
12458c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
12488c2ecf20Sopenharmony_ci	if (sense->mc == 0)
12498c2ecf20Sopenharmony_ci		return;
12508c2ecf20Sopenharmony_ci	if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) {
12518c2ecf20Sopenharmony_ci		if (tape_3590_msg[sense->mc] != NULL)
12528c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "The tape unit has "
12538c2ecf20Sopenharmony_ci				"issued sense message %s\n",
12548c2ecf20Sopenharmony_ci				tape_3590_msg[sense->mc]);
12558c2ecf20Sopenharmony_ci		else
12568c2ecf20Sopenharmony_ci			dev_warn (&device->cdev->dev, "The tape unit has "
12578c2ecf20Sopenharmony_ci				"issued an unknown sense message code 0x%x\n",
12588c2ecf20Sopenharmony_ci				sense->mc);
12598c2ecf20Sopenharmony_ci		return;
12608c2ecf20Sopenharmony_ci	}
12618c2ecf20Sopenharmony_ci	if (sense->mc == 0xf0) {
12628c2ecf20Sopenharmony_ci		/* Standard Media Information Message */
12638c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "MIM SEV=%i, MC=%02x, ES=%x/%x, "
12648c2ecf20Sopenharmony_ci			"RC=%02x-%04x-%02x\n", sense->fmt.f70.sev, sense->mc,
12658c2ecf20Sopenharmony_ci			sense->fmt.f70.emc, sense->fmt.f70.smc,
12668c2ecf20Sopenharmony_ci			sense->fmt.f70.refcode, sense->fmt.f70.mid,
12678c2ecf20Sopenharmony_ci			sense->fmt.f70.fid);
12688c2ecf20Sopenharmony_ci		tape_3590_print_mim_msg_f0(device, irb);
12698c2ecf20Sopenharmony_ci		return;
12708c2ecf20Sopenharmony_ci	}
12718c2ecf20Sopenharmony_ci	if (sense->mc == 0xf1) {
12728c2ecf20Sopenharmony_ci		/* Standard I/O Subsystem Service Information Message */
12738c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "IOSIM SEV=%i, DEVTYPE=3590/%02x,"
12748c2ecf20Sopenharmony_ci			" MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
12758c2ecf20Sopenharmony_ci			sense->fmt.f71.sev, device->cdev->id.dev_model,
12768c2ecf20Sopenharmony_ci			sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc,
12778c2ecf20Sopenharmony_ci			sense->fmt.f71.refcode1, sense->fmt.f71.refcode2,
12788c2ecf20Sopenharmony_ci			sense->fmt.f71.refcode3);
12798c2ecf20Sopenharmony_ci		tape_3590_print_io_sim_msg_f1(device, irb);
12808c2ecf20Sopenharmony_ci		return;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci	if (sense->mc == 0xf2) {
12838c2ecf20Sopenharmony_ci		/* Standard Device Service Information Message */
12848c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "DEVSIM SEV=%i, DEVTYPE=3590/%02x"
12858c2ecf20Sopenharmony_ci			", MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
12868c2ecf20Sopenharmony_ci			sense->fmt.f71.sev, device->cdev->id.dev_model,
12878c2ecf20Sopenharmony_ci			sense->mc, sense->fmt.f71.emc, sense->fmt.f71.smc,
12888c2ecf20Sopenharmony_ci			sense->fmt.f71.refcode1, sense->fmt.f71.refcode2,
12898c2ecf20Sopenharmony_ci			sense->fmt.f71.refcode3);
12908c2ecf20Sopenharmony_ci		tape_3590_print_dev_sim_msg_f2(device, irb);
12918c2ecf20Sopenharmony_ci		return;
12928c2ecf20Sopenharmony_ci	}
12938c2ecf20Sopenharmony_ci	if (sense->mc == 0xf3) {
12948c2ecf20Sopenharmony_ci		/* Standard Library Service Information Message */
12958c2ecf20Sopenharmony_ci		return;
12968c2ecf20Sopenharmony_ci	}
12978c2ecf20Sopenharmony_ci	dev_warn (&device->cdev->dev, "The tape unit has issued an unknown "
12988c2ecf20Sopenharmony_ci		"sense message code %x\n", sense->mc);
12998c2ecf20Sopenharmony_ci}
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_cistatic int tape_3590_crypt_error(struct tape_device *device,
13028c2ecf20Sopenharmony_ci				 struct tape_request *request, struct irb *irb)
13038c2ecf20Sopenharmony_ci{
13048c2ecf20Sopenharmony_ci	u8 cu_rc;
13058c2ecf20Sopenharmony_ci	u16 ekm_rc2;
13068c2ecf20Sopenharmony_ci	char *sense;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
13098c2ecf20Sopenharmony_ci	cu_rc = sense[0];
13108c2ecf20Sopenharmony_ci	ekm_rc2 = *((u16*) &sense[10]);
13118c2ecf20Sopenharmony_ci	if ((cu_rc == 0) && (ekm_rc2 == 0xee31))
13128c2ecf20Sopenharmony_ci		/* key not defined on EKM */
13138c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EKEYREJECTED);
13148c2ecf20Sopenharmony_ci	if ((cu_rc == 1) || (cu_rc == 2))
13158c2ecf20Sopenharmony_ci		/* No connection to EKM */
13168c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOTCONN);
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	dev_err (&device->cdev->dev, "The tape unit failed to obtain the "
13198c2ecf20Sopenharmony_ci		"encryption key from EKM\n");
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_ci	return tape_3590_erp_basic(device, request, irb, -ENOKEY);
13228c2ecf20Sopenharmony_ci}
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci/*
13258c2ecf20Sopenharmony_ci *  3590 error Recovery routine:
13268c2ecf20Sopenharmony_ci *  If possible, it tries to recover from the error. If this is not possible,
13278c2ecf20Sopenharmony_ci *  inform the user about the problem.
13288c2ecf20Sopenharmony_ci */
13298c2ecf20Sopenharmony_cistatic int
13308c2ecf20Sopenharmony_citape_3590_unit_check(struct tape_device *device, struct tape_request *request,
13318c2ecf20Sopenharmony_ci		     struct irb *irb)
13328c2ecf20Sopenharmony_ci{
13338c2ecf20Sopenharmony_ci	struct tape_3590_sense *sense;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	sense = (struct tape_3590_sense *) irb->ecw;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	DBF_EVENT(6, "Unit Check: RQC = %x\n", sense->rc_rqc);
13388c2ecf20Sopenharmony_ci
13398c2ecf20Sopenharmony_ci	/*
13408c2ecf20Sopenharmony_ci	 * First check all RC-QRCs where we want to do something special
13418c2ecf20Sopenharmony_ci	 *   - "break":     basic error recovery is done
13428c2ecf20Sopenharmony_ci	 *   - "goto out:": just print error message if available
13438c2ecf20Sopenharmony_ci	 */
13448c2ecf20Sopenharmony_ci	switch (sense->rc_rqc) {
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	case 0x1110:
13478c2ecf20Sopenharmony_ci		tape_3590_print_era_msg(device, irb);
13488c2ecf20Sopenharmony_ci		return tape_3590_erp_read_buf_log(device, request, irb);
13498c2ecf20Sopenharmony_ci
13508c2ecf20Sopenharmony_ci	case 0x2011:
13518c2ecf20Sopenharmony_ci		tape_3590_print_era_msg(device, irb);
13528c2ecf20Sopenharmony_ci		return tape_3590_erp_read_alternate(device, request, irb);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	case 0x2230:
13558c2ecf20Sopenharmony_ci	case 0x2231:
13568c2ecf20Sopenharmony_ci		tape_3590_print_era_msg(device, irb);
13578c2ecf20Sopenharmony_ci		return tape_3590_erp_special_interrupt(device, request, irb);
13588c2ecf20Sopenharmony_ci	case 0x2240:
13598c2ecf20Sopenharmony_ci		return tape_3590_crypt_error(device, request, irb);
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	case 0x3010:
13628c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): Backward at Beginning of Partition\n",
13638c2ecf20Sopenharmony_ci			  device->cdev_id);
13648c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
13658c2ecf20Sopenharmony_ci	case 0x3012:
13668c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): Forward at End of Partition\n",
13678c2ecf20Sopenharmony_ci			  device->cdev_id);
13688c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
13698c2ecf20Sopenharmony_ci	case 0x3020:
13708c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): End of Data Mark\n", device->cdev_id);
13718c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	case 0x3122:
13748c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): Rewind Unload initiated\n",
13758c2ecf20Sopenharmony_ci			  device->cdev_id);
13768c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EIO);
13778c2ecf20Sopenharmony_ci	case 0x3123:
13788c2ecf20Sopenharmony_ci		DBF_EVENT(2, "(%08x): Rewind Unload complete\n",
13798c2ecf20Sopenharmony_ci			  device->cdev_id);
13808c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
13818c2ecf20Sopenharmony_ci		tape_3590_schedule_work(device, TO_CRYPT_OFF);
13828c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, 0);
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci	case 0x4010:
13858c2ecf20Sopenharmony_ci		/*
13868c2ecf20Sopenharmony_ci		 * print additional msg since default msg
13878c2ecf20Sopenharmony_ci		 * "device intervention" is not very meaningfull
13888c2ecf20Sopenharmony_ci		 */
13898c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
13908c2ecf20Sopenharmony_ci		tape_3590_schedule_work(device, TO_CRYPT_OFF);
13918c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
13928c2ecf20Sopenharmony_ci	case 0x4012:		/* Device Long Busy */
13938c2ecf20Sopenharmony_ci		/* XXX: Also use long busy handling here? */
13948c2ecf20Sopenharmony_ci		DBF_EVENT(6, "(%08x): LONG BUSY\n", device->cdev_id);
13958c2ecf20Sopenharmony_ci		tape_3590_print_era_msg(device, irb);
13968c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EBUSY);
13978c2ecf20Sopenharmony_ci	case 0x4014:
13988c2ecf20Sopenharmony_ci		DBF_EVENT(6, "(%08x): Crypto LONG BUSY\n", device->cdev_id);
13998c2ecf20Sopenharmony_ci		return tape_3590_erp_long_busy(device, request, irb);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci	case 0x5010:
14028c2ecf20Sopenharmony_ci		if (sense->rac == 0xd0) {
14038c2ecf20Sopenharmony_ci			/* Swap */
14048c2ecf20Sopenharmony_ci			tape_3590_print_era_msg(device, irb);
14058c2ecf20Sopenharmony_ci			return tape_3590_erp_swap(device, request, irb);
14068c2ecf20Sopenharmony_ci		}
14078c2ecf20Sopenharmony_ci		if (sense->rac == 0x26) {
14088c2ecf20Sopenharmony_ci			/* Read Opposite */
14098c2ecf20Sopenharmony_ci			tape_3590_print_era_msg(device, irb);
14108c2ecf20Sopenharmony_ci			return tape_3590_erp_read_opposite(device, request,
14118c2ecf20Sopenharmony_ci							   irb);
14128c2ecf20Sopenharmony_ci		}
14138c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EIO);
14148c2ecf20Sopenharmony_ci	case 0x5020:
14158c2ecf20Sopenharmony_ci	case 0x5021:
14168c2ecf20Sopenharmony_ci	case 0x5022:
14178c2ecf20Sopenharmony_ci	case 0x5040:
14188c2ecf20Sopenharmony_ci	case 0x5041:
14198c2ecf20Sopenharmony_ci	case 0x5042:
14208c2ecf20Sopenharmony_ci		tape_3590_print_era_msg(device, irb);
14218c2ecf20Sopenharmony_ci		return tape_3590_erp_swap(device, request, irb);
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	case 0x5110:
14248c2ecf20Sopenharmony_ci	case 0x5111:
14258c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	case 0x5120:
14288c2ecf20Sopenharmony_ci	case 0x1120:
14298c2ecf20Sopenharmony_ci		tape_med_state_set(device, MS_UNLOADED);
14308c2ecf20Sopenharmony_ci		tape_3590_schedule_work(device, TO_CRYPT_OFF);
14318c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci	case 0x6020:
14348c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	case 0x8011:
14378c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EPERM);
14388c2ecf20Sopenharmony_ci	case 0x8013:
14398c2ecf20Sopenharmony_ci		dev_warn (&device->cdev->dev, "A different host has privileged"
14408c2ecf20Sopenharmony_ci			" access to the tape unit\n");
14418c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EPERM);
14428c2ecf20Sopenharmony_ci	default:
14438c2ecf20Sopenharmony_ci		return tape_3590_erp_basic(device, request, irb, -EIO);
14448c2ecf20Sopenharmony_ci	}
14458c2ecf20Sopenharmony_ci}
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci/*
14488c2ecf20Sopenharmony_ci * 3590 interrupt handler:
14498c2ecf20Sopenharmony_ci */
14508c2ecf20Sopenharmony_cistatic int
14518c2ecf20Sopenharmony_citape_3590_irq(struct tape_device *device, struct tape_request *request,
14528c2ecf20Sopenharmony_ci	      struct irb *irb)
14538c2ecf20Sopenharmony_ci{
14548c2ecf20Sopenharmony_ci	if (request == NULL)
14558c2ecf20Sopenharmony_ci		return tape_3590_unsolicited_irq(device, irb);
14568c2ecf20Sopenharmony_ci
14578c2ecf20Sopenharmony_ci	if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
14588c2ecf20Sopenharmony_ci	    (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
14598c2ecf20Sopenharmony_ci	    (request->op == TO_WRI)) {
14608c2ecf20Sopenharmony_ci		/* Write at end of volume */
14618c2ecf20Sopenharmony_ci		DBF_EVENT(2, "End of volume\n");
14628c2ecf20Sopenharmony_ci		return tape_3590_erp_failed(device, request, irb, -ENOSPC);
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
14668c2ecf20Sopenharmony_ci		return tape_3590_unit_check(device, request, irb);
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
14698c2ecf20Sopenharmony_ci		if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) {
14708c2ecf20Sopenharmony_ci			if (request->op == TO_FSB || request->op == TO_BSB)
14718c2ecf20Sopenharmony_ci				request->rescnt++;
14728c2ecf20Sopenharmony_ci			else
14738c2ecf20Sopenharmony_ci				DBF_EVENT(5, "Unit Exception!\n");
14748c2ecf20Sopenharmony_ci		}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci		return tape_3590_done(device, request);
14778c2ecf20Sopenharmony_ci	}
14788c2ecf20Sopenharmony_ci
14798c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) {
14808c2ecf20Sopenharmony_ci		DBF_EVENT(2, "channel end\n");
14818c2ecf20Sopenharmony_ci		return TAPE_IO_PENDING;
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
14858c2ecf20Sopenharmony_ci		DBF_EVENT(2, "Unit Attention when busy..\n");
14868c2ecf20Sopenharmony_ci		return TAPE_IO_PENDING;
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	DBF_EVENT(6, "xunknownirq\n");
14908c2ecf20Sopenharmony_ci	tape_dump_sense_dbf(device, request, irb);
14918c2ecf20Sopenharmony_ci	return TAPE_IO_STOP;
14928c2ecf20Sopenharmony_ci}
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic int tape_3590_read_dev_chars(struct tape_device *device,
14968c2ecf20Sopenharmony_ci				    struct tape_3590_rdc_data *rdc_data)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci	int rc;
14998c2ecf20Sopenharmony_ci	struct tape_request *request;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	request = tape_alloc_request(1, sizeof(*rdc_data));
15028c2ecf20Sopenharmony_ci	if (IS_ERR(request))
15038c2ecf20Sopenharmony_ci		return PTR_ERR(request);
15048c2ecf20Sopenharmony_ci	request->op = TO_RDC;
15058c2ecf20Sopenharmony_ci	tape_ccw_end(request->cpaddr, CCW_CMD_RDC, sizeof(*rdc_data),
15068c2ecf20Sopenharmony_ci		     request->cpdata);
15078c2ecf20Sopenharmony_ci	rc = tape_do_io(device, request);
15088c2ecf20Sopenharmony_ci	if (rc == 0)
15098c2ecf20Sopenharmony_ci		memcpy(rdc_data, request->cpdata, sizeof(*rdc_data));
15108c2ecf20Sopenharmony_ci	tape_free_request(request);
15118c2ecf20Sopenharmony_ci	return rc;
15128c2ecf20Sopenharmony_ci}
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_ci/*
15158c2ecf20Sopenharmony_ci * Setup device function
15168c2ecf20Sopenharmony_ci */
15178c2ecf20Sopenharmony_cistatic int
15188c2ecf20Sopenharmony_citape_3590_setup_device(struct tape_device *device)
15198c2ecf20Sopenharmony_ci{
15208c2ecf20Sopenharmony_ci	int rc;
15218c2ecf20Sopenharmony_ci	struct tape_3590_disc_data *data;
15228c2ecf20Sopenharmony_ci	struct tape_3590_rdc_data *rdc_data;
15238c2ecf20Sopenharmony_ci
15248c2ecf20Sopenharmony_ci	DBF_EVENT(6, "3590 device setup\n");
15258c2ecf20Sopenharmony_ci	data = kzalloc(sizeof(struct tape_3590_disc_data), GFP_KERNEL | GFP_DMA);
15268c2ecf20Sopenharmony_ci	if (data == NULL)
15278c2ecf20Sopenharmony_ci		return -ENOMEM;
15288c2ecf20Sopenharmony_ci	data->read_back_op = READ_PREVIOUS;
15298c2ecf20Sopenharmony_ci	device->discdata = data;
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	rdc_data = kmalloc(sizeof(*rdc_data), GFP_KERNEL | GFP_DMA);
15328c2ecf20Sopenharmony_ci	if (!rdc_data) {
15338c2ecf20Sopenharmony_ci		rc = -ENOMEM;
15348c2ecf20Sopenharmony_ci		goto fail_kmalloc;
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci	rc = tape_3590_read_dev_chars(device, rdc_data);
15378c2ecf20Sopenharmony_ci	if (rc) {
15388c2ecf20Sopenharmony_ci		DBF_LH(3, "Read device characteristics failed!\n");
15398c2ecf20Sopenharmony_ci		goto fail_rdc_data;
15408c2ecf20Sopenharmony_ci	}
15418c2ecf20Sopenharmony_ci	rc = tape_std_assign(device);
15428c2ecf20Sopenharmony_ci	if (rc)
15438c2ecf20Sopenharmony_ci		goto fail_rdc_data;
15448c2ecf20Sopenharmony_ci	if (rdc_data->data[31] == 0x13) {
15458c2ecf20Sopenharmony_ci		data->crypt_info.capability |= TAPE390_CRYPT_SUPPORTED_MASK;
15468c2ecf20Sopenharmony_ci		tape_3592_disable_crypt(device);
15478c2ecf20Sopenharmony_ci	} else {
15488c2ecf20Sopenharmony_ci		DBF_EVENT(6, "Device has NO crypto support\n");
15498c2ecf20Sopenharmony_ci	}
15508c2ecf20Sopenharmony_ci	/* Try to find out if medium is loaded */
15518c2ecf20Sopenharmony_ci	rc = tape_3590_sense_medium(device);
15528c2ecf20Sopenharmony_ci	if (rc) {
15538c2ecf20Sopenharmony_ci		DBF_LH(3, "3590 medium sense returned %d\n", rc);
15548c2ecf20Sopenharmony_ci		goto fail_rdc_data;
15558c2ecf20Sopenharmony_ci	}
15568c2ecf20Sopenharmony_ci	return 0;
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_cifail_rdc_data:
15598c2ecf20Sopenharmony_ci	kfree(rdc_data);
15608c2ecf20Sopenharmony_cifail_kmalloc:
15618c2ecf20Sopenharmony_ci	kfree(data);
15628c2ecf20Sopenharmony_ci	return rc;
15638c2ecf20Sopenharmony_ci}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci/*
15668c2ecf20Sopenharmony_ci * Cleanup device function
15678c2ecf20Sopenharmony_ci */
15688c2ecf20Sopenharmony_cistatic void
15698c2ecf20Sopenharmony_citape_3590_cleanup_device(struct tape_device *device)
15708c2ecf20Sopenharmony_ci{
15718c2ecf20Sopenharmony_ci	flush_workqueue(tape_3590_wq);
15728c2ecf20Sopenharmony_ci	tape_std_unassign(device);
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	kfree(device->discdata);
15758c2ecf20Sopenharmony_ci	device->discdata = NULL;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci/*
15798c2ecf20Sopenharmony_ci * List of 3590 magnetic tape commands.
15808c2ecf20Sopenharmony_ci */
15818c2ecf20Sopenharmony_cistatic tape_mtop_fn tape_3590_mtop[TAPE_NR_MTOPS] = {
15828c2ecf20Sopenharmony_ci	[MTRESET]	 = tape_std_mtreset,
15838c2ecf20Sopenharmony_ci	[MTFSF]		 = tape_std_mtfsf,
15848c2ecf20Sopenharmony_ci	[MTBSF]		 = tape_std_mtbsf,
15858c2ecf20Sopenharmony_ci	[MTFSR]		 = tape_std_mtfsr,
15868c2ecf20Sopenharmony_ci	[MTBSR]		 = tape_std_mtbsr,
15878c2ecf20Sopenharmony_ci	[MTWEOF]	 = tape_std_mtweof,
15888c2ecf20Sopenharmony_ci	[MTREW]		 = tape_std_mtrew,
15898c2ecf20Sopenharmony_ci	[MTOFFL]	 = tape_std_mtoffl,
15908c2ecf20Sopenharmony_ci	[MTNOP]		 = tape_std_mtnop,
15918c2ecf20Sopenharmony_ci	[MTRETEN]	 = tape_std_mtreten,
15928c2ecf20Sopenharmony_ci	[MTBSFM]	 = tape_std_mtbsfm,
15938c2ecf20Sopenharmony_ci	[MTFSFM]	 = tape_std_mtfsfm,
15948c2ecf20Sopenharmony_ci	[MTEOM]		 = tape_std_mteom,
15958c2ecf20Sopenharmony_ci	[MTERASE]	 = tape_std_mterase,
15968c2ecf20Sopenharmony_ci	[MTRAS1]	 = NULL,
15978c2ecf20Sopenharmony_ci	[MTRAS2]	 = NULL,
15988c2ecf20Sopenharmony_ci	[MTRAS3]	 = NULL,
15998c2ecf20Sopenharmony_ci	[MTSETBLK]	 = tape_std_mtsetblk,
16008c2ecf20Sopenharmony_ci	[MTSETDENSITY]	 = NULL,
16018c2ecf20Sopenharmony_ci	[MTSEEK]	 = tape_3590_mtseek,
16028c2ecf20Sopenharmony_ci	[MTTELL]	 = tape_3590_mttell,
16038c2ecf20Sopenharmony_ci	[MTSETDRVBUFFER] = NULL,
16048c2ecf20Sopenharmony_ci	[MTFSS]		 = NULL,
16058c2ecf20Sopenharmony_ci	[MTBSS]		 = NULL,
16068c2ecf20Sopenharmony_ci	[MTWSM]		 = NULL,
16078c2ecf20Sopenharmony_ci	[MTLOCK]	 = NULL,
16088c2ecf20Sopenharmony_ci	[MTUNLOCK]	 = NULL,
16098c2ecf20Sopenharmony_ci	[MTLOAD]	 = tape_std_mtload,
16108c2ecf20Sopenharmony_ci	[MTUNLOAD]	 = tape_std_mtunload,
16118c2ecf20Sopenharmony_ci	[MTCOMPRESSION]	 = tape_std_mtcompression,
16128c2ecf20Sopenharmony_ci	[MTSETPART]	 = NULL,
16138c2ecf20Sopenharmony_ci	[MTMKPART]	 = NULL
16148c2ecf20Sopenharmony_ci};
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci/*
16178c2ecf20Sopenharmony_ci * Tape discipline structure for 3590.
16188c2ecf20Sopenharmony_ci */
16198c2ecf20Sopenharmony_cistatic struct tape_discipline tape_discipline_3590 = {
16208c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
16218c2ecf20Sopenharmony_ci	.setup_device = tape_3590_setup_device,
16228c2ecf20Sopenharmony_ci	.cleanup_device = tape_3590_cleanup_device,
16238c2ecf20Sopenharmony_ci	.process_eov = tape_std_process_eov,
16248c2ecf20Sopenharmony_ci	.irq = tape_3590_irq,
16258c2ecf20Sopenharmony_ci	.read_block = tape_std_read_block,
16268c2ecf20Sopenharmony_ci	.write_block = tape_std_write_block,
16278c2ecf20Sopenharmony_ci	.ioctl_fn = tape_3590_ioctl,
16288c2ecf20Sopenharmony_ci	.mtop_array = tape_3590_mtop
16298c2ecf20Sopenharmony_ci};
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_cistatic struct ccw_device_id tape_3590_ids[] = {
16328c2ecf20Sopenharmony_ci	{CCW_DEVICE_DEVTYPE(0x3590, 0, 0x3590, 0), .driver_info = tape_3590},
16338c2ecf20Sopenharmony_ci	{CCW_DEVICE_DEVTYPE(0x3592, 0, 0x3592, 0), .driver_info = tape_3592},
16348c2ecf20Sopenharmony_ci	{ /* end of list */ }
16358c2ecf20Sopenharmony_ci};
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_cistatic int
16388c2ecf20Sopenharmony_citape_3590_online(struct ccw_device *cdev)
16398c2ecf20Sopenharmony_ci{
16408c2ecf20Sopenharmony_ci	return tape_generic_online(dev_get_drvdata(&cdev->dev),
16418c2ecf20Sopenharmony_ci				   &tape_discipline_3590);
16428c2ecf20Sopenharmony_ci}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_cistatic struct ccw_driver tape_3590_driver = {
16458c2ecf20Sopenharmony_ci	.driver = {
16468c2ecf20Sopenharmony_ci		.name = "tape_3590",
16478c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
16488c2ecf20Sopenharmony_ci	},
16498c2ecf20Sopenharmony_ci	.ids = tape_3590_ids,
16508c2ecf20Sopenharmony_ci	.probe = tape_generic_probe,
16518c2ecf20Sopenharmony_ci	.remove = tape_generic_remove,
16528c2ecf20Sopenharmony_ci	.set_offline = tape_generic_offline,
16538c2ecf20Sopenharmony_ci	.set_online = tape_3590_online,
16548c2ecf20Sopenharmony_ci	.freeze = tape_generic_pm_suspend,
16558c2ecf20Sopenharmony_ci	.int_class = IRQIO_TAP,
16568c2ecf20Sopenharmony_ci};
16578c2ecf20Sopenharmony_ci
16588c2ecf20Sopenharmony_ci/*
16598c2ecf20Sopenharmony_ci * Setup discipline structure.
16608c2ecf20Sopenharmony_ci */
16618c2ecf20Sopenharmony_cistatic int
16628c2ecf20Sopenharmony_citape_3590_init(void)
16638c2ecf20Sopenharmony_ci{
16648c2ecf20Sopenharmony_ci	int rc;
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_ci	TAPE_DBF_AREA = debug_register("tape_3590", 2, 2, 4 * sizeof(long));
16678c2ecf20Sopenharmony_ci	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
16688c2ecf20Sopenharmony_ci#ifdef DBF_LIKE_HELL
16698c2ecf20Sopenharmony_ci	debug_set_level(TAPE_DBF_AREA, 6);
16708c2ecf20Sopenharmony_ci#endif
16718c2ecf20Sopenharmony_ci
16728c2ecf20Sopenharmony_ci	DBF_EVENT(3, "3590 init\n");
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ci	tape_3590_wq = alloc_workqueue("tape_3590", 0, 0);
16758c2ecf20Sopenharmony_ci	if (!tape_3590_wq)
16768c2ecf20Sopenharmony_ci		return -ENOMEM;
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_ci	/* Register driver for 3590 tapes. */
16798c2ecf20Sopenharmony_ci	rc = ccw_driver_register(&tape_3590_driver);
16808c2ecf20Sopenharmony_ci	if (rc) {
16818c2ecf20Sopenharmony_ci		destroy_workqueue(tape_3590_wq);
16828c2ecf20Sopenharmony_ci		DBF_EVENT(3, "3590 init failed\n");
16838c2ecf20Sopenharmony_ci	} else
16848c2ecf20Sopenharmony_ci		DBF_EVENT(3, "3590 registered\n");
16858c2ecf20Sopenharmony_ci	return rc;
16868c2ecf20Sopenharmony_ci}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_cistatic void
16898c2ecf20Sopenharmony_citape_3590_exit(void)
16908c2ecf20Sopenharmony_ci{
16918c2ecf20Sopenharmony_ci	ccw_driver_unregister(&tape_3590_driver);
16928c2ecf20Sopenharmony_ci	destroy_workqueue(tape_3590_wq);
16938c2ecf20Sopenharmony_ci	debug_unregister(TAPE_DBF_AREA);
16948c2ecf20Sopenharmony_ci}
16958c2ecf20Sopenharmony_ci
16968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(ccw, tape_3590_ids);
16978c2ecf20Sopenharmony_ciMODULE_AUTHOR("(C) 2001,2006 IBM Corporation");
16988c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux on zSeries channel attached 3590 tape device driver");
16998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_cimodule_init(tape_3590_init);
17028c2ecf20Sopenharmony_cimodule_exit(tape_3590_exit);
1703