ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / char / ftape / zftape / zftape-rw.c
1 /*
2  *      Copyright (C) 1996, 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/zftape/zftape-rw.c,v $
20  * $Revision: 1.2 $
21  * $Date: 1997/10/05 19:19:08 $
22  *
23  *      This file contains some common code for the r/w code for
24  *      zftape.
25  */
26
27 #include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */
28 #include <linux/errno.h>
29 #include <linux/mm.h>
30
31 #include <linux/zftape.h>
32 #include "../zftape/zftape-init.h"
33 #include "../zftape/zftape-eof.h"
34 #include "../zftape/zftape-ctl.h"
35 #include "../zftape/zftape-write.h"
36 #include "../zftape/zftape-read.h"
37 #include "../zftape/zftape-rw.h"
38 #include "../zftape/zftape-vtbl.h"
39
40 /*      Global vars.
41  */
42
43 __u8 *zft_deblock_buf;
44 __u8 *zft_hseg_buf;
45 int zft_deblock_segment = -1;
46 zft_status_enum zft_io_state = zft_idle;
47 int zft_header_changed;
48 int zft_bad_sector_map_changed;
49 int zft_qic113; /* conform to old specs. and old zftape */
50 int zft_use_compression;
51 zft_position zft_pos = {
52         -1, /* seg_pos */
53         0,  /* seg_byte_pos */
54         0,  /* tape_pos */
55         0   /* volume_pos */
56 };
57 unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ;
58 __s64 zft_capacity;
59
60 unsigned int zft_written_segments;
61 int zft_label_changed;
62
63 /*      Local vars.
64  */
65
66 unsigned int zft_get_seg_sz(unsigned int segment)
67 {
68         int size;
69         TRACE_FUN(ft_t_any);
70         
71         size = FT_SEGMENT_SIZE - 
72                 count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE;
73         if (size > 0) {
74                 TRACE_EXIT (unsigned)size; 
75         } else {
76                 TRACE_EXIT 0;
77         }
78 }
79
80 /* ftape_set_flags(). Claus-Justus Heine, 1994/1995
81  */
82 void zft_set_flags(unsigned minor_unit)
83 {     
84         TRACE_FUN(ft_t_flow);
85         
86         zft_use_compression = zft_qic_mode = 0;
87         switch (minor_unit & ZFT_MINOR_OP_MASK) {
88         case (ZFT_Q80_MODE | ZFT_ZIP_MODE):
89         case ZFT_ZIP_MODE:
90                 zft_use_compression = 1;
91         case 0:
92         case ZFT_Q80_MODE:
93                 zft_qic_mode = 1;
94                 if (zft_mt_compression) { /* override the default */
95                         zft_use_compression = 1;
96                 }
97                 break;
98         case ZFT_RAW_MODE:
99                 TRACE(ft_t_noise, "switching to raw mode");
100                 break;
101         default:
102                 TRACE(ft_t_warn, "Warning:\n"
103                       KERN_INFO "Wrong combination of minor device bits.\n"
104                       KERN_INFO "Switching to raw read-only mode.");
105                 zft_write_protected = 1;
106                 break;
107         }
108         TRACE_EXIT;
109 }
110
111 /* computes the segment and byte offset inside the segment
112  * corresponding to tape_pos.
113  *
114  * tape_pos gives the offset in bytes from the beginning of the
115  * ft_first_data_segment *seg_byte_pos is the offset in the current
116  * segment in bytes
117  *
118  * Of, if this routine was called often one should cache the last data
119  * pos it was called with, but actually this is only needed in
120  * ftape_seek_block(), that is, almost never.
121  */
122 int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos)
123 {
124         int segment;
125         int seg_sz;
126         TRACE_FUN(ft_t_flow);
127         
128         if (tape_pos == 0) {
129                 *seg_byte_pos = 0;
130                 segment = ft_first_data_segment;
131         } else {
132                 seg_sz = 0;
133                 
134                 for (segment = ft_first_data_segment; 
135                      ((tape_pos > 0) && (segment <= ft_last_data_segment));
136                      segment++) {
137                         seg_sz = zft_get_seg_sz(segment); 
138                         tape_pos -= seg_sz;
139                 }
140                 if(tape_pos >= 0) {
141                         /* the case tape_pos > != 0 means that the
142                          * argument tape_pos lies beyond the EOT.
143                          */
144                         *seg_byte_pos= 0;
145                 } else { /* tape_pos < 0 */
146                         segment--;
147                         *seg_byte_pos= tape_pos + seg_sz;
148                 }
149         }
150         TRACE_EXIT(segment);
151 }
152
153 /* ftape_calc_tape_pos().
154  *
155  * computes the offset in bytes from the beginning of the
156  * ft_first_data_segment inverse to ftape_calc_seg_byte_coord
157  *
158  * We should do some caching. But how:
159  *
160  * Each time the header segments are read in, this routine is called
161  * with ft_tracks_per_tape*segments_per_track argumnet. So this should be
162  * the time to reset the cache.
163  *
164  * Also, it might be in the future that the bad sector map gets
165  * changed.  -> reset the cache
166  */
167 static int seg_pos;
168 static __s64 tape_pos;
169
170 __s64 zft_get_capacity(void)
171 {
172         seg_pos  = ft_first_data_segment;
173         tape_pos = 0;
174
175         while (seg_pos <= ft_last_data_segment) {
176                 tape_pos += zft_get_seg_sz(seg_pos ++);
177         }
178         return tape_pos;
179 }
180
181 __s64 zft_calc_tape_pos(int segment)
182 {
183         int d1, d2, d3;
184         TRACE_FUN(ft_t_any);
185         
186         if (segment > ft_last_data_segment) {
187                 TRACE_EXIT zft_capacity;
188         }
189         if (segment < ft_first_data_segment) {
190                 TRACE_EXIT 0;
191         }
192         d2 = segment - seg_pos;
193         if (-d2 > 10) {
194                 d1 = segment - ft_first_data_segment;
195                 if (-d2 > d1) {
196                         tape_pos = 0;
197                         seg_pos = ft_first_data_segment;
198                         d2 = d1;
199                 }
200         }
201         if (d2 > 10) {
202                 d3 = ft_last_data_segment - segment;
203                 if (d2 > d3) {
204                         tape_pos = zft_capacity;
205                         seg_pos  = ft_last_data_segment + 1;
206                         d2 = -d3;
207                 }
208         }               
209         if (d2 > 0) {
210                 while (seg_pos < segment) {
211                         tape_pos +=  zft_get_seg_sz(seg_pos++);
212                 }
213         } else {
214                 while (seg_pos > segment) {
215                         tape_pos -=  zft_get_seg_sz(--seg_pos);
216                 }
217         }
218         TRACE(ft_t_noise, "new cached pos: %d", seg_pos);
219
220         TRACE_EXIT tape_pos;
221 }
222
223 /* copy Z-label string to buffer, keeps track of the correct offset in
224  * `buffer' 
225  */
226 void zft_update_label(__u8 *buffer)
227
228         TRACE_FUN(ft_t_flow);
229         
230         if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, 
231                     sizeof(ZFTAPE_LABEL)-1) != 0) {
232                 TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"",
233                       &buffer[FT_LABEL], ZFTAPE_LABEL);
234                 strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL);
235                 memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', 
236                        FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1));
237                 PUT4(buffer, FT_LABEL_DATE, 0);
238                 zft_label_changed = zft_header_changed = 1; /* changed */
239         }
240         TRACE_EXIT;
241 }
242
243 int zft_verify_write_segments(unsigned int segment, 
244                               __u8 *data, size_t size,
245                               __u8 *buffer)
246 {
247         int result;
248         __u8 *write_buf;
249         __u8 *src_buf;
250         int single;
251         int seg_pos;
252         int seg_sz;
253         int remaining;
254         ft_write_mode_t write_mode;
255         TRACE_FUN(ft_t_flow);
256
257         seg_pos   = segment;
258         seg_sz    = zft_get_seg_sz(seg_pos);
259         src_buf   = data;
260         single    = size <= seg_sz;
261         remaining = size;
262         do {
263                 TRACE(ft_t_noise, "\n"
264                       KERN_INFO "remaining: %d\n"
265                       KERN_INFO "seg_sz   : %d\n"
266                       KERN_INFO "segment  : %d",
267                       remaining, seg_sz, seg_pos);
268                 if (remaining == seg_sz) {
269                         write_buf = src_buf;
270                         write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
271                         remaining = 0;
272                 } else if (remaining > seg_sz) {
273                         write_buf = src_buf;
274                         write_mode = FT_WR_ASYNC; /* don't start tape */
275                         remaining -= seg_sz;
276                 } else { /* remaining < seg_sz */
277                         write_buf = buffer;
278                         memcpy(write_buf, src_buf, remaining);
279                         memset(&write_buf[remaining],'\0',seg_sz-remaining);
280                         write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI;
281                         remaining = 0;
282                 }
283                 if ((result = ftape_write_segment(seg_pos, 
284                                                   write_buf, 
285                                                   write_mode)) != seg_sz) {
286                         TRACE(ft_t_err, "Error: "
287                               "Couldn't write segment %d", seg_pos);
288                         TRACE_EXIT result < 0 ? result : -EIO; /* bail out */
289                 }
290                 zft_written_segments ++;
291                 seg_sz = zft_get_seg_sz(++seg_pos);
292                 src_buf += result;
293         } while (remaining > 0);
294         if (ftape_get_status()->fti_state == writing) {
295                 TRACE_CATCH(ftape_loop_until_writes_done(),);
296                 TRACE_CATCH(ftape_abort_operation(),);
297                 zft_prevent_flush();
298         }
299         seg_pos = segment;
300         src_buf = data;
301         remaining = size;
302         do {
303                 TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, 
304                                                         single ? FT_RD_SINGLE
305                                                         : FT_RD_AHEAD),);
306                 if (memcmp(src_buf, buffer, 
307                            remaining > result ? result : remaining) != 0) {
308                         TRACE_ABORT(-EIO, ft_t_err,
309                                     "Failed to verify written segment %d",
310                                     seg_pos);
311                 }
312                 remaining -= result;
313                 TRACE(ft_t_noise, "verify successful:\n"
314                       KERN_INFO "segment  : %d\n"
315                       KERN_INFO "segsize  : %d\n"
316                       KERN_INFO "remaining: %d",
317                       seg_pos, result, remaining);
318                 src_buf   += seg_sz;
319                 seg_pos++;
320         } while (remaining > 0);
321         TRACE_EXIT size;
322 }
323
324
325 /* zft_erase().  implemented compression-handling
326  *
327  * calculate the first data-segment when using/not using compression.
328  *
329  * update header-segment and compression-map-segment.
330  */
331 int zft_erase(void)
332 {
333         int result = 0;
334         TRACE_FUN(ft_t_flow);
335         
336         if (!zft_header_read) {
337                 TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf,
338                                              FT_SEGMENT_SIZE),);
339                 /* no need to read the vtbl and compression map */
340                 TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),);
341                 if ((zft_old_ftape = 
342                      zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) {
343                         zft_ftape_extract_file_marks(zft_hseg_buf);
344                 }
345                 TRACE(ft_t_noise,
346                       "ft_first_data_segment: %d, ft_last_data_segment: %d", 
347                       ft_first_data_segment, ft_last_data_segment);
348                 zft_qic113 = (ft_format_code != fmt_normal &&
349                               ft_format_code != fmt_1100ft &&
350                               ft_format_code != fmt_425ft);
351         }
352         if (zft_old_ftape) {
353                 zft_clear_ftape_file_marks();
354                 zft_old_ftape = 0; /* no longer old ftape */
355         }
356         PUT2(zft_hseg_buf, FT_CMAP_START, 0);
357         zft_volume_table_changed = 1;
358         zft_capacity = zft_get_capacity();
359         zft_init_vtbl();
360         /* the rest must be done in ftape_update_header_segments 
361          */
362         zft_header_read = 1;
363         zft_header_changed = 1; /* force update of timestamp */
364         result = zft_update_header_segments();
365
366         ftape_abort_operation();
367
368         zft_reset_position(&zft_pos);
369         zft_set_flags (zft_unit);
370         TRACE_EXIT result;
371 }
372
373 unsigned int zft_get_time(void) 
374 {
375         unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */
376         return date;
377 }