diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/s390/char/tape_std.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/s390/char/tape_std.c')
-rw-r--r-- | drivers/s390/char/tape_std.c | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c new file mode 100644 index 000000000000..2f9fe30989a7 --- /dev/null +++ b/drivers/s390/char/tape_std.c | |||
@@ -0,0 +1,765 @@ | |||
1 | /* | ||
2 | * drivers/s390/char/tape_std.c | ||
3 | * standard tape device functions for ibm tapes. | ||
4 | * | ||
5 | * S390 and zSeries version | ||
6 | * Copyright (C) 2001,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation | ||
7 | * Author(s): Carsten Otte <cotte@de.ibm.com> | ||
8 | * Michael Holzheu <holzheu@de.ibm.com> | ||
9 | * Tuan Ngo-Anh <ngoanh@de.ibm.com> | ||
10 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
11 | * Stefan Bader <shbader@de.ibm.com> | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/stddef.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/bio.h> | ||
18 | #include <linux/timer.h> | ||
19 | |||
20 | #include <asm/types.h> | ||
21 | #include <asm/idals.h> | ||
22 | #include <asm/ebcdic.h> | ||
23 | #include <asm/tape390.h> | ||
24 | |||
25 | #define TAPE_DBF_AREA tape_core_dbf | ||
26 | |||
27 | #include "tape.h" | ||
28 | #include "tape_std.h" | ||
29 | |||
30 | #define PRINTK_HEADER "TAPE_STD: " | ||
31 | |||
32 | /* | ||
33 | * tape_std_assign | ||
34 | */ | ||
35 | static void | ||
36 | tape_std_assign_timeout(unsigned long data) | ||
37 | { | ||
38 | struct tape_request * request; | ||
39 | struct tape_device * device; | ||
40 | |||
41 | request = (struct tape_request *) data; | ||
42 | if ((device = request->device) == NULL) | ||
43 | BUG(); | ||
44 | |||
45 | spin_lock_irq(get_ccwdev_lock(device->cdev)); | ||
46 | if (request->callback != NULL) { | ||
47 | DBF_EVENT(3, "%08x: Assignment timeout. Device busy.\n", | ||
48 | device->cdev_id); | ||
49 | PRINT_ERR("%s: Assignment timeout. Device busy.\n", | ||
50 | device->cdev->dev.bus_id); | ||
51 | ccw_device_clear(device->cdev, (long) request); | ||
52 | } | ||
53 | spin_unlock_irq(get_ccwdev_lock(device->cdev)); | ||
54 | } | ||
55 | |||
56 | int | ||
57 | tape_std_assign(struct tape_device *device) | ||
58 | { | ||
59 | int rc; | ||
60 | struct timer_list timeout; | ||
61 | struct tape_request *request; | ||
62 | |||
63 | request = tape_alloc_request(2, 11); | ||
64 | if (IS_ERR(request)) | ||
65 | return PTR_ERR(request); | ||
66 | |||
67 | request->op = TO_ASSIGN; | ||
68 | tape_ccw_cc(request->cpaddr, ASSIGN, 11, request->cpdata); | ||
69 | tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); | ||
70 | |||
71 | /* | ||
72 | * The assign command sometimes blocks if the device is assigned | ||
73 | * to another host (actually this shouldn't happen but it does). | ||
74 | * So we set up a timeout for this call. | ||
75 | */ | ||
76 | init_timer(&timeout); | ||
77 | timeout.function = tape_std_assign_timeout; | ||
78 | timeout.data = (unsigned long) request; | ||
79 | timeout.expires = jiffies + 2 * HZ; | ||
80 | add_timer(&timeout); | ||
81 | |||
82 | rc = tape_do_io_interruptible(device, request); | ||
83 | |||
84 | del_timer(&timeout); | ||
85 | |||
86 | if (rc != 0) { | ||
87 | PRINT_WARN("%s: assign failed - device might be busy\n", | ||
88 | device->cdev->dev.bus_id); | ||
89 | DBF_EVENT(3, "%08x: assign failed - device might be busy\n", | ||
90 | device->cdev_id); | ||
91 | } else { | ||
92 | DBF_EVENT(3, "%08x: Tape assigned\n", device->cdev_id); | ||
93 | } | ||
94 | tape_free_request(request); | ||
95 | return rc; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * tape_std_unassign | ||
100 | */ | ||
101 | int | ||
102 | tape_std_unassign (struct tape_device *device) | ||
103 | { | ||
104 | int rc; | ||
105 | struct tape_request *request; | ||
106 | |||
107 | if (device->tape_state == TS_NOT_OPER) { | ||
108 | DBF_EVENT(3, "(%08x): Can't unassign device\n", | ||
109 | device->cdev_id); | ||
110 | PRINT_WARN("(%s): Can't unassign device - device gone\n", | ||
111 | device->cdev->dev.bus_id); | ||
112 | return -EIO; | ||
113 | } | ||
114 | |||
115 | request = tape_alloc_request(2, 11); | ||
116 | if (IS_ERR(request)) | ||
117 | return PTR_ERR(request); | ||
118 | |||
119 | request->op = TO_UNASSIGN; | ||
120 | tape_ccw_cc(request->cpaddr, UNASSIGN, 11, request->cpdata); | ||
121 | tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); | ||
122 | |||
123 | if ((rc = tape_do_io(device, request)) != 0) { | ||
124 | DBF_EVENT(3, "%08x: Unassign failed\n", device->cdev_id); | ||
125 | PRINT_WARN("%s: Unassign failed\n", device->cdev->dev.bus_id); | ||
126 | } else { | ||
127 | DBF_EVENT(3, "%08x: Tape unassigned\n", device->cdev_id); | ||
128 | } | ||
129 | tape_free_request(request); | ||
130 | return rc; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * TAPE390_DISPLAY: Show a string on the tape display. | ||
135 | */ | ||
136 | int | ||
137 | tape_std_display(struct tape_device *device, struct display_struct *disp) | ||
138 | { | ||
139 | struct tape_request *request; | ||
140 | int rc; | ||
141 | |||
142 | request = tape_alloc_request(2, 17); | ||
143 | if (IS_ERR(request)) { | ||
144 | DBF_EVENT(3, "TAPE: load display failed\n"); | ||
145 | return PTR_ERR(request); | ||
146 | } | ||
147 | request->op = TO_DIS; | ||
148 | |||
149 | *(unsigned char *) request->cpdata = disp->cntrl; | ||
150 | DBF_EVENT(5, "TAPE: display cntrl=%04x\n", disp->cntrl); | ||
151 | memcpy(((unsigned char *) request->cpdata) + 1, disp->message1, 8); | ||
152 | memcpy(((unsigned char *) request->cpdata) + 9, disp->message2, 8); | ||
153 | ASCEBC(((unsigned char*) request->cpdata) + 1, 16); | ||
154 | |||
155 | tape_ccw_cc(request->cpaddr, LOAD_DISPLAY, 17, request->cpdata); | ||
156 | tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); | ||
157 | |||
158 | rc = tape_do_io_interruptible(device, request); | ||
159 | tape_free_request(request); | ||
160 | return rc; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Read block id. | ||
165 | */ | ||
166 | int | ||
167 | tape_std_read_block_id(struct tape_device *device, __u64 *id) | ||
168 | { | ||
169 | struct tape_request *request; | ||
170 | int rc; | ||
171 | |||
172 | request = tape_alloc_request(3, 8); | ||
173 | if (IS_ERR(request)) | ||
174 | return PTR_ERR(request); | ||
175 | request->op = TO_RBI; | ||
176 | /* setup ccws */ | ||
177 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
178 | tape_ccw_cc(request->cpaddr + 1, READ_BLOCK_ID, 8, request->cpdata); | ||
179 | tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); | ||
180 | /* execute it */ | ||
181 | rc = tape_do_io(device, request); | ||
182 | if (rc == 0) | ||
183 | /* Get result from read buffer. */ | ||
184 | *id = *(__u64 *) request->cpdata; | ||
185 | tape_free_request(request); | ||
186 | return rc; | ||
187 | } | ||
188 | |||
189 | int | ||
190 | tape_std_terminate_write(struct tape_device *device) | ||
191 | { | ||
192 | int rc; | ||
193 | |||
194 | if(device->required_tapemarks == 0) | ||
195 | return 0; | ||
196 | |||
197 | DBF_LH(5, "tape%d: terminate write %dxEOF\n", device->first_minor, | ||
198 | device->required_tapemarks); | ||
199 | |||
200 | rc = tape_mtop(device, MTWEOF, device->required_tapemarks); | ||
201 | if (rc) | ||
202 | return rc; | ||
203 | |||
204 | device->required_tapemarks = 0; | ||
205 | return tape_mtop(device, MTBSR, 1); | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * MTLOAD: Loads the tape. | ||
210 | * The default implementation just wait until the tape medium state changes | ||
211 | * to MS_LOADED. | ||
212 | */ | ||
213 | int | ||
214 | tape_std_mtload(struct tape_device *device, int count) | ||
215 | { | ||
216 | return wait_event_interruptible(device->state_change_wq, | ||
217 | (device->medium_state == MS_LOADED)); | ||
218 | } | ||
219 | |||
220 | /* | ||
221 | * MTSETBLK: Set block size. | ||
222 | */ | ||
223 | int | ||
224 | tape_std_mtsetblk(struct tape_device *device, int count) | ||
225 | { | ||
226 | struct idal_buffer *new; | ||
227 | |||
228 | DBF_LH(6, "tape_std_mtsetblk(%d)\n", count); | ||
229 | if (count <= 0) { | ||
230 | /* | ||
231 | * Just set block_size to 0. tapechar_read/tapechar_write | ||
232 | * will realloc the idal buffer if a bigger one than the | ||
233 | * current is needed. | ||
234 | */ | ||
235 | device->char_data.block_size = 0; | ||
236 | return 0; | ||
237 | } | ||
238 | if (device->char_data.idal_buf != NULL && | ||
239 | device->char_data.idal_buf->size == count) | ||
240 | /* We already have a idal buffer of that size. */ | ||
241 | return 0; | ||
242 | |||
243 | if (count > MAX_BLOCKSIZE) { | ||
244 | DBF_EVENT(3, "Invalid block size (%d > %d) given.\n", | ||
245 | count, MAX_BLOCKSIZE); | ||
246 | PRINT_ERR("Invalid block size (%d > %d) given.\n", | ||
247 | count, MAX_BLOCKSIZE); | ||
248 | return -EINVAL; | ||
249 | } | ||
250 | |||
251 | /* Allocate a new idal buffer. */ | ||
252 | new = idal_buffer_alloc(count, 0); | ||
253 | if (new == NULL) | ||
254 | return -ENOMEM; | ||
255 | if (device->char_data.idal_buf != NULL) | ||
256 | idal_buffer_free(device->char_data.idal_buf); | ||
257 | device->char_data.idal_buf = new; | ||
258 | device->char_data.block_size = count; | ||
259 | |||
260 | DBF_LH(6, "new blocksize is %d\n", device->char_data.block_size); | ||
261 | |||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * MTRESET: Set block size to 0. | ||
267 | */ | ||
268 | int | ||
269 | tape_std_mtreset(struct tape_device *device, int count) | ||
270 | { | ||
271 | DBF_EVENT(6, "TCHAR:devreset:\n"); | ||
272 | device->char_data.block_size = 0; | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * MTFSF: Forward space over 'count' file marks. The tape is positioned | ||
278 | * at the EOT (End of Tape) side of the file mark. | ||
279 | */ | ||
280 | int | ||
281 | tape_std_mtfsf(struct tape_device *device, int mt_count) | ||
282 | { | ||
283 | struct tape_request *request; | ||
284 | struct ccw1 *ccw; | ||
285 | |||
286 | request = tape_alloc_request(mt_count + 2, 0); | ||
287 | if (IS_ERR(request)) | ||
288 | return PTR_ERR(request); | ||
289 | request->op = TO_FSF; | ||
290 | /* setup ccws */ | ||
291 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
292 | device->modeset_byte); | ||
293 | ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count); | ||
294 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
295 | |||
296 | /* execute it */ | ||
297 | return tape_do_io_free(device, request); | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * MTFSR: Forward space over 'count' tape blocks (blocksize is set | ||
302 | * via MTSETBLK. | ||
303 | */ | ||
304 | int | ||
305 | tape_std_mtfsr(struct tape_device *device, int mt_count) | ||
306 | { | ||
307 | struct tape_request *request; | ||
308 | struct ccw1 *ccw; | ||
309 | int rc; | ||
310 | |||
311 | request = tape_alloc_request(mt_count + 2, 0); | ||
312 | if (IS_ERR(request)) | ||
313 | return PTR_ERR(request); | ||
314 | request->op = TO_FSB; | ||
315 | /* setup ccws */ | ||
316 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
317 | device->modeset_byte); | ||
318 | ccw = tape_ccw_repeat(ccw, FORSPACEBLOCK, mt_count); | ||
319 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
320 | |||
321 | /* execute it */ | ||
322 | rc = tape_do_io(device, request); | ||
323 | if (rc == 0 && request->rescnt > 0) { | ||
324 | DBF_LH(3, "FSR over tapemark\n"); | ||
325 | rc = 1; | ||
326 | } | ||
327 | tape_free_request(request); | ||
328 | |||
329 | return rc; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * MTBSR: Backward space over 'count' tape blocks. | ||
334 | * (blocksize is set via MTSETBLK. | ||
335 | */ | ||
336 | int | ||
337 | tape_std_mtbsr(struct tape_device *device, int mt_count) | ||
338 | { | ||
339 | struct tape_request *request; | ||
340 | struct ccw1 *ccw; | ||
341 | int rc; | ||
342 | |||
343 | request = tape_alloc_request(mt_count + 2, 0); | ||
344 | if (IS_ERR(request)) | ||
345 | return PTR_ERR(request); | ||
346 | request->op = TO_BSB; | ||
347 | /* setup ccws */ | ||
348 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
349 | device->modeset_byte); | ||
350 | ccw = tape_ccw_repeat(ccw, BACKSPACEBLOCK, mt_count); | ||
351 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
352 | |||
353 | /* execute it */ | ||
354 | rc = tape_do_io(device, request); | ||
355 | if (rc == 0 && request->rescnt > 0) { | ||
356 | DBF_LH(3, "BSR over tapemark\n"); | ||
357 | rc = 1; | ||
358 | } | ||
359 | tape_free_request(request); | ||
360 | |||
361 | return rc; | ||
362 | } | ||
363 | |||
364 | /* | ||
365 | * MTWEOF: Write 'count' file marks at the current position. | ||
366 | */ | ||
367 | int | ||
368 | tape_std_mtweof(struct tape_device *device, int mt_count) | ||
369 | { | ||
370 | struct tape_request *request; | ||
371 | struct ccw1 *ccw; | ||
372 | |||
373 | request = tape_alloc_request(mt_count + 2, 0); | ||
374 | if (IS_ERR(request)) | ||
375 | return PTR_ERR(request); | ||
376 | request->op = TO_WTM; | ||
377 | /* setup ccws */ | ||
378 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
379 | device->modeset_byte); | ||
380 | ccw = tape_ccw_repeat(ccw, WRITETAPEMARK, mt_count); | ||
381 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
382 | |||
383 | /* execute it */ | ||
384 | return tape_do_io_free(device, request); | ||
385 | } | ||
386 | |||
387 | /* | ||
388 | * MTBSFM: Backward space over 'count' file marks. | ||
389 | * The tape is positioned at the BOT (Begin Of Tape) side of the | ||
390 | * last skipped file mark. | ||
391 | */ | ||
392 | int | ||
393 | tape_std_mtbsfm(struct tape_device *device, int mt_count) | ||
394 | { | ||
395 | struct tape_request *request; | ||
396 | struct ccw1 *ccw; | ||
397 | |||
398 | request = tape_alloc_request(mt_count + 2, 0); | ||
399 | if (IS_ERR(request)) | ||
400 | return PTR_ERR(request); | ||
401 | request->op = TO_BSF; | ||
402 | /* setup ccws */ | ||
403 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
404 | device->modeset_byte); | ||
405 | ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count); | ||
406 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
407 | |||
408 | /* execute it */ | ||
409 | return tape_do_io_free(device, request); | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * MTBSF: Backward space over 'count' file marks. The tape is positioned at | ||
414 | * the EOT (End of Tape) side of the last skipped file mark. | ||
415 | */ | ||
416 | int | ||
417 | tape_std_mtbsf(struct tape_device *device, int mt_count) | ||
418 | { | ||
419 | struct tape_request *request; | ||
420 | struct ccw1 *ccw; | ||
421 | int rc; | ||
422 | |||
423 | request = tape_alloc_request(mt_count + 2, 0); | ||
424 | if (IS_ERR(request)) | ||
425 | return PTR_ERR(request); | ||
426 | request->op = TO_BSF; | ||
427 | /* setup ccws */ | ||
428 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
429 | device->modeset_byte); | ||
430 | ccw = tape_ccw_repeat(ccw, BACKSPACEFILE, mt_count); | ||
431 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
432 | /* execute it */ | ||
433 | rc = tape_do_io_free(device, request); | ||
434 | if (rc == 0) { | ||
435 | rc = tape_mtop(device, MTFSR, 1); | ||
436 | if (rc > 0) | ||
437 | rc = 0; | ||
438 | } | ||
439 | return rc; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * MTFSFM: Forward space over 'count' file marks. | ||
444 | * The tape is positioned at the BOT (Begin Of Tape) side | ||
445 | * of the last skipped file mark. | ||
446 | */ | ||
447 | int | ||
448 | tape_std_mtfsfm(struct tape_device *device, int mt_count) | ||
449 | { | ||
450 | struct tape_request *request; | ||
451 | struct ccw1 *ccw; | ||
452 | int rc; | ||
453 | |||
454 | request = tape_alloc_request(mt_count + 2, 0); | ||
455 | if (IS_ERR(request)) | ||
456 | return PTR_ERR(request); | ||
457 | request->op = TO_FSF; | ||
458 | /* setup ccws */ | ||
459 | ccw = tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
460 | device->modeset_byte); | ||
461 | ccw = tape_ccw_repeat(ccw, FORSPACEFILE, mt_count); | ||
462 | ccw = tape_ccw_end(ccw, NOP, 0, NULL); | ||
463 | /* execute it */ | ||
464 | rc = tape_do_io_free(device, request); | ||
465 | if (rc == 0) { | ||
466 | rc = tape_mtop(device, MTBSR, 1); | ||
467 | if (rc > 0) | ||
468 | rc = 0; | ||
469 | } | ||
470 | |||
471 | return rc; | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * MTREW: Rewind the tape. | ||
476 | */ | ||
477 | int | ||
478 | tape_std_mtrew(struct tape_device *device, int mt_count) | ||
479 | { | ||
480 | struct tape_request *request; | ||
481 | |||
482 | request = tape_alloc_request(3, 0); | ||
483 | if (IS_ERR(request)) | ||
484 | return PTR_ERR(request); | ||
485 | request->op = TO_REW; | ||
486 | /* setup ccws */ | ||
487 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, | ||
488 | device->modeset_byte); | ||
489 | tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL); | ||
490 | tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); | ||
491 | |||
492 | /* execute it */ | ||
493 | return tape_do_io_free(device, request); | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * MTOFFL: Rewind the tape and put the drive off-line. | ||
498 | * Implement 'rewind unload' | ||
499 | */ | ||
500 | int | ||
501 | tape_std_mtoffl(struct tape_device *device, int mt_count) | ||
502 | { | ||
503 | struct tape_request *request; | ||
504 | |||
505 | request = tape_alloc_request(3, 0); | ||
506 | if (IS_ERR(request)) | ||
507 | return PTR_ERR(request); | ||
508 | request->op = TO_RUN; | ||
509 | /* setup ccws */ | ||
510 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
511 | tape_ccw_cc(request->cpaddr + 1, REWIND_UNLOAD, 0, NULL); | ||
512 | tape_ccw_end(request->cpaddr + 2, NOP, 0, NULL); | ||
513 | |||
514 | /* execute it */ | ||
515 | return tape_do_io_free(device, request); | ||
516 | } | ||
517 | |||
518 | /* | ||
519 | * MTNOP: 'No operation'. | ||
520 | */ | ||
521 | int | ||
522 | tape_std_mtnop(struct tape_device *device, int mt_count) | ||
523 | { | ||
524 | struct tape_request *request; | ||
525 | |||
526 | request = tape_alloc_request(2, 0); | ||
527 | if (IS_ERR(request)) | ||
528 | return PTR_ERR(request); | ||
529 | request->op = TO_NOP; | ||
530 | /* setup ccws */ | ||
531 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
532 | tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); | ||
533 | /* execute it */ | ||
534 | return tape_do_io_free(device, request); | ||
535 | } | ||
536 | |||
537 | /* | ||
538 | * MTEOM: positions at the end of the portion of the tape already used | ||
539 | * for recordind data. MTEOM positions after the last file mark, ready for | ||
540 | * appending another file. | ||
541 | */ | ||
542 | int | ||
543 | tape_std_mteom(struct tape_device *device, int mt_count) | ||
544 | { | ||
545 | int rc; | ||
546 | |||
547 | /* | ||
548 | * Seek from the beginning of tape (rewind). | ||
549 | */ | ||
550 | if ((rc = tape_mtop(device, MTREW, 1)) < 0) | ||
551 | return rc; | ||
552 | |||
553 | /* | ||
554 | * The logical end of volume is given by two sewuential tapemarks. | ||
555 | * Look for this by skipping to the next file (over one tapemark) | ||
556 | * and then test for another one (fsr returns 1 if a tapemark was | ||
557 | * encountered). | ||
558 | */ | ||
559 | do { | ||
560 | if ((rc = tape_mtop(device, MTFSF, 1)) < 0) | ||
561 | return rc; | ||
562 | if ((rc = tape_mtop(device, MTFSR, 1)) < 0) | ||
563 | return rc; | ||
564 | } while (rc == 0); | ||
565 | |||
566 | return tape_mtop(device, MTBSR, 1); | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * MTRETEN: Retension the tape, i.e. forward space to end of tape and rewind. | ||
571 | */ | ||
572 | int | ||
573 | tape_std_mtreten(struct tape_device *device, int mt_count) | ||
574 | { | ||
575 | struct tape_request *request; | ||
576 | int rc; | ||
577 | |||
578 | request = tape_alloc_request(4, 0); | ||
579 | if (IS_ERR(request)) | ||
580 | return PTR_ERR(request); | ||
581 | request->op = TO_FSF; | ||
582 | /* setup ccws */ | ||
583 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
584 | tape_ccw_cc(request->cpaddr + 1,FORSPACEFILE, 0, NULL); | ||
585 | tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL); | ||
586 | tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr); | ||
587 | /* execute it, MTRETEN rc gets ignored */ | ||
588 | rc = tape_do_io_interruptible(device, request); | ||
589 | tape_free_request(request); | ||
590 | return tape_mtop(device, MTREW, 1); | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * MTERASE: erases the tape. | ||
595 | */ | ||
596 | int | ||
597 | tape_std_mterase(struct tape_device *device, int mt_count) | ||
598 | { | ||
599 | struct tape_request *request; | ||
600 | |||
601 | request = tape_alloc_request(6, 0); | ||
602 | if (IS_ERR(request)) | ||
603 | return PTR_ERR(request); | ||
604 | request->op = TO_DSE; | ||
605 | /* setup ccws */ | ||
606 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
607 | tape_ccw_cc(request->cpaddr + 1, REWIND, 0, NULL); | ||
608 | tape_ccw_cc(request->cpaddr + 2, ERASE_GAP, 0, NULL); | ||
609 | tape_ccw_cc(request->cpaddr + 3, DATA_SEC_ERASE, 0, NULL); | ||
610 | tape_ccw_cc(request->cpaddr + 4, REWIND, 0, NULL); | ||
611 | tape_ccw_end(request->cpaddr + 5, NOP, 0, NULL); | ||
612 | |||
613 | /* execute it */ | ||
614 | return tape_do_io_free(device, request); | ||
615 | } | ||
616 | |||
617 | /* | ||
618 | * MTUNLOAD: Rewind the tape and unload it. | ||
619 | */ | ||
620 | int | ||
621 | tape_std_mtunload(struct tape_device *device, int mt_count) | ||
622 | { | ||
623 | return tape_mtop(device, MTOFFL, mt_count); | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * MTCOMPRESSION: used to enable compression. | ||
628 | * Sets the IDRC on/off. | ||
629 | */ | ||
630 | int | ||
631 | tape_std_mtcompression(struct tape_device *device, int mt_count) | ||
632 | { | ||
633 | struct tape_request *request; | ||
634 | |||
635 | if (mt_count < 0 || mt_count > 1) { | ||
636 | DBF_EXCEPTION(6, "xcom parm\n"); | ||
637 | if (*device->modeset_byte & 0x08) | ||
638 | PRINT_INFO("(%s) Compression is currently on\n", | ||
639 | device->cdev->dev.bus_id); | ||
640 | else | ||
641 | PRINT_INFO("(%s) Compression is currently off\n", | ||
642 | device->cdev->dev.bus_id); | ||
643 | PRINT_INFO("Use 1 to switch compression on, 0 to " | ||
644 | "switch it off\n"); | ||
645 | return -EINVAL; | ||
646 | } | ||
647 | request = tape_alloc_request(2, 0); | ||
648 | if (IS_ERR(request)) | ||
649 | return PTR_ERR(request); | ||
650 | request->op = TO_NOP; | ||
651 | /* setup ccws */ | ||
652 | *device->modeset_byte = (mt_count == 0) ? 0x00 : 0x08; | ||
653 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
654 | tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); | ||
655 | /* execute it */ | ||
656 | return tape_do_io_free(device, request); | ||
657 | } | ||
658 | |||
659 | /* | ||
660 | * Read Block | ||
661 | */ | ||
662 | struct tape_request * | ||
663 | tape_std_read_block(struct tape_device *device, size_t count) | ||
664 | { | ||
665 | struct tape_request *request; | ||
666 | |||
667 | /* | ||
668 | * We have to alloc 4 ccws in order to be able to transform request | ||
669 | * into a read backward request in error case. | ||
670 | */ | ||
671 | request = tape_alloc_request(4, 0); | ||
672 | if (IS_ERR(request)) { | ||
673 | DBF_EXCEPTION(6, "xrbl fail"); | ||
674 | return request; | ||
675 | } | ||
676 | request->op = TO_RFO; | ||
677 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
678 | tape_ccw_end_idal(request->cpaddr + 1, READ_FORWARD, | ||
679 | device->char_data.idal_buf); | ||
680 | DBF_EVENT(6, "xrbl ccwg\n"); | ||
681 | return request; | ||
682 | } | ||
683 | |||
684 | /* | ||
685 | * Read Block backward transformation function. | ||
686 | */ | ||
687 | void | ||
688 | tape_std_read_backward(struct tape_device *device, struct tape_request *request) | ||
689 | { | ||
690 | /* | ||
691 | * We have allocated 4 ccws in tape_std_read, so we can now | ||
692 | * transform the request to a read backward, followed by a | ||
693 | * forward space block. | ||
694 | */ | ||
695 | request->op = TO_RBA; | ||
696 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
697 | tape_ccw_cc_idal(request->cpaddr + 1, READ_BACKWARD, | ||
698 | device->char_data.idal_buf); | ||
699 | tape_ccw_cc(request->cpaddr + 2, FORSPACEBLOCK, 0, NULL); | ||
700 | tape_ccw_end(request->cpaddr + 3, NOP, 0, NULL); | ||
701 | DBF_EVENT(6, "xrop ccwg");} | ||
702 | |||
703 | /* | ||
704 | * Write Block | ||
705 | */ | ||
706 | struct tape_request * | ||
707 | tape_std_write_block(struct tape_device *device, size_t count) | ||
708 | { | ||
709 | struct tape_request *request; | ||
710 | |||
711 | request = tape_alloc_request(2, 0); | ||
712 | if (IS_ERR(request)) { | ||
713 | DBF_EXCEPTION(6, "xwbl fail\n"); | ||
714 | return request; | ||
715 | } | ||
716 | request->op = TO_WRI; | ||
717 | tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); | ||
718 | tape_ccw_end_idal(request->cpaddr + 1, WRITE_CMD, | ||
719 | device->char_data.idal_buf); | ||
720 | DBF_EVENT(6, "xwbl ccwg\n"); | ||
721 | return request; | ||
722 | } | ||
723 | |||
724 | /* | ||
725 | * This routine is called by frontend after an ENOSP on write | ||
726 | */ | ||
727 | void | ||
728 | tape_std_process_eov(struct tape_device *device) | ||
729 | { | ||
730 | /* | ||
731 | * End of volume: We have to backspace the last written record, then | ||
732 | * we TRY to write a tapemark and then backspace over the written TM | ||
733 | */ | ||
734 | if (tape_mtop(device, MTBSR, 1) == 0 && | ||
735 | tape_mtop(device, MTWEOF, 1) == 0) { | ||
736 | tape_mtop(device, MTBSR, 1); | ||
737 | } | ||
738 | } | ||
739 | |||
740 | EXPORT_SYMBOL(tape_std_assign); | ||
741 | EXPORT_SYMBOL(tape_std_unassign); | ||
742 | EXPORT_SYMBOL(tape_std_display); | ||
743 | EXPORT_SYMBOL(tape_std_read_block_id); | ||
744 | EXPORT_SYMBOL(tape_std_mtload); | ||
745 | EXPORT_SYMBOL(tape_std_mtsetblk); | ||
746 | EXPORT_SYMBOL(tape_std_mtreset); | ||
747 | EXPORT_SYMBOL(tape_std_mtfsf); | ||
748 | EXPORT_SYMBOL(tape_std_mtfsr); | ||
749 | EXPORT_SYMBOL(tape_std_mtbsr); | ||
750 | EXPORT_SYMBOL(tape_std_mtweof); | ||
751 | EXPORT_SYMBOL(tape_std_mtbsfm); | ||
752 | EXPORT_SYMBOL(tape_std_mtbsf); | ||
753 | EXPORT_SYMBOL(tape_std_mtfsfm); | ||
754 | EXPORT_SYMBOL(tape_std_mtrew); | ||
755 | EXPORT_SYMBOL(tape_std_mtoffl); | ||
756 | EXPORT_SYMBOL(tape_std_mtnop); | ||
757 | EXPORT_SYMBOL(tape_std_mteom); | ||
758 | EXPORT_SYMBOL(tape_std_mtreten); | ||
759 | EXPORT_SYMBOL(tape_std_mterase); | ||
760 | EXPORT_SYMBOL(tape_std_mtunload); | ||
761 | EXPORT_SYMBOL(tape_std_mtcompression); | ||
762 | EXPORT_SYMBOL(tape_std_read_block); | ||
763 | EXPORT_SYMBOL(tape_std_read_backward); | ||
764 | EXPORT_SYMBOL(tape_std_write_block); | ||
765 | EXPORT_SYMBOL(tape_std_process_eov); | ||