2 * Copyright (C) 1993-1995 Bas Laarhoven,
3 * (C) 1996-1997 Claus-Justus Heine.
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)
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.
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.
20 * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.c,v $
21 * $Revision: 1.3.4.1 $
22 * $Date: 1997/11/14 18:07:04 $
24 * This file contains the writing code
25 * for the QIC-117 floppy-tape driver for Linux.
28 #include <linux/string.h>
29 #include <linux/errno.h>
32 #include <linux/ftape.h>
33 #include <linux/qic117.h>
34 #include "../lowlevel/ftape-tracing.h"
35 #include "../lowlevel/ftape-write.h"
36 #include "../lowlevel/ftape-read.h"
37 #include "../lowlevel/ftape-io.h"
38 #include "../lowlevel/ftape-ctl.h"
39 #include "../lowlevel/ftape-rw.h"
40 #include "../lowlevel/ftape-ecc.h"
41 #include "../lowlevel/ftape-bsm.h"
42 #include "../lowlevel/fdc-isr.h"
49 static int last_write_failed
;
51 void ftape_zap_write_buffers(void)
55 for (i
= 0; i
< ft_nr_buffers
; ++i
) {
56 ft_buffer
[i
]->status
= done
;
61 static int copy_and_gen_ecc(void *destination
,
63 const SectorMap bad_sector_map
)
66 struct memory_segment mseg
;
67 int bads
= count_ones(bad_sector_map
);
71 TRACE(ft_t_noise
, "bad sectors in map: %d", bads
);
73 if (bads
+ 3 >= FT_SECTORS_PER_SEGMENT
) {
74 TRACE(ft_t_noise
, "empty segment");
75 mseg
.blocks
= 0; /* skip entire segment */
76 result
= 0; /* nothing written */
78 mseg
.blocks
= FT_SECTORS_PER_SEGMENT
- bads
;
79 mseg
.data
= destination
;
80 memcpy(mseg
.data
, source
, (mseg
.blocks
- 3) * FT_SECTOR_SIZE
);
81 result
= ftape_ecc_set_segment_parity(&mseg
);
83 TRACE(ft_t_err
, "ecc_set_segment_parity failed");
85 result
= (mseg
.blocks
- 3) * FT_SECTOR_SIZE
;
92 int ftape_start_writing(const ft_write_mode_t mode
)
94 buffer_struct
*head
= ftape_get_buffer(ft_queue_head
);
95 int segment_id
= head
->segment_id
;
97 buffer_state_enum wanted_state
= (mode
== FT_WR_DELETE
100 TRACE_FUN(ft_t_flow
);
102 if ((ft_driver_state
!= wanted_state
) || head
->status
!= waiting
) {
105 ftape_setup_new_segment(head
, segment_id
, 1);
106 if (mode
== FT_WR_SINGLE
) {
107 /* stop tape instead of pause */
108 head
->next_segment
= 0;
110 ftape_calc_next_cluster(head
); /* prepare */
111 head
->status
= ft_driver_state
; /* either writing or deleting */
112 if (ft_runner_status
== idle
) {
114 "starting runner for segment %d", segment_id
);
115 TRACE_CATCH(ftape_start_tape(segment_id
,head
->sector_offset
),);
117 TRACE(ft_t_noise
, "runner not idle, not starting tape");
120 result
= fdc_setup_read_write(head
, (mode
== FT_WR_DELETE
121 ? FDC_WRITE_DELETED
: FDC_WRITE
));
122 ftape_set_state(wanted_state
); /* should not be necessary */
126 /* Wait until all data is actually written to tape.
128 * There is a problem: when the tape runs into logical EOT, then this
129 * failes. We need to restart the runner in this case.
131 int ftape_loop_until_writes_done(void)
134 TRACE_FUN(ft_t_flow
);
136 while ((ft_driver_state
== writing
|| ft_driver_state
== deleting
) &&
137 ftape_get_buffer(ft_queue_head
)->status
!= done
) {
138 /* set the runner status to idle if at lEOT */
139 TRACE_CATCH(ftape_handle_logical_eot(), last_write_failed
= 1);
140 /* restart the tape if necessary */
141 if (ft_runner_status
== idle
) {
142 TRACE(ft_t_noise
, "runner is idle, restarting");
143 if (ft_driver_state
== deleting
) {
144 TRACE_CATCH(ftape_start_writing(FT_WR_DELETE
),
145 last_write_failed
= 1);
147 TRACE_CATCH(ftape_start_writing(FT_WR_MULTI
),
148 last_write_failed
= 1);
151 TRACE(ft_t_noise
, "tail: %d, head: %d",
152 ftape_buffer_id(ft_queue_tail
),
153 ftape_buffer_id(ft_queue_head
));
154 TRACE_CATCH(fdc_interrupt_wait(5 * FT_SECOND
),
155 last_write_failed
= 1);
156 head
= ftape_get_buffer(ft_queue_head
);
157 if (head
->status
== error
) {
158 /* Allow escape from loop when signaled !
160 FT_SIGNAL_EXIT(_DONT_BLOCK
);
161 if (head
->hard_error_map
!= 0) {
162 /* Implement hard write error recovery here
166 head
->status
= waiting
;
167 if (ft_runner_status
== aborting
) {
170 if (ft_runner_status
!= idle
) {
171 TRACE_ABORT(-EIO
, ft_t_err
,
173 "ft_runner_status != idle");
175 ftape_start_writing(ft_driver_state
== deleting
176 ? FT_WR_MULTI
: FT_WR_DELETE
);
178 TRACE(ft_t_noise
, "looping until writes done");
180 ftape_set_state(idle
);
184 /* Write given segment from buffer at address to tape.
186 static int write_segment(const int segment_id
,
188 const ft_write_mode_t write_mode
)
190 int bytes_written
= 0;
192 buffer_state_enum wanted_state
= (write_mode
== FT_WR_DELETE
193 ? deleting
: writing
);
194 TRACE_FUN(ft_t_flow
);
196 TRACE(ft_t_noise
, "segment_id = %d", segment_id
);
197 if (ft_driver_state
!= wanted_state
) {
198 if (ft_driver_state
== deleting
||
199 wanted_state
== deleting
) {
200 TRACE_CATCH(ftape_loop_until_writes_done(),);
202 TRACE(ft_t_noise
, "calling ftape_abort_operation");
203 TRACE_CATCH(ftape_abort_operation(),);
204 ftape_zap_write_buffers();
205 ftape_set_state(wanted_state
);
207 /* if all buffers full we'll have to wait...
209 ftape_wait_segment(wanted_state
);
210 tail
= ftape_get_buffer(ft_queue_tail
);
211 switch(tail
->status
) {
213 ft_history
.defects
+= count_ones(tail
->hard_error_map
);
216 /* this could happen with multiple EMPTY_SEGMENTs, but
217 * shouldn't happen any more as we re-start the runner even
218 * with an empty segment.
220 bytes_written
= -EAGAIN
;
225 tail
->status
= waiting
;
226 bytes_written
= -EAGAIN
; /* force retry */
227 if (tail
->hard_error_map
!= 0) {
229 "warning: %d hard error(s) in written segment",
230 count_ones(tail
->hard_error_map
));
231 TRACE(ft_t_noise
, "hard_error_map = 0x%08lx",
232 (long)tail
->hard_error_map
);
233 /* Implement hard write error recovery here
238 TRACE_ABORT(-EIO
, ft_t_err
,
239 "wait for empty segment failed, tail status: %d",
242 /* should runner stop ?
244 if (ft_runner_status
== aborting
) {
245 buffer_struct
*head
= ftape_get_buffer(ft_queue_head
);
246 if (head
->status
== wanted_state
) {
247 head
->status
= done
; /* ???? */
249 /* don't call abort_operation(), we don't want to zap
252 TRACE_CATCH(ftape_dumb_stop(),);
254 /* If just passed last segment on tape: wait for BOT
255 * or EOT mark. Sets ft_runner_status to idle if at lEOT
258 TRACE_CATCH(ftape_handle_logical_eot(),);
260 if (tail
->status
== done
) {
261 /* now at least one buffer is empty, fill it with our
262 * data. skip bad sectors and generate ecc.
263 * copy_and_gen_ecc return nr of bytes written, range
264 * 0..29 Kb inclusive!
266 * Empty segments are handled inside coyp_and_gen_ecc()
268 if (write_mode
!= FT_WR_DELETE
) {
269 TRACE_CATCH(bytes_written
= copy_and_gen_ecc(
270 tail
->address
, address
,
271 ftape_get_bad_sector_entry(segment_id
)),);
273 tail
->segment_id
= segment_id
;
274 tail
->status
= waiting
;
275 tail
= ftape_next_buffer(ft_queue_tail
);
277 /* Start tape only if all buffers full or flush mode.
278 * This will give higher probability of streaming.
280 if (ft_runner_status
!= running
&&
281 ((tail
->status
== waiting
&&
282 ftape_get_buffer(ft_queue_head
) == tail
) ||
283 write_mode
!= FT_WR_ASYNC
)) {
284 TRACE_CATCH(ftape_start_writing(write_mode
),);
286 TRACE_EXIT bytes_written
;
289 /* Write as much as fits from buffer to the given segment on tape
290 * and handle retries.
291 * Return the number of bytes written (>= 0), or:
293 * -EINTR interrupted by signal
294 * -ENOSPC device full
296 int ftape_write_segment(const int segment_id
,
298 const ft_write_mode_t flush
)
302 TRACE_FUN(ft_t_flow
);
304 ft_history
.used
|= 2;
305 if (segment_id
>= ft_tracks_per_tape
*ft_segments_per_track
) {
307 TRACE_ABORT(-ENOSPC
, ft_t_err
,
308 "invalid segment id: %d (max %d)",
310 ft_tracks_per_tape
* ft_segments_per_track
-1);
313 if ((result
= write_segment(segment_id
, buffer
, flush
)) >= 0) {
314 if (result
== 0) { /* empty segment */
316 "empty segment, nothing written");
320 if (result
== -EAGAIN
) {
321 if (++retry
> 100) { /* give up */
322 TRACE_ABORT(-EIO
, ft_t_err
,
323 "write failed, >100 retries in segment");
325 TRACE(ft_t_warn
, "write error, retry %d (%d)",
327 ftape_get_buffer(ft_queue_tail
)->segment_id
);
329 TRACE_ABORT(result
, ft_t_err
,
330 "write_segment failed, error: %d", result
);
332 /* Allow escape from loop when signaled !
334 FT_SIGNAL_EXIT(_DONT_BLOCK
);