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