Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux...
[linux-2.6] / drivers / char / ftape / lowlevel / ftape-bsm.c
1 /*
2  *      Copyright (C) 1994-1996 Bas Laarhoven,
3  *                (C) 1996-1997 Claus 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-bsm.c,v $
21  * $Revision: 1.3 $
22  * $Date: 1997/10/05 19:15:15 $
23  *
24  *      This file contains the bad-sector map handling code for
25  *      the QIC-117 floppy tape driver for Linux.
26  *      QIC-40, QIC-80, QIC-3010 and QIC-3020 maps are implemented.
27  */
28
29 #include <linux/string.h>
30
31 #include <linux/ftape.h>
32 #include "../lowlevel/ftape-tracing.h"
33 #include "../lowlevel/ftape-bsm.h"
34 #include "../lowlevel/ftape-ctl.h"
35 #include "../lowlevel/ftape-rw.h"
36
37 /*      Global vars.
38  */
39
40 /*      Local vars.
41  */
42 static __u8 *bad_sector_map;
43 static SectorCount *bsm_hash_ptr; 
44
45 typedef enum {
46         forward, backward
47 } mode_type;
48
49 #if 0
50 static void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map);
51 #endif
52
53 #if 0
54 /*  fix_tape converts a normal QIC-80 tape into a 'wide' tape.
55  *  For testing purposes only !
56  */
57 void fix_tape(__u8 * buffer, ft_format_type new_code)
58 {
59         static __u8 list[BAD_SECTOR_MAP_SIZE];
60         SectorMap *src_ptr = (SectorMap *) list;
61         __u8 *dst_ptr = bad_sector_map;
62         SectorMap map;
63         unsigned int sector = 1;
64         int i;
65
66         if (format_code != fmt_var && format_code != fmt_big) {
67                 memcpy(list, bad_sector_map, sizeof(list));
68                 memset(bad_sector_map, 0, sizeof(bad_sector_map));
69                 while ((__u8 *) src_ptr - list < sizeof(list)) {
70                         map = *src_ptr++;
71                         if (map == EMPTY_SEGMENT) {
72                                 *(SectorMap *) dst_ptr = 0x800000 + sector;
73                                 dst_ptr += 3;
74                                 sector += SECTORS_PER_SEGMENT;
75                         } else {
76                                 for (i = 0; i < SECTORS_PER_SEGMENT; ++i) {
77                                         if (map & 1) {
78                                                 *(SewctorMap *) dst_ptr = sector;
79                                                 dst_ptr += 3;
80                                         }
81                                         map >>= 1;
82                                         ++sector;
83                                 }
84                         }
85                 }
86         }
87         bad_sector_map_changed = 1;
88         *(buffer + 4) = new_code;       /* put new format code */
89         if (format_code != fmt_var && new_code == fmt_big) {
90                 PUT4(buffer, FT_6_HSEG_1,   (__u32)GET2(buffer, 6));
91                 PUT4(buffer, FT_6_HSEG_2,   (__u32)GET2(buffer, 8));
92                 PUT4(buffer, FT_6_FRST_SEG, (__u32)GET2(buffer, 10));
93                 PUT4(buffer, FT_6_LAST_SEG, (__u32)GET2(buffer, 12));
94                 memset(buffer+6, '\0', 8);
95         }
96         format_code = new_code;
97 }
98
99 #endif
100
101 /*   given buffer that contains a header segment, find the end of
102  *   of the bsm list
103  */
104 __u8 * ftape_find_end_of_bsm_list(__u8 * address)
105 {
106         __u8 *ptr   = address + FT_HEADER_END; /* start of bsm list */
107         __u8 *limit = address + FT_SEGMENT_SIZE;
108         while (ptr + 2 < limit) {
109                 if (ptr[0] || ptr[1] || ptr[2]) {
110                         ptr += 3;
111                 } else {
112                         return ptr;
113                 }
114         }
115         return NULL;
116 }
117
118 static inline void put_sector(SectorCount *ptr, unsigned int sector)
119 {
120         ptr->bytes[0] = sector & 0xff;
121         sector >>= 8;
122         ptr->bytes[1] = sector & 0xff;
123         sector >>= 8;
124         ptr->bytes[2] = sector & 0xff;
125 }
126
127 static inline unsigned int get_sector(SectorCount *ptr)
128 {
129 #if 1
130         unsigned int sector;
131
132         sector  = ptr->bytes[0];
133         sector += ptr->bytes[1] <<  8;
134         sector += ptr->bytes[2] << 16;
135
136         return sector;
137 #else
138         /*  GET4 gets the next four bytes in Intel little endian order
139          *  and converts them to host byte order and handles unaligned
140          *  access.
141          */
142         return (GET4(ptr, 0) & 0x00ffffff); /* back to host byte order */
143 #endif
144 }
145
146 static void bsm_debug_fake(void)
147 {
148         /* for testing of bad sector handling at end of tape
149          */
150 #if 0
151         ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 3,
152                                    0x000003e0;
153         ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 2,
154                                    0xff3fffff;
155         ftape_put_bad_sector_entry(segments_per_track * tracks_per_tape - 1,
156                                    0xffffe000;
157 #endif
158         /*  Enable to test bad sector handling
159          */
160 #if 0
161         ftape_put_bad_sector_entry(30, 0xfffffffe)
162         ftape_put_bad_sector_entry(32, 0x7fffffff);
163         ftape_put_bad_sector_entry(34, 0xfffeffff);
164         ftape_put_bad_sector_entry(36, 0x55555555);
165         ftape_put_bad_sector_entry(38, 0xffffffff);
166         ftape_put_bad_sector_entry(50, 0xffff0000);
167         ftape_put_bad_sector_entry(51, 0xffffffff);
168         ftape_put_bad_sector_entry(52, 0xffffffff);
169         ftape_put_bad_sector_entry(53, 0x0000ffff);
170 #endif
171         /*  Enable when testing multiple volume tar dumps.
172          */
173 #if 0
174         {
175                 int i;
176
177                 for (i = ft_first_data_segment;
178                      i <= ft_last_data_segment - 7; ++i) {
179                         ftape_put_bad_sector_entry(i, EMPTY_SEGMENT);
180                 }
181         }
182 #endif
183         /*  Enable when testing bit positions in *_error_map
184          */
185 #if 0
186         {
187                 int i;
188                 
189                 for (i = first_data_segment; i <= last_data_segment; ++i) {
190                         ftape_put_bad_sector_entry(i,
191                                            ftape_get_bad_sector_entry(i) 
192                                            | 0x00ff00ff);
193                 }
194         }
195 #endif
196 }
197
198 static void print_bad_sector_map(void)
199 {
200         unsigned int good_sectors;
201         unsigned int total_bad = 0;
202         int i;
203         TRACE_FUN(ft_t_flow);
204
205         if (ft_format_code == fmt_big || 
206             ft_format_code == fmt_var || 
207             ft_format_code == fmt_1100ft) {
208                 SectorCount *ptr = (SectorCount *)bad_sector_map;
209                 unsigned int sector;
210                 __u16 *ptr16;
211
212                 while((sector = get_sector(ptr++)) != 0) {
213                         if ((ft_format_code == fmt_big || 
214                              ft_format_code == fmt_var) &&
215                             sector & 0x800000) {
216                                 total_bad += FT_SECTORS_PER_SEGMENT - 3;
217                                 TRACE(ft_t_noise, "bad segment at sector: %6d",
218                                       sector & 0x7fffff);
219                         } else {
220                                 ++total_bad;
221                                 TRACE(ft_t_noise, "bad sector: %6d", sector);
222                         }
223                 }
224                 /*  Display old ftape's end-of-file marks
225                  */
226                 ptr16 = (__u16*)ptr;
227                 while ((sector = get_unaligned(ptr16++)) != 0) {
228                         TRACE(ft_t_noise, "Old ftape eof mark: %4d/%2d",
229                               sector, get_unaligned(ptr16++));
230                 }
231         } else { /* fixed size format */
232                 for (i = ft_first_data_segment;
233                      i < (int)(ft_segments_per_track * ft_tracks_per_tape); ++i) {
234                         SectorMap map = ((SectorMap *) bad_sector_map)[i];
235
236                         if (map) {
237                                 TRACE(ft_t_noise,
238                                       "bsm for segment %4d: 0x%08x", i, (unsigned int)map);
239                                 total_bad += ((map == EMPTY_SEGMENT)
240                                                ? FT_SECTORS_PER_SEGMENT - 3
241                                                : count_ones(map));
242                         }
243                 }
244         }
245         good_sectors =
246                 ((ft_segments_per_track * ft_tracks_per_tape - ft_first_data_segment)
247                  * (FT_SECTORS_PER_SEGMENT - 3)) - total_bad;
248         TRACE(ft_t_info, "%d Kb usable on this tape", good_sectors);
249         if (total_bad == 0) {
250                 TRACE(ft_t_info,
251                       "WARNING: this tape has no bad blocks registered !");
252         } else {
253                 TRACE(ft_t_info, "%d bad sectors", total_bad);
254         }
255         TRACE_EXIT;
256 }
257
258
259 void ftape_extract_bad_sector_map(__u8 * buffer)
260 {
261         TRACE_FUN(ft_t_any);
262
263         /*  Fill the bad sector map with the contents of buffer.
264          */
265         if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
266                 /* QIC-3010/3020 and wide QIC-80 tapes no longer have a failed
267                  * sector log but use this area to extend the bad sector map.
268                  */
269                 bad_sector_map = &buffer[FT_HEADER_END];
270         } else {
271                 /* non-wide QIC-80 tapes have a failed sector log area that
272                  * mustn't be included in the bad sector map.
273                  */
274                 bad_sector_map = &buffer[FT_FSL + FT_FSL_SIZE];
275         }
276         if (ft_format_code == fmt_1100ft || 
277             ft_format_code == fmt_var    ||
278             ft_format_code == fmt_big) {
279                 bsm_hash_ptr = (SectorCount *)bad_sector_map;
280         } else {
281                 bsm_hash_ptr = NULL;
282         }
283         bsm_debug_fake();
284         if (TRACE_LEVEL >= ft_t_info) {
285                 print_bad_sector_map();
286         }
287         TRACE_EXIT;
288 }
289
290 static inline SectorMap cvt2map(unsigned int sector)
291 {
292         return 1 << (((sector & 0x7fffff) - 1) % FT_SECTORS_PER_SEGMENT);
293 }
294
295 static inline int cvt2segment(unsigned int sector)
296 {
297         return ((sector & 0x7fffff) - 1) / FT_SECTORS_PER_SEGMENT;
298 }
299
300 static int forward_seek_entry(int segment_id, 
301                               SectorCount **ptr, 
302                               SectorMap *map)
303 {
304         unsigned int sector;
305         int segment;
306
307         do {
308                 sector = get_sector((*ptr)++);
309                 segment = cvt2segment(sector);
310         } while (sector != 0 && segment < segment_id);
311         (*ptr) --; /* point to first sector >= segment_id */
312         /*  Get all sectors in segment_id
313          */
314         if (sector == 0 || segment != segment_id) {
315                 *map = 0;
316                 return 0;
317         } else if ((sector & 0x800000) &&
318                    (ft_format_code == fmt_var || ft_format_code == fmt_big)) {
319                 *map = EMPTY_SEGMENT;
320                 return FT_SECTORS_PER_SEGMENT;
321         } else {
322                 int count = 1;
323                 SectorCount *tmp_ptr = (*ptr) + 1;
324                 
325                 *map = cvt2map(sector);
326                 while ((sector = get_sector(tmp_ptr++)) != 0 &&
327                        (segment = cvt2segment(sector)) == segment_id) {
328                         *map |= cvt2map(sector);
329                         ++count;
330                 }
331                 return count;
332         }
333 }
334
335 static int backwards_seek_entry(int segment_id,
336                                 SectorCount **ptr,
337                                 SectorMap *map)
338 {
339         unsigned int sector;
340         int segment; /* max unsigned int */
341
342         if (*ptr <= (SectorCount *)bad_sector_map) {
343                 *map = 0;
344                 return 0;
345         }
346         do {
347                 sector  = get_sector(--(*ptr));
348                 segment = cvt2segment(sector);
349         } while (*ptr > (SectorCount *)bad_sector_map && segment > segment_id);
350         if (segment > segment_id) { /*  at start of list, no entry found */
351                 *map = 0;
352                 return 0;
353         } else if (segment < segment_id) {
354                 /*  before smaller entry, adjust for overshoot */
355                 (*ptr) ++;
356                 *map = 0;
357                 return 0;
358         } else if ((sector & 0x800000) &&
359                    (ft_format_code == fmt_big || ft_format_code == fmt_var)) {
360                 *map = EMPTY_SEGMENT;
361                 return FT_SECTORS_PER_SEGMENT;
362         } else { /*  get all sectors in segment_id */
363                 int count = 1;
364
365                 *map = cvt2map(sector);
366                 while(*ptr > (SectorCount *)bad_sector_map) {
367                         sector = get_sector(--(*ptr));
368                         segment = cvt2segment(sector);
369                         if (segment != segment_id) {
370                                 break;
371                         }
372                         *map |= cvt2map(sector);
373                         ++count;
374                 }
375                 if (segment < segment_id) {
376                         (*ptr) ++;
377                 }
378                 return count;
379         }
380 }
381
382 #if 0
383 static void ftape_put_bad_sector_entry(int segment_id, SectorMap new_map)
384 {
385         SectorCount *ptr = (SectorCount *)bad_sector_map;
386         int count;
387         int new_count;
388         SectorMap map;
389         TRACE_FUN(ft_t_any);
390
391         if (ft_format_code == fmt_1100ft || 
392             ft_format_code == fmt_var || 
393             ft_format_code == fmt_big) {
394                 count = forward_seek_entry(segment_id, &ptr, &map);
395                 new_count = count_ones(new_map);
396                 /* If format code == 4 put empty segment instead of 32
397                  * bad sectors.
398                  */
399                 if (ft_format_code == fmt_var || ft_format_code == fmt_big) {
400                         if (new_count == FT_SECTORS_PER_SEGMENT) {
401                                 new_count = 1;
402                         }
403                         if (count == FT_SECTORS_PER_SEGMENT) {
404                                 count = 1;
405                         }
406                 }
407                 if (count != new_count) {
408                         /* insert (or delete if < 0) new_count - count
409                          * entries.  Move trailing part of list
410                          * including terminating 0.
411                          */
412                         SectorCount *hi_ptr = ptr;
413
414                         do {
415                         } while (get_sector(hi_ptr++) != 0);
416                         /*  Note: ptr is of type byte *, and each bad sector
417                          *  consumes 3 bytes.
418                          */
419                         memmove(ptr + new_count, ptr + count,
420                                 (size_t)(hi_ptr - (ptr + count))*sizeof(SectorCount));
421                 }
422                 TRACE(ft_t_noise, "putting map 0x%08x at %p, segment %d",
423                       (unsigned int)new_map, ptr, segment_id);
424                 if (new_count == 1 && new_map == EMPTY_SEGMENT) {
425                         put_sector(ptr++, (0x800001 + 
426                                           segment_id * 
427                                           FT_SECTORS_PER_SEGMENT));
428                 } else {
429                         int i = 0;
430
431                         while (new_map) {
432                                 if (new_map & 1) {
433                                         put_sector(ptr++, 
434                                                    1 + segment_id * 
435                                                    FT_SECTORS_PER_SEGMENT + i);
436                                 }
437                                 ++i;
438                                 new_map >>= 1;
439                         }
440                 }
441         } else {
442                 ((SectorMap *) bad_sector_map)[segment_id] = new_map;
443         }
444         TRACE_EXIT;
445 }
446 #endif  /*  0  */
447
448 SectorMap ftape_get_bad_sector_entry(int segment_id)
449 {
450         if (ft_used_header_segment == -1) {
451                 /*  When reading header segment we'll need a blank map.
452                  */
453                 return 0;
454         } else if (bsm_hash_ptr != NULL) {
455                 /*  Invariants:
456                  *    map - mask value returned on last call.
457                  *    bsm_hash_ptr - points to first sector greater or equal to
458                  *          first sector in last_referenced segment.
459                  *    last_referenced - segment id used in the last call,
460                  *                      sector and map belong to this id.
461                  *  This code is designed for sequential access and retries.
462                  *  For true random access it may have to be redesigned.
463                  */
464                 static int last_reference = -1;
465                 static SectorMap map;
466
467                 if (segment_id > last_reference) {
468                         /*  Skip all sectors before segment_id
469                          */
470                         forward_seek_entry(segment_id, &bsm_hash_ptr, &map);
471                 } else if (segment_id < last_reference) {
472                         /* Skip backwards until begin of buffer or
473                          * first sector in segment_id 
474                          */
475                         backwards_seek_entry(segment_id, &bsm_hash_ptr, &map);
476                 }               /* segment_id == last_reference : keep map */
477                 last_reference = segment_id;
478                 return map;
479         } else {
480                 return ((SectorMap *) bad_sector_map)[segment_id];
481         }
482 }
483
484 /*  This is simply here to prevent us from overwriting other kernel
485  *  data. Writes will result in NULL Pointer dereference.
486  */
487 void ftape_init_bsm(void)
488 {
489         bad_sector_map = NULL;
490         bsm_hash_ptr   = NULL;
491 }