diff options
Diffstat (limited to 'drivers/char/ftape/lowlevel/ftape-rw.c')
-rw-r--r-- | drivers/char/ftape/lowlevel/ftape-rw.c | 1092 |
1 files changed, 0 insertions, 1092 deletions
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.c b/drivers/char/ftape/lowlevel/ftape-rw.c deleted file mode 100644 index c0d6dc2cbfd3..000000000000 --- a/drivers/char/ftape/lowlevel/ftape-rw.c +++ /dev/null | |||
@@ -1,1092 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus Heine. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; see the file COPYING. If not, write to | ||
17 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | |||
19 | * | ||
20 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.c,v $ | ||
21 | * $Revision: 1.7 $ | ||
22 | * $Date: 1997/10/28 14:26:49 $ | ||
23 | * | ||
24 | * This file contains some common code for the segment read and | ||
25 | * segment write routines for the QIC-117 floppy-tape driver for | ||
26 | * Linux. | ||
27 | */ | ||
28 | |||
29 | #include <linux/string.h> | ||
30 | #include <linux/errno.h> | ||
31 | |||
32 | #include <linux/ftape.h> | ||
33 | #include <linux/qic117.h> | ||
34 | #include "../lowlevel/ftape-tracing.h" | ||
35 | #include "../lowlevel/ftape-rw.h" | ||
36 | #include "../lowlevel/fdc-io.h" | ||
37 | #include "../lowlevel/ftape-init.h" | ||
38 | #include "../lowlevel/ftape-io.h" | ||
39 | #include "../lowlevel/ftape-ctl.h" | ||
40 | #include "../lowlevel/ftape-read.h" | ||
41 | #include "../lowlevel/ftape-ecc.h" | ||
42 | #include "../lowlevel/ftape-bsm.h" | ||
43 | |||
44 | /* Global vars. | ||
45 | */ | ||
46 | int ft_nr_buffers; | ||
47 | buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS]; | ||
48 | static volatile int ft_head; | ||
49 | static volatile int ft_tail; /* not volatile but need same type as head */ | ||
50 | int fdc_setup_error; | ||
51 | location_record ft_location = {-1, 0}; | ||
52 | volatile int ftape_tape_running; | ||
53 | |||
54 | /* Local vars. | ||
55 | */ | ||
56 | static int overrun_count_offset; | ||
57 | static int inhibit_correction; | ||
58 | |||
59 | /* maxmimal allowed overshoot when fast seeking | ||
60 | */ | ||
61 | #define OVERSHOOT_LIMIT 10 | ||
62 | |||
63 | /* Increment cyclic buffer nr. | ||
64 | */ | ||
65 | buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos) | ||
66 | { | ||
67 | switch (pos) { | ||
68 | case ft_queue_head: | ||
69 | if (++ft_head >= ft_nr_buffers) { | ||
70 | ft_head = 0; | ||
71 | } | ||
72 | return ft_buffer[ft_head]; | ||
73 | case ft_queue_tail: | ||
74 | if (++ft_tail >= ft_nr_buffers) { | ||
75 | ft_tail = 0; | ||
76 | } | ||
77 | return ft_buffer[ft_tail]; | ||
78 | default: | ||
79 | return NULL; | ||
80 | } | ||
81 | } | ||
82 | int ftape_buffer_id(ft_buffer_queue_t pos) | ||
83 | { | ||
84 | switch(pos) { | ||
85 | case ft_queue_head: return ft_head; | ||
86 | case ft_queue_tail: return ft_tail; | ||
87 | default: return -1; | ||
88 | } | ||
89 | } | ||
90 | buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos) | ||
91 | { | ||
92 | switch(pos) { | ||
93 | case ft_queue_head: return ft_buffer[ft_head]; | ||
94 | case ft_queue_tail: return ft_buffer[ft_tail]; | ||
95 | default: return NULL; | ||
96 | } | ||
97 | } | ||
98 | void ftape_reset_buffer(void) | ||
99 | { | ||
100 | ft_head = ft_tail = 0; | ||
101 | } | ||
102 | |||
103 | buffer_state_enum ftape_set_state(buffer_state_enum new_state) | ||
104 | { | ||
105 | buffer_state_enum old_state = ft_driver_state; | ||
106 | |||
107 | ft_driver_state = new_state; | ||
108 | return old_state; | ||
109 | } | ||
110 | /* Calculate Floppy Disk Controller and DMA parameters for a segment. | ||
111 | * head: selects buffer struct in array. | ||
112 | * offset: number of physical sectors to skip (including bad ones). | ||
113 | * count: number of physical sectors to handle (including bad ones). | ||
114 | */ | ||
115 | static int setup_segment(buffer_struct * buff, | ||
116 | int segment_id, | ||
117 | unsigned int sector_offset, | ||
118 | unsigned int sector_count, | ||
119 | int retry) | ||
120 | { | ||
121 | SectorMap offset_mask; | ||
122 | SectorMap mask; | ||
123 | TRACE_FUN(ft_t_any); | ||
124 | |||
125 | buff->segment_id = segment_id; | ||
126 | buff->sector_offset = sector_offset; | ||
127 | buff->remaining = sector_count; | ||
128 | buff->head = segment_id / ftape_segments_per_head; | ||
129 | buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder; | ||
130 | buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1; | ||
131 | buff->deleted = 0; | ||
132 | offset_mask = (1 << buff->sector_offset) - 1; | ||
133 | mask = ftape_get_bad_sector_entry(segment_id) & offset_mask; | ||
134 | while (mask) { | ||
135 | if (mask & 1) { | ||
136 | offset_mask >>= 1; /* don't count bad sector */ | ||
137 | } | ||
138 | mask >>= 1; | ||
139 | } | ||
140 | buff->data_offset = count_ones(offset_mask); /* good sectors to skip */ | ||
141 | buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE; | ||
142 | TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset); | ||
143 | if (retry) { | ||
144 | buff->soft_error_map &= offset_mask; /* keep skipped part */ | ||
145 | } else { | ||
146 | buff->hard_error_map = buff->soft_error_map = 0; | ||
147 | } | ||
148 | buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id); | ||
149 | if (buff->bad_sector_map != 0) { | ||
150 | TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx", | ||
151 | buff->segment_id, (long)buff->bad_sector_map); | ||
152 | } else { | ||
153 | TRACE(ft_t_flow, "segment: %d", buff->segment_id); | ||
154 | } | ||
155 | if (buff->sector_offset > 0) { | ||
156 | buff->bad_sector_map >>= buff->sector_offset; | ||
157 | } | ||
158 | if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) { | ||
159 | TRACE(ft_t_flow, "sector offset = %d, count = %d", | ||
160 | buff->sector_offset, buff->remaining); | ||
161 | } | ||
162 | /* Segments with 3 or less sectors are not written with valid | ||
163 | * data because there is no space left for the ecc. The | ||
164 | * data written is whatever happens to be in the buffer. | ||
165 | * Reading such a segment will return a zero byte-count. | ||
166 | * To allow us to read/write segments with all bad sectors | ||
167 | * we fake one readable sector in the segment. This | ||
168 | * prevents having to handle these segments in a very | ||
169 | * special way. It is not important if the reading of this | ||
170 | * bad sector fails or not (the data is ignored). It is | ||
171 | * only read to keep the driver running. | ||
172 | * | ||
173 | * The QIC-40/80 spec. has no information on how to handle | ||
174 | * this case, so this is my interpretation. | ||
175 | */ | ||
176 | if (buff->bad_sector_map == EMPTY_SEGMENT) { | ||
177 | TRACE(ft_t_flow, "empty segment %d, fake first sector good", | ||
178 | buff->segment_id); | ||
179 | if (buff->ptr != buff->address) { | ||
180 | TRACE(ft_t_bug, "This is a bug: %p/%p", | ||
181 | buff->ptr, buff->address); | ||
182 | } | ||
183 | buff->bad_sector_map = FAKE_SEGMENT; | ||
184 | } | ||
185 | fdc_setup_error = 0; | ||
186 | buff->next_segment = segment_id + 1; | ||
187 | TRACE_EXIT 0; | ||
188 | } | ||
189 | |||
190 | /* Calculate Floppy Disk Controller and DMA parameters for a new segment. | ||
191 | */ | ||
192 | int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip) | ||
193 | { | ||
194 | int result = 0; | ||
195 | static int old_segment_id = -1; | ||
196 | static buffer_state_enum old_ft_driver_state = idle; | ||
197 | int retry = 0; | ||
198 | unsigned offset = 0; | ||
199 | int count = FT_SECTORS_PER_SEGMENT; | ||
200 | TRACE_FUN(ft_t_flow); | ||
201 | |||
202 | TRACE(ft_t_flow, "%s segment %d (old = %d)", | ||
203 | (ft_driver_state == reading || ft_driver_state == verifying) | ||
204 | ? "reading" : "writing", | ||
205 | segment_id, old_segment_id); | ||
206 | if (ft_driver_state != old_ft_driver_state) { /* when verifying */ | ||
207 | old_segment_id = -1; | ||
208 | old_ft_driver_state = ft_driver_state; | ||
209 | } | ||
210 | if (segment_id == old_segment_id) { | ||
211 | ++buff->retry; | ||
212 | ++ft_history.retries; | ||
213 | TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry); | ||
214 | retry = 1; | ||
215 | if (skip && buff->skip > 0) { /* allow skip on retry */ | ||
216 | offset = buff->skip; | ||
217 | count -= offset; | ||
218 | TRACE(ft_t_flow, "skipping %d sectors", offset); | ||
219 | } | ||
220 | } else { | ||
221 | buff->retry = 0; | ||
222 | buff->skip = 0; | ||
223 | old_segment_id = segment_id; | ||
224 | } | ||
225 | result = setup_segment(buff, segment_id, offset, count, retry); | ||
226 | TRACE_EXIT result; | ||
227 | } | ||
228 | |||
229 | /* Determine size of next cluster of good sectors. | ||
230 | */ | ||
231 | int ftape_calc_next_cluster(buffer_struct * buff) | ||
232 | { | ||
233 | /* Skip bad sectors. | ||
234 | */ | ||
235 | while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) { | ||
236 | buff->bad_sector_map >>= 1; | ||
237 | ++buff->sector_offset; | ||
238 | --buff->remaining; | ||
239 | } | ||
240 | /* Find next cluster of good sectors | ||
241 | */ | ||
242 | if (buff->bad_sector_map == 0) { /* speed up */ | ||
243 | buff->sector_count = buff->remaining; | ||
244 | } else { | ||
245 | SectorMap map = buff->bad_sector_map; | ||
246 | |||
247 | buff->sector_count = 0; | ||
248 | while (buff->sector_count < buff->remaining && (map & 1) == 0) { | ||
249 | ++buff->sector_count; | ||
250 | map >>= 1; | ||
251 | } | ||
252 | } | ||
253 | return buff->sector_count; | ||
254 | } | ||
255 | |||
256 | /* if just passed the last segment on a track, wait for BOT | ||
257 | * or EOT mark. | ||
258 | */ | ||
259 | int ftape_handle_logical_eot(void) | ||
260 | { | ||
261 | TRACE_FUN(ft_t_flow); | ||
262 | |||
263 | if (ft_runner_status == logical_eot) { | ||
264 | int status; | ||
265 | |||
266 | TRACE(ft_t_noise, "tape at logical EOT"); | ||
267 | TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),); | ||
268 | if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) { | ||
269 | TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached"); | ||
270 | } | ||
271 | ft_runner_status = end_of_tape; | ||
272 | } | ||
273 | if (ft_runner_status == end_of_tape) { | ||
274 | TRACE(ft_t_noise, "runner stopped because of logical EOT"); | ||
275 | ft_runner_status = idle; | ||
276 | } | ||
277 | TRACE_EXIT 0; | ||
278 | } | ||
279 | |||
280 | static int check_bot_eot(int status) | ||
281 | { | ||
282 | TRACE_FUN(ft_t_flow); | ||
283 | |||
284 | if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) { | ||
285 | ft_location.bot = ((ft_location.track & 1) == 0 ? | ||
286 | (status & QIC_STATUS_AT_BOT) != 0: | ||
287 | (status & QIC_STATUS_AT_EOT) != 0); | ||
288 | ft_location.eot = !ft_location.bot; | ||
289 | ft_location.segment = (ft_location.track + | ||
290 | (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1; | ||
291 | ft_location.sector = -1; | ||
292 | ft_location.known = 1; | ||
293 | TRACE(ft_t_flow, "tape at logical %s", | ||
294 | ft_location.bot ? "bot" : "eot"); | ||
295 | TRACE(ft_t_flow, "segment = %d", ft_location.segment); | ||
296 | } else { | ||
297 | ft_location.known = 0; | ||
298 | } | ||
299 | TRACE_EXIT ft_location.known; | ||
300 | } | ||
301 | |||
302 | /* Read Id of first sector passing tape head. | ||
303 | */ | ||
304 | static int ftape_read_id(void) | ||
305 | { | ||
306 | int status; | ||
307 | __u8 out[2]; | ||
308 | TRACE_FUN(ft_t_any); | ||
309 | |||
310 | /* Assume tape is running on entry, be able to handle | ||
311 | * situation where it stopped or is stopping. | ||
312 | */ | ||
313 | ft_location.known = 0; /* default is location not known */ | ||
314 | out[0] = FDC_READID; | ||
315 | out[1] = ft_drive_sel; | ||
316 | TRACE_CATCH(fdc_command(out, 2),); | ||
317 | switch (fdc_interrupt_wait(20 * FT_SECOND)) { | ||
318 | case 0: | ||
319 | if (fdc_sect == 0) { | ||
320 | if (ftape_report_drive_status(&status) >= 0 && | ||
321 | (status & QIC_STATUS_READY)) { | ||
322 | ftape_tape_running = 0; | ||
323 | TRACE(ft_t_flow, "tape has stopped"); | ||
324 | check_bot_eot(status); | ||
325 | } | ||
326 | } else { | ||
327 | ft_location.known = 1; | ||
328 | ft_location.segment = (ftape_segments_per_head | ||
329 | * fdc_head | ||
330 | + ftape_segments_per_cylinder | ||
331 | * fdc_cyl | ||
332 | + (fdc_sect - 1) | ||
333 | / FT_SECTORS_PER_SEGMENT); | ||
334 | ft_location.sector = ((fdc_sect - 1) | ||
335 | % FT_SECTORS_PER_SEGMENT); | ||
336 | ft_location.eot = ft_location.bot = 0; | ||
337 | } | ||
338 | break; | ||
339 | case -ETIME: | ||
340 | /* Didn't find id on tape, must be near end: Wait | ||
341 | * until stopped. | ||
342 | */ | ||
343 | if (ftape_ready_wait(FT_FOREVER, &status) >= 0) { | ||
344 | ftape_tape_running = 0; | ||
345 | TRACE(ft_t_flow, "tape has stopped"); | ||
346 | check_bot_eot(status); | ||
347 | } | ||
348 | break; | ||
349 | default: | ||
350 | /* Interrupted or otherwise failing | ||
351 | * fdc_interrupt_wait() | ||
352 | */ | ||
353 | TRACE(ft_t_err, "fdc_interrupt_wait failed"); | ||
354 | break; | ||
355 | } | ||
356 | if (!ft_location.known) { | ||
357 | TRACE_ABORT(-EIO, ft_t_flow, "no id found"); | ||
358 | } | ||
359 | if (ft_location.sector == 0) { | ||
360 | TRACE(ft_t_flow, "passing segment %d/%d", | ||
361 | ft_location.segment, ft_location.sector); | ||
362 | } else { | ||
363 | TRACE(ft_t_fdc_dma, "passing segment %d/%d", | ||
364 | ft_location.segment, ft_location.sector); | ||
365 | } | ||
366 | TRACE_EXIT 0; | ||
367 | } | ||
368 | |||
369 | static int logical_forward(void) | ||
370 | { | ||
371 | ftape_tape_running = 1; | ||
372 | return ftape_command(QIC_LOGICAL_FORWARD); | ||
373 | } | ||
374 | |||
375 | int ftape_stop_tape(int *pstatus) | ||
376 | { | ||
377 | int retry = 0; | ||
378 | int result; | ||
379 | TRACE_FUN(ft_t_flow); | ||
380 | |||
381 | do { | ||
382 | result = ftape_command_wait(QIC_STOP_TAPE, | ||
383 | ftape_timeout.stop, pstatus); | ||
384 | if (result == 0) { | ||
385 | if ((*pstatus & QIC_STATUS_READY) == 0) { | ||
386 | result = -EIO; | ||
387 | } else { | ||
388 | ftape_tape_running = 0; | ||
389 | } | ||
390 | } | ||
391 | } while (result < 0 && ++retry <= 3); | ||
392 | if (result < 0) { | ||
393 | TRACE(ft_t_err, "failed ! (fatal)"); | ||
394 | } | ||
395 | TRACE_EXIT result; | ||
396 | } | ||
397 | |||
398 | int ftape_dumb_stop(void) | ||
399 | { | ||
400 | int result; | ||
401 | int status; | ||
402 | TRACE_FUN(ft_t_flow); | ||
403 | |||
404 | /* Abort current fdc operation if it's busy (probably read | ||
405 | * or write operation pending) with a reset. | ||
406 | */ | ||
407 | if (fdc_ready_wait(100 /* usec */) < 0) { | ||
408 | TRACE(ft_t_noise, "aborting fdc operation"); | ||
409 | fdc_reset(); | ||
410 | } | ||
411 | /* Reading id's after the last segment on a track may fail | ||
412 | * but eventually the drive will become ready (logical eot). | ||
413 | */ | ||
414 | result = ftape_report_drive_status(&status); | ||
415 | ft_location.known = 0; | ||
416 | do { | ||
417 | if (result == 0 && status & QIC_STATUS_READY) { | ||
418 | /* Tape is not running any more. | ||
419 | */ | ||
420 | TRACE(ft_t_noise, "tape already halted"); | ||
421 | check_bot_eot(status); | ||
422 | ftape_tape_running = 0; | ||
423 | } else if (ftape_tape_running) { | ||
424 | /* Tape is (was) still moving. | ||
425 | */ | ||
426 | #ifdef TESTING | ||
427 | ftape_read_id(); | ||
428 | #endif | ||
429 | result = ftape_stop_tape(&status); | ||
430 | } else { | ||
431 | /* Tape not yet ready but stopped. | ||
432 | */ | ||
433 | result = ftape_ready_wait(ftape_timeout.pause,&status); | ||
434 | } | ||
435 | } while (ftape_tape_running | ||
436 | && !(sigtestsetmask(¤t->pending.signal, _NEVER_BLOCK))); | ||
437 | #ifndef TESTING | ||
438 | ft_location.known = 0; | ||
439 | #endif | ||
440 | if (ft_runner_status == aborting || ft_runner_status == do_abort) { | ||
441 | ft_runner_status = idle; | ||
442 | } | ||
443 | TRACE_EXIT result; | ||
444 | } | ||
445 | |||
446 | /* Wait until runner has finished tail buffer. | ||
447 | * | ||
448 | */ | ||
449 | int ftape_wait_segment(buffer_state_enum state) | ||
450 | { | ||
451 | int status; | ||
452 | int result = 0; | ||
453 | TRACE_FUN(ft_t_flow); | ||
454 | |||
455 | while (ft_buffer[ft_tail]->status == state) { | ||
456 | TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status); | ||
457 | /* First buffer still being worked on, wait up to timeout. | ||
458 | * | ||
459 | * Note: we check two times for being killed. 50 | ||
460 | * seconds are quite long. Note that | ||
461 | * fdc_interrupt_wait() is not killable by any | ||
462 | * means. ftape_read_segment() wants us to return | ||
463 | * -EINTR in case of a signal. | ||
464 | */ | ||
465 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
466 | result = fdc_interrupt_wait(50 * FT_SECOND); | ||
467 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
468 | if (result < 0) { | ||
469 | TRACE_ABORT(result, | ||
470 | ft_t_err, "fdc_interrupt_wait failed"); | ||
471 | } | ||
472 | if (fdc_setup_error) { | ||
473 | /* recover... FIXME */ | ||
474 | TRACE_ABORT(-EIO, ft_t_err, "setup error"); | ||
475 | } | ||
476 | } | ||
477 | if (ft_buffer[ft_tail]->status != error) { | ||
478 | TRACE_EXIT 0; | ||
479 | } | ||
480 | TRACE_CATCH(ftape_report_drive_status(&status),); | ||
481 | TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status); | ||
482 | if ((status & QIC_STATUS_READY) && | ||
483 | (status & QIC_STATUS_ERROR)) { | ||
484 | unsigned int error; | ||
485 | qic117_cmd_t command; | ||
486 | |||
487 | /* Report and clear error state. | ||
488 | * In case the drive can't operate at the selected | ||
489 | * rate, select the next lower data rate. | ||
490 | */ | ||
491 | ftape_report_error(&error, &command, 1); | ||
492 | if (error == 31 && command == QIC_LOGICAL_FORWARD) { | ||
493 | /* drive does not accept this data rate */ | ||
494 | if (ft_data_rate > 250) { | ||
495 | TRACE(ft_t_info, | ||
496 | "Probable data rate conflict"); | ||
497 | TRACE(ft_t_info, | ||
498 | "Lowering data rate to %d Kbps", | ||
499 | ft_data_rate / 2); | ||
500 | ftape_half_data_rate(); | ||
501 | if (ft_buffer[ft_tail]->retry > 0) { | ||
502 | /* give it a chance */ | ||
503 | --ft_buffer[ft_tail]->retry; | ||
504 | } | ||
505 | } else { | ||
506 | /* no rate is accepted... */ | ||
507 | TRACE(ft_t_err, "We're dead :("); | ||
508 | } | ||
509 | } else { | ||
510 | TRACE(ft_t_err, "Unknown error"); | ||
511 | } | ||
512 | TRACE_EXIT -EIO; /* g.p. error */ | ||
513 | } | ||
514 | TRACE_EXIT 0; | ||
515 | } | ||
516 | |||
517 | /* forward */ static int seek_forward(int segment_id, int fast); | ||
518 | |||
519 | static int fast_seek(int count, int reverse) | ||
520 | { | ||
521 | int result = 0; | ||
522 | int status; | ||
523 | TRACE_FUN(ft_t_flow); | ||
524 | |||
525 | if (count > 0) { | ||
526 | /* If positioned at begin or end of tape, fast seeking needs | ||
527 | * special treatment. | ||
528 | * Starting from logical bot needs a (slow) seek to the first | ||
529 | * segment before the high speed seek. Most drives do this | ||
530 | * automatically but some older don't, so we treat them | ||
531 | * all the same. | ||
532 | * Starting from logical eot is even more difficult because | ||
533 | * we cannot (slow) reverse seek to the last segment. | ||
534 | * TO BE IMPLEMENTED. | ||
535 | */ | ||
536 | inhibit_correction = 0; | ||
537 | if (ft_location.known && | ||
538 | ((ft_location.bot && !reverse) || | ||
539 | (ft_location.eot && reverse))) { | ||
540 | if (!reverse) { | ||
541 | /* (slow) skip to first segment on a track | ||
542 | */ | ||
543 | seek_forward(ft_location.track * ft_segments_per_track, 0); | ||
544 | --count; | ||
545 | } else { | ||
546 | /* When seeking backwards from | ||
547 | * end-of-tape the number of erased | ||
548 | * gaps found seems to be higher than | ||
549 | * expected. Therefor the drive must | ||
550 | * skip some more segments than | ||
551 | * calculated, but we don't know how | ||
552 | * many. Thus we will prevent the | ||
553 | * re-calculation of offset and | ||
554 | * overshoot when seeking backwards. | ||
555 | */ | ||
556 | inhibit_correction = 1; | ||
557 | count += 3; /* best guess */ | ||
558 | } | ||
559 | } | ||
560 | } else { | ||
561 | TRACE(ft_t_flow, "warning: zero or negative count: %d", count); | ||
562 | } | ||
563 | if (count > 0) { | ||
564 | int i; | ||
565 | int nibbles = count > 255 ? 3 : 2; | ||
566 | |||
567 | if (count > 4095) { | ||
568 | TRACE(ft_t_noise, "skipping clipped at 4095 segment"); | ||
569 | count = 4095; | ||
570 | } | ||
571 | /* Issue this tape command first. */ | ||
572 | if (!reverse) { | ||
573 | TRACE(ft_t_noise, "skipping %d segment(s)", count); | ||
574 | result = ftape_command(nibbles == 3 ? | ||
575 | QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD); | ||
576 | } else { | ||
577 | TRACE(ft_t_noise, "backing up %d segment(s)", count); | ||
578 | result = ftape_command(nibbles == 3 ? | ||
579 | QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE); | ||
580 | } | ||
581 | if (result < 0) { | ||
582 | TRACE(ft_t_noise, "Skip command failed"); | ||
583 | } else { | ||
584 | --count; /* 0 means one gap etc. */ | ||
585 | for (i = 0; i < nibbles; ++i) { | ||
586 | if (result >= 0) { | ||
587 | result = ftape_parameter(count & 15); | ||
588 | count /= 16; | ||
589 | } | ||
590 | } | ||
591 | result = ftape_ready_wait(ftape_timeout.rewind, &status); | ||
592 | if (result >= 0) { | ||
593 | ftape_tape_running = 0; | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | TRACE_EXIT result; | ||
598 | } | ||
599 | |||
600 | static int validate(int id) | ||
601 | { | ||
602 | /* Check to see if position found is off-track as reported | ||
603 | * once. Because all tracks in one direction lie next to | ||
604 | * each other, if off-track the error will be approximately | ||
605 | * 2 * ft_segments_per_track. | ||
606 | */ | ||
607 | if (ft_location.track == -1) { | ||
608 | return 1; /* unforseen situation, don't generate error */ | ||
609 | } else { | ||
610 | /* Use margin of ft_segments_per_track on both sides | ||
611 | * because ftape needs some margin and the error we're | ||
612 | * looking for is much larger ! | ||
613 | */ | ||
614 | int lo = (ft_location.track - 1) * ft_segments_per_track; | ||
615 | int hi = (ft_location.track + 2) * ft_segments_per_track; | ||
616 | |||
617 | return (id >= lo && id < hi); | ||
618 | } | ||
619 | } | ||
620 | |||
621 | static int seek_forward(int segment_id, int fast) | ||
622 | { | ||
623 | int failures = 0; | ||
624 | int count; | ||
625 | static int margin = 1; /* fixed: stop this before target */ | ||
626 | static int overshoot = 1; | ||
627 | static int min_count = 8; | ||
628 | int expected = -1; | ||
629 | int target = segment_id - margin; | ||
630 | int fast_seeking; | ||
631 | int prev_segment = ft_location.segment; | ||
632 | TRACE_FUN(ft_t_flow); | ||
633 | |||
634 | if (!ft_location.known) { | ||
635 | TRACE_ABORT(-EIO, ft_t_err, | ||
636 | "fatal: cannot seek from unknown location"); | ||
637 | } | ||
638 | if (!validate(segment_id)) { | ||
639 | ftape_sleep(1 * FT_SECOND); | ||
640 | ft_failure = 1; | ||
641 | TRACE_ABORT(-EIO, ft_t_err, | ||
642 | "fatal: head off track (bad hardware?)"); | ||
643 | } | ||
644 | TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", | ||
645 | ft_location.segment, ft_location.sector,segment_id,margin); | ||
646 | count = target - ft_location.segment - overshoot; | ||
647 | fast_seeking = (fast && | ||
648 | count > (min_count + (ft_location.bot ? 1 : 0))); | ||
649 | if (fast_seeking) { | ||
650 | TRACE(ft_t_noise, "fast skipping %d segments", count); | ||
651 | expected = segment_id - margin; | ||
652 | fast_seek(count, 0); | ||
653 | } | ||
654 | if (!ftape_tape_running) { | ||
655 | logical_forward(); | ||
656 | } | ||
657 | while (ft_location.segment < segment_id) { | ||
658 | /* This requires at least one sector in a (bad) segment to | ||
659 | * have a valid and readable sector id ! | ||
660 | * It looks like this is not guaranteed, so we must try | ||
661 | * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!! | ||
662 | */ | ||
663 | if (ftape_read_id() < 0 || !ft_location.known || | ||
664 | sigtestsetmask(¤t->pending.signal, _DONT_BLOCK)) { | ||
665 | ft_location.known = 0; | ||
666 | if (!ftape_tape_running || | ||
667 | ++failures > FT_SECTORS_PER_SEGMENT) { | ||
668 | TRACE_ABORT(-EIO, ft_t_err, | ||
669 | "read_id failed completely"); | ||
670 | } | ||
671 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
672 | TRACE(ft_t_flow, "read_id failed, retry (%d)", | ||
673 | failures); | ||
674 | continue; | ||
675 | } | ||
676 | if (fast_seeking) { | ||
677 | TRACE(ft_t_noise, "ended at %d/%d (%d,%d)", | ||
678 | ft_location.segment, ft_location.sector, | ||
679 | overshoot, inhibit_correction); | ||
680 | if (!inhibit_correction && | ||
681 | (ft_location.segment < expected || | ||
682 | ft_location.segment > expected + margin)) { | ||
683 | int error = ft_location.segment - expected; | ||
684 | TRACE(ft_t_noise, | ||
685 | "adjusting overshoot from %d to %d", | ||
686 | overshoot, overshoot + error); | ||
687 | overshoot += error; | ||
688 | /* All overshoots have the same | ||
689 | * direction, so it should never | ||
690 | * become negative, but who knows. | ||
691 | */ | ||
692 | if (overshoot < -5 || | ||
693 | overshoot > OVERSHOOT_LIMIT) { | ||
694 | if (overshoot < 0) { | ||
695 | /* keep sane value */ | ||
696 | overshoot = -5; | ||
697 | } else { | ||
698 | /* keep sane value */ | ||
699 | overshoot = OVERSHOOT_LIMIT; | ||
700 | } | ||
701 | TRACE(ft_t_noise, | ||
702 | "clipped overshoot to %d", | ||
703 | overshoot); | ||
704 | } | ||
705 | } | ||
706 | fast_seeking = 0; | ||
707 | } | ||
708 | if (ft_location.known) { | ||
709 | if (ft_location.segment > prev_segment + 1) { | ||
710 | TRACE(ft_t_noise, | ||
711 | "missed segment %d while skipping", | ||
712 | prev_segment + 1); | ||
713 | } | ||
714 | prev_segment = ft_location.segment; | ||
715 | } | ||
716 | } | ||
717 | if (ft_location.segment > segment_id) { | ||
718 | TRACE_ABORT(-EIO, | ||
719 | ft_t_noise, "failed: skip ended at segment %d/%d", | ||
720 | ft_location.segment, ft_location.sector); | ||
721 | } | ||
722 | TRACE_EXIT 0; | ||
723 | } | ||
724 | |||
725 | static int skip_reverse(int segment_id, int *pstatus) | ||
726 | { | ||
727 | int failures = 0; | ||
728 | static int overshoot = 1; | ||
729 | static int min_rewind = 2; /* 1 + overshoot */ | ||
730 | static const int margin = 1; /* stop this before target */ | ||
731 | int expected = 0; | ||
732 | int count = 1; | ||
733 | int short_seek; | ||
734 | int target = segment_id - margin; | ||
735 | TRACE_FUN(ft_t_flow); | ||
736 | |||
737 | if (ft_location.known && !validate(segment_id)) { | ||
738 | ftape_sleep(1 * FT_SECOND); | ||
739 | ft_failure = 1; | ||
740 | TRACE_ABORT(-EIO, ft_t_err, | ||
741 | "fatal: head off track (bad hardware?)"); | ||
742 | } | ||
743 | do { | ||
744 | if (!ft_location.known) { | ||
745 | TRACE(ft_t_warn, "warning: location not known"); | ||
746 | } | ||
747 | TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", | ||
748 | ft_location.segment, ft_location.sector, | ||
749 | segment_id, margin); | ||
750 | /* min_rewind == 1 + overshoot_when_doing_minimum_rewind | ||
751 | * overshoot == overshoot_when_doing_larger_rewind | ||
752 | * Initially min_rewind == 1 + overshoot, optimization | ||
753 | * of both values will be done separately. | ||
754 | * overshoot and min_rewind can be negative as both are | ||
755 | * sums of three components: | ||
756 | * any_overshoot == rewind_overshoot - | ||
757 | * stop_overshoot - | ||
758 | * start_overshoot | ||
759 | */ | ||
760 | if (ft_location.segment - target - (min_rewind - 1) < 1) { | ||
761 | short_seek = 1; | ||
762 | } else { | ||
763 | count = ft_location.segment - target - overshoot; | ||
764 | short_seek = (count < 1); | ||
765 | } | ||
766 | if (short_seek) { | ||
767 | count = 1; /* do shortest rewind */ | ||
768 | expected = ft_location.segment - min_rewind; | ||
769 | if (expected/ft_segments_per_track != ft_location.track) { | ||
770 | expected = (ft_location.track * | ||
771 | ft_segments_per_track); | ||
772 | } | ||
773 | } else { | ||
774 | expected = target; | ||
775 | } | ||
776 | fast_seek(count, 1); | ||
777 | logical_forward(); | ||
778 | if (ftape_read_id() < 0 || !ft_location.known || | ||
779 | (sigtestsetmask(¤t->pending.signal, _DONT_BLOCK))) { | ||
780 | if ((!ftape_tape_running && !ft_location.known) || | ||
781 | ++failures > FT_SECTORS_PER_SEGMENT) { | ||
782 | TRACE_ABORT(-EIO, ft_t_err, | ||
783 | "read_id failed completely"); | ||
784 | } | ||
785 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
786 | TRACE_CATCH(ftape_report_drive_status(pstatus),); | ||
787 | TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)", | ||
788 | failures); | ||
789 | continue; | ||
790 | } | ||
791 | TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)", | ||
792 | ft_location.segment, ft_location.sector, | ||
793 | min_rewind, overshoot, inhibit_correction); | ||
794 | if (!inhibit_correction && | ||
795 | (ft_location.segment < expected || | ||
796 | ft_location.segment > expected + margin)) { | ||
797 | int error = expected - ft_location.segment; | ||
798 | if (short_seek) { | ||
799 | TRACE(ft_t_noise, | ||
800 | "adjusting min_rewind from %d to %d", | ||
801 | min_rewind, min_rewind + error); | ||
802 | min_rewind += error; | ||
803 | if (min_rewind < -5) { | ||
804 | /* is this right ? FIXME ! */ | ||
805 | /* keep sane value */ | ||
806 | min_rewind = -5; | ||
807 | TRACE(ft_t_noise, | ||
808 | "clipped min_rewind to %d", | ||
809 | min_rewind); | ||
810 | } | ||
811 | } else { | ||
812 | TRACE(ft_t_noise, | ||
813 | "adjusting overshoot from %d to %d", | ||
814 | overshoot, overshoot + error); | ||
815 | overshoot += error; | ||
816 | if (overshoot < -5 || | ||
817 | overshoot > OVERSHOOT_LIMIT) { | ||
818 | if (overshoot < 0) { | ||
819 | /* keep sane value */ | ||
820 | overshoot = -5; | ||
821 | } else { | ||
822 | /* keep sane value */ | ||
823 | overshoot = OVERSHOOT_LIMIT; | ||
824 | } | ||
825 | TRACE(ft_t_noise, | ||
826 | "clipped overshoot to %d", | ||
827 | overshoot); | ||
828 | } | ||
829 | } | ||
830 | } | ||
831 | } while (ft_location.segment > segment_id); | ||
832 | if (ft_location.known) { | ||
833 | TRACE(ft_t_noise, "current location: %d/%d", | ||
834 | ft_location.segment, ft_location.sector); | ||
835 | } | ||
836 | TRACE_EXIT 0; | ||
837 | } | ||
838 | |||
839 | static int determine_position(void) | ||
840 | { | ||
841 | int retry = 0; | ||
842 | int status; | ||
843 | int result; | ||
844 | TRACE_FUN(ft_t_flow); | ||
845 | |||
846 | if (!ftape_tape_running) { | ||
847 | /* This should only happen if tape is stopped by isr. | ||
848 | */ | ||
849 | TRACE(ft_t_flow, "waiting for tape stop"); | ||
850 | if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) { | ||
851 | TRACE(ft_t_flow, "drive still running (fatal)"); | ||
852 | ftape_tape_running = 1; /* ? */ | ||
853 | } | ||
854 | } else { | ||
855 | ftape_report_drive_status(&status); | ||
856 | } | ||
857 | if (status & QIC_STATUS_READY) { | ||
858 | /* Drive must be ready to check error state ! | ||
859 | */ | ||
860 | TRACE(ft_t_flow, "drive is ready"); | ||
861 | if (status & QIC_STATUS_ERROR) { | ||
862 | unsigned int error; | ||
863 | qic117_cmd_t command; | ||
864 | |||
865 | /* Report and clear error state, try to continue. | ||
866 | */ | ||
867 | TRACE(ft_t_flow, "error status set"); | ||
868 | ftape_report_error(&error, &command, 1); | ||
869 | ftape_ready_wait(ftape_timeout.reset, &status); | ||
870 | ftape_tape_running = 0; /* ? */ | ||
871 | } | ||
872 | if (check_bot_eot(status)) { | ||
873 | if (ft_location.bot) { | ||
874 | if ((status & QIC_STATUS_READY) == 0) { | ||
875 | /* tape moving away from | ||
876 | * bot/eot, let's see if we | ||
877 | * can catch up with the first | ||
878 | * segment on this track. | ||
879 | */ | ||
880 | } else { | ||
881 | TRACE(ft_t_flow, | ||
882 | "start tape from logical bot"); | ||
883 | logical_forward(); /* start moving */ | ||
884 | } | ||
885 | } else { | ||
886 | if ((status & QIC_STATUS_READY) == 0) { | ||
887 | TRACE(ft_t_noise, "waiting for logical end of track"); | ||
888 | result = ftape_ready_wait(ftape_timeout.reset, &status); | ||
889 | /* error handling needed ? */ | ||
890 | } else { | ||
891 | TRACE(ft_t_noise, | ||
892 | "tape at logical end of track"); | ||
893 | } | ||
894 | } | ||
895 | } else { | ||
896 | TRACE(ft_t_flow, "start tape"); | ||
897 | logical_forward(); /* start moving */ | ||
898 | ft_location.known = 0; /* not cleared by logical forward ! */ | ||
899 | } | ||
900 | } | ||
901 | /* tape should be moving now, start reading id's | ||
902 | */ | ||
903 | while (!ft_location.known && | ||
904 | retry++ < FT_SECTORS_PER_SEGMENT && | ||
905 | (result = ftape_read_id()) < 0) { | ||
906 | |||
907 | TRACE(ft_t_flow, "location unknown"); | ||
908 | |||
909 | /* exit on signal | ||
910 | */ | ||
911 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
912 | |||
913 | /* read-id somehow failed, tape may | ||
914 | * have reached end or some other | ||
915 | * error happened. | ||
916 | */ | ||
917 | TRACE(ft_t_flow, "read-id failed"); | ||
918 | TRACE_CATCH(ftape_report_drive_status(&status),); | ||
919 | TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status); | ||
920 | if (status & QIC_STATUS_READY) { | ||
921 | ftape_tape_running = 0; | ||
922 | TRACE(ft_t_noise, "tape stopped for unknown reason! " | ||
923 | "status = 0x%02x", status); | ||
924 | if (status & QIC_STATUS_ERROR || | ||
925 | !check_bot_eot(status)) { | ||
926 | /* oops, tape stopped but not at end! | ||
927 | */ | ||
928 | TRACE_EXIT -EIO; | ||
929 | } | ||
930 | } | ||
931 | } | ||
932 | TRACE(ft_t_flow, | ||
933 | "tape is positioned at segment %d", ft_location.segment); | ||
934 | TRACE_EXIT ft_location.known ? 0 : -EIO; | ||
935 | } | ||
936 | |||
937 | /* Get the tape running and position it just before the | ||
938 | * requested segment. | ||
939 | * Seek tape-track and reposition as needed. | ||
940 | */ | ||
941 | int ftape_start_tape(int segment_id, int sector_offset) | ||
942 | { | ||
943 | int track = segment_id / ft_segments_per_track; | ||
944 | int result = -EIO; | ||
945 | int status; | ||
946 | static int last_segment = -1; | ||
947 | static int bad_bus_timing = 0; | ||
948 | /* number of segments passing the head between starting the tape | ||
949 | * and being able to access the first sector. | ||
950 | */ | ||
951 | static int start_offset = 1; | ||
952 | int retry; | ||
953 | TRACE_FUN(ft_t_flow); | ||
954 | |||
955 | /* If sector_offset > 0, seek into wanted segment instead of | ||
956 | * into previous. | ||
957 | * This allows error recovery if a part of the segment is bad | ||
958 | * (erased) causing the tape drive to generate an index pulse | ||
959 | * thus causing a no-data error before the requested sector | ||
960 | * is reached. | ||
961 | */ | ||
962 | ftape_tape_running = 0; | ||
963 | TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset, | ||
964 | ft_buffer[ft_head]->retry > 0 ? " retry" : ""); | ||
965 | if (ft_buffer[ft_head]->retry > 0) { /* this is a retry */ | ||
966 | int dist = segment_id - last_segment; | ||
967 | |||
968 | if ((int)ft_history.overrun_errors < overrun_count_offset) { | ||
969 | overrun_count_offset = ft_history.overrun_errors; | ||
970 | } else if (dist < 0 || dist > 50) { | ||
971 | overrun_count_offset = ft_history.overrun_errors; | ||
972 | } else if ((ft_history.overrun_errors - | ||
973 | overrun_count_offset) >= 8) { | ||
974 | if (ftape_increase_threshold() >= 0) { | ||
975 | --ft_buffer[ft_head]->retry; | ||
976 | overrun_count_offset = | ||
977 | ft_history.overrun_errors; | ||
978 | TRACE(ft_t_warn, "increased threshold because " | ||
979 | "of excessive overrun errors"); | ||
980 | } else if (!bad_bus_timing && ft_data_rate >= 1000) { | ||
981 | ftape_half_data_rate(); | ||
982 | --ft_buffer[ft_head]->retry; | ||
983 | bad_bus_timing = 1; | ||
984 | overrun_count_offset = | ||
985 | ft_history.overrun_errors; | ||
986 | TRACE(ft_t_warn, "reduced datarate because " | ||
987 | "of excessive overrun errors"); | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | last_segment = segment_id; | ||
992 | if (ft_location.track != track || | ||
993 | (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) { | ||
994 | /* current track unknown or not equal to destination | ||
995 | */ | ||
996 | ftape_ready_wait(ftape_timeout.seek, &status); | ||
997 | ftape_seek_head_to_track(track); | ||
998 | /* overrun_count_offset = ft_history.overrun_errors; */ | ||
999 | } | ||
1000 | result = -EIO; | ||
1001 | retry = 0; | ||
1002 | while (result < 0 && | ||
1003 | retry++ <= 5 && | ||
1004 | !ft_failure && | ||
1005 | !(sigtestsetmask(¤t->pending.signal, _DONT_BLOCK))) { | ||
1006 | |||
1007 | if (retry && start_offset < 5) { | ||
1008 | start_offset ++; | ||
1009 | } | ||
1010 | /* Check if we are able to catch the requested | ||
1011 | * segment in time. | ||
1012 | */ | ||
1013 | if ((ft_location.known || (determine_position() == 0)) && | ||
1014 | ft_location.segment >= | ||
1015 | (segment_id - | ||
1016 | ((ftape_tape_running || ft_location.bot) | ||
1017 | ? 0 : start_offset))) { | ||
1018 | /* Too far ahead (in or past target segment). | ||
1019 | */ | ||
1020 | if (ftape_tape_running) { | ||
1021 | if ((result = ftape_stop_tape(&status)) < 0) { | ||
1022 | TRACE(ft_t_err, | ||
1023 | "stop tape failed with code %d", | ||
1024 | result); | ||
1025 | break; | ||
1026 | } | ||
1027 | TRACE(ft_t_noise, "tape stopped"); | ||
1028 | ftape_tape_running = 0; | ||
1029 | } | ||
1030 | TRACE(ft_t_noise, "repositioning"); | ||
1031 | ++ft_history.rewinds; | ||
1032 | if (segment_id % ft_segments_per_track < start_offset){ | ||
1033 | TRACE(ft_t_noise, "end of track condition\n" | ||
1034 | KERN_INFO "segment_id : %d\n" | ||
1035 | KERN_INFO "ft_segments_per_track: %d\n" | ||
1036 | KERN_INFO "start_offset : %d", | ||
1037 | segment_id, ft_segments_per_track, | ||
1038 | start_offset); | ||
1039 | |||
1040 | /* If seeking to first segments on | ||
1041 | * track better do a complete rewind | ||
1042 | * to logical begin of track to get a | ||
1043 | * more steady tape motion. | ||
1044 | */ | ||
1045 | result = ftape_command_wait( | ||
1046 | (ft_location.track & 1) | ||
1047 | ? QIC_PHYSICAL_FORWARD | ||
1048 | : QIC_PHYSICAL_REVERSE, | ||
1049 | ftape_timeout.rewind, &status); | ||
1050 | check_bot_eot(status); /* update location */ | ||
1051 | } else { | ||
1052 | result= skip_reverse(segment_id - start_offset, | ||
1053 | &status); | ||
1054 | } | ||
1055 | } | ||
1056 | if (!ft_location.known) { | ||
1057 | TRACE(ft_t_bug, "panic: location not known"); | ||
1058 | result = -EIO; | ||
1059 | continue; /* while() will check for failure */ | ||
1060 | } | ||
1061 | TRACE(ft_t_noise, "current segment: %d/%d", | ||
1062 | ft_location.segment, ft_location.sector); | ||
1063 | /* We're on the right track somewhere before the | ||
1064 | * wanted segment. Start tape movement if needed and | ||
1065 | * skip to just before or inside the requested | ||
1066 | * segment. Keep tape running. | ||
1067 | */ | ||
1068 | result = 0; | ||
1069 | if (ft_location.segment < | ||
1070 | (segment_id - ((ftape_tape_running || ft_location.bot) | ||
1071 | ? 0 : start_offset))) { | ||
1072 | if (sector_offset > 0) { | ||
1073 | result = seek_forward(segment_id, | ||
1074 | retry <= 3); | ||
1075 | } else { | ||
1076 | result = seek_forward(segment_id - 1, | ||
1077 | retry <= 3); | ||
1078 | } | ||
1079 | } | ||
1080 | if (result == 0 && | ||
1081 | ft_location.segment != | ||
1082 | (segment_id - (sector_offset > 0 ? 0 : 1))) { | ||
1083 | result = -EIO; | ||
1084 | } | ||
1085 | } | ||
1086 | if (result < 0) { | ||
1087 | TRACE(ft_t_err, "failed to reposition"); | ||
1088 | } else { | ||
1089 | ft_runner_status = running; | ||
1090 | } | ||
1091 | TRACE_EXIT result; | ||
1092 | } | ||