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