Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.19 344 lines 10 kB view raw
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 47static spinlock_t ftape_format_lock; 48 49/* 50 * first segment of the new buffer 51 */ 52static 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 */ 64static 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 */ 92int 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 */ 143int 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 */ 205int 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}