diff options
Diffstat (limited to 'drivers/char/ftape/zftape/zftape-ctl.c')
-rw-r--r-- | drivers/char/ftape/zftape/zftape-ctl.c | 1417 |
1 files changed, 0 insertions, 1417 deletions
diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c deleted file mode 100644 index 22ba0f5d00cf..000000000000 --- a/drivers/char/ftape/zftape/zftape-ctl.c +++ /dev/null | |||
@@ -1,1417 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996, 1997 Claus-Justus Heine | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 2, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program; see the file COPYING. If not, write to | ||
16 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
17 | |||
18 | * | ||
19 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.c,v $ | ||
20 | * $Revision: 1.2.6.2 $ | ||
21 | * $Date: 1997/11/14 18:07:33 $ | ||
22 | * | ||
23 | * This file contains the non-read/write zftape functions | ||
24 | * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. | ||
25 | */ | ||
26 | |||
27 | #include <linux/errno.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/module.h> | ||
30 | #include <linux/fcntl.h> | ||
31 | |||
32 | #include <linux/zftape.h> | ||
33 | |||
34 | #include <asm/uaccess.h> | ||
35 | |||
36 | #include "../zftape/zftape-init.h" | ||
37 | #include "../zftape/zftape-eof.h" | ||
38 | #include "../zftape/zftape-ctl.h" | ||
39 | #include "../zftape/zftape-write.h" | ||
40 | #include "../zftape/zftape-read.h" | ||
41 | #include "../zftape/zftape-rw.h" | ||
42 | #include "../zftape/zftape-vtbl.h" | ||
43 | |||
44 | /* Global vars. | ||
45 | */ | ||
46 | int zft_write_protected; /* this is when cartridge rdonly or O_RDONLY */ | ||
47 | int zft_header_read; | ||
48 | int zft_offline; | ||
49 | unsigned int zft_unit; | ||
50 | int zft_resid; | ||
51 | int zft_mt_compression; | ||
52 | |||
53 | /* Local vars. | ||
54 | */ | ||
55 | static int going_offline; | ||
56 | |||
57 | typedef int (mt_fun)(int *argptr); | ||
58 | typedef int (*mt_funp)(int *argptr); | ||
59 | typedef struct | ||
60 | { | ||
61 | mt_funp function; | ||
62 | unsigned offline : 1; /* op permitted if offline or no_tape */ | ||
63 | unsigned write_protected : 1; /* op permitted if write-protected */ | ||
64 | unsigned not_formatted : 1; /* op permitted if tape not formatted */ | ||
65 | unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */ | ||
66 | unsigned need_idle_state : 1; /* need to call def_idle_state */ | ||
67 | char *name; | ||
68 | } fun_entry; | ||
69 | |||
70 | static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop, | ||
71 | mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity, | ||
72 | mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf, | ||
73 | mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression; | ||
74 | |||
75 | static fun_entry mt_funs[]= | ||
76 | { | ||
77 | {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */ | ||
78 | {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" }, | ||
79 | {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" }, | ||
80 | {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" }, | ||
81 | {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" }, | ||
82 | {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */ | ||
83 | {mt_rew , 0, 1, 1, 1, 0, "MT_REW" }, | ||
84 | {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" }, | ||
85 | {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" }, | ||
86 | {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" }, | ||
87 | {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */ | ||
88 | {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" }, | ||
89 | {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" }, | ||
90 | {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" }, | ||
91 | {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" }, | ||
92 | {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" }, | ||
93 | {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" }, | ||
94 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
95 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
96 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
97 | {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */ | ||
98 | {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"}, | ||
99 | {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" }, | ||
100 | {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */ | ||
101 | {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" }, | ||
102 | {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */ | ||
103 | {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" }, | ||
104 | {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" }, | ||
105 | {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" }, | ||
106 | {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"}, | ||
107 | {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */ | ||
108 | {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"}, | ||
109 | {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"}, | ||
110 | {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"}, | ||
111 | {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"} | ||
112 | }; | ||
113 | |||
114 | #define NR_MT_CMDS NR_ITEMS(mt_funs) | ||
115 | |||
116 | void zft_reset_position(zft_position *pos) | ||
117 | { | ||
118 | TRACE_FUN(ft_t_flow); | ||
119 | |||
120 | pos->seg_byte_pos = | ||
121 | pos->volume_pos = 0; | ||
122 | if (zft_header_read) { | ||
123 | /* need to keep track of the volume table and | ||
124 | * compression map. We therefor simply | ||
125 | * position at the beginning of the first | ||
126 | * volume. This covers old ftape archives as | ||
127 | * well has various flavours of the | ||
128 | * compression map segments. The worst case is | ||
129 | * that the compression map shows up as a | ||
130 | * additional volume in front of all others. | ||
131 | */ | ||
132 | pos->seg_pos = zft_find_volume(0)->start_seg; | ||
133 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
134 | } else { | ||
135 | pos->tape_pos = 0; | ||
136 | pos->seg_pos = -1; | ||
137 | } | ||
138 | zft_just_before_eof = 0; | ||
139 | zft_deblock_segment = -1; | ||
140 | zft_io_state = zft_idle; | ||
141 | zft_zap_read_buffers(); | ||
142 | zft_prevent_flush(); | ||
143 | /* unlock the compresison module if it is loaded. | ||
144 | * The zero arg means not to try to load the module. | ||
145 | */ | ||
146 | if (zft_cmpr_lock(0) == 0) { | ||
147 | (*zft_cmpr_ops->reset)(); /* unlock */ | ||
148 | } | ||
149 | TRACE_EXIT; | ||
150 | } | ||
151 | |||
152 | static void zft_init_driver(void) | ||
153 | { | ||
154 | TRACE_FUN(ft_t_flow); | ||
155 | |||
156 | zft_resid = | ||
157 | zft_header_read = | ||
158 | zft_old_ftape = | ||
159 | zft_offline = | ||
160 | zft_write_protected = | ||
161 | going_offline = | ||
162 | zft_mt_compression = | ||
163 | zft_header_changed = | ||
164 | zft_volume_table_changed = | ||
165 | zft_written_segments = 0; | ||
166 | zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; | ||
167 | zft_reset_position(&zft_pos); /* does most of the stuff */ | ||
168 | ftape_zap_read_buffers(); | ||
169 | ftape_set_state(idle); | ||
170 | TRACE_EXIT; | ||
171 | } | ||
172 | |||
173 | int zft_def_idle_state(void) | ||
174 | { | ||
175 | int result = 0; | ||
176 | TRACE_FUN(ft_t_flow); | ||
177 | |||
178 | if (!zft_header_read) { | ||
179 | result = zft_read_header_segments(); | ||
180 | } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) { | ||
181 | /* don't move past eof | ||
182 | */ | ||
183 | (void)zft_close_volume(&zft_pos); | ||
184 | } | ||
185 | if (ftape_abort_operation() < 0) { | ||
186 | TRACE(ft_t_warn, "ftape_abort_operation() failed"); | ||
187 | result = -EIO; | ||
188 | } | ||
189 | /* clear remaining read buffers */ | ||
190 | zft_zap_read_buffers(); | ||
191 | zft_io_state = zft_idle; | ||
192 | TRACE_EXIT result; | ||
193 | } | ||
194 | |||
195 | /***************************************************************************** | ||
196 | * * | ||
197 | * functions for the MTIOCTOP commands * | ||
198 | * * | ||
199 | *****************************************************************************/ | ||
200 | |||
201 | static int mt_dummy(int *dummy) | ||
202 | { | ||
203 | TRACE_FUN(ft_t_flow); | ||
204 | |||
205 | TRACE_EXIT -ENOSYS; | ||
206 | } | ||
207 | |||
208 | static int mt_reset(int *dummy) | ||
209 | { | ||
210 | TRACE_FUN(ft_t_flow); | ||
211 | |||
212 | (void)ftape_seek_to_bot(); | ||
213 | TRACE_CATCH(ftape_reset_drive(), | ||
214 | zft_init_driver(); zft_uninit_mem(); zft_offline = 1); | ||
215 | /* fake a re-open of the device. This will set all flage and | ||
216 | * allocate buffers as appropriate. The new tape condition will | ||
217 | * force the open routine to do anything we need. | ||
218 | */ | ||
219 | TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),); | ||
220 | TRACE_EXIT 0; | ||
221 | } | ||
222 | |||
223 | static int mt_fsf(int *arg) | ||
224 | { | ||
225 | int result; | ||
226 | TRACE_FUN(ft_t_flow); | ||
227 | |||
228 | result = zft_skip_volumes(*arg, &zft_pos); | ||
229 | zft_just_before_eof = 0; | ||
230 | TRACE_EXIT result; | ||
231 | } | ||
232 | |||
233 | static int mt_bsf(int *arg) | ||
234 | { | ||
235 | int result = 0; | ||
236 | TRACE_FUN(ft_t_flow); | ||
237 | |||
238 | if (*arg != 0) { | ||
239 | result = zft_skip_volumes(-*arg + 1, &zft_pos); | ||
240 | } | ||
241 | TRACE_EXIT result; | ||
242 | } | ||
243 | |||
244 | static int seek_block(__s64 data_offset, | ||
245 | __s64 block_increment, | ||
246 | zft_position *pos) | ||
247 | { | ||
248 | int result = 0; | ||
249 | __s64 new_block_pos; | ||
250 | __s64 vol_block_count; | ||
251 | const zft_volinfo *volume; | ||
252 | int exceed; | ||
253 | TRACE_FUN(ft_t_flow); | ||
254 | |||
255 | volume = zft_find_volume(pos->seg_pos); | ||
256 | if (volume->start_seg == 0 || volume->end_seg == 0) { | ||
257 | TRACE_EXIT -EIO; | ||
258 | } | ||
259 | new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz) | ||
260 | + block_increment); | ||
261 | vol_block_count = zft_div_blksz(volume->size, volume->blk_sz); | ||
262 | if (new_block_pos < 0) { | ||
263 | TRACE(ft_t_noise, | ||
264 | "new_block_pos " LL_X " < 0", LL(new_block_pos)); | ||
265 | zft_resid = (int)new_block_pos; | ||
266 | new_block_pos = 0; | ||
267 | exceed = 1; | ||
268 | } else if (new_block_pos > vol_block_count) { | ||
269 | TRACE(ft_t_noise, | ||
270 | "new_block_pos " LL_X " exceeds size of volume " LL_X, | ||
271 | LL(new_block_pos), LL(vol_block_count)); | ||
272 | zft_resid = (int)(vol_block_count - new_block_pos); | ||
273 | new_block_pos = vol_block_count; | ||
274 | exceed = 1; | ||
275 | } else { | ||
276 | exceed = 0; | ||
277 | } | ||
278 | if (zft_use_compression && volume->use_compression) { | ||
279 | TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); | ||
280 | result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume, | ||
281 | zft_deblock_buf); | ||
282 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
283 | pos->tape_pos += pos->seg_byte_pos; | ||
284 | } else { | ||
285 | pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz); | ||
286 | pos->tape_pos = zft_calc_tape_pos(volume->start_seg); | ||
287 | pos->tape_pos += pos->volume_pos; | ||
288 | pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos, | ||
289 | pos->tape_pos); | ||
290 | } | ||
291 | zft_just_before_eof = volume->size == pos->volume_pos; | ||
292 | if (zft_just_before_eof) { | ||
293 | /* why this? because zft_file_no checks agains start | ||
294 | * and end segment of a volume. We do not want to | ||
295 | * advance to the next volume with this function. | ||
296 | */ | ||
297 | TRACE(ft_t_noise, "set zft_just_before_eof"); | ||
298 | zft_position_before_eof(pos, volume); | ||
299 | } | ||
300 | TRACE(ft_t_noise, "\n" | ||
301 | KERN_INFO "new_seg_pos : %d\n" | ||
302 | KERN_INFO "new_tape_pos: " LL_X "\n" | ||
303 | KERN_INFO "vol_size : " LL_X "\n" | ||
304 | KERN_INFO "seg_byte_pos: %d\n" | ||
305 | KERN_INFO "blk_sz : %d", | ||
306 | pos->seg_pos, LL(pos->tape_pos), | ||
307 | LL(volume->size), pos->seg_byte_pos, | ||
308 | volume->blk_sz); | ||
309 | if (!exceed) { | ||
310 | zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos, | ||
311 | volume->blk_sz); | ||
312 | } | ||
313 | if (zft_resid < 0) { | ||
314 | zft_resid = -zft_resid; | ||
315 | } | ||
316 | TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result; | ||
317 | } | ||
318 | |||
319 | static int mt_fsr(int *arg) | ||
320 | { | ||
321 | int result; | ||
322 | TRACE_FUN(ft_t_flow); | ||
323 | |||
324 | result = seek_block(zft_pos.volume_pos, *arg, &zft_pos); | ||
325 | TRACE_EXIT result; | ||
326 | } | ||
327 | |||
328 | static int mt_bsr(int *arg) | ||
329 | { | ||
330 | int result; | ||
331 | TRACE_FUN(ft_t_flow); | ||
332 | |||
333 | result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos); | ||
334 | TRACE_EXIT result; | ||
335 | } | ||
336 | |||
337 | static int mt_weof(int *arg) | ||
338 | { | ||
339 | int result; | ||
340 | TRACE_FUN(ft_t_flow); | ||
341 | |||
342 | TRACE_CATCH(zft_flush_buffers(),); | ||
343 | result = zft_weof(*arg, &zft_pos); | ||
344 | TRACE_EXIT result; | ||
345 | } | ||
346 | |||
347 | static int mt_rew(int *dummy) | ||
348 | { | ||
349 | int result; | ||
350 | TRACE_FUN(ft_t_flow); | ||
351 | |||
352 | if(zft_header_read) { | ||
353 | (void)zft_def_idle_state(); | ||
354 | } | ||
355 | result = ftape_seek_to_bot(); | ||
356 | zft_reset_position(&zft_pos); | ||
357 | TRACE_EXIT result; | ||
358 | } | ||
359 | |||
360 | static int mt_offl(int *dummy) | ||
361 | { | ||
362 | int result; | ||
363 | TRACE_FUN(ft_t_flow); | ||
364 | |||
365 | going_offline= 1; | ||
366 | result = mt_rew(NULL); | ||
367 | TRACE_EXIT result; | ||
368 | } | ||
369 | |||
370 | static int mt_nop(int *dummy) | ||
371 | { | ||
372 | TRACE_FUN(ft_t_flow); | ||
373 | /* should we set tape status? | ||
374 | */ | ||
375 | if (!zft_offline) { /* offline includes no_tape */ | ||
376 | (void)zft_def_idle_state(); | ||
377 | } | ||
378 | TRACE_EXIT 0; | ||
379 | } | ||
380 | |||
381 | static int mt_reten(int *dummy) | ||
382 | { | ||
383 | int result; | ||
384 | TRACE_FUN(ft_t_flow); | ||
385 | |||
386 | if(zft_header_read) { | ||
387 | (void)zft_def_idle_state(); | ||
388 | } | ||
389 | result = ftape_seek_to_eot(); | ||
390 | if (result >= 0) { | ||
391 | result = ftape_seek_to_bot(); | ||
392 | } | ||
393 | TRACE_EXIT(result); | ||
394 | } | ||
395 | |||
396 | static int fsfbsfm(int arg, zft_position *pos) | ||
397 | { | ||
398 | const zft_volinfo *vtbl; | ||
399 | __s64 block_pos; | ||
400 | TRACE_FUN(ft_t_flow); | ||
401 | |||
402 | /* What to do? This should seek to the next file-mark and | ||
403 | * position BEFORE. That is, a next write would just extend | ||
404 | * the current file. Well. Let's just seek to the end of the | ||
405 | * current file, if count == 1. If count > 1, then do a | ||
406 | * "mt_fsf(count - 1)", and then seek to the end of that file. | ||
407 | * If count == 0, do nothing | ||
408 | */ | ||
409 | if (arg == 0) { | ||
410 | TRACE_EXIT 0; | ||
411 | } | ||
412 | zft_just_before_eof = 0; | ||
413 | TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos), | ||
414 | if (arg > 0) { | ||
415 | zft_resid ++; | ||
416 | }); | ||
417 | vtbl = zft_find_volume(pos->seg_pos); | ||
418 | block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz); | ||
419 | (void)seek_block(0, block_pos, pos); | ||
420 | if (pos->volume_pos != vtbl->size) { | ||
421 | zft_just_before_eof = 0; | ||
422 | zft_resid = 1; | ||
423 | /* we didn't managed to go there */ | ||
424 | TRACE_ABORT(-EIO, ft_t_err, | ||
425 | "wanted file position " LL_X ", arrived at " LL_X, | ||
426 | LL(vtbl->size), LL(pos->volume_pos)); | ||
427 | } | ||
428 | zft_just_before_eof = 1; | ||
429 | TRACE_EXIT 0; | ||
430 | } | ||
431 | |||
432 | static int mt_bsfm(int *arg) | ||
433 | { | ||
434 | int result; | ||
435 | TRACE_FUN(ft_t_flow); | ||
436 | |||
437 | result = fsfbsfm(-*arg, &zft_pos); | ||
438 | TRACE_EXIT result; | ||
439 | } | ||
440 | |||
441 | static int mt_fsfm(int *arg) | ||
442 | { | ||
443 | int result; | ||
444 | TRACE_FUN(ft_t_flow); | ||
445 | |||
446 | result = fsfbsfm(*arg, &zft_pos); | ||
447 | TRACE_EXIT result; | ||
448 | } | ||
449 | |||
450 | static int mt_eom(int *dummy) | ||
451 | { | ||
452 | TRACE_FUN(ft_t_flow); | ||
453 | |||
454 | zft_skip_to_eom(&zft_pos); | ||
455 | TRACE_EXIT 0; | ||
456 | } | ||
457 | |||
458 | static int mt_erase(int *dummy) | ||
459 | { | ||
460 | int result; | ||
461 | TRACE_FUN(ft_t_flow); | ||
462 | |||
463 | result = zft_erase(); | ||
464 | TRACE_EXIT result; | ||
465 | } | ||
466 | |||
467 | static int mt_ras2(int *dummy) | ||
468 | { | ||
469 | int result; | ||
470 | TRACE_FUN(ft_t_flow); | ||
471 | |||
472 | result = -ENOSYS; | ||
473 | TRACE_EXIT result; | ||
474 | } | ||
475 | |||
476 | /* Sets the new blocksize in BYTES | ||
477 | * | ||
478 | */ | ||
479 | static int mt_setblk(int *new_size) | ||
480 | { | ||
481 | TRACE_FUN(ft_t_flow); | ||
482 | |||
483 | if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) { | ||
484 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
485 | "desired blk_sz (%d) should be <= %d bytes", | ||
486 | *new_size, ZFT_MAX_BLK_SZ); | ||
487 | } | ||
488 | if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) { | ||
489 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
490 | "desired blk_sz (%d) must be a multiple of %d bytes", | ||
491 | *new_size, FT_SECTOR_SIZE); | ||
492 | } | ||
493 | if (*new_size == 0) { | ||
494 | if (zft_use_compression) { | ||
495 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
496 | "Variable block size not yet " | ||
497 | "supported with compression"); | ||
498 | } | ||
499 | *new_size = 1; | ||
500 | } | ||
501 | zft_blk_sz = *new_size; | ||
502 | TRACE_EXIT 0; | ||
503 | } | ||
504 | |||
505 | static int mt_setdensity(int *arg) | ||
506 | { | ||
507 | TRACE_FUN(ft_t_flow); | ||
508 | |||
509 | SET_TRACE_LEVEL(*arg); | ||
510 | TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL); | ||
511 | if ((int)TRACE_LEVEL != *arg) { | ||
512 | TRACE_EXIT -EINVAL; | ||
513 | } | ||
514 | TRACE_EXIT 0; | ||
515 | } | ||
516 | |||
517 | static int mt_seek(int *new_block_pos) | ||
518 | { | ||
519 | int result= 0; | ||
520 | TRACE_FUN(ft_t_any); | ||
521 | |||
522 | result = seek_block(0, (__s64)*new_block_pos, &zft_pos); | ||
523 | TRACE_EXIT result; | ||
524 | } | ||
525 | |||
526 | /* OK, this is totally different from SCSI, but the worst thing that can | ||
527 | * happen is that there is not enough defragmentated memory that can be | ||
528 | * allocated. Also, there is a hardwired limit of 16 dma buffers in the | ||
529 | * stock ftape module. This shouldn't bring the system down. | ||
530 | * | ||
531 | * NOTE: the argument specifies the total number of dma buffers to use. | ||
532 | * The driver needs at least 3 buffers to function at all. | ||
533 | * | ||
534 | */ | ||
535 | static int mt_setdrvbuffer(int *cnt) | ||
536 | { | ||
537 | TRACE_FUN(ft_t_flow); | ||
538 | |||
539 | if (*cnt < 3) { | ||
540 | TRACE_EXIT -EINVAL; | ||
541 | } | ||
542 | TRACE_CATCH(ftape_set_nr_buffers(*cnt),); | ||
543 | TRACE_EXIT 0; | ||
544 | } | ||
545 | /* return the block position from start of volume | ||
546 | */ | ||
547 | static int mt_tell(int *arg) | ||
548 | { | ||
549 | TRACE_FUN(ft_t_flow); | ||
550 | |||
551 | *arg = zft_div_blksz(zft_pos.volume_pos, | ||
552 | zft_find_volume(zft_pos.seg_pos)->blk_sz); | ||
553 | TRACE_EXIT 0; | ||
554 | } | ||
555 | |||
556 | static int mt_compression(int *arg) | ||
557 | { | ||
558 | TRACE_FUN(ft_t_flow); | ||
559 | |||
560 | /* Ok. We could also check whether compression is available at | ||
561 | * all by trying to load the compression module. We could | ||
562 | * also check for a block size of 1 byte which is illegal | ||
563 | * with compression. Instead of doing it here we rely on | ||
564 | * zftape_write() to do the proper checks. | ||
565 | */ | ||
566 | if ((unsigned int)*arg > 1) { | ||
567 | TRACE_EXIT -EINVAL; | ||
568 | } | ||
569 | if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */ | ||
570 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
571 | "Compression not yet supported " | ||
572 | "with variable block size"); | ||
573 | } | ||
574 | zft_mt_compression = *arg; | ||
575 | if ((zft_unit & ZFT_ZIP_MODE) == 0) { | ||
576 | zft_use_compression = zft_mt_compression; | ||
577 | } | ||
578 | TRACE_EXIT 0; | ||
579 | } | ||
580 | |||
581 | /* check whether write access is allowed. Write access is denied when | ||
582 | * + zft_write_protected == 1 -- this accounts for either hard write | ||
583 | * protection of the cartridge or for | ||
584 | * O_RDONLY access mode of the tape device | ||
585 | * + zft_offline == 1 -- this meany that there is either no tape | ||
586 | * or that the MTOFFLINE ioctl has been | ||
587 | * previously issued (`soft eject') | ||
588 | * + ft_formatted == 0 -- this means that the cartridge is not | ||
589 | * formatted | ||
590 | * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try | ||
591 | * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we | ||
592 | * deny writes when | ||
593 | * + zft_qic_mode ==1 && | ||
594 | * (!zft_tape_at_lbot() && -- tape no at logical BOT | ||
595 | * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD) | ||
596 | * (zft_tape_at_eom() && | ||
597 | * zft_old_ftape()))) -- we can't add new volume to tapes | ||
598 | * written by old ftape because ftape | ||
599 | * don't use the volume table | ||
600 | * | ||
601 | * when the drive is in true raw mode (aka /dev/rawft0) then we don't | ||
602 | * care about LBOT and EOM conditions. This device is intended for a | ||
603 | * user level program that wants to truly implement the QIC-80 compliance | ||
604 | * at the logical data layout level of the cartridge, i.e. implement all | ||
605 | * that volume table and volume directory stuff etc.< | ||
606 | */ | ||
607 | int zft_check_write_access(zft_position *pos) | ||
608 | { | ||
609 | TRACE_FUN(ft_t_flow); | ||
610 | |||
611 | if (zft_offline) { /* offline includes no_tape */ | ||
612 | TRACE_ABORT(-ENXIO, | ||
613 | ft_t_info, "tape is offline or no cartridge"); | ||
614 | } | ||
615 | if (!ft_formatted) { | ||
616 | TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); | ||
617 | } | ||
618 | if (zft_write_protected) { | ||
619 | TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected"); | ||
620 | } | ||
621 | if (zft_qic_mode) { | ||
622 | /* check BOT condition */ | ||
623 | if (!zft_tape_at_lbot(pos)) { | ||
624 | /* protect cartridges written by old ftape if | ||
625 | * not at BOT because they use the vtbl | ||
626 | * segment for storing data | ||
627 | */ | ||
628 | if (zft_old_ftape) { | ||
629 | TRACE_ABORT(-EACCES, ft_t_warn, | ||
630 | "Cannot write to cartridges written by old ftape when not at BOT"); | ||
631 | } | ||
632 | /* not at BOT, but allow writes at EOD, of course | ||
633 | */ | ||
634 | if (!zft_tape_at_eod(pos)) { | ||
635 | TRACE_ABORT(-EACCES, ft_t_info, | ||
636 | "tape not at BOT and not at EOD"); | ||
637 | } | ||
638 | } | ||
639 | /* fine. Now the tape is either at BOT or at EOD. */ | ||
640 | } | ||
641 | /* or in raw mode in which case we don't care about BOT and EOD */ | ||
642 | TRACE_EXIT 0; | ||
643 | } | ||
644 | |||
645 | /* OPEN routine called by kernel-interface code | ||
646 | * | ||
647 | * NOTE: this is also called by mt_reset() with dev_minor == -1 | ||
648 | * to fake a reopen after a reset. | ||
649 | */ | ||
650 | int _zft_open(unsigned int dev_minor, unsigned int access_mode) | ||
651 | { | ||
652 | static unsigned int tape_unit; | ||
653 | static unsigned int file_access_mode; | ||
654 | int result; | ||
655 | TRACE_FUN(ft_t_flow); | ||
656 | |||
657 | if ((int)dev_minor == -1) { | ||
658 | /* fake reopen */ | ||
659 | zft_unit = tape_unit; | ||
660 | access_mode = file_access_mode; | ||
661 | zft_init_driver(); /* reset all static data to defaults */ | ||
662 | } else { | ||
663 | tape_unit = dev_minor; | ||
664 | file_access_mode = access_mode; | ||
665 | if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) { | ||
666 | TRACE_ABORT(-ENXIO, ft_t_err, | ||
667 | "ftape_enable failed: %d", result); | ||
668 | } | ||
669 | if (ft_new_tape || ft_no_tape || !ft_formatted || | ||
670 | (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) || | ||
671 | (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) { | ||
672 | /* reset all static data to defaults, | ||
673 | */ | ||
674 | zft_init_driver(); | ||
675 | } | ||
676 | zft_unit = dev_minor; | ||
677 | } | ||
678 | zft_set_flags(zft_unit); /* decode the minor bits */ | ||
679 | if (zft_blk_sz == 1 && zft_use_compression) { | ||
680 | ftape_disable(); /* resets ft_no_tape */ | ||
681 | TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet " | ||
682 | "supported with compression"); | ||
683 | } | ||
684 | /* no need for most of the buffers when no tape or not | ||
685 | * formatted. for the read/write operations, it is the | ||
686 | * regardless whether there is no tape, a not-formatted tape | ||
687 | * or the whether the driver is soft offline. | ||
688 | * Nevertheless we allow some ioctls with non-formatted tapes, | ||
689 | * like rewind and reset. | ||
690 | */ | ||
691 | if (ft_no_tape || !ft_formatted) { | ||
692 | zft_uninit_mem(); | ||
693 | } | ||
694 | if (ft_no_tape) { | ||
695 | zft_offline = 1; /* so we need not test two variables */ | ||
696 | } | ||
697 | if ((access_mode == O_WRONLY || access_mode == O_RDWR) && | ||
698 | (ft_write_protected || ft_no_tape)) { | ||
699 | ftape_disable(); /* resets ft_no_tape */ | ||
700 | TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS, | ||
701 | ft_t_warn, "wrong access mode %s cartridge", | ||
702 | ft_no_tape ? "without a" : "with write protected"); | ||
703 | } | ||
704 | zft_write_protected = (access_mode == O_RDONLY || | ||
705 | ft_write_protected != 0); | ||
706 | if (zft_write_protected) { | ||
707 | TRACE(ft_t_noise, | ||
708 | "read only access mode: %d, " | ||
709 | "drive write protected: %d", | ||
710 | access_mode == O_RDONLY, | ||
711 | ft_write_protected != 0); | ||
712 | } | ||
713 | if (!zft_offline) { | ||
714 | TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE), | ||
715 | ftape_disable()); | ||
716 | } | ||
717 | /* zft_seg_pos should be greater than the vtbl segpos but not | ||
718 | * if in compatibility mode and only after we read in the | ||
719 | * header segments | ||
720 | * | ||
721 | * might also be a problem if the user makes a backup with a | ||
722 | * *qft* device and rewinds it with a raw device. | ||
723 | */ | ||
724 | if (zft_qic_mode && | ||
725 | !zft_old_ftape && | ||
726 | zft_pos.seg_pos >= 0 && | ||
727 | zft_header_read && | ||
728 | zft_pos.seg_pos <= ft_first_data_segment) { | ||
729 | TRACE(ft_t_noise, "you probably mixed up the zftape devices!"); | ||
730 | zft_reset_position(&zft_pos); | ||
731 | } | ||
732 | TRACE_EXIT 0; | ||
733 | } | ||
734 | |||
735 | /* RELEASE routine called by kernel-interface code | ||
736 | */ | ||
737 | int _zft_close(void) | ||
738 | { | ||
739 | int result = 0; | ||
740 | TRACE_FUN(ft_t_flow); | ||
741 | |||
742 | if (zft_offline) { | ||
743 | /* call the hardware release routine. Puts the drive offline */ | ||
744 | ftape_disable(); | ||
745 | TRACE_EXIT 0; | ||
746 | } | ||
747 | if (!(ft_write_protected || zft_old_ftape)) { | ||
748 | result = zft_flush_buffers(); | ||
749 | TRACE(ft_t_noise, "writing file mark at current position"); | ||
750 | if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) { | ||
751 | zft_move_past_eof(&zft_pos); | ||
752 | } | ||
753 | if ((zft_tape_at_lbot(&zft_pos) || | ||
754 | !(zft_unit & FTAPE_NO_REWIND))) { | ||
755 | if (result >= 0) { | ||
756 | result = zft_update_header_segments(); | ||
757 | } else { | ||
758 | TRACE(ft_t_err, | ||
759 | "Error: unable to update header segments"); | ||
760 | } | ||
761 | } | ||
762 | } | ||
763 | ftape_abort_operation(); | ||
764 | if (!(zft_unit & FTAPE_NO_REWIND)) { | ||
765 | TRACE(ft_t_noise, "rewinding tape"); | ||
766 | if (ftape_seek_to_bot() < 0 && result >= 0) { | ||
767 | result = -EIO; /* keep old value */ | ||
768 | } | ||
769 | zft_reset_position(&zft_pos); | ||
770 | } | ||
771 | zft_zap_read_buffers(); | ||
772 | /* now free up memory as much as possible. We don't destroy | ||
773 | * the deblock buffer if it containes a valid segment. | ||
774 | */ | ||
775 | if (zft_deblock_segment == -1) { | ||
776 | zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); | ||
777 | } | ||
778 | /* high level driver status, forces creation of a new volume | ||
779 | * when calling ftape_write again and not zft_just_before_eof | ||
780 | */ | ||
781 | zft_io_state = zft_idle; | ||
782 | if (going_offline) { | ||
783 | zft_init_driver(); | ||
784 | zft_uninit_mem(); | ||
785 | going_offline = 0; | ||
786 | zft_offline = 1; | ||
787 | } else if (zft_cmpr_lock(0 /* don't load */) == 0) { | ||
788 | (*zft_cmpr_ops->reset)(); /* unlock it again */ | ||
789 | } | ||
790 | zft_memory_stats(); | ||
791 | /* call the hardware release routine. Puts the drive offline */ | ||
792 | ftape_disable(); | ||
793 | TRACE_EXIT result; | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * the wrapper function around the wrapper MTIOCTOP ioctl | ||
798 | */ | ||
799 | static int mtioctop(struct mtop *mtop, int arg_size) | ||
800 | { | ||
801 | int result = 0; | ||
802 | fun_entry *mt_fun_entry; | ||
803 | TRACE_FUN(ft_t_flow); | ||
804 | |||
805 | if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) { | ||
806 | TRACE_EXIT -EINVAL; | ||
807 | } | ||
808 | TRACE(ft_t_noise, "calling MTIOCTOP command: %s", | ||
809 | mt_funs[mtop->mt_op].name); | ||
810 | mt_fun_entry= &mt_funs[mtop->mt_op]; | ||
811 | zft_resid = mtop->mt_count; | ||
812 | if (!mt_fun_entry->offline && zft_offline) { | ||
813 | if (ft_no_tape) { | ||
814 | TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); | ||
815 | } else { | ||
816 | TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); | ||
817 | } | ||
818 | } | ||
819 | if (!mt_fun_entry->not_formatted && !ft_formatted) { | ||
820 | TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); | ||
821 | } | ||
822 | if (!mt_fun_entry->write_protected) { | ||
823 | TRACE_CATCH(zft_check_write_access(&zft_pos),); | ||
824 | } | ||
825 | if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) { | ||
826 | TRACE_CATCH(zft_def_idle_state(),); | ||
827 | } | ||
828 | if (!zft_qic_mode && !mt_fun_entry->raw_mode) { | ||
829 | TRACE_ABORT(-EACCES, ft_t_info, | ||
830 | "Drive needs to be in QIC-80 compatibility mode for this command"); | ||
831 | } | ||
832 | result = (mt_fun_entry->function)(&mtop->mt_count); | ||
833 | if (zft_tape_at_lbot(&zft_pos)) { | ||
834 | TRACE_CATCH(zft_update_header_segments(),); | ||
835 | } | ||
836 | if (result >= 0) { | ||
837 | zft_resid = 0; | ||
838 | } | ||
839 | TRACE_EXIT result; | ||
840 | } | ||
841 | |||
842 | /* | ||
843 | * standard MTIOCGET ioctl | ||
844 | */ | ||
845 | static int mtiocget(struct mtget *mtget, int arg_size) | ||
846 | { | ||
847 | const zft_volinfo *volume; | ||
848 | __s64 max_tape_pos; | ||
849 | TRACE_FUN(ft_t_flow); | ||
850 | |||
851 | if (arg_size != sizeof(struct mtget)) { | ||
852 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
853 | arg_size); | ||
854 | } | ||
855 | mtget->mt_type = ft_drive_type.vendor_id + 0x800000; | ||
856 | mtget->mt_dsreg = ft_last_status.space; | ||
857 | mtget->mt_erreg = ft_last_error.space; /* error register */ | ||
858 | mtget->mt_resid = zft_resid; /* residuum of writes, reads and | ||
859 | * MTIOCTOP commands | ||
860 | */ | ||
861 | if (!zft_offline) { /* neither no_tape nor soft offline */ | ||
862 | mtget->mt_gstat = GMT_ONLINE(~0UL); | ||
863 | /* should rather return the status of the cartridge | ||
864 | * than the access mode of the file, therefor use | ||
865 | * ft_write_protected, not zft_write_protected | ||
866 | */ | ||
867 | if (ft_write_protected) { | ||
868 | mtget->mt_gstat |= GMT_WR_PROT(~0UL); | ||
869 | } | ||
870 | if(zft_header_read) { /* this catches non-formatted */ | ||
871 | volume = zft_find_volume(zft_pos.seg_pos); | ||
872 | mtget->mt_fileno = volume->count; | ||
873 | max_tape_pos = zft_capacity - zft_blk_sz; | ||
874 | if (zft_use_compression) { | ||
875 | max_tape_pos -= ZFT_CMPR_OVERHEAD; | ||
876 | } | ||
877 | if (zft_tape_at_eod(&zft_pos)) { | ||
878 | mtget->mt_gstat |= GMT_EOD(~0UL); | ||
879 | } | ||
880 | if (zft_pos.tape_pos > max_tape_pos) { | ||
881 | mtget->mt_gstat |= GMT_EOT(~0UL); | ||
882 | } | ||
883 | mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos, | ||
884 | volume->blk_sz); | ||
885 | if (zft_just_before_eof) { | ||
886 | mtget->mt_gstat |= GMT_EOF(~0UL); | ||
887 | } | ||
888 | if (zft_tape_at_lbot(&zft_pos)) { | ||
889 | mtget->mt_gstat |= GMT_BOT(~0UL); | ||
890 | } | ||
891 | } else { | ||
892 | mtget->mt_fileno = mtget->mt_blkno = -1; | ||
893 | if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) { | ||
894 | mtget->mt_gstat |= GMT_BOT(~0UL); | ||
895 | } | ||
896 | } | ||
897 | } else { | ||
898 | if (ft_no_tape) { | ||
899 | mtget->mt_gstat = GMT_DR_OPEN(~0UL); | ||
900 | } else { | ||
901 | mtget->mt_gstat = 0UL; | ||
902 | } | ||
903 | mtget->mt_fileno = mtget->mt_blkno = -1; | ||
904 | } | ||
905 | TRACE_EXIT 0; | ||
906 | } | ||
907 | |||
908 | #ifdef MTIOCRDFTSEG | ||
909 | /* | ||
910 | * Read a floppy tape segment. This is useful for manipulating the | ||
911 | * volume table, and read the old header segment before re-formatting | ||
912 | * the cartridge. | ||
913 | */ | ||
914 | static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size) | ||
915 | { | ||
916 | TRACE_FUN(ft_t_flow); | ||
917 | |||
918 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG"); | ||
919 | if (zft_qic_mode) { | ||
920 | TRACE_ABORT(-EACCES, ft_t_info, | ||
921 | "driver needs to be in raw mode for this ioctl"); | ||
922 | } | ||
923 | if (arg_size != sizeof(struct mtftseg)) { | ||
924 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
925 | arg_size); | ||
926 | } | ||
927 | if (zft_offline) { | ||
928 | TRACE_EXIT -ENXIO; | ||
929 | } | ||
930 | if (mtftseg->mt_mode != FT_RD_SINGLE && | ||
931 | mtftseg->mt_mode != FT_RD_AHEAD) { | ||
932 | TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode"); | ||
933 | } | ||
934 | if (!ft_formatted) { | ||
935 | TRACE_EXIT -EACCES; /* -ENXIO ? */ | ||
936 | |||
937 | } | ||
938 | if (!zft_header_read) { | ||
939 | TRACE_CATCH(zft_def_idle_state(),); | ||
940 | } | ||
941 | if (mtftseg->mt_segno > ft_last_data_segment) { | ||
942 | TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large"); | ||
943 | } | ||
944 | mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno, | ||
945 | zft_deblock_buf, | ||
946 | mtftseg->mt_mode); | ||
947 | if (mtftseg->mt_result < 0) { | ||
948 | /* a negativ result is not an ioctl error. if | ||
949 | * the user wants to read damaged tapes, | ||
950 | * it's up to her/him | ||
951 | */ | ||
952 | TRACE_EXIT 0; | ||
953 | } | ||
954 | if (copy_to_user(mtftseg->mt_data, | ||
955 | zft_deblock_buf, | ||
956 | mtftseg->mt_result) != 0) { | ||
957 | TRACE_EXIT -EFAULT; | ||
958 | } | ||
959 | TRACE_EXIT 0; | ||
960 | } | ||
961 | #endif | ||
962 | |||
963 | #ifdef MTIOCWRFTSEG | ||
964 | /* | ||
965 | * write a floppy tape segment. This version features writing of | ||
966 | * deleted address marks, and gracefully ignores the (software) | ||
967 | * ft_formatted flag to support writing of header segments after | ||
968 | * formatting. | ||
969 | */ | ||
970 | static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size) | ||
971 | { | ||
972 | int result; | ||
973 | TRACE_FUN(ft_t_flow); | ||
974 | |||
975 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG"); | ||
976 | if (zft_write_protected || zft_qic_mode) { | ||
977 | TRACE_EXIT -EACCES; | ||
978 | } | ||
979 | if (arg_size != sizeof(struct mtftseg)) { | ||
980 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
981 | arg_size); | ||
982 | } | ||
983 | if (zft_offline) { | ||
984 | TRACE_EXIT -ENXIO; | ||
985 | } | ||
986 | if (mtftseg->mt_mode != FT_WR_ASYNC && | ||
987 | mtftseg->mt_mode != FT_WR_MULTI && | ||
988 | mtftseg->mt_mode != FT_WR_SINGLE && | ||
989 | mtftseg->mt_mode != FT_WR_DELETE) { | ||
990 | TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode"); | ||
991 | } | ||
992 | /* | ||
993 | * We don't check for ft_formatted, because this gives | ||
994 | * only the software status of the driver. | ||
995 | * | ||
996 | * We assume that the user knows what it is | ||
997 | * doing. And rely on the low level stuff to fail | ||
998 | * when the tape isn't formatted. We only make sure | ||
999 | * that The header segment buffer is allocated, | ||
1000 | * because it holds the bad sector map. | ||
1001 | */ | ||
1002 | if (zft_hseg_buf == NULL) { | ||
1003 | TRACE_EXIT -ENXIO; | ||
1004 | } | ||
1005 | if (mtftseg->mt_mode != FT_WR_DELETE) { | ||
1006 | if (copy_from_user(zft_deblock_buf, | ||
1007 | mtftseg->mt_data, | ||
1008 | FT_SEGMENT_SIZE) != 0) { | ||
1009 | TRACE_EXIT -EFAULT; | ||
1010 | } | ||
1011 | } | ||
1012 | mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno, | ||
1013 | zft_deblock_buf, | ||
1014 | mtftseg->mt_mode); | ||
1015 | if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) { | ||
1016 | /* | ||
1017 | * a negativ result is not an ioctl error. if | ||
1018 | * the user wants to write damaged tapes, | ||
1019 | * it's up to her/him | ||
1020 | */ | ||
1021 | if ((result = ftape_loop_until_writes_done()) < 0) { | ||
1022 | mtftseg->mt_result = result; | ||
1023 | } | ||
1024 | } | ||
1025 | TRACE_EXIT 0; | ||
1026 | } | ||
1027 | #endif | ||
1028 | |||
1029 | #ifdef MTIOCVOLINFO | ||
1030 | /* | ||
1031 | * get information about volume positioned at. | ||
1032 | */ | ||
1033 | static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size) | ||
1034 | { | ||
1035 | const zft_volinfo *volume; | ||
1036 | TRACE_FUN(ft_t_flow); | ||
1037 | |||
1038 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO"); | ||
1039 | if (arg_size != sizeof(struct mtvolinfo)) { | ||
1040 | TRACE_ABORT(-EINVAL, | ||
1041 | ft_t_info, "bad argument size: %d", arg_size); | ||
1042 | } | ||
1043 | if (zft_offline) { | ||
1044 | TRACE_EXIT -ENXIO; | ||
1045 | } | ||
1046 | if (!ft_formatted) { | ||
1047 | TRACE_EXIT -EACCES; | ||
1048 | } | ||
1049 | TRACE_CATCH(zft_def_idle_state(),); | ||
1050 | volume = zft_find_volume(zft_pos.seg_pos); | ||
1051 | volinfo->mt_volno = volume->count; | ||
1052 | volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz; | ||
1053 | volinfo->mt_size = volume->size >> 10; | ||
1054 | volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) - | ||
1055 | (zft_calc_tape_pos(volume->start_seg) >> 10)); | ||
1056 | volinfo->mt_cmpr = volume->use_compression; | ||
1057 | TRACE_EXIT 0; | ||
1058 | } | ||
1059 | #endif | ||
1060 | |||
1061 | #ifdef ZFT_OBSOLETE | ||
1062 | static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size) | ||
1063 | { | ||
1064 | TRACE_FUN(ft_t_flow); | ||
1065 | |||
1066 | TRACE(ft_t_noise, "\n" | ||
1067 | KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n" | ||
1068 | KERN_INFO "This ioctl is here merely for compatibility.\n" | ||
1069 | KERN_INFO "Please use MTIOCVOLINFO instead"); | ||
1070 | if (arg_size != sizeof(struct mtblksz)) { | ||
1071 | TRACE_ABORT(-EINVAL, | ||
1072 | ft_t_info, "bad argument size: %d", arg_size); | ||
1073 | } | ||
1074 | if (zft_offline) { | ||
1075 | TRACE_EXIT -ENXIO; | ||
1076 | } | ||
1077 | if (!ft_formatted) { | ||
1078 | TRACE_EXIT -EACCES; | ||
1079 | } | ||
1080 | TRACE_CATCH(zft_def_idle_state(),); | ||
1081 | blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz; | ||
1082 | TRACE_EXIT 0; | ||
1083 | } | ||
1084 | #endif | ||
1085 | |||
1086 | #ifdef MTIOCGETSIZE | ||
1087 | /* | ||
1088 | * get the capacity of the tape cartridge. | ||
1089 | */ | ||
1090 | static int mtiocgetsize(struct mttapesize *size, int arg_size) | ||
1091 | { | ||
1092 | TRACE_FUN(ft_t_flow); | ||
1093 | |||
1094 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE"); | ||
1095 | if (arg_size != sizeof(struct mttapesize)) { | ||
1096 | TRACE_ABORT(-EINVAL, | ||
1097 | ft_t_info, "bad argument size: %d", arg_size); | ||
1098 | } | ||
1099 | if (zft_offline) { | ||
1100 | TRACE_EXIT -ENXIO; | ||
1101 | } | ||
1102 | if (!ft_formatted) { | ||
1103 | TRACE_EXIT -EACCES; | ||
1104 | } | ||
1105 | TRACE_CATCH(zft_def_idle_state(),); | ||
1106 | size->mt_capacity = (unsigned int)(zft_capacity>>10); | ||
1107 | size->mt_used = (unsigned int)(zft_get_eom_pos()>>10); | ||
1108 | TRACE_EXIT 0; | ||
1109 | } | ||
1110 | #endif | ||
1111 | |||
1112 | static int mtiocpos(struct mtpos *mtpos, int arg_size) | ||
1113 | { | ||
1114 | int result; | ||
1115 | TRACE_FUN(ft_t_flow); | ||
1116 | |||
1117 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS"); | ||
1118 | if (arg_size != sizeof(struct mtpos)) { | ||
1119 | TRACE_ABORT(-EINVAL, | ||
1120 | ft_t_info, "bad argument size: %d", arg_size); | ||
1121 | } | ||
1122 | result = mt_tell((int *)&mtpos->mt_blkno); | ||
1123 | TRACE_EXIT result; | ||
1124 | } | ||
1125 | |||
1126 | #ifdef MTIOCFTFORMAT | ||
1127 | /* | ||
1128 | * formatting of floppy tape cartridges. This is intended to be used | ||
1129 | * together with the MTIOCFTCMD ioctl and the new mmap feature | ||
1130 | */ | ||
1131 | |||
1132 | /* | ||
1133 | * This function uses ftape_decode_header_segment() to inform the low | ||
1134 | * level ftape module about the new parameters. | ||
1135 | * | ||
1136 | * It erases the hseg_buf. The calling process must specify all | ||
1137 | * parameters to assure proper operation. | ||
1138 | * | ||
1139 | * return values: -EINVAL - wrong argument size | ||
1140 | * -EINVAL - if ftape_decode_header_segment() failed. | ||
1141 | */ | ||
1142 | static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf) | ||
1143 | { | ||
1144 | ft_trace_t old_level = TRACE_LEVEL; | ||
1145 | TRACE_FUN(ft_t_flow); | ||
1146 | |||
1147 | TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS"); | ||
1148 | memset(hseg_buf, 0, FT_SEGMENT_SIZE); | ||
1149 | PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC); | ||
1150 | |||
1151 | /* fill in user specified parameters | ||
1152 | */ | ||
1153 | hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode; | ||
1154 | PUT2(hseg_buf, FT_SPT, p->ft_spt); | ||
1155 | hseg_buf[FT_TPC] = (__u8)p->ft_tpc; | ||
1156 | hseg_buf[FT_FHM] = (__u8)p->ft_fhm; | ||
1157 | hseg_buf[FT_FTM] = (__u8)p->ft_ftm; | ||
1158 | |||
1159 | /* fill in sane defaults to make ftape happy. | ||
1160 | */ | ||
1161 | hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */ | ||
1162 | if (p->ft_fmtcode == fmt_big) { | ||
1163 | PUT4(hseg_buf, FT_6_HSEG_1, 0); | ||
1164 | PUT4(hseg_buf, FT_6_HSEG_2, 1); | ||
1165 | PUT4(hseg_buf, FT_6_FRST_SEG, 2); | ||
1166 | PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1); | ||
1167 | } else { | ||
1168 | PUT2(hseg_buf, FT_HSEG_1, 0); | ||
1169 | PUT2(hseg_buf, FT_HSEG_2, 1); | ||
1170 | PUT2(hseg_buf, FT_FRST_SEG, 2); | ||
1171 | PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1); | ||
1172 | } | ||
1173 | |||
1174 | /* Synchronize with the low level module. This is particularly | ||
1175 | * needed for unformatted cartridges as the QIC std was previously | ||
1176 | * unknown BUT is needed to set data rate and to calculate timeouts. | ||
1177 | */ | ||
1178 | TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK), | ||
1179 | _res = -EINVAL); | ||
1180 | |||
1181 | /* The following will also recalcualte the timeouts for the tape | ||
1182 | * length and QIC std we want to format to. | ||
1183 | * abort with -EINVAL rather than -EIO | ||
1184 | */ | ||
1185 | SET_TRACE_LEVEL(ft_t_warn); | ||
1186 | TRACE_CATCH(ftape_decode_header_segment(hseg_buf), | ||
1187 | SET_TRACE_LEVEL(old_level); _res = -EINVAL); | ||
1188 | SET_TRACE_LEVEL(old_level); | ||
1189 | TRACE_EXIT 0; | ||
1190 | } | ||
1191 | |||
1192 | /* | ||
1193 | * Return the internal SOFTWARE status of the kernel driver. This does | ||
1194 | * NOT query the tape drive about its status. | ||
1195 | */ | ||
1196 | static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer) | ||
1197 | { | ||
1198 | TRACE_FUN(ft_t_flow); | ||
1199 | |||
1200 | TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS"); | ||
1201 | p->ft_qicstd = ft_qic_std; | ||
1202 | p->ft_fmtcode = ft_format_code; | ||
1203 | p->ft_fhm = hseg_buffer[FT_FHM]; | ||
1204 | p->ft_ftm = hseg_buffer[FT_FTM]; | ||
1205 | p->ft_spt = ft_segments_per_track; | ||
1206 | p->ft_tpc = ft_tracks_per_tape; | ||
1207 | TRACE_EXIT 0; | ||
1208 | } | ||
1209 | |||
1210 | static int mtiocftformat(struct mtftformat *mtftformat, int arg_size) | ||
1211 | { | ||
1212 | int result; | ||
1213 | union fmt_arg *arg = &mtftformat->fmt_arg; | ||
1214 | TRACE_FUN(ft_t_flow); | ||
1215 | |||
1216 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT"); | ||
1217 | if (zft_offline) { | ||
1218 | if (ft_no_tape) { | ||
1219 | TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); | ||
1220 | } else { | ||
1221 | TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); | ||
1222 | } | ||
1223 | } | ||
1224 | if (zft_qic_mode) { | ||
1225 | TRACE_ABORT(-EACCES, ft_t_info, | ||
1226 | "driver needs to be in raw mode for this ioctl"); | ||
1227 | } | ||
1228 | if (zft_hseg_buf == NULL) { | ||
1229 | TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); | ||
1230 | } | ||
1231 | zft_header_read = 0; | ||
1232 | switch(mtftformat->fmt_op) { | ||
1233 | case FTFMT_SET_PARMS: | ||
1234 | TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),); | ||
1235 | TRACE_EXIT 0; | ||
1236 | case FTFMT_GET_PARMS: | ||
1237 | TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),); | ||
1238 | TRACE_EXIT 0; | ||
1239 | case FTFMT_FORMAT_TRACK: | ||
1240 | if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) || | ||
1241 | (!ft_formatted && zft_write_protected)) { | ||
1242 | TRACE_ABORT(-EACCES, ft_t_info, "Write access denied"); | ||
1243 | } | ||
1244 | TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track, | ||
1245 | arg->fmt_track.ft_gap3),); | ||
1246 | TRACE_EXIT 0; | ||
1247 | case FTFMT_STATUS: | ||
1248 | TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),); | ||
1249 | TRACE_EXIT 0; | ||
1250 | case FTFMT_VERIFY: | ||
1251 | TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment, | ||
1252 | (SectorMap *)&arg->fmt_verify.ft_bsm),); | ||
1253 | TRACE_EXIT 0; | ||
1254 | default: | ||
1255 | TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation"); | ||
1256 | } | ||
1257 | TRACE_EXIT result; | ||
1258 | } | ||
1259 | #endif | ||
1260 | |||
1261 | #ifdef MTIOCFTCMD | ||
1262 | /* | ||
1263 | * send a QIC-117 command to the drive, with optional timeouts, | ||
1264 | * parameter and result bits. This is intended to be used together | ||
1265 | * with the formatting ioctl. | ||
1266 | */ | ||
1267 | static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size) | ||
1268 | { | ||
1269 | int i; | ||
1270 | TRACE_FUN(ft_t_flow); | ||
1271 | |||
1272 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD"); | ||
1273 | if (!capable(CAP_SYS_ADMIN)) { | ||
1274 | TRACE_ABORT(-EPERM, ft_t_info, | ||
1275 | "need CAP_SYS_ADMIN capability to send raw qic-117 commands"); | ||
1276 | } | ||
1277 | if (zft_qic_mode) { | ||
1278 | TRACE_ABORT(-EACCES, ft_t_info, | ||
1279 | "driver needs to be in raw mode for this ioctl"); | ||
1280 | } | ||
1281 | if (arg_size != sizeof(struct mtftcmd)) { | ||
1282 | TRACE_ABORT(-EINVAL, | ||
1283 | ft_t_info, "bad argument size: %d", arg_size); | ||
1284 | } | ||
1285 | if (ftcmd->ft_wait_before) { | ||
1286 | TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before, | ||
1287 | &ftcmd->ft_status),); | ||
1288 | } | ||
1289 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1290 | goto ftmtcmd_error; | ||
1291 | if (ftcmd->ft_result_bits != 0) { | ||
1292 | TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result, | ||
1293 | ftcmd->ft_cmd, | ||
1294 | ftcmd->ft_result_bits),); | ||
1295 | } else { | ||
1296 | TRACE_CATCH(ftape_command(ftcmd->ft_cmd),); | ||
1297 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1298 | goto ftmtcmd_error; | ||
1299 | for (i = 0; i < ftcmd->ft_parm_cnt; i++) { | ||
1300 | TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),); | ||
1301 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1302 | goto ftmtcmd_error; | ||
1303 | } | ||
1304 | } | ||
1305 | if (ftcmd->ft_wait_after != 0) { | ||
1306 | TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after, | ||
1307 | &ftcmd->ft_status),); | ||
1308 | } | ||
1309 | ftmtcmd_error: | ||
1310 | if (ftcmd->ft_status & QIC_STATUS_ERROR) { | ||
1311 | TRACE(ft_t_noise, "error status set"); | ||
1312 | TRACE_CATCH(ftape_report_error(&ftcmd->ft_error, | ||
1313 | &ftcmd->ft_cmd, 1),); | ||
1314 | } | ||
1315 | TRACE_EXIT 0; /* this is not an i/o error */ | ||
1316 | } | ||
1317 | #endif | ||
1318 | |||
1319 | /* IOCTL routine called by kernel-interface code | ||
1320 | */ | ||
1321 | int _zft_ioctl(unsigned int command, void __user * arg) | ||
1322 | { | ||
1323 | int result; | ||
1324 | union { struct mtop mtop; | ||
1325 | struct mtget mtget; | ||
1326 | struct mtpos mtpos; | ||
1327 | #ifdef MTIOCRDFTSEG | ||
1328 | struct mtftseg mtftseg; | ||
1329 | #endif | ||
1330 | #ifdef MTIOCVOLINFO | ||
1331 | struct mtvolinfo mtvolinfo; | ||
1332 | #endif | ||
1333 | #ifdef MTIOCGETSIZE | ||
1334 | struct mttapesize mttapesize; | ||
1335 | #endif | ||
1336 | #ifdef MTIOCFTFORMAT | ||
1337 | struct mtftformat mtftformat; | ||
1338 | #endif | ||
1339 | #ifdef ZFT_OBSOLETE | ||
1340 | struct mtblksz mtblksz; | ||
1341 | #endif | ||
1342 | #ifdef MTIOCFTCMD | ||
1343 | struct mtftcmd mtftcmd; | ||
1344 | #endif | ||
1345 | } krnl_arg; | ||
1346 | int arg_size = _IOC_SIZE(command); | ||
1347 | int dir = _IOC_DIR(command); | ||
1348 | TRACE_FUN(ft_t_flow); | ||
1349 | |||
1350 | /* This check will only catch arguments that are too large ! | ||
1351 | */ | ||
1352 | if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) { | ||
1353 | TRACE_ABORT(-EINVAL, | ||
1354 | ft_t_info, "bad argument size: %d", arg_size); | ||
1355 | } | ||
1356 | if (dir & _IOC_WRITE) { | ||
1357 | if (copy_from_user(&krnl_arg, arg, arg_size) != 0) { | ||
1358 | TRACE_EXIT -EFAULT; | ||
1359 | } | ||
1360 | } | ||
1361 | TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command); | ||
1362 | switch (command) { | ||
1363 | case MTIOCTOP: | ||
1364 | result = mtioctop(&krnl_arg.mtop, arg_size); | ||
1365 | break; | ||
1366 | case MTIOCGET: | ||
1367 | result = mtiocget(&krnl_arg.mtget, arg_size); | ||
1368 | break; | ||
1369 | case MTIOCPOS: | ||
1370 | result = mtiocpos(&krnl_arg.mtpos, arg_size); | ||
1371 | break; | ||
1372 | #ifdef MTIOCVOLINFO | ||
1373 | case MTIOCVOLINFO: | ||
1374 | result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size); | ||
1375 | break; | ||
1376 | #endif | ||
1377 | #ifdef ZFT_OBSOLETE | ||
1378 | case MTIOC_ZFTAPE_GETBLKSZ: | ||
1379 | result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size); | ||
1380 | break; | ||
1381 | #endif | ||
1382 | #ifdef MTIOCRDFTSEG | ||
1383 | case MTIOCRDFTSEG: /* read a segment via ioctl */ | ||
1384 | result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size); | ||
1385 | break; | ||
1386 | #endif | ||
1387 | #ifdef MTIOCWRFTSEG | ||
1388 | case MTIOCWRFTSEG: /* write a segment via ioctl */ | ||
1389 | result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size); | ||
1390 | break; | ||
1391 | #endif | ||
1392 | #ifdef MTIOCGETSIZE | ||
1393 | case MTIOCGETSIZE: | ||
1394 | result = mtiocgetsize(&krnl_arg.mttapesize, arg_size); | ||
1395 | break; | ||
1396 | #endif | ||
1397 | #ifdef MTIOCFTFORMAT | ||
1398 | case MTIOCFTFORMAT: | ||
1399 | result = mtiocftformat(&krnl_arg.mtftformat, arg_size); | ||
1400 | break; | ||
1401 | #endif | ||
1402 | #ifdef MTIOCFTCMD | ||
1403 | case MTIOCFTCMD: | ||
1404 | result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size); | ||
1405 | break; | ||
1406 | #endif | ||
1407 | default: | ||
1408 | result = -EINVAL; | ||
1409 | break; | ||
1410 | } | ||
1411 | if ((result >= 0) && (dir & _IOC_READ)) { | ||
1412 | if (copy_to_user(arg, &krnl_arg, arg_size) != 0) { | ||
1413 | TRACE_EXIT -EFAULT; | ||
1414 | } | ||
1415 | } | ||
1416 | TRACE_EXIT result; | ||
1417 | } | ||