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