xref: /openbmc/linux/drivers/s390/char/tape_3590.c (revision b6cba4ee31e7376fa363c4b89ca502ac5e17eac1)
1*b6cba4eeSStefan Bader /*
2*b6cba4eeSStefan Bader  *  drivers/s390/char/tape_3590.c
3*b6cba4eeSStefan Bader  *    tape device discipline for 3590 tapes.
4*b6cba4eeSStefan Bader  *
5*b6cba4eeSStefan Bader  *    Copyright (C) IBM Corp. 2001,2006
6*b6cba4eeSStefan Bader  *    Author(s): Stefan Bader <shbader@de.ibm.com>
7*b6cba4eeSStefan Bader  *		 Michael Holzheu <holzheu@de.ibm.com>
8*b6cba4eeSStefan Bader  *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
9*b6cba4eeSStefan Bader  */
10*b6cba4eeSStefan Bader 
11*b6cba4eeSStefan Bader #include <linux/config.h>
12*b6cba4eeSStefan Bader #include <linux/module.h>
13*b6cba4eeSStefan Bader #include <linux/init.h>
14*b6cba4eeSStefan Bader #include <linux/bio.h>
15*b6cba4eeSStefan Bader 
16*b6cba4eeSStefan Bader #define TAPE_DBF_AREA	tape_3590_dbf
17*b6cba4eeSStefan Bader 
18*b6cba4eeSStefan Bader #include "tape.h"
19*b6cba4eeSStefan Bader #include "tape_std.h"
20*b6cba4eeSStefan Bader #include "tape_3590.h"
21*b6cba4eeSStefan Bader 
22*b6cba4eeSStefan Bader /*
23*b6cba4eeSStefan Bader  * Pointer to debug area.
24*b6cba4eeSStefan Bader  */
25*b6cba4eeSStefan Bader debug_info_t *TAPE_DBF_AREA = NULL;
26*b6cba4eeSStefan Bader EXPORT_SYMBOL(TAPE_DBF_AREA);
27*b6cba4eeSStefan Bader 
28*b6cba4eeSStefan Bader /*******************************************************************
29*b6cba4eeSStefan Bader  * Error Recovery fuctions:
30*b6cba4eeSStefan Bader  * - Read Opposite:		 implemented
31*b6cba4eeSStefan Bader  * - Read Device (buffered) log: BRA
32*b6cba4eeSStefan Bader  * - Read Library log:		 BRA
33*b6cba4eeSStefan Bader  * - Swap Devices:		 BRA
34*b6cba4eeSStefan Bader  * - Long Busy:			 BRA
35*b6cba4eeSStefan Bader  * - Special Intercept:		 BRA
36*b6cba4eeSStefan Bader  * - Read Alternate:		 implemented
37*b6cba4eeSStefan Bader  *******************************************************************/
38*b6cba4eeSStefan Bader 
39*b6cba4eeSStefan Bader #define PRINTK_HEADER "TAPE_3590: "
40*b6cba4eeSStefan Bader 
41*b6cba4eeSStefan Bader static const char *tape_3590_msg[TAPE_3590_MAX_MSG] = {
42*b6cba4eeSStefan Bader 	[0x00] = "",
43*b6cba4eeSStefan Bader 	[0x10] = "Lost Sense",
44*b6cba4eeSStefan Bader 	[0x11] = "Assigned Elsewhere",
45*b6cba4eeSStefan Bader 	[0x12] = "Allegiance Reset",
46*b6cba4eeSStefan Bader 	[0x13] = "Shared Access Violation",
47*b6cba4eeSStefan Bader 	[0x20] = "Command Reject",
48*b6cba4eeSStefan Bader 	[0x21] = "Configuration Error",
49*b6cba4eeSStefan Bader 	[0x22] = "Protection Exception",
50*b6cba4eeSStefan Bader 	[0x23] = "Write Protect",
51*b6cba4eeSStefan Bader 	[0x24] = "Write Length",
52*b6cba4eeSStefan Bader 	[0x25] = "Read-Only Format",
53*b6cba4eeSStefan Bader 	[0x31] = "Beginning of Partition",
54*b6cba4eeSStefan Bader 	[0x33] = "End of Partition",
55*b6cba4eeSStefan Bader 	[0x34] = "End of Data",
56*b6cba4eeSStefan Bader 	[0x35] = "Block not found",
57*b6cba4eeSStefan Bader 	[0x40] = "Device Intervention",
58*b6cba4eeSStefan Bader 	[0x41] = "Loader Intervention",
59*b6cba4eeSStefan Bader 	[0x42] = "Library Intervention",
60*b6cba4eeSStefan Bader 	[0x50] = "Write Error",
61*b6cba4eeSStefan Bader 	[0x51] = "Erase Error",
62*b6cba4eeSStefan Bader 	[0x52] = "Formatting Error",
63*b6cba4eeSStefan Bader 	[0x53] = "Read Error",
64*b6cba4eeSStefan Bader 	[0x54] = "Unsupported Format",
65*b6cba4eeSStefan Bader 	[0x55] = "No Formatting",
66*b6cba4eeSStefan Bader 	[0x56] = "Positioning lost",
67*b6cba4eeSStefan Bader 	[0x57] = "Read Length",
68*b6cba4eeSStefan Bader 	[0x60] = "Unsupported Medium",
69*b6cba4eeSStefan Bader 	[0x61] = "Medium Length Error",
70*b6cba4eeSStefan Bader 	[0x62] = "Medium removed",
71*b6cba4eeSStefan Bader 	[0x64] = "Load Check",
72*b6cba4eeSStefan Bader 	[0x65] = "Unload Check",
73*b6cba4eeSStefan Bader 	[0x70] = "Equipment Check",
74*b6cba4eeSStefan Bader 	[0x71] = "Bus out Check",
75*b6cba4eeSStefan Bader 	[0x72] = "Protocol Error",
76*b6cba4eeSStefan Bader 	[0x73] = "Interface Error",
77*b6cba4eeSStefan Bader 	[0x74] = "Overrun",
78*b6cba4eeSStefan Bader 	[0x75] = "Halt Signal",
79*b6cba4eeSStefan Bader 	[0x90] = "Device fenced",
80*b6cba4eeSStefan Bader 	[0x91] = "Device Path fenced",
81*b6cba4eeSStefan Bader 	[0xa0] = "Volume misplaced",
82*b6cba4eeSStefan Bader 	[0xa1] = "Volume inaccessible",
83*b6cba4eeSStefan Bader 	[0xa2] = "Volume in input",
84*b6cba4eeSStefan Bader 	[0xa3] = "Volume ejected",
85*b6cba4eeSStefan Bader 	[0xa4] = "All categories reserved",
86*b6cba4eeSStefan Bader 	[0xa5] = "Duplicate Volume",
87*b6cba4eeSStefan Bader 	[0xa6] = "Library Manager Offline",
88*b6cba4eeSStefan Bader 	[0xa7] = "Library Output Station full",
89*b6cba4eeSStefan Bader 	[0xa8] = "Vision System non-operational",
90*b6cba4eeSStefan Bader 	[0xa9] = "Library Manager Equipment Check",
91*b6cba4eeSStefan Bader 	[0xaa] = "Library Equipment Check",
92*b6cba4eeSStefan Bader 	[0xab] = "All Library Cells full",
93*b6cba4eeSStefan Bader 	[0xac] = "No Cleaner Volumes in Library",
94*b6cba4eeSStefan Bader 	[0xad] = "I/O Station door open",
95*b6cba4eeSStefan Bader 	[0xae] = "Subsystem environmental alert",
96*b6cba4eeSStefan Bader };
97*b6cba4eeSStefan Bader 
98*b6cba4eeSStefan Bader /*
99*b6cba4eeSStefan Bader  * 3590 IOCTL Overload
100*b6cba4eeSStefan Bader  */
101*b6cba4eeSStefan Bader static int
102*b6cba4eeSStefan Bader tape_3590_ioctl(struct tape_device *device, unsigned int cmd, unsigned long arg)
103*b6cba4eeSStefan Bader {
104*b6cba4eeSStefan Bader 	switch (cmd) {
105*b6cba4eeSStefan Bader 	case TAPE390_DISPLAY: {
106*b6cba4eeSStefan Bader 		struct display_struct disp;
107*b6cba4eeSStefan Bader 
108*b6cba4eeSStefan Bader 		if (copy_from_user(&disp, (char __user *) arg, sizeof(disp)))
109*b6cba4eeSStefan Bader 			return -EFAULT;
110*b6cba4eeSStefan Bader 
111*b6cba4eeSStefan Bader 		return tape_std_display(device, &disp);
112*b6cba4eeSStefan Bader 	}
113*b6cba4eeSStefan Bader 	default:
114*b6cba4eeSStefan Bader 		return -EINVAL;	/* no additional ioctls */
115*b6cba4eeSStefan Bader 	}
116*b6cba4eeSStefan Bader }
117*b6cba4eeSStefan Bader 
118*b6cba4eeSStefan Bader /*
119*b6cba4eeSStefan Bader  * SENSE Medium: Get Sense data about medium state
120*b6cba4eeSStefan Bader  */
121*b6cba4eeSStefan Bader static int
122*b6cba4eeSStefan Bader tape_3590_sense_medium(struct tape_device *device)
123*b6cba4eeSStefan Bader {
124*b6cba4eeSStefan Bader 	struct tape_request *request;
125*b6cba4eeSStefan Bader 
126*b6cba4eeSStefan Bader 	request = tape_alloc_request(1, 128);
127*b6cba4eeSStefan Bader 	if (IS_ERR(request))
128*b6cba4eeSStefan Bader 		return PTR_ERR(request);
129*b6cba4eeSStefan Bader 	request->op = TO_MSEN;
130*b6cba4eeSStefan Bader 	tape_ccw_end(request->cpaddr, MEDIUM_SENSE, 128, request->cpdata);
131*b6cba4eeSStefan Bader 	return tape_do_io_free(device, request);
132*b6cba4eeSStefan Bader }
133*b6cba4eeSStefan Bader 
134*b6cba4eeSStefan Bader /*
135*b6cba4eeSStefan Bader  * MTTELL: Tell block. Return the number of block relative to current file.
136*b6cba4eeSStefan Bader  */
137*b6cba4eeSStefan Bader static int
138*b6cba4eeSStefan Bader tape_3590_mttell(struct tape_device *device, int mt_count)
139*b6cba4eeSStefan Bader {
140*b6cba4eeSStefan Bader 	__u64 block_id;
141*b6cba4eeSStefan Bader 	int rc;
142*b6cba4eeSStefan Bader 
143*b6cba4eeSStefan Bader 	rc = tape_std_read_block_id(device, &block_id);
144*b6cba4eeSStefan Bader 	if (rc)
145*b6cba4eeSStefan Bader 		return rc;
146*b6cba4eeSStefan Bader 	return block_id >> 32;
147*b6cba4eeSStefan Bader }
148*b6cba4eeSStefan Bader 
149*b6cba4eeSStefan Bader /*
150*b6cba4eeSStefan Bader  * MTSEEK: seek to the specified block.
151*b6cba4eeSStefan Bader  */
152*b6cba4eeSStefan Bader static int
153*b6cba4eeSStefan Bader tape_3590_mtseek(struct tape_device *device, int count)
154*b6cba4eeSStefan Bader {
155*b6cba4eeSStefan Bader 	struct tape_request *request;
156*b6cba4eeSStefan Bader 
157*b6cba4eeSStefan Bader 	DBF_EVENT(6, "xsee id: %x\n", count);
158*b6cba4eeSStefan Bader 	request = tape_alloc_request(3, 4);
159*b6cba4eeSStefan Bader 	if (IS_ERR(request))
160*b6cba4eeSStefan Bader 		return PTR_ERR(request);
161*b6cba4eeSStefan Bader 	request->op = TO_LBL;
162*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
163*b6cba4eeSStefan Bader 	*(__u32 *) request->cpdata = count;
164*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
165*b6cba4eeSStefan Bader 	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
166*b6cba4eeSStefan Bader 	return tape_do_io_free(device, request);
167*b6cba4eeSStefan Bader }
168*b6cba4eeSStefan Bader 
169*b6cba4eeSStefan Bader /*
170*b6cba4eeSStefan Bader  * Read Opposite Error Recovery Function:
171*b6cba4eeSStefan Bader  * Used, when Read Forward does not work
172*b6cba4eeSStefan Bader  */
173*b6cba4eeSStefan Bader static void
174*b6cba4eeSStefan Bader tape_3590_read_opposite(struct tape_device *device,
175*b6cba4eeSStefan Bader 			struct tape_request *request)
176*b6cba4eeSStefan Bader {
177*b6cba4eeSStefan Bader 	struct tape_3590_disc_data *data;
178*b6cba4eeSStefan Bader 
179*b6cba4eeSStefan Bader 	/*
180*b6cba4eeSStefan Bader 	 * We have allocated 4 ccws in tape_std_read, so we can now
181*b6cba4eeSStefan Bader 	 * transform the request to a read backward, followed by a
182*b6cba4eeSStefan Bader 	 * forward space block.
183*b6cba4eeSStefan Bader 	 */
184*b6cba4eeSStefan Bader 	request->op = TO_RBA;
185*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte);
186*b6cba4eeSStefan Bader 	data = device->discdata;
187*b6cba4eeSStefan Bader 	tape_ccw_cc_idal(request->cpaddr + 1, data->read_back_op,
188*b6cba4eeSStefan Bader 			 device->char_data.idal_buf);
189*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL);
190*b6cba4eeSStefan Bader 	tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL);
191*b6cba4eeSStefan Bader 	DBF_EVENT(6, "xrop ccwg\n");
192*b6cba4eeSStefan Bader }
193*b6cba4eeSStefan Bader 
194*b6cba4eeSStefan Bader /*
195*b6cba4eeSStefan Bader  * Read Attention Msg
196*b6cba4eeSStefan Bader  * This should be done after an interrupt with attention bit (0x80)
197*b6cba4eeSStefan Bader  * in device state.
198*b6cba4eeSStefan Bader  *
199*b6cba4eeSStefan Bader  * After a "read attention message" request there are two possible
200*b6cba4eeSStefan Bader  * results:
201*b6cba4eeSStefan Bader  *
202*b6cba4eeSStefan Bader  * 1. A unit check is presented, when attention sense is present (e.g. when
203*b6cba4eeSStefan Bader  * a medium has been unloaded). The attention sense comes then
204*b6cba4eeSStefan Bader  * together with the unit check. The recovery action is either "retry"
205*b6cba4eeSStefan Bader  * (in case there is an attention message pending) or "permanent error".
206*b6cba4eeSStefan Bader  *
207*b6cba4eeSStefan Bader  * 2. The attention msg is written to the "read subsystem data" buffer.
208*b6cba4eeSStefan Bader  * In this case we probably should print it to the console.
209*b6cba4eeSStefan Bader  */
210*b6cba4eeSStefan Bader static int
211*b6cba4eeSStefan Bader tape_3590_read_attmsg(struct tape_device *device)
212*b6cba4eeSStefan Bader {
213*b6cba4eeSStefan Bader 	struct tape_request *request;
214*b6cba4eeSStefan Bader 	char *buf;
215*b6cba4eeSStefan Bader 
216*b6cba4eeSStefan Bader 	request = tape_alloc_request(3, 4096);
217*b6cba4eeSStefan Bader 	if (IS_ERR(request))
218*b6cba4eeSStefan Bader 		return PTR_ERR(request);
219*b6cba4eeSStefan Bader 	request->op = TO_READ_ATTMSG;
220*b6cba4eeSStefan Bader 	buf = request->cpdata;
221*b6cba4eeSStefan Bader 	buf[0] = PREP_RD_SS_DATA;
222*b6cba4eeSStefan Bader 	buf[6] = RD_ATTMSG;	/* read att msg */
223*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr, PERFORM_SS_FUNC, 12, buf);
224*b6cba4eeSStefan Bader 	tape_ccw_cc(request->cpaddr + 1, READ_SS_DATA, 4096 - 12, buf + 12);
225*b6cba4eeSStefan Bader 	tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL);
226*b6cba4eeSStefan Bader 	return tape_do_io_free(device, request);
227*b6cba4eeSStefan Bader }
228*b6cba4eeSStefan Bader 
229*b6cba4eeSStefan Bader /*
230*b6cba4eeSStefan Bader  * These functions are used to schedule follow-up actions from within an
231*b6cba4eeSStefan Bader  * interrupt context (like unsolicited interrupts).
232*b6cba4eeSStefan Bader  */
233*b6cba4eeSStefan Bader static void
234*b6cba4eeSStefan Bader tape_3590_work_handler(void *data)
235*b6cba4eeSStefan Bader {
236*b6cba4eeSStefan Bader 	struct {
237*b6cba4eeSStefan Bader 		struct tape_device *device;
238*b6cba4eeSStefan Bader 		enum tape_op op;
239*b6cba4eeSStefan Bader 		struct work_struct work;
240*b6cba4eeSStefan Bader 	} *p = data;
241*b6cba4eeSStefan Bader 
242*b6cba4eeSStefan Bader 	switch (p->op) {
243*b6cba4eeSStefan Bader 	case TO_MSEN:
244*b6cba4eeSStefan Bader 		tape_3590_sense_medium(p->device);
245*b6cba4eeSStefan Bader 		break;
246*b6cba4eeSStefan Bader 	case TO_READ_ATTMSG:
247*b6cba4eeSStefan Bader 		tape_3590_read_attmsg(p->device);
248*b6cba4eeSStefan Bader 		break;
249*b6cba4eeSStefan Bader 	default:
250*b6cba4eeSStefan Bader 		DBF_EVENT(3, "T3590: work handler undefined for "
251*b6cba4eeSStefan Bader 			  "operation 0x%02x\n", p->op);
252*b6cba4eeSStefan Bader 	}
253*b6cba4eeSStefan Bader 	tape_put_device(p->device);
254*b6cba4eeSStefan Bader 	kfree(p);
255*b6cba4eeSStefan Bader }
256*b6cba4eeSStefan Bader 
257*b6cba4eeSStefan Bader static int
258*b6cba4eeSStefan Bader tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
259*b6cba4eeSStefan Bader {
260*b6cba4eeSStefan Bader 	struct {
261*b6cba4eeSStefan Bader 		struct tape_device *device;
262*b6cba4eeSStefan Bader 		enum tape_op op;
263*b6cba4eeSStefan Bader 		struct work_struct work;
264*b6cba4eeSStefan Bader 	} *p;
265*b6cba4eeSStefan Bader 
266*b6cba4eeSStefan Bader 	if ((p = kzalloc(sizeof(*p), GFP_ATOMIC)) == NULL)
267*b6cba4eeSStefan Bader 		return -ENOMEM;
268*b6cba4eeSStefan Bader 
269*b6cba4eeSStefan Bader 	INIT_WORK(&p->work, tape_3590_work_handler, p);
270*b6cba4eeSStefan Bader 
271*b6cba4eeSStefan Bader 	p->device = tape_get_device_reference(device);
272*b6cba4eeSStefan Bader 	p->op = op;
273*b6cba4eeSStefan Bader 
274*b6cba4eeSStefan Bader 	schedule_work(&p->work);
275*b6cba4eeSStefan Bader 	return 0;
276*b6cba4eeSStefan Bader }
277*b6cba4eeSStefan Bader 
278*b6cba4eeSStefan Bader #ifdef CONFIG_S390_TAPE_BLOCK
279*b6cba4eeSStefan Bader /*
280*b6cba4eeSStefan Bader  * Tape Block READ
281*b6cba4eeSStefan Bader  */
282*b6cba4eeSStefan Bader static struct tape_request *
283*b6cba4eeSStefan Bader tape_3590_bread(struct tape_device *device, struct request *req)
284*b6cba4eeSStefan Bader {
285*b6cba4eeSStefan Bader 	struct tape_request *request;
286*b6cba4eeSStefan Bader 	struct ccw1 *ccw;
287*b6cba4eeSStefan Bader 	int count = 0, start_block, i;
288*b6cba4eeSStefan Bader 	unsigned off;
289*b6cba4eeSStefan Bader 	char *dst;
290*b6cba4eeSStefan Bader 	struct bio_vec *bv;
291*b6cba4eeSStefan Bader 	struct bio *bio;
292*b6cba4eeSStefan Bader 
293*b6cba4eeSStefan Bader 	DBF_EVENT(6, "xBREDid:");
294*b6cba4eeSStefan Bader 	start_block = req->sector >> TAPEBLOCK_HSEC_S2B;
295*b6cba4eeSStefan Bader 	DBF_EVENT(6, "start_block = %i\n", start_block);
296*b6cba4eeSStefan Bader 
297*b6cba4eeSStefan Bader 	rq_for_each_bio(bio, req) {
298*b6cba4eeSStefan Bader 		bio_for_each_segment(bv, bio, i) {
299*b6cba4eeSStefan Bader 			count += bv->bv_len >> (TAPEBLOCK_HSEC_S2B + 9);
300*b6cba4eeSStefan Bader 		}
301*b6cba4eeSStefan Bader 	}
302*b6cba4eeSStefan Bader 	request = tape_alloc_request(2 + count + 1, 4);
303*b6cba4eeSStefan Bader 	if (IS_ERR(request))
304*b6cba4eeSStefan Bader 		return request;
305*b6cba4eeSStefan Bader 	request->op = TO_BLOCK;
306*b6cba4eeSStefan Bader 	*(__u32 *) request->cpdata = start_block;
307*b6cba4eeSStefan Bader 	ccw = request->cpaddr;
308*b6cba4eeSStefan Bader 	ccw = tape_ccw_cc(ccw, MODE_SET_DB, 1, device->modeset_byte);
309*b6cba4eeSStefan Bader 
310*b6cba4eeSStefan Bader 	/*
311*b6cba4eeSStefan Bader 	 * We always setup a nop after the mode set ccw. This slot is
312*b6cba4eeSStefan Bader 	 * used in tape_std_check_locate to insert a locate ccw if the
313*b6cba4eeSStefan Bader 	 * current tape position doesn't match the start block to be read.
314*b6cba4eeSStefan Bader 	 */
315*b6cba4eeSStefan Bader 	ccw = tape_ccw_cc(ccw, NOP, 0, NULL);
316*b6cba4eeSStefan Bader 
317*b6cba4eeSStefan Bader 	rq_for_each_bio(bio, req) {
318*b6cba4eeSStefan Bader 		bio_for_each_segment(bv, bio, i) {
319*b6cba4eeSStefan Bader 			dst = kmap(bv->bv_page) + bv->bv_offset;
320*b6cba4eeSStefan Bader 			for (off = 0; off < bv->bv_len;
321*b6cba4eeSStefan Bader 			     off += TAPEBLOCK_HSEC_SIZE) {
322*b6cba4eeSStefan Bader 				ccw->flags = CCW_FLAG_CC;
323*b6cba4eeSStefan Bader 				ccw->cmd_code = READ_FORWARD;
324*b6cba4eeSStefan Bader 				ccw->count = TAPEBLOCK_HSEC_SIZE;
325*b6cba4eeSStefan Bader 				set_normalized_cda(ccw, (void *) __pa(dst));
326*b6cba4eeSStefan Bader 				ccw++;
327*b6cba4eeSStefan Bader 				dst += TAPEBLOCK_HSEC_SIZE;
328*b6cba4eeSStefan Bader 			}
329*b6cba4eeSStefan Bader 			if (off > bv->bv_len)
330*b6cba4eeSStefan Bader 				BUG();
331*b6cba4eeSStefan Bader 		}
332*b6cba4eeSStefan Bader 	}
333*b6cba4eeSStefan Bader 	ccw = tape_ccw_end(ccw, NOP, 0, NULL);
334*b6cba4eeSStefan Bader 	DBF_EVENT(6, "xBREDccwg\n");
335*b6cba4eeSStefan Bader 	return request;
336*b6cba4eeSStefan Bader }
337*b6cba4eeSStefan Bader 
338*b6cba4eeSStefan Bader static void
339*b6cba4eeSStefan Bader tape_3590_free_bread(struct tape_request *request)
340*b6cba4eeSStefan Bader {
341*b6cba4eeSStefan Bader 	struct ccw1 *ccw;
342*b6cba4eeSStefan Bader 
343*b6cba4eeSStefan Bader 	/* Last ccw is a nop and doesn't need clear_normalized_cda */
344*b6cba4eeSStefan Bader 	for (ccw = request->cpaddr; ccw->flags & CCW_FLAG_CC; ccw++)
345*b6cba4eeSStefan Bader 		if (ccw->cmd_code == READ_FORWARD)
346*b6cba4eeSStefan Bader 			clear_normalized_cda(ccw);
347*b6cba4eeSStefan Bader 	tape_free_request(request);
348*b6cba4eeSStefan Bader }
349*b6cba4eeSStefan Bader 
350*b6cba4eeSStefan Bader /*
351*b6cba4eeSStefan Bader  * check_locate is called just before the tape request is passed to
352*b6cba4eeSStefan Bader  * the common io layer for execution. It has to check the current
353*b6cba4eeSStefan Bader  * tape position and insert a locate ccw if it doesn't match the
354*b6cba4eeSStefan Bader  * start block for the request.
355*b6cba4eeSStefan Bader  */
356*b6cba4eeSStefan Bader static void
357*b6cba4eeSStefan Bader tape_3590_check_locate(struct tape_device *device, struct tape_request *request)
358*b6cba4eeSStefan Bader {
359*b6cba4eeSStefan Bader 	__u32 *start_block;
360*b6cba4eeSStefan Bader 
361*b6cba4eeSStefan Bader 	start_block = (__u32 *) request->cpdata;
362*b6cba4eeSStefan Bader 	if (*start_block != device->blk_data.block_position) {
363*b6cba4eeSStefan Bader 		/* Add the start offset of the file to get the real block. */
364*b6cba4eeSStefan Bader 		*start_block += device->bof;
365*b6cba4eeSStefan Bader 		tape_ccw_cc(request->cpaddr + 1, LOCATE, 4, request->cpdata);
366*b6cba4eeSStefan Bader 	}
367*b6cba4eeSStefan Bader }
368*b6cba4eeSStefan Bader #endif
369*b6cba4eeSStefan Bader 
370*b6cba4eeSStefan Bader /*
371*b6cba4eeSStefan Bader  * The done handler is called at device/channel end and wakes up the sleeping
372*b6cba4eeSStefan Bader  * process
373*b6cba4eeSStefan Bader  */
374*b6cba4eeSStefan Bader static int
375*b6cba4eeSStefan Bader tape_3590_done(struct tape_device *device, struct tape_request *request)
376*b6cba4eeSStefan Bader {
377*b6cba4eeSStefan Bader 	struct tape_3590_med_sense *sense;
378*b6cba4eeSStefan Bader 
379*b6cba4eeSStefan Bader 	DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
380*b6cba4eeSStefan Bader 
381*b6cba4eeSStefan Bader 	switch (request->op) {
382*b6cba4eeSStefan Bader 	case TO_BSB:
383*b6cba4eeSStefan Bader 	case TO_BSF:
384*b6cba4eeSStefan Bader 	case TO_DSE:
385*b6cba4eeSStefan Bader 	case TO_FSB:
386*b6cba4eeSStefan Bader 	case TO_FSF:
387*b6cba4eeSStefan Bader 	case TO_LBL:
388*b6cba4eeSStefan Bader 	case TO_RFO:
389*b6cba4eeSStefan Bader 	case TO_RBA:
390*b6cba4eeSStefan Bader 	case TO_REW:
391*b6cba4eeSStefan Bader 	case TO_WRI:
392*b6cba4eeSStefan Bader 	case TO_WTM:
393*b6cba4eeSStefan Bader 	case TO_BLOCK:
394*b6cba4eeSStefan Bader 	case TO_LOAD:
395*b6cba4eeSStefan Bader 		tape_med_state_set(device, MS_LOADED);
396*b6cba4eeSStefan Bader 		break;
397*b6cba4eeSStefan Bader 	case TO_RUN:
398*b6cba4eeSStefan Bader 		tape_med_state_set(device, MS_UNLOADED);
399*b6cba4eeSStefan Bader 		break;
400*b6cba4eeSStefan Bader 	case TO_MSEN:
401*b6cba4eeSStefan Bader 		sense = (struct tape_3590_med_sense *) request->cpdata;
402*b6cba4eeSStefan Bader 		if (sense->masst == MSENSE_UNASSOCIATED)
403*b6cba4eeSStefan Bader 			tape_med_state_set(device, MS_UNLOADED);
404*b6cba4eeSStefan Bader 		if (sense->masst == MSENSE_ASSOCIATED_MOUNT)
405*b6cba4eeSStefan Bader 			tape_med_state_set(device, MS_LOADED);
406*b6cba4eeSStefan Bader 		break;
407*b6cba4eeSStefan Bader 	case TO_RBI:	/* RBI seems to succeed even without medium loaded. */
408*b6cba4eeSStefan Bader 	case TO_NOP:	/* Same to NOP. */
409*b6cba4eeSStefan Bader 	case TO_READ_CONFIG:
410*b6cba4eeSStefan Bader 	case TO_READ_ATTMSG:
411*b6cba4eeSStefan Bader 	case TO_DIS:
412*b6cba4eeSStefan Bader 	case TO_ASSIGN:
413*b6cba4eeSStefan Bader 	case TO_UNASSIGN:
414*b6cba4eeSStefan Bader 		break;
415*b6cba4eeSStefan Bader 	case TO_SIZE:
416*b6cba4eeSStefan Bader 		break;
417*b6cba4eeSStefan Bader 	}
418*b6cba4eeSStefan Bader 	return TAPE_IO_SUCCESS;
419*b6cba4eeSStefan Bader }
420*b6cba4eeSStefan Bader 
421*b6cba4eeSStefan Bader /*
422*b6cba4eeSStefan Bader  * This fuction is called, when error recovery was successfull
423*b6cba4eeSStefan Bader  */
424*b6cba4eeSStefan Bader static inline int
425*b6cba4eeSStefan Bader tape_3590_erp_succeded(struct tape_device *device, struct tape_request *request)
426*b6cba4eeSStefan Bader {
427*b6cba4eeSStefan Bader 	DBF_EVENT(3, "Error Recovery successfull for %s\n",
428*b6cba4eeSStefan Bader 		  tape_op_verbose[request->op]);
429*b6cba4eeSStefan Bader 	return tape_3590_done(device, request);
430*b6cba4eeSStefan Bader }
431*b6cba4eeSStefan Bader 
432*b6cba4eeSStefan Bader /*
433*b6cba4eeSStefan Bader  * This fuction is called, when error recovery was not successfull
434*b6cba4eeSStefan Bader  */
435*b6cba4eeSStefan Bader static inline int
436*b6cba4eeSStefan Bader tape_3590_erp_failed(struct tape_device *device, struct tape_request *request,
437*b6cba4eeSStefan Bader 		     struct irb *irb, int rc)
438*b6cba4eeSStefan Bader {
439*b6cba4eeSStefan Bader 	DBF_EVENT(3, "Error Recovery failed for %s\n",
440*b6cba4eeSStefan Bader 		  tape_op_verbose[request->op]);
441*b6cba4eeSStefan Bader 	tape_dump_sense_dbf(device, request, irb);
442*b6cba4eeSStefan Bader 	return rc;
443*b6cba4eeSStefan Bader }
444*b6cba4eeSStefan Bader 
445*b6cba4eeSStefan Bader /*
446*b6cba4eeSStefan Bader  * Error Recovery do retry
447*b6cba4eeSStefan Bader  */
448*b6cba4eeSStefan Bader static inline int
449*b6cba4eeSStefan Bader tape_3590_erp_retry(struct tape_device *device, struct tape_request *request,
450*b6cba4eeSStefan Bader 		    struct irb *irb)
451*b6cba4eeSStefan Bader {
452*b6cba4eeSStefan Bader 	DBF_EVENT(2, "Retry: %s\n", tape_op_verbose[request->op]);
453*b6cba4eeSStefan Bader 	tape_dump_sense_dbf(device, request, irb);
454*b6cba4eeSStefan Bader 	return TAPE_IO_RETRY;
455*b6cba4eeSStefan Bader }
456*b6cba4eeSStefan Bader 
457*b6cba4eeSStefan Bader /*
458*b6cba4eeSStefan Bader  * Handle unsolicited interrupts
459*b6cba4eeSStefan Bader  */
460*b6cba4eeSStefan Bader static int
461*b6cba4eeSStefan Bader tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
462*b6cba4eeSStefan Bader {
463*b6cba4eeSStefan Bader 	if (irb->scsw.dstat == DEV_STAT_CHN_END)
464*b6cba4eeSStefan Bader 		/* Probably result of halt ssch */
465*b6cba4eeSStefan Bader 		return TAPE_IO_PENDING;
466*b6cba4eeSStefan Bader 	else if (irb->scsw.dstat == 0x85)
467*b6cba4eeSStefan Bader 		/* Device Ready -> check medium state */
468*b6cba4eeSStefan Bader 		tape_3590_schedule_work(device, TO_MSEN);
469*b6cba4eeSStefan Bader 	else if (irb->scsw.dstat & DEV_STAT_ATTENTION)
470*b6cba4eeSStefan Bader 		tape_3590_schedule_work(device, TO_READ_ATTMSG);
471*b6cba4eeSStefan Bader 	else {
472*b6cba4eeSStefan Bader 		DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
473*b6cba4eeSStefan Bader 		PRINT_WARN("Unsolicited IRQ (Device End) caught.\n");
474*b6cba4eeSStefan Bader 		tape_dump_sense(device, NULL, irb);
475*b6cba4eeSStefan Bader 	}
476*b6cba4eeSStefan Bader 	return TAPE_IO_SUCCESS;
477*b6cba4eeSStefan Bader }
478*b6cba4eeSStefan Bader 
479*b6cba4eeSStefan Bader /*
480*b6cba4eeSStefan Bader  * Basic Recovery routine
481*b6cba4eeSStefan Bader  */
482*b6cba4eeSStefan Bader static int
483*b6cba4eeSStefan Bader tape_3590_erp_basic(struct tape_device *device, struct tape_request *request,
484*b6cba4eeSStefan Bader 		    struct irb *irb, int rc)
485*b6cba4eeSStefan Bader {
486*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
487*b6cba4eeSStefan Bader 
488*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
489*b6cba4eeSStefan Bader 
490*b6cba4eeSStefan Bader 	switch (sense->bra) {
491*b6cba4eeSStefan Bader 	case SENSE_BRA_PER:
492*b6cba4eeSStefan Bader 		return tape_3590_erp_failed(device, request, irb, rc);
493*b6cba4eeSStefan Bader 	case SENSE_BRA_CONT:
494*b6cba4eeSStefan Bader 		return tape_3590_erp_succeded(device, request);
495*b6cba4eeSStefan Bader 	case SENSE_BRA_RE:
496*b6cba4eeSStefan Bader 		return tape_3590_erp_retry(device, request, irb);
497*b6cba4eeSStefan Bader 	case SENSE_BRA_DRE:
498*b6cba4eeSStefan Bader 		return tape_3590_erp_failed(device, request, irb, rc);
499*b6cba4eeSStefan Bader 	default:
500*b6cba4eeSStefan Bader 		PRINT_ERR("Unknown BRA %x - This should not happen!\n",
501*b6cba4eeSStefan Bader 			  sense->bra);
502*b6cba4eeSStefan Bader 		BUG();
503*b6cba4eeSStefan Bader 		return TAPE_IO_STOP;
504*b6cba4eeSStefan Bader 	}
505*b6cba4eeSStefan Bader }
506*b6cba4eeSStefan Bader 
507*b6cba4eeSStefan Bader /*
508*b6cba4eeSStefan Bader  *  RDL: Read Device (buffered) log
509*b6cba4eeSStefan Bader  */
510*b6cba4eeSStefan Bader static int
511*b6cba4eeSStefan Bader tape_3590_erp_read_buf_log(struct tape_device *device,
512*b6cba4eeSStefan Bader 			   struct tape_request *request, struct irb *irb)
513*b6cba4eeSStefan Bader {
514*b6cba4eeSStefan Bader 	/*
515*b6cba4eeSStefan Bader 	 * We just do the basic error recovery at the moment (retry).
516*b6cba4eeSStefan Bader 	 * Perhaps in the future, we read the log and dump it somewhere...
517*b6cba4eeSStefan Bader 	 */
518*b6cba4eeSStefan Bader 	return tape_3590_erp_basic(device, request, irb, -EIO);
519*b6cba4eeSStefan Bader }
520*b6cba4eeSStefan Bader 
521*b6cba4eeSStefan Bader /*
522*b6cba4eeSStefan Bader  *  SWAP: Swap Devices
523*b6cba4eeSStefan Bader  */
524*b6cba4eeSStefan Bader static int
525*b6cba4eeSStefan Bader tape_3590_erp_swap(struct tape_device *device, struct tape_request *request,
526*b6cba4eeSStefan Bader 		   struct irb *irb)
527*b6cba4eeSStefan Bader {
528*b6cba4eeSStefan Bader 	/*
529*b6cba4eeSStefan Bader 	 * This error recovery should swap the tapes
530*b6cba4eeSStefan Bader 	 * if the original has a problem. The operation
531*b6cba4eeSStefan Bader 	 * should proceed with the new tape... this
532*b6cba4eeSStefan Bader 	 * should probably be done in user space!
533*b6cba4eeSStefan Bader 	 */
534*b6cba4eeSStefan Bader 	PRINT_WARN("(%s): Swap Tape Device!\n", device->cdev->dev.bus_id);
535*b6cba4eeSStefan Bader 	return tape_3590_erp_basic(device, request, irb, -EIO);
536*b6cba4eeSStefan Bader }
537*b6cba4eeSStefan Bader 
538*b6cba4eeSStefan Bader /*
539*b6cba4eeSStefan Bader  *  LBY: Long Busy
540*b6cba4eeSStefan Bader  */
541*b6cba4eeSStefan Bader static int
542*b6cba4eeSStefan Bader tape_3590_erp_long_busy(struct tape_device *device,
543*b6cba4eeSStefan Bader 			struct tape_request *request, struct irb *irb)
544*b6cba4eeSStefan Bader {
545*b6cba4eeSStefan Bader 	/* FIXME: how about WAITING for a minute ? */
546*b6cba4eeSStefan Bader 	PRINT_WARN("(%s): Device is busy! Please wait a minute!\n",
547*b6cba4eeSStefan Bader 		   device->cdev->dev.bus_id);
548*b6cba4eeSStefan Bader 	return tape_3590_erp_basic(device, request, irb, -EBUSY);
549*b6cba4eeSStefan Bader }
550*b6cba4eeSStefan Bader 
551*b6cba4eeSStefan Bader /*
552*b6cba4eeSStefan Bader  *  SPI: Special Intercept
553*b6cba4eeSStefan Bader  */
554*b6cba4eeSStefan Bader static int
555*b6cba4eeSStefan Bader tape_3590_erp_special_interrupt(struct tape_device *device,
556*b6cba4eeSStefan Bader 				struct tape_request *request, struct irb *irb)
557*b6cba4eeSStefan Bader {
558*b6cba4eeSStefan Bader 	return tape_3590_erp_basic(device, request, irb, -EIO);
559*b6cba4eeSStefan Bader }
560*b6cba4eeSStefan Bader 
561*b6cba4eeSStefan Bader /*
562*b6cba4eeSStefan Bader  *  RDA: Read Alternate
563*b6cba4eeSStefan Bader  */
564*b6cba4eeSStefan Bader static int
565*b6cba4eeSStefan Bader tape_3590_erp_read_alternate(struct tape_device *device,
566*b6cba4eeSStefan Bader 			     struct tape_request *request, struct irb *irb)
567*b6cba4eeSStefan Bader {
568*b6cba4eeSStefan Bader 	struct tape_3590_disc_data *data;
569*b6cba4eeSStefan Bader 
570*b6cba4eeSStefan Bader 	/*
571*b6cba4eeSStefan Bader 	 * The issued Read Backward or Read Previous command is not
572*b6cba4eeSStefan Bader 	 * supported by the device
573*b6cba4eeSStefan Bader 	 * The recovery action should be to issue another command:
574*b6cba4eeSStefan Bader 	 * Read Revious: if Read Backward is not supported
575*b6cba4eeSStefan Bader 	 * Read Backward: if Read Previous is not supported
576*b6cba4eeSStefan Bader 	 */
577*b6cba4eeSStefan Bader 	data = device->discdata;
578*b6cba4eeSStefan Bader 	if (data->read_back_op == READ_PREVIOUS) {
579*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): No support for READ_PREVIOUS command\n",
580*b6cba4eeSStefan Bader 			  device->cdev_id);
581*b6cba4eeSStefan Bader 		data->read_back_op = READ_BACKWARD;
582*b6cba4eeSStefan Bader 	} else {
583*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): No support for READ_BACKWARD command\n",
584*b6cba4eeSStefan Bader 			  device->cdev_id);
585*b6cba4eeSStefan Bader 		data->read_back_op = READ_PREVIOUS;
586*b6cba4eeSStefan Bader 	}
587*b6cba4eeSStefan Bader 	tape_3590_read_opposite(device, request);
588*b6cba4eeSStefan Bader 	return tape_3590_erp_retry(device, request, irb);
589*b6cba4eeSStefan Bader }
590*b6cba4eeSStefan Bader 
591*b6cba4eeSStefan Bader /*
592*b6cba4eeSStefan Bader  * Error Recovery read opposite
593*b6cba4eeSStefan Bader  */
594*b6cba4eeSStefan Bader static int
595*b6cba4eeSStefan Bader tape_3590_erp_read_opposite(struct tape_device *device,
596*b6cba4eeSStefan Bader 			    struct tape_request *request, struct irb *irb)
597*b6cba4eeSStefan Bader {
598*b6cba4eeSStefan Bader 	switch (request->op) {
599*b6cba4eeSStefan Bader 	case TO_RFO:
600*b6cba4eeSStefan Bader 		/*
601*b6cba4eeSStefan Bader 		 * We did read forward, but the data could not be read.
602*b6cba4eeSStefan Bader 		 * We will read backward and then skip forward again.
603*b6cba4eeSStefan Bader 		 */
604*b6cba4eeSStefan Bader 		tape_3590_read_opposite(device, request);
605*b6cba4eeSStefan Bader 		return tape_3590_erp_retry(device, request, irb);
606*b6cba4eeSStefan Bader 	case TO_RBA:
607*b6cba4eeSStefan Bader 		/* We tried to read forward and backward, but hat no success */
608*b6cba4eeSStefan Bader 		return tape_3590_erp_failed(device, request, irb, -EIO);
609*b6cba4eeSStefan Bader 		break;
610*b6cba4eeSStefan Bader 	default:
611*b6cba4eeSStefan Bader 		PRINT_WARN("read_opposite_recovery_called_with_op: %s\n",
612*b6cba4eeSStefan Bader 			   tape_op_verbose[request->op]);
613*b6cba4eeSStefan Bader 		return tape_3590_erp_failed(device, request, irb, -EIO);
614*b6cba4eeSStefan Bader 	}
615*b6cba4eeSStefan Bader }
616*b6cba4eeSStefan Bader 
617*b6cba4eeSStefan Bader /*
618*b6cba4eeSStefan Bader  * Print an MIM (Media Information  Message) (message code f0)
619*b6cba4eeSStefan Bader  */
620*b6cba4eeSStefan Bader static void
621*b6cba4eeSStefan Bader tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb)
622*b6cba4eeSStefan Bader {
623*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
624*b6cba4eeSStefan Bader 
625*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
626*b6cba4eeSStefan Bader 	/* Exception Message */
627*b6cba4eeSStefan Bader 	switch (sense->fmt.f70.emc) {
628*b6cba4eeSStefan Bader 	case 0x02:
629*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Data degraded\n", device->cdev->dev.bus_id);
630*b6cba4eeSStefan Bader 		break;
631*b6cba4eeSStefan Bader 	case 0x03:
632*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Data degraded in partion %i\n",
633*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.mp);
634*b6cba4eeSStefan Bader 		break;
635*b6cba4eeSStefan Bader 	case 0x04:
636*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Medium degraded\n", device->cdev->dev.bus_id);
637*b6cba4eeSStefan Bader 		break;
638*b6cba4eeSStefan Bader 	case 0x05:
639*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Medium degraded in partition %i\n",
640*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.mp);
641*b6cba4eeSStefan Bader 		break;
642*b6cba4eeSStefan Bader 	case 0x06:
643*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Block 0 Error\n", device->cdev->dev.bus_id);
644*b6cba4eeSStefan Bader 		break;
645*b6cba4eeSStefan Bader 	case 0x07:
646*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Medium Exception 0x%02x\n",
647*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.md);
648*b6cba4eeSStefan Bader 		break;
649*b6cba4eeSStefan Bader 	default:
650*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): MIM ExMsg: 0x%02x\n",
651*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.emc);
652*b6cba4eeSStefan Bader 		break;
653*b6cba4eeSStefan Bader 	}
654*b6cba4eeSStefan Bader 	/* Service Message */
655*b6cba4eeSStefan Bader 	switch (sense->fmt.f70.smc) {
656*b6cba4eeSStefan Bader 	case 0x02:
657*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Reference Media maintenance procedure %i\n",
658*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.md);
659*b6cba4eeSStefan Bader 		break;
660*b6cba4eeSStefan Bader 	default:
661*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): MIM ServiceMsg: 0x%02x\n",
662*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f70.smc);
663*b6cba4eeSStefan Bader 		break;
664*b6cba4eeSStefan Bader 	}
665*b6cba4eeSStefan Bader }
666*b6cba4eeSStefan Bader 
667*b6cba4eeSStefan Bader /*
668*b6cba4eeSStefan Bader  * Print an I/O Subsystem Service Information Message (message code f1)
669*b6cba4eeSStefan Bader  */
670*b6cba4eeSStefan Bader static void
671*b6cba4eeSStefan Bader tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb)
672*b6cba4eeSStefan Bader {
673*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
674*b6cba4eeSStefan Bader 
675*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
676*b6cba4eeSStefan Bader 	/* Exception Message */
677*b6cba4eeSStefan Bader 	switch (sense->fmt.f71.emc) {
678*b6cba4eeSStefan Bader 	case 0x01:
679*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Effect of failure is unknown\n",
680*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
681*b6cba4eeSStefan Bader 		break;
682*b6cba4eeSStefan Bader 	case 0x02:
683*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception - no performance impact\n",
684*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
685*b6cba4eeSStefan Bader 		break;
686*b6cba4eeSStefan Bader 	case 0x03:
687*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception on channel interface 0x%02x\n",
688*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
689*b6cba4eeSStefan Bader 		break;
690*b6cba4eeSStefan Bader 	case 0x04:
691*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception on device path 0x%02x\n",
692*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
693*b6cba4eeSStefan Bader 		break;
694*b6cba4eeSStefan Bader 	case 0x05:
695*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception on library path 0x%02x\n",
696*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
697*b6cba4eeSStefan Bader 		break;
698*b6cba4eeSStefan Bader 	case 0x06:
699*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception on node 0x%02x\n",
700*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
701*b6cba4eeSStefan Bader 		break;
702*b6cba4eeSStefan Bader 	case 0x07:
703*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): CU Exception on partition 0x%02x\n",
704*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
705*b6cba4eeSStefan Bader 		break;
706*b6cba4eeSStefan Bader 	default:
707*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): SIM ExMsg: 0x%02x\n",
708*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.emc);
709*b6cba4eeSStefan Bader 	}
710*b6cba4eeSStefan Bader 	/* Service Message */
711*b6cba4eeSStefan Bader 	switch (sense->fmt.f71.smc) {
712*b6cba4eeSStefan Bader 	case 0x01:
713*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair impact is unknown\n",
714*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
715*b6cba4eeSStefan Bader 		break;
716*b6cba4eeSStefan Bader 	case 0x02:
717*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair will not impact cu performance\n",
718*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
719*b6cba4eeSStefan Bader 		break;
720*b6cba4eeSStefan Bader 	case 0x03:
721*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
722*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable node "
723*b6cba4eeSStefan Bader 				   "0x%x on CU\n",
724*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
725*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
726*b6cba4eeSStefan Bader 		else
727*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable nodes "
728*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on CU\n",
729*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
730*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
731*b6cba4eeSStefan Bader 		break;
732*b6cba4eeSStefan Bader 	case 0x04:
733*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
734*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable cannel path "
735*b6cba4eeSStefan Bader 				   "0x%x on CU\n",
736*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
737*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
738*b6cba4eeSStefan Bader 		else
739*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable cannel paths "
740*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on CU\n",
741*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
742*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
743*b6cba4eeSStefan Bader 		break;
744*b6cba4eeSStefan Bader 	case 0x05:
745*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
746*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable device path "
747*b6cba4eeSStefan Bader 				   "0x%x on CU\n",
748*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
749*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
750*b6cba4eeSStefan Bader 		else
751*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable device paths "
752*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on CU\n",
753*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
754*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
755*b6cba4eeSStefan Bader 		break;
756*b6cba4eeSStefan Bader 	case 0x06:
757*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
758*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable library path "
759*b6cba4eeSStefan Bader 				   "0x%x on CU\n",
760*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
761*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
762*b6cba4eeSStefan Bader 		else
763*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable library paths "
764*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on CU\n",
765*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
766*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
767*b6cba4eeSStefan Bader 		break;
768*b6cba4eeSStefan Bader 	case 0x07:
769*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair will disable access to CU\n",
770*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
771*b6cba4eeSStefan Bader 		break;
772*b6cba4eeSStefan Bader 	default:
773*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): SIM ServiceMsg: 0x%02x\n",
774*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.smc);
775*b6cba4eeSStefan Bader 	}
776*b6cba4eeSStefan Bader }
777*b6cba4eeSStefan Bader 
778*b6cba4eeSStefan Bader /*
779*b6cba4eeSStefan Bader  * Print an Device Subsystem Service Information Message (message code f2)
780*b6cba4eeSStefan Bader  */
781*b6cba4eeSStefan Bader static void
782*b6cba4eeSStefan Bader tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb)
783*b6cba4eeSStefan Bader {
784*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
785*b6cba4eeSStefan Bader 
786*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
787*b6cba4eeSStefan Bader 	/* Exception Message */
788*b6cba4eeSStefan Bader 	switch (sense->fmt.f71.emc) {
789*b6cba4eeSStefan Bader 	case 0x01:
790*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Effect of failure is unknown\n",
791*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
792*b6cba4eeSStefan Bader 		break;
793*b6cba4eeSStefan Bader 	case 0x02:
794*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception - no performance impact\n",
795*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
796*b6cba4eeSStefan Bader 		break;
797*b6cba4eeSStefan Bader 	case 0x03:
798*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception on channel interface 0x%02x\n",
799*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
800*b6cba4eeSStefan Bader 		break;
801*b6cba4eeSStefan Bader 	case 0x04:
802*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception on loader 0x%02x\n",
803*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
804*b6cba4eeSStefan Bader 		break;
805*b6cba4eeSStefan Bader 	case 0x05:
806*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception on message display 0x%02x\n",
807*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
808*b6cba4eeSStefan Bader 		break;
809*b6cba4eeSStefan Bader 	case 0x06:
810*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception in tape path\n",
811*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
812*b6cba4eeSStefan Bader 		break;
813*b6cba4eeSStefan Bader 	case 0x07:
814*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DV Exception in drive\n",
815*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
816*b6cba4eeSStefan Bader 		break;
817*b6cba4eeSStefan Bader 	default:
818*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DSIM ExMsg: 0x%02x\n",
819*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.emc);
820*b6cba4eeSStefan Bader 	}
821*b6cba4eeSStefan Bader 	/* Service Message */
822*b6cba4eeSStefan Bader 	switch (sense->fmt.f71.smc) {
823*b6cba4eeSStefan Bader 	case 0x01:
824*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair impact is unknown\n",
825*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
826*b6cba4eeSStefan Bader 		break;
827*b6cba4eeSStefan Bader 	case 0x02:
828*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair will not impact device performance\n",
829*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
830*b6cba4eeSStefan Bader 		break;
831*b6cba4eeSStefan Bader 	case 0x03:
832*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
833*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable channel path "
834*b6cba4eeSStefan Bader 				   "0x%x on DV\n",
835*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
836*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
837*b6cba4eeSStefan Bader 		else
838*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable channel path "
839*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on DV\n",
840*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
841*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
842*b6cba4eeSStefan Bader 		break;
843*b6cba4eeSStefan Bader 	case 0x04:
844*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
845*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable interface 0x%x "
846*b6cba4eeSStefan Bader 				   "on DV\n",
847*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
848*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
849*b6cba4eeSStefan Bader 		else
850*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable interfaces "
851*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on DV\n",
852*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
853*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
854*b6cba4eeSStefan Bader 		break;
855*b6cba4eeSStefan Bader 	case 0x05:
856*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
857*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable loader 0x%x "
858*b6cba4eeSStefan Bader 				   "on DV\n",
859*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
860*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
861*b6cba4eeSStefan Bader 		else
862*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable loader "
863*b6cba4eeSStefan Bader 				   "(0x%x-0x%x) on DV\n",
864*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
865*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
866*b6cba4eeSStefan Bader 		break;
867*b6cba4eeSStefan Bader 	case 0x07:
868*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Repair will disable access to DV\n",
869*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
870*b6cba4eeSStefan Bader 		break;
871*b6cba4eeSStefan Bader 	case 0x08:
872*b6cba4eeSStefan Bader 		if (sense->fmt.f71.mdf == 0)
873*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable message "
874*b6cba4eeSStefan Bader 				   "display 0x%x on DV\n",
875*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
876*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1]);
877*b6cba4eeSStefan Bader 		else
878*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Repair will disable message "
879*b6cba4eeSStefan Bader 				   "displays (0x%x-0x%x) on DV\n",
880*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id,
881*b6cba4eeSStefan Bader 				   sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
882*b6cba4eeSStefan Bader 		break;
883*b6cba4eeSStefan Bader 	case 0x09:
884*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Clean DV\n", device->cdev->dev.bus_id);
885*b6cba4eeSStefan Bader 		break;
886*b6cba4eeSStefan Bader 	default:
887*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DSIM ServiceMsg: 0x%02x\n",
888*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.smc);
889*b6cba4eeSStefan Bader 	}
890*b6cba4eeSStefan Bader }
891*b6cba4eeSStefan Bader 
892*b6cba4eeSStefan Bader /*
893*b6cba4eeSStefan Bader  * Print standard ERA Message
894*b6cba4eeSStefan Bader  */
895*b6cba4eeSStefan Bader static void
896*b6cba4eeSStefan Bader tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
897*b6cba4eeSStefan Bader {
898*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
899*b6cba4eeSStefan Bader 
900*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
901*b6cba4eeSStefan Bader 	if (sense->mc == 0)
902*b6cba4eeSStefan Bader 		return;
903*b6cba4eeSStefan Bader 	if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) {
904*b6cba4eeSStefan Bader 		if (tape_3590_msg[sense->mc] != NULL)
905*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): %s\n", device->cdev->dev.bus_id,
906*b6cba4eeSStefan Bader 				   tape_3590_msg[sense->mc]);
907*b6cba4eeSStefan Bader 		else {
908*b6cba4eeSStefan Bader 			PRINT_WARN("(%s): Message Code 0x%x\n",
909*b6cba4eeSStefan Bader 				   device->cdev->dev.bus_id, sense->mc);
910*b6cba4eeSStefan Bader 		}
911*b6cba4eeSStefan Bader 		return;
912*b6cba4eeSStefan Bader 	}
913*b6cba4eeSStefan Bader 	if (sense->mc == 0xf0) {
914*b6cba4eeSStefan Bader 		/* Standard Media Information Message */
915*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): MIM SEV=%i, MC=%02x, ES=%x/%x, "
916*b6cba4eeSStefan Bader 			   "RC=%02x-%04x-%02x\n", device->cdev->dev.bus_id,
917*b6cba4eeSStefan Bader 			   sense->fmt.f70.sev, sense->mc,
918*b6cba4eeSStefan Bader 			   sense->fmt.f70.emc, sense->fmt.f70.smc,
919*b6cba4eeSStefan Bader 			   sense->fmt.f70.refcode, sense->fmt.f70.mid,
920*b6cba4eeSStefan Bader 			   sense->fmt.f70.fid);
921*b6cba4eeSStefan Bader 		tape_3590_print_mim_msg_f0(device, irb);
922*b6cba4eeSStefan Bader 		return;
923*b6cba4eeSStefan Bader 	}
924*b6cba4eeSStefan Bader 	if (sense->mc == 0xf1) {
925*b6cba4eeSStefan Bader 		/* Standard I/O Subsystem Service Information Message */
926*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): IOSIM SEV=%i, DEVTYPE=3590/%02x, "
927*b6cba4eeSStefan Bader 			   "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
928*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.sev,
929*b6cba4eeSStefan Bader 			   device->cdev->id.dev_model,
930*b6cba4eeSStefan Bader 			   sense->mc, sense->fmt.f71.emc,
931*b6cba4eeSStefan Bader 			   sense->fmt.f71.smc, sense->fmt.f71.refcode1,
932*b6cba4eeSStefan Bader 			   sense->fmt.f71.refcode2, sense->fmt.f71.refcode3);
933*b6cba4eeSStefan Bader 		tape_3590_print_io_sim_msg_f1(device, irb);
934*b6cba4eeSStefan Bader 		return;
935*b6cba4eeSStefan Bader 	}
936*b6cba4eeSStefan Bader 	if (sense->mc == 0xf2) {
937*b6cba4eeSStefan Bader 		/* Standard Device Service Information Message */
938*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): DEVSIM SEV=%i, DEVTYPE=3590/%02x, "
939*b6cba4eeSStefan Bader 			   "MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
940*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id, sense->fmt.f71.sev,
941*b6cba4eeSStefan Bader 			   device->cdev->id.dev_model,
942*b6cba4eeSStefan Bader 			   sense->mc, sense->fmt.f71.emc,
943*b6cba4eeSStefan Bader 			   sense->fmt.f71.smc, sense->fmt.f71.refcode1,
944*b6cba4eeSStefan Bader 			   sense->fmt.f71.refcode2, sense->fmt.f71.refcode3);
945*b6cba4eeSStefan Bader 		tape_3590_print_dev_sim_msg_f2(device, irb);
946*b6cba4eeSStefan Bader 		return;
947*b6cba4eeSStefan Bader 	}
948*b6cba4eeSStefan Bader 	if (sense->mc == 0xf3) {
949*b6cba4eeSStefan Bader 		/* Standard Library Service Information Message */
950*b6cba4eeSStefan Bader 		return;
951*b6cba4eeSStefan Bader 	}
952*b6cba4eeSStefan Bader 	PRINT_WARN("(%s): Device Message(%x)\n",
953*b6cba4eeSStefan Bader 		   device->cdev->dev.bus_id, sense->mc);
954*b6cba4eeSStefan Bader }
955*b6cba4eeSStefan Bader 
956*b6cba4eeSStefan Bader /*
957*b6cba4eeSStefan Bader  *  3590 error Recovery routine:
958*b6cba4eeSStefan Bader  *  If possible, it tries to recover from the error. If this is not possible,
959*b6cba4eeSStefan Bader  *  inform the user about the problem.
960*b6cba4eeSStefan Bader  */
961*b6cba4eeSStefan Bader static int
962*b6cba4eeSStefan Bader tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
963*b6cba4eeSStefan Bader 		     struct irb *irb)
964*b6cba4eeSStefan Bader {
965*b6cba4eeSStefan Bader 	struct tape_3590_sense *sense;
966*b6cba4eeSStefan Bader 	int rc;
967*b6cba4eeSStefan Bader 
968*b6cba4eeSStefan Bader #ifdef CONFIG_S390_TAPE_BLOCK
969*b6cba4eeSStefan Bader 	if (request->op == TO_BLOCK) {
970*b6cba4eeSStefan Bader 		/*
971*b6cba4eeSStefan Bader 		 * Recovery for block device requests. Set the block_position
972*b6cba4eeSStefan Bader 		 * to something invalid and retry.
973*b6cba4eeSStefan Bader 		 */
974*b6cba4eeSStefan Bader 		device->blk_data.block_position = -1;
975*b6cba4eeSStefan Bader 		if (request->retries-- <= 0)
976*b6cba4eeSStefan Bader 			return tape_3590_erp_failed(device, request, irb, -EIO);
977*b6cba4eeSStefan Bader 		else
978*b6cba4eeSStefan Bader 			return tape_3590_erp_retry(device, request, irb);
979*b6cba4eeSStefan Bader 	}
980*b6cba4eeSStefan Bader #endif
981*b6cba4eeSStefan Bader 
982*b6cba4eeSStefan Bader 	sense = (struct tape_3590_sense *) irb->ecw;
983*b6cba4eeSStefan Bader 
984*b6cba4eeSStefan Bader 	/*
985*b6cba4eeSStefan Bader 	 * First check all RC-QRCs where we want to do something special
986*b6cba4eeSStefan Bader 	 *   - "break":     basic error recovery is done
987*b6cba4eeSStefan Bader 	 *   - "goto out:": just print error message if available
988*b6cba4eeSStefan Bader 	 */
989*b6cba4eeSStefan Bader 	rc = -EIO;
990*b6cba4eeSStefan Bader 	switch (sense->rc_rqc) {
991*b6cba4eeSStefan Bader 
992*b6cba4eeSStefan Bader 	case 0x1110:
993*b6cba4eeSStefan Bader 		tape_3590_print_era_msg(device, irb);
994*b6cba4eeSStefan Bader 		return tape_3590_erp_read_buf_log(device, request, irb);
995*b6cba4eeSStefan Bader 
996*b6cba4eeSStefan Bader 	case 0x2011:
997*b6cba4eeSStefan Bader 		tape_3590_print_era_msg(device, irb);
998*b6cba4eeSStefan Bader 		return tape_3590_erp_read_alternate(device, request, irb);
999*b6cba4eeSStefan Bader 
1000*b6cba4eeSStefan Bader 	case 0x2230:
1001*b6cba4eeSStefan Bader 	case 0x2231:
1002*b6cba4eeSStefan Bader 		tape_3590_print_era_msg(device, irb);
1003*b6cba4eeSStefan Bader 		return tape_3590_erp_special_interrupt(device, request, irb);
1004*b6cba4eeSStefan Bader 
1005*b6cba4eeSStefan Bader 	case 0x3010:
1006*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): Backward at Beginning of Partition\n",
1007*b6cba4eeSStefan Bader 			  device->cdev_id);
1008*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
1009*b6cba4eeSStefan Bader 	case 0x3012:
1010*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): Forward at End of Partition\n",
1011*b6cba4eeSStefan Bader 			  device->cdev_id);
1012*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
1013*b6cba4eeSStefan Bader 	case 0x3020:
1014*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): End of Data Mark\n", device->cdev_id);
1015*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -ENOSPC);
1016*b6cba4eeSStefan Bader 
1017*b6cba4eeSStefan Bader 	case 0x3122:
1018*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): Rewind Unload initiated\n",
1019*b6cba4eeSStefan Bader 			  device->cdev_id);
1020*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EIO);
1021*b6cba4eeSStefan Bader 	case 0x3123:
1022*b6cba4eeSStefan Bader 		DBF_EVENT(2, "(%08x): Rewind Unload complete\n",
1023*b6cba4eeSStefan Bader 			  device->cdev_id);
1024*b6cba4eeSStefan Bader 		tape_med_state_set(device, MS_UNLOADED);
1025*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, 0);
1026*b6cba4eeSStefan Bader 
1027*b6cba4eeSStefan Bader 	case 0x4010:
1028*b6cba4eeSStefan Bader 		/*
1029*b6cba4eeSStefan Bader 		 * print additional msg since default msg
1030*b6cba4eeSStefan Bader 		 * "device intervention" is not very meaningfull
1031*b6cba4eeSStefan Bader 		 */
1032*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Tape operation when medium not loaded\n",
1033*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
1034*b6cba4eeSStefan Bader 		tape_med_state_set(device, MS_UNLOADED);
1035*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
1036*b6cba4eeSStefan Bader 	case 0x4012:		/* Device Long Busy */
1037*b6cba4eeSStefan Bader 		tape_3590_print_era_msg(device, irb);
1038*b6cba4eeSStefan Bader 		return tape_3590_erp_long_busy(device, request, irb);
1039*b6cba4eeSStefan Bader 
1040*b6cba4eeSStefan Bader 	case 0x5010:
1041*b6cba4eeSStefan Bader 		if (sense->rac == 0xd0) {
1042*b6cba4eeSStefan Bader 			/* Swap */
1043*b6cba4eeSStefan Bader 			tape_3590_print_era_msg(device, irb);
1044*b6cba4eeSStefan Bader 			return tape_3590_erp_swap(device, request, irb);
1045*b6cba4eeSStefan Bader 		}
1046*b6cba4eeSStefan Bader 		if (sense->rac == 0x26) {
1047*b6cba4eeSStefan Bader 			/* Read Opposite */
1048*b6cba4eeSStefan Bader 			tape_3590_print_era_msg(device, irb);
1049*b6cba4eeSStefan Bader 			return tape_3590_erp_read_opposite(device, request,
1050*b6cba4eeSStefan Bader 							   irb);
1051*b6cba4eeSStefan Bader 		}
1052*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EIO);
1053*b6cba4eeSStefan Bader 	case 0x5020:
1054*b6cba4eeSStefan Bader 	case 0x5021:
1055*b6cba4eeSStefan Bader 	case 0x5022:
1056*b6cba4eeSStefan Bader 	case 0x5040:
1057*b6cba4eeSStefan Bader 	case 0x5041:
1058*b6cba4eeSStefan Bader 	case 0x5042:
1059*b6cba4eeSStefan Bader 		tape_3590_print_era_msg(device, irb);
1060*b6cba4eeSStefan Bader 		return tape_3590_erp_swap(device, request, irb);
1061*b6cba4eeSStefan Bader 
1062*b6cba4eeSStefan Bader 	case 0x5110:
1063*b6cba4eeSStefan Bader 	case 0x5111:
1064*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
1065*b6cba4eeSStefan Bader 
1066*b6cba4eeSStefan Bader 	case 0x5120:
1067*b6cba4eeSStefan Bader 	case 0x1120:
1068*b6cba4eeSStefan Bader 		tape_med_state_set(device, MS_UNLOADED);
1069*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
1070*b6cba4eeSStefan Bader 
1071*b6cba4eeSStefan Bader 	case 0x6020:
1072*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Cartridge of wrong type ?\n",
1073*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
1074*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
1075*b6cba4eeSStefan Bader 
1076*b6cba4eeSStefan Bader 	case 0x8011:
1077*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Another host has reserved the tape device\n",
1078*b6cba4eeSStefan Bader 			   device->cdev->dev.bus_id);
1079*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EPERM);
1080*b6cba4eeSStefan Bader 	case 0x8013:
1081*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): Another host has priviliged access to the "
1082*b6cba4eeSStefan Bader 			   "tape device\n", device->cdev->dev.bus_id);
1083*b6cba4eeSStefan Bader 		PRINT_WARN("(%s): To solve the problem unload the current "
1084*b6cba4eeSStefan Bader 			   "cartridge!\n", device->cdev->dev.bus_id);
1085*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EPERM);
1086*b6cba4eeSStefan Bader 	default:
1087*b6cba4eeSStefan Bader 		return tape_3590_erp_basic(device, request, irb, -EIO);
1088*b6cba4eeSStefan Bader 	}
1089*b6cba4eeSStefan Bader }
1090*b6cba4eeSStefan Bader 
1091*b6cba4eeSStefan Bader /*
1092*b6cba4eeSStefan Bader  * 3590 interrupt handler:
1093*b6cba4eeSStefan Bader  */
1094*b6cba4eeSStefan Bader static int
1095*b6cba4eeSStefan Bader tape_3590_irq(struct tape_device *device, struct tape_request *request,
1096*b6cba4eeSStefan Bader 	      struct irb *irb)
1097*b6cba4eeSStefan Bader {
1098*b6cba4eeSStefan Bader 	if (request == NULL)
1099*b6cba4eeSStefan Bader 		return tape_3590_unsolicited_irq(device, irb);
1100*b6cba4eeSStefan Bader 
1101*b6cba4eeSStefan Bader 	if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
1102*b6cba4eeSStefan Bader 	    (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) {
1103*b6cba4eeSStefan Bader 		/* Write at end of volume */
1104*b6cba4eeSStefan Bader 		DBF_EVENT(2, "End of volume\n");
1105*b6cba4eeSStefan Bader 		return tape_3590_erp_failed(device, request, irb, -ENOSPC);
1106*b6cba4eeSStefan Bader 	}
1107*b6cba4eeSStefan Bader 
1108*b6cba4eeSStefan Bader 	if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
1109*b6cba4eeSStefan Bader 		return tape_3590_unit_check(device, request, irb);
1110*b6cba4eeSStefan Bader 
1111*b6cba4eeSStefan Bader 	if (irb->scsw.dstat & DEV_STAT_DEV_END) {
1112*b6cba4eeSStefan Bader 		if (irb->scsw.dstat == DEV_STAT_UNIT_EXCEP) {
1113*b6cba4eeSStefan Bader 			if (request->op == TO_FSB || request->op == TO_BSB)
1114*b6cba4eeSStefan Bader 				request->rescnt++;
1115*b6cba4eeSStefan Bader 			else
1116*b6cba4eeSStefan Bader 				DBF_EVENT(5, "Unit Exception!\n");
1117*b6cba4eeSStefan Bader 		}
1118*b6cba4eeSStefan Bader 
1119*b6cba4eeSStefan Bader 		return tape_3590_done(device, request);
1120*b6cba4eeSStefan Bader 	}
1121*b6cba4eeSStefan Bader 
1122*b6cba4eeSStefan Bader 	if (irb->scsw.dstat & DEV_STAT_CHN_END) {
1123*b6cba4eeSStefan Bader 		DBF_EVENT(2, "cannel end\n");
1124*b6cba4eeSStefan Bader 		return TAPE_IO_PENDING;
1125*b6cba4eeSStefan Bader 	}
1126*b6cba4eeSStefan Bader 
1127*b6cba4eeSStefan Bader 	if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
1128*b6cba4eeSStefan Bader 		DBF_EVENT(2, "Unit Attention when busy..\n");
1129*b6cba4eeSStefan Bader 		return TAPE_IO_PENDING;
1130*b6cba4eeSStefan Bader 	}
1131*b6cba4eeSStefan Bader 
1132*b6cba4eeSStefan Bader 	DBF_EVENT(6, "xunknownirq\n");
1133*b6cba4eeSStefan Bader 	PRINT_ERR("Unexpected interrupt.\n");
1134*b6cba4eeSStefan Bader 	PRINT_ERR("Current op is: %s", tape_op_verbose[request->op]);
1135*b6cba4eeSStefan Bader 	tape_dump_sense(device, request, irb);
1136*b6cba4eeSStefan Bader 	return TAPE_IO_STOP;
1137*b6cba4eeSStefan Bader }
1138*b6cba4eeSStefan Bader 
1139*b6cba4eeSStefan Bader /*
1140*b6cba4eeSStefan Bader  * Setup device function
1141*b6cba4eeSStefan Bader  */
1142*b6cba4eeSStefan Bader static int
1143*b6cba4eeSStefan Bader tape_3590_setup_device(struct tape_device *device)
1144*b6cba4eeSStefan Bader {
1145*b6cba4eeSStefan Bader 	int rc;
1146*b6cba4eeSStefan Bader 	struct tape_3590_disc_data *data;
1147*b6cba4eeSStefan Bader 
1148*b6cba4eeSStefan Bader 	DBF_EVENT(6, "3590 device setup\n");
1149*b6cba4eeSStefan Bader 	data = kmalloc(sizeof(struct tape_3590_disc_data),
1150*b6cba4eeSStefan Bader 		       GFP_KERNEL | GFP_DMA);
1151*b6cba4eeSStefan Bader 	if (data == NULL)
1152*b6cba4eeSStefan Bader 		return -ENOMEM;
1153*b6cba4eeSStefan Bader 	data->read_back_op = READ_PREVIOUS;
1154*b6cba4eeSStefan Bader 	device->discdata = data;
1155*b6cba4eeSStefan Bader 
1156*b6cba4eeSStefan Bader 	if ((rc = tape_std_assign(device)) == 0) {
1157*b6cba4eeSStefan Bader 		/* Try to find out if medium is loaded */
1158*b6cba4eeSStefan Bader 		if ((rc = tape_3590_sense_medium(device)) != 0)
1159*b6cba4eeSStefan Bader 			DBF_LH(3, "3590 medium sense returned %d\n", rc);
1160*b6cba4eeSStefan Bader 	}
1161*b6cba4eeSStefan Bader 
1162*b6cba4eeSStefan Bader 	return rc;
1163*b6cba4eeSStefan Bader }
1164*b6cba4eeSStefan Bader 
1165*b6cba4eeSStefan Bader /*
1166*b6cba4eeSStefan Bader  * Cleanup device function
1167*b6cba4eeSStefan Bader  */
1168*b6cba4eeSStefan Bader static void
1169*b6cba4eeSStefan Bader tape_3590_cleanup_device(struct tape_device *device)
1170*b6cba4eeSStefan Bader {
1171*b6cba4eeSStefan Bader 	tape_std_unassign(device);
1172*b6cba4eeSStefan Bader 
1173*b6cba4eeSStefan Bader 	kfree(device->discdata);
1174*b6cba4eeSStefan Bader 	device->discdata = NULL;
1175*b6cba4eeSStefan Bader }
1176*b6cba4eeSStefan Bader 
1177*b6cba4eeSStefan Bader /*
1178*b6cba4eeSStefan Bader  * List of 3590 magnetic tape commands.
1179*b6cba4eeSStefan Bader  */
1180*b6cba4eeSStefan Bader static tape_mtop_fn tape_3590_mtop[TAPE_NR_MTOPS] = {
1181*b6cba4eeSStefan Bader 	[MTRESET]	 = tape_std_mtreset,
1182*b6cba4eeSStefan Bader 	[MTFSF]		 = tape_std_mtfsf,
1183*b6cba4eeSStefan Bader 	[MTBSF]		 = tape_std_mtbsf,
1184*b6cba4eeSStefan Bader 	[MTFSR]		 = tape_std_mtfsr,
1185*b6cba4eeSStefan Bader 	[MTBSR]		 = tape_std_mtbsr,
1186*b6cba4eeSStefan Bader 	[MTWEOF]	 = tape_std_mtweof,
1187*b6cba4eeSStefan Bader 	[MTREW]		 = tape_std_mtrew,
1188*b6cba4eeSStefan Bader 	[MTOFFL]	 = tape_std_mtoffl,
1189*b6cba4eeSStefan Bader 	[MTNOP]		 = tape_std_mtnop,
1190*b6cba4eeSStefan Bader 	[MTRETEN]	 = tape_std_mtreten,
1191*b6cba4eeSStefan Bader 	[MTBSFM]	 = tape_std_mtbsfm,
1192*b6cba4eeSStefan Bader 	[MTFSFM]	 = tape_std_mtfsfm,
1193*b6cba4eeSStefan Bader 	[MTEOM]		 = tape_std_mteom,
1194*b6cba4eeSStefan Bader 	[MTERASE]	 = tape_std_mterase,
1195*b6cba4eeSStefan Bader 	[MTRAS1]	 = NULL,
1196*b6cba4eeSStefan Bader 	[MTRAS2]	 = NULL,
1197*b6cba4eeSStefan Bader 	[MTRAS3]	 = NULL,
1198*b6cba4eeSStefan Bader 	[MTSETBLK]	 = tape_std_mtsetblk,
1199*b6cba4eeSStefan Bader 	[MTSETDENSITY]	 = NULL,
1200*b6cba4eeSStefan Bader 	[MTSEEK]	 = tape_3590_mtseek,
1201*b6cba4eeSStefan Bader 	[MTTELL]	 = tape_3590_mttell,
1202*b6cba4eeSStefan Bader 	[MTSETDRVBUFFER] = NULL,
1203*b6cba4eeSStefan Bader 	[MTFSS]		 = NULL,
1204*b6cba4eeSStefan Bader 	[MTBSS]		 = NULL,
1205*b6cba4eeSStefan Bader 	[MTWSM]		 = NULL,
1206*b6cba4eeSStefan Bader 	[MTLOCK]	 = NULL,
1207*b6cba4eeSStefan Bader 	[MTUNLOCK]	 = NULL,
1208*b6cba4eeSStefan Bader 	[MTLOAD]	 = tape_std_mtload,
1209*b6cba4eeSStefan Bader 	[MTUNLOAD]	 = tape_std_mtunload,
1210*b6cba4eeSStefan Bader 	[MTCOMPRESSION]	 = tape_std_mtcompression,
1211*b6cba4eeSStefan Bader 	[MTSETPART]	 = NULL,
1212*b6cba4eeSStefan Bader 	[MTMKPART]	 = NULL
1213*b6cba4eeSStefan Bader };
1214*b6cba4eeSStefan Bader 
1215*b6cba4eeSStefan Bader /*
1216*b6cba4eeSStefan Bader  * Tape discipline structure for 3590.
1217*b6cba4eeSStefan Bader  */
1218*b6cba4eeSStefan Bader static struct tape_discipline tape_discipline_3590 = {
1219*b6cba4eeSStefan Bader 	.owner = THIS_MODULE,
1220*b6cba4eeSStefan Bader 	.setup_device = tape_3590_setup_device,
1221*b6cba4eeSStefan Bader 	.cleanup_device = tape_3590_cleanup_device,
1222*b6cba4eeSStefan Bader 	.process_eov = tape_std_process_eov,
1223*b6cba4eeSStefan Bader 	.irq = tape_3590_irq,
1224*b6cba4eeSStefan Bader 	.read_block = tape_std_read_block,
1225*b6cba4eeSStefan Bader 	.write_block = tape_std_write_block,
1226*b6cba4eeSStefan Bader #ifdef CONFIG_S390_TAPE_BLOCK
1227*b6cba4eeSStefan Bader 	.bread = tape_3590_bread,
1228*b6cba4eeSStefan Bader 	.free_bread = tape_3590_free_bread,
1229*b6cba4eeSStefan Bader 	.check_locate = tape_3590_check_locate,
1230*b6cba4eeSStefan Bader #endif
1231*b6cba4eeSStefan Bader 	.ioctl_fn = tape_3590_ioctl,
1232*b6cba4eeSStefan Bader 	.mtop_array = tape_3590_mtop
1233*b6cba4eeSStefan Bader };
1234*b6cba4eeSStefan Bader 
1235*b6cba4eeSStefan Bader static struct ccw_device_id tape_3590_ids[] = {
1236*b6cba4eeSStefan Bader 	{CCW_DEVICE_DEVTYPE(0x3590, 0, 0x3590, 0), .driver_info = tape_3590},
1237*b6cba4eeSStefan Bader 	{ /* end of list */ }
1238*b6cba4eeSStefan Bader };
1239*b6cba4eeSStefan Bader 
1240*b6cba4eeSStefan Bader static int
1241*b6cba4eeSStefan Bader tape_3590_online(struct ccw_device *cdev)
1242*b6cba4eeSStefan Bader {
1243*b6cba4eeSStefan Bader 	return tape_generic_online(cdev->dev.driver_data,
1244*b6cba4eeSStefan Bader 				   &tape_discipline_3590);
1245*b6cba4eeSStefan Bader }
1246*b6cba4eeSStefan Bader 
1247*b6cba4eeSStefan Bader static int
1248*b6cba4eeSStefan Bader tape_3590_offline(struct ccw_device *cdev)
1249*b6cba4eeSStefan Bader {
1250*b6cba4eeSStefan Bader 	return tape_generic_offline(cdev->dev.driver_data);
1251*b6cba4eeSStefan Bader }
1252*b6cba4eeSStefan Bader 
1253*b6cba4eeSStefan Bader static struct ccw_driver tape_3590_driver = {
1254*b6cba4eeSStefan Bader 	.name = "tape_3590",
1255*b6cba4eeSStefan Bader 	.owner = THIS_MODULE,
1256*b6cba4eeSStefan Bader 	.ids = tape_3590_ids,
1257*b6cba4eeSStefan Bader 	.probe = tape_generic_probe,
1258*b6cba4eeSStefan Bader 	.remove = tape_generic_remove,
1259*b6cba4eeSStefan Bader 	.set_offline = tape_3590_offline,
1260*b6cba4eeSStefan Bader 	.set_online = tape_3590_online,
1261*b6cba4eeSStefan Bader };
1262*b6cba4eeSStefan Bader 
1263*b6cba4eeSStefan Bader /*
1264*b6cba4eeSStefan Bader  * Setup discipline structure.
1265*b6cba4eeSStefan Bader  */
1266*b6cba4eeSStefan Bader static int
1267*b6cba4eeSStefan Bader tape_3590_init(void)
1268*b6cba4eeSStefan Bader {
1269*b6cba4eeSStefan Bader 	int rc;
1270*b6cba4eeSStefan Bader 
1271*b6cba4eeSStefan Bader 	TAPE_DBF_AREA = debug_register("tape_3590", 2, 2, 4 * sizeof(long));
1272*b6cba4eeSStefan Bader 	debug_register_view(TAPE_DBF_AREA, &debug_sprintf_view);
1273*b6cba4eeSStefan Bader #ifdef DBF_LIKE_HELL
1274*b6cba4eeSStefan Bader 	debug_set_level(TAPE_DBF_AREA, 6);
1275*b6cba4eeSStefan Bader #endif
1276*b6cba4eeSStefan Bader 
1277*b6cba4eeSStefan Bader 	DBF_EVENT(3, "3590 init\n");
1278*b6cba4eeSStefan Bader 	/* Register driver for 3590 tapes. */
1279*b6cba4eeSStefan Bader 	rc = ccw_driver_register(&tape_3590_driver);
1280*b6cba4eeSStefan Bader 	if (rc)
1281*b6cba4eeSStefan Bader 		DBF_EVENT(3, "3590 init failed\n");
1282*b6cba4eeSStefan Bader 	else
1283*b6cba4eeSStefan Bader 		DBF_EVENT(3, "3590 registered\n");
1284*b6cba4eeSStefan Bader 	return rc;
1285*b6cba4eeSStefan Bader }
1286*b6cba4eeSStefan Bader 
1287*b6cba4eeSStefan Bader static void
1288*b6cba4eeSStefan Bader tape_3590_exit(void)
1289*b6cba4eeSStefan Bader {
1290*b6cba4eeSStefan Bader 	ccw_driver_unregister(&tape_3590_driver);
1291*b6cba4eeSStefan Bader 
1292*b6cba4eeSStefan Bader 	debug_unregister(TAPE_DBF_AREA);
1293*b6cba4eeSStefan Bader }
1294*b6cba4eeSStefan Bader 
1295*b6cba4eeSStefan Bader MODULE_DEVICE_TABLE(ccw, tape_3590_ids);
1296*b6cba4eeSStefan Bader MODULE_AUTHOR("(C) 2001,2006 IBM Corporation");
1297*b6cba4eeSStefan Bader MODULE_DESCRIPTION("Linux on zSeries channel attached 3590 tape device driver");
1298*b6cba4eeSStefan Bader MODULE_LICENSE("GPL");
1299*b6cba4eeSStefan Bader 
1300*b6cba4eeSStefan Bader module_init(tape_3590_init);
1301*b6cba4eeSStefan Bader module_exit(tape_3590_exit);
1302