[DLM] Clean up lowcomms
[sfrench/cifs-2.6.git] / drivers / char / ftape / lowlevel / ftape-format.c
1 /*
2  * Copyright (C) 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/lowlevel/ftape-format.c,v $
20  * $Revision: 1.2.4.1 $
21  * $Date: 1997/11/14 16:05:39 $
22  *
23  *      This file contains the code to support formatting of floppy
24  *      tape cartridges with the QIC-40/80/3010/3020 floppy-tape
25  *      driver "ftape" for Linux.
26  */
27  
28 #include <linux/string.h>
29 #include <linux/errno.h>
30
31 #include <linux/ftape.h>
32 #include <linux/qic117.h>
33 #include "../lowlevel/ftape-tracing.h"
34 #include "../lowlevel/ftape-io.h"
35 #include "../lowlevel/ftape-ctl.h"
36 #include "../lowlevel/ftape-rw.h"
37 #include "../lowlevel/ftape-ecc.h"
38 #include "../lowlevel/ftape-bsm.h"
39 #include "../lowlevel/ftape-format.h"
40
41 #if defined(TESTING)
42 #define FT_FMT_SEGS_PER_BUF 50
43 #else
44 #define FT_FMT_SEGS_PER_BUF (FT_BUFF_SIZE/(4*FT_SECTORS_PER_SEGMENT))
45 #endif
46
47 static spinlock_t ftape_format_lock;
48
49 /*
50  *  first segment of the new buffer
51  */
52 static int switch_segment;
53
54 /*
55  *  at most 256 segments fit into one 32 kb buffer.  Even TR-1 cartridges have
56  *  more than this many segments per track, so better be careful.
57  *
58  *  buffer_struct *buff: buffer to store the formatting coordinates in
59  *  int  start: starting segment for this buffer.
60  *  int    spt: segments per track
61  *
62  *  Note: segment ids are relative to the start of the track here.
63  */
64 static void setup_format_buffer(buffer_struct *buff, int start, int spt,
65                                 __u8 gap3)
66 {
67         int to_do = spt - start;
68         TRACE_FUN(ft_t_flow);
69
70         if (to_do > FT_FMT_SEGS_PER_BUF) {
71                 to_do = FT_FMT_SEGS_PER_BUF;
72         }
73         buff->ptr          = buff->address;
74         buff->remaining    = to_do * FT_SECTORS_PER_SEGMENT; /* # sectors */
75         buff->bytes        = buff->remaining * 4; /* need 4 bytes per sector */
76         buff->gap3         = gap3;
77         buff->segment_id   = start;
78         buff->next_segment = start + to_do;
79         if (buff->next_segment >= spt) {
80                 buff->next_segment = 0; /* 0 means: stop runner */
81         }
82         buff->status       = waiting; /* tells the isr that it can use
83                                        * this buffer
84                                        */
85         TRACE_EXIT;
86 }
87
88
89 /*
90  *  start formatting a new track.
91  */
92 int ftape_format_track(const unsigned int track, const __u8 gap3)
93 {
94         unsigned long flags;
95         buffer_struct *tail, *head;
96         int status;
97         TRACE_FUN(ft_t_flow);
98
99         TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),);
100         if (track & 1) {
101                 if (!(status & QIC_STATUS_AT_EOT)) {
102                         TRACE_CATCH(ftape_seek_to_eot(),);
103                 }
104         } else {
105                 if (!(status & QIC_STATUS_AT_BOT)) {
106                         TRACE_CATCH(ftape_seek_to_bot(),);
107                 }
108         }
109         ftape_abort_operation(); /* this sets ft_head = ft_tail = 0 */
110         ftape_set_state(formatting);
111
112         TRACE(ft_t_noise,
113               "Formatting track %d, logical: from segment %d to %d",
114               track, track * ft_segments_per_track, 
115               (track + 1) * ft_segments_per_track - 1);
116         
117         /*
118          *  initialize the buffer switching protocol for this track
119          */
120         head = ftape_get_buffer(ft_queue_head); /* tape isn't running yet */
121         tail = ftape_get_buffer(ft_queue_tail); /* tape isn't running yet */
122         switch_segment = 0;
123         do {
124                 FT_SIGNAL_EXIT(_DONT_BLOCK);
125                 setup_format_buffer(tail, switch_segment,
126                                     ft_segments_per_track, gap3);
127                 switch_segment = tail->next_segment;
128         } while ((switch_segment != 0) &&
129                  ((tail = ftape_next_buffer(ft_queue_tail)) != head));
130         /* go */
131         head->status = formatting;
132         TRACE_CATCH(ftape_seek_head_to_track(track),);
133         TRACE_CATCH(ftape_command(QIC_LOGICAL_FORWARD),);
134         spin_lock_irqsave(&ftape_format_lock, flags);
135         TRACE_CATCH(fdc_setup_formatting(head), restore_flags(flags));
136         spin_unlock_irqrestore(&ftape_format_lock, flags);
137         TRACE_EXIT 0;
138 }
139
140 /*   return segment id of segment currently being formatted and do the
141  *   buffer switching stuff.
142  */
143 int ftape_format_status(unsigned int *segment_id)
144 {
145         buffer_struct *tail = ftape_get_buffer(ft_queue_tail);
146         int result;
147         TRACE_FUN(ft_t_flow);
148
149         while (switch_segment != 0 &&
150                ftape_get_buffer(ft_queue_head) != tail) {
151                 FT_SIGNAL_EXIT(_DONT_BLOCK);
152                 /*  need more buffers, first wait for empty buffer
153                  */
154                 TRACE_CATCH(ftape_wait_segment(formatting),);
155                 /*  don't worry for gap3. If we ever hit this piece of code,
156                  *  then all buffer already have the correct gap3 set!
157                  */
158                 setup_format_buffer(tail, switch_segment,
159                                     ft_segments_per_track, tail->gap3);
160                 switch_segment = tail->next_segment;
161                 if (switch_segment != 0) {
162                         tail = ftape_next_buffer(ft_queue_tail);
163                 }
164         }
165         /*    should runner stop ?
166          */
167         if (ft_runner_status == aborting || ft_runner_status == do_abort) {
168                 buffer_struct *head = ftape_get_buffer(ft_queue_head);
169                 TRACE(ft_t_warn, "Error formatting segment %d",
170                       ftape_get_buffer(ft_queue_head)->segment_id);
171                 (void)ftape_abort_operation();
172                 TRACE_EXIT (head->status != error) ? -EAGAIN : -EIO;
173         }
174         /*
175          *  don't care if the timer expires, this is just kind of a
176          *  "select" operation that lets the calling process sleep
177          *  until something has happened
178          */
179         if (fdc_interrupt_wait(5 * FT_SECOND) < 0) {
180                 TRACE(ft_t_noise, "End of track %d at segment %d",
181                       ft_location.track,
182                       ftape_get_buffer(ft_queue_head)->segment_id);
183                 result = 1;  /* end of track, unlock module */
184         } else {
185                 result = 0;
186         }
187         /*
188          *  the calling process should use the seg id to determine
189          *  which parts of the dma buffers can be safely overwritten
190          *  with new data.
191          */
192         *segment_id = ftape_get_buffer(ft_queue_head)->segment_id;
193         /*
194          *  Internally we start counting segment ids from the start of
195          *  each track when formatting, but externally we keep them
196          *  relative to the start of the tape:
197          */
198         *segment_id += ft_location.track * ft_segments_per_track;
199         TRACE_EXIT result;
200 }
201
202 /*
203  *  The segment id is relative to the start of the tape
204  */
205 int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm)
206 {
207         int result;
208         int verify_done = 0;
209         TRACE_FUN(ft_t_flow);
210
211         TRACE(ft_t_noise, "Verifying segment %d", segment_id);
212
213         if (ft_driver_state != verifying) {
214                 TRACE(ft_t_noise, "calling ftape_abort_operation");
215                 if (ftape_abort_operation() < 0) {
216                         TRACE(ft_t_err, "ftape_abort_operation failed");
217                         TRACE_EXIT -EIO;
218                 }
219         }
220         *bsm = 0x00000000;
221         ftape_set_state(verifying);
222         for (;;) {
223                 buffer_struct *tail;
224                 /*
225                  *  Allow escape from this loop on signal
226                  */
227                 FT_SIGNAL_EXIT(_DONT_BLOCK);
228                 /*
229                  *  Search all full buffers for the first matching the
230                  *  wanted segment.  Clear other buffers on the fly.
231                  */
232                 tail = ftape_get_buffer(ft_queue_tail);
233                 while (!verify_done && tail->status == done) {
234                         /*
235                          *  Allow escape from this loop on signal !
236                          */
237                         FT_SIGNAL_EXIT(_DONT_BLOCK);
238                         if (tail->segment_id == segment_id) {
239                                 /*  If out buffer is already full,
240                                  *  return its contents.  
241                                  */
242                                 TRACE(ft_t_flow, "found segment in cache: %d",
243                                       segment_id);
244                                 if ((tail->soft_error_map |
245                                      tail->hard_error_map) != 0) {
246                                         TRACE(ft_t_info,"bsm[%d] = 0x%08lx",
247                                               segment_id,
248                                               (unsigned long)
249                                               (tail->soft_error_map |
250                                               tail->hard_error_map));
251                                         *bsm = (tail->soft_error_map |
252                                                 tail->hard_error_map);
253                                 }
254                                 verify_done = 1;
255                         } else {
256                                 TRACE(ft_t_flow,"zapping segment in cache: %d",
257                                       tail->segment_id);
258                         }
259                         tail->status = waiting;
260                         tail = ftape_next_buffer(ft_queue_tail);
261                 }
262                 if (!verify_done && tail->status == verifying) {
263                         if (tail->segment_id == segment_id) {
264                                 switch(ftape_wait_segment(verifying)) {
265                                 case 0:
266                                         break;
267                                 case -EINTR:
268                                         TRACE_ABORT(-EINTR, ft_t_warn,
269                                                     "interrupted by "
270                                                     "non-blockable signal");
271                                         break;
272                                 default:
273                                         ftape_abort_operation();
274                                         ftape_set_state(verifying);
275                                         /* be picky */
276                                         TRACE_ABORT(-EIO, ft_t_warn,
277                                                     "wait_segment failed");
278                                 }
279                         } else {
280                                 /*  We're reading the wrong segment,
281                                  *  stop runner.
282                                  */
283                                 TRACE(ft_t_noise, "verifying wrong segment");
284                                 ftape_abort_operation();
285                                 ftape_set_state(verifying);
286                         }
287                 }
288                 /*    should runner stop ?
289                  */
290                 if (ft_runner_status == aborting) {
291                         buffer_struct *head = ftape_get_buffer(ft_queue_head);
292                         if (head->status == error ||
293                             head->status == verifying) {
294                                 /* no data or overrun error */
295                                 head->status = waiting;
296                         }
297                         TRACE_CATCH(ftape_dumb_stop(),);
298                 } else {
299                         /*  If just passed last segment on tape: wait
300                          *  for BOT or EOT mark. Sets ft_runner_status to
301                          *  idle if at lEOT and successful 
302                          */
303                         TRACE_CATCH(ftape_handle_logical_eot(),);
304                 }
305                 if (verify_done) {
306                         TRACE_EXIT 0;
307                 }
308                 /*    Now at least one buffer is idle!
309                  *    Restart runner & tape if needed.
310                  */
311                 /*  We could optimize the following a little bit. We know that 
312                  *  the bad sector map is empty.
313                  */
314                 tail = ftape_get_buffer(ft_queue_tail);
315                 if (tail->status == waiting) {
316                         buffer_struct *head = ftape_get_buffer(ft_queue_head);
317
318                         ftape_setup_new_segment(head, segment_id, -1);
319                         ftape_calc_next_cluster(head);
320                         if (ft_runner_status == idle) {
321                                 result = ftape_start_tape(segment_id,
322                                                           head->sector_offset);
323                                 switch(result) {
324                                 case 0:
325                                         break;
326                                 case -ETIME:
327                                 case -EINTR:
328                                         TRACE_ABORT(result, ft_t_err, "Error: "
329                                                     "segment %d unreachable",
330                                                     segment_id);
331                                         break;
332                                 default:
333                                         *bsm = EMPTY_SEGMENT;
334                                         TRACE_EXIT 0;
335                                         break;
336                                 }
337                         }
338                         head->status = verifying;
339                         fdc_setup_read_write(head, FDC_VERIFY);
340                 }
341         }
342         /* not reached */
343         TRACE_EXIT -EIO;
344 }