diff options
Diffstat (limited to 'drivers/char/ftape')
62 files changed, 19417 insertions, 0 deletions
diff --git a/drivers/char/ftape/Kconfig b/drivers/char/ftape/Kconfig new file mode 100644 index 000000000000..7d3ecb56a1bd --- /dev/null +++ b/drivers/char/ftape/Kconfig | |||
@@ -0,0 +1,340 @@ | |||
1 | # | ||
2 | # Ftape configuration | ||
3 | # | ||
4 | config ZFTAPE | ||
5 | tristate "Zftape, the VFS interface" | ||
6 | depends on FTAPE | ||
7 | ---help--- | ||
8 | Normally, you want to say Y or M. DON'T say N here or you | ||
9 | WON'T BE ABLE TO USE YOUR FLOPPY TAPE DRIVE. | ||
10 | |||
11 | The ftape module itself no longer contains the routines necessary | ||
12 | to interface with the kernel VFS layer (i.e. to actually write data | ||
13 | to and read data from the tape drive). Instead the file system | ||
14 | interface (i.e. the hardware independent part of the driver) has | ||
15 | been moved to a separate module. | ||
16 | |||
17 | To compile this driver as a module, choose M here: the | ||
18 | module will be called zftape. | ||
19 | |||
20 | Regardless of whether you say Y or M here, an additional runtime | ||
21 | loadable module called `zft-compressor' which contains code to | ||
22 | support user transparent on-the-fly compression based on Ross | ||
23 | William's lzrw3 algorithm will be produced. If you have enabled the | ||
24 | kernel module loader (i.e. have said Y to "Kernel module loader | ||
25 | support", above) then `zft-compressor' will be loaded | ||
26 | automatically by zftape when needed. | ||
27 | |||
28 | Despite its name, zftape does NOT use compression by default. The | ||
29 | file <file:Documentation/ftape.txt> contains a short description of | ||
30 | the most important changes in the file system interface compared to | ||
31 | previous versions of ftape. The ftape home page | ||
32 | <http://www.instmath.rwth-aachen.de/~heine/ftape/> contains | ||
33 | further information. | ||
34 | |||
35 | IMPORTANT NOTE: zftape can read archives created by previous | ||
36 | versions of ftape and provide file mark support (i.e. fast skipping | ||
37 | between tape archives) but previous version of ftape will lack file | ||
38 | mark support when reading archives produced by zftape. | ||
39 | |||
40 | config ZFT_DFLT_BLK_SZ | ||
41 | int "Default block size" | ||
42 | depends on ZFTAPE | ||
43 | default "10240" | ||
44 | ---help--- | ||
45 | If unsure leave this at its default value, i.e. 10240. Note that | ||
46 | you specify only the default block size here. The block size can be | ||
47 | changed at run time using the MTSETBLK tape operation with the | ||
48 | MTIOCTOP ioctl (i.e. with "mt -f /dev/qft0 setblk #BLKSZ" from the | ||
49 | shell command line). | ||
50 | |||
51 | The probably most striking difference between zftape and previous | ||
52 | versions of ftape is the fact that all data must be written or read | ||
53 | in multiples of a fixed block size. The block size defaults to | ||
54 | 10240 which is what GNU tar uses. The values for the block size | ||
55 | should be either 1 or multiples of 1024 up to a maximum value of | ||
56 | 63488 (i.e. 62 K). If you specify `1' then zftape's builtin | ||
57 | compression will be disabled. | ||
58 | |||
59 | Reasonable values are `10240' (GNU tar's default block size), | ||
60 | `5120' (afio's default block size), `32768' (default block size some | ||
61 | backup programs assume for SCSI tape drives) or `1' (no restriction | ||
62 | on block size, but disables builtin compression). | ||
63 | |||
64 | comment "The compressor will be built as a module only!" | ||
65 | depends on FTAPE && ZFTAPE | ||
66 | |||
67 | config ZFT_COMPRESSOR | ||
68 | tristate | ||
69 | depends on FTAPE!=n && ZFTAPE!=n | ||
70 | default m | ||
71 | |||
72 | config FT_NR_BUFFERS | ||
73 | int "Number of ftape buffers (EXPERIMENTAL)" | ||
74 | depends on FTAPE && EXPERIMENTAL | ||
75 | default "3" | ||
76 | help | ||
77 | Please leave this at `3' unless you REALLY know what you are doing. | ||
78 | It is not necessary to change this value. Values below 3 make the | ||
79 | proper use of ftape impossible, values greater than 3 are a waste of | ||
80 | memory. You can change the amount of DMA memory used by ftape at | ||
81 | runtime with "mt -f /dev/qft0 setdrvbuffer #NUMBUFFERS". Each buffer | ||
82 | wastes 32 KB of memory. Please note that this memory cannot be | ||
83 | swapped out. | ||
84 | |||
85 | config FT_PROC_FS | ||
86 | bool "Enable procfs status report (+2kb)" | ||
87 | depends on FTAPE && PROC_FS | ||
88 | ---help--- | ||
89 | Optional. Saying Y will result in creation of a directory | ||
90 | `/proc/ftape' under the /proc file system. The files can be viewed | ||
91 | with your favorite pager (i.e. use "more /proc/ftape/history" or | ||
92 | "less /proc/ftape/history" or simply "cat /proc/ftape/history"). The | ||
93 | file will contain some status information about the inserted | ||
94 | cartridge, the kernel driver, your tape drive, the floppy disk | ||
95 | controller and the error history for the most recent use of the | ||
96 | kernel driver. Saying Y will enlarge the size of the ftape driver | ||
97 | by approximately 2 KB. | ||
98 | |||
99 | WARNING: When compiling ftape as a module (i.e. saying M to "Floppy | ||
100 | tape drive") it is dangerous to use ftape's /proc file system | ||
101 | interface. Accessing `/proc/ftape' while the module is unloaded will | ||
102 | result in a kernel Oops. This cannot be fixed from inside ftape. | ||
103 | |||
104 | choice | ||
105 | prompt "Debugging output" | ||
106 | depends on FTAPE | ||
107 | default FT_NORMAL_DEBUG | ||
108 | |||
109 | config FT_NORMAL_DEBUG | ||
110 | bool "Normal" | ||
111 | ---help--- | ||
112 | This option controls the amount of debugging output the ftape driver | ||
113 | is ABLE to produce; it does not increase or diminish the debugging | ||
114 | level itself. If unsure, leave this at its default setting, | ||
115 | i.e. choose "Normal". | ||
116 | |||
117 | Ftape can print lots of debugging messages to the system console | ||
118 | resp. kernel log files. Reducing the amount of possible debugging | ||
119 | output reduces the size of the kernel module by some KB, so it might | ||
120 | be a good idea to use "None" for emergency boot floppies. | ||
121 | |||
122 | If you want to save memory then the following strategy is | ||
123 | recommended: leave this option at its default setting "Normal" until | ||
124 | you know that the driver works as expected, afterwards reconfigure | ||
125 | the kernel, this time specifying "Reduced" or "None" and recompile | ||
126 | and install the kernel as usual. Note that choosing "Excessive" | ||
127 | debugging output does not increase the amount of debugging output | ||
128 | printed to the console but only makes it possible to produce | ||
129 | "Excessive" debugging output. | ||
130 | |||
131 | Please read <file:Documentation/ftape.txt> for a short description | ||
132 | how to control the amount of debugging output. | ||
133 | |||
134 | config FT_FULL_DEBUG | ||
135 | bool "Excessive" | ||
136 | help | ||
137 | Extremely verbose output for driver debugging purposes. | ||
138 | |||
139 | config FT_NO_TRACE | ||
140 | bool "Reduced" | ||
141 | help | ||
142 | Reduced tape driver debugging output. | ||
143 | |||
144 | config FT_NO_TRACE_AT_ALL | ||
145 | bool "None" | ||
146 | help | ||
147 | Suppress all debugging output from the tape drive. | ||
148 | |||
149 | endchoice | ||
150 | |||
151 | comment "Hardware configuration" | ||
152 | depends on FTAPE | ||
153 | |||
154 | choice | ||
155 | prompt "Floppy tape controllers" | ||
156 | depends on FTAPE | ||
157 | default FT_STD_FDC | ||
158 | |||
159 | config FT_STD_FDC | ||
160 | bool "Standard" | ||
161 | ---help--- | ||
162 | Only change this setting if you have a special controller. If you | ||
163 | didn't plug any add-on card into your computer system but just | ||
164 | plugged the floppy tape cable into the already existing floppy drive | ||
165 | controller then you don't want to change the default setting, | ||
166 | i.e. choose "Standard". | ||
167 | |||
168 | Choose "MACH-2" if you have a Mountain Mach-2 controller. | ||
169 | Choose "FC-10/FC-20" if you have a Colorado FC-10 or FC-20 | ||
170 | controller. | ||
171 | Choose "Alt/82078" if you have another controller that is located at | ||
172 | an IO base address different from the standard floppy drive | ||
173 | controller's base address of `0x3f0', or uses an IRQ (interrupt) | ||
174 | channel different from `6', or a DMA channel different from | ||
175 | `2'. This is necessary for any controller card that is based on | ||
176 | Intel's 82078 FDC such as Seagate's, Exabyte's and Iomega's "high | ||
177 | speed" controllers. | ||
178 | |||
179 | If you choose something other than "Standard" then please make | ||
180 | sure that the settings for the IO base address and the IRQ and DMA | ||
181 | channel in the configuration menus below are correct. Use the manual | ||
182 | of your tape drive to determine the correct settings! | ||
183 | |||
184 | If you are already successfully using your tape drive with another | ||
185 | operating system then you definitely should use the same settings | ||
186 | for the IO base, the IRQ and DMA channel that have proven to work | ||
187 | with that other OS. | ||
188 | |||
189 | Note that this menu lets you specify only the default setting for | ||
190 | the hardware setup. The hardware configuration can be changed at | ||
191 | boot time (when ftape is compiled into the kernel, i.e. if you | ||
192 | have said Y to "Floppy tape drive") or module load time (i.e. if you | ||
193 | have said M to "Floppy tape drive"). | ||
194 | |||
195 | Please read also the file <file:Documentation/ftape.txt> which | ||
196 | contains a short description of the parameters that can be set at | ||
197 | boot or load time. If you want to use your floppy tape drive on a | ||
198 | PCI-bus based system, please read the file | ||
199 | <file:drivers/char/ftape/README.PCI>. | ||
200 | |||
201 | config FT_MACH2 | ||
202 | bool "MACH-2" | ||
203 | |||
204 | config FT_PROBE_FC10 | ||
205 | bool "FC-10/FC-20" | ||
206 | |||
207 | config FT_ALT_FDC | ||
208 | bool "Alt/82078" | ||
209 | |||
210 | endchoice | ||
211 | |||
212 | comment "Consult the manuals of your tape drive for the correct settings!" | ||
213 | depends on FTAPE && !FT_STD_FDC | ||
214 | |||
215 | config FT_FDC_BASE | ||
216 | hex "IO base of the floppy disk controller" | ||
217 | depends on FTAPE && !FT_STD_FDC | ||
218 | default "0" | ||
219 | ---help--- | ||
220 | You don't need to specify a value if the following default | ||
221 | settings for the base IO address are correct: | ||
222 | <<< MACH-2 : 0x1E0 >>> | ||
223 | <<< FC-10/FC-20: 0x180 >>> | ||
224 | <<< Secondary : 0x370 >>> | ||
225 | Secondary refers to a secondary FDC controller like the "high speed" | ||
226 | controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. | ||
227 | Please make sure that the setting for the IO base address | ||
228 | specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR | ||
229 | CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already | ||
230 | successfully using the tape drive with another operating system then | ||
231 | you definitely should use the same settings for the IO base that has | ||
232 | proven to work with that other OS. | ||
233 | |||
234 | Note that this menu lets you specify only the default setting for | ||
235 | the IO base. The hardware configuration can be changed at boot time | ||
236 | (when ftape is compiled into the kernel, i.e. if you specified Y to | ||
237 | "Floppy tape drive") or module load time (i.e. if you have said M to | ||
238 | "Floppy tape drive"). | ||
239 | |||
240 | Please read also the file <file:Documentation/ftape.txt> which | ||
241 | contains a short description of the parameters that can be set at | ||
242 | boot or load time. | ||
243 | |||
244 | config FT_FDC_IRQ | ||
245 | int "IRQ channel of the floppy disk controller" | ||
246 | depends on FTAPE && !FT_STD_FDC | ||
247 | default "0" | ||
248 | ---help--- | ||
249 | You don't need to specify a value if the following default | ||
250 | settings for the interrupt channel are correct: | ||
251 | <<< MACH-2 : 6 >>> | ||
252 | <<< FC-10/FC-20: 9 >>> | ||
253 | <<< Secondary : 6 >>> | ||
254 | Secondary refers to secondary a FDC controller like the "high speed" | ||
255 | controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. | ||
256 | Please make sure that the setting for the IO base address | ||
257 | specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR | ||
258 | CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already | ||
259 | successfully using the tape drive with another operating system then | ||
260 | you definitely should use the same settings for the IO base that has | ||
261 | proven to work with that other OS. | ||
262 | |||
263 | Note that this menu lets you specify only the default setting for | ||
264 | the IRQ channel. The hardware configuration can be changed at boot | ||
265 | time (when ftape is compiled into the kernel, i.e. if you said Y to | ||
266 | "Floppy tape drive") or module load time (i.e. if you said M to | ||
267 | "Floppy tape drive"). | ||
268 | |||
269 | Please read also the file <file:Documentation/ftape.txt> which | ||
270 | contains a short description of the parameters that can be set at | ||
271 | boot or load time. | ||
272 | |||
273 | config FT_FDC_DMA | ||
274 | int "DMA channel of the floppy disk controller" | ||
275 | depends on FTAPE && !FT_STD_FDC | ||
276 | default "0" | ||
277 | ---help--- | ||
278 | You don't need to specify a value if the following default | ||
279 | settings for the DMA channel are correct: | ||
280 | <<< MACH-2 : 2 >>> | ||
281 | <<< FC-10/FC-20: 3 >>> | ||
282 | <<< Secondary : 2 >>> | ||
283 | Secondary refers to a secondary FDC controller like the "high speed" | ||
284 | controllers delivered by Seagate or Exabyte or Iomega's Ditto Dash. | ||
285 | Please make sure that the setting for the IO base address | ||
286 | specified here is correct. USE THE MANUAL OF YOUR TAPE DRIVE OR | ||
287 | CONTROLLER CARD TO DETERMINE THE CORRECT SETTING. If you are already | ||
288 | successfully using the tape drive with another operating system then | ||
289 | you definitely should use the same settings for the IO base that has | ||
290 | proven to work with that other OS. | ||
291 | |||
292 | Note that this menu lets you specify only the default setting for | ||
293 | the DMA channel. The hardware configuration can be changed at boot | ||
294 | time (when ftape is compiled into the kernel, i.e. if you said Y to | ||
295 | "Floppy tape drive") or module load time (i.e. if you said M to | ||
296 | "Floppy tape drive"). | ||
297 | |||
298 | Please read also the file <file:Documentation/ftape.txt> which | ||
299 | contains a short description of the parameters that can be set at | ||
300 | boot or load time. | ||
301 | |||
302 | config FT_FDC_THR | ||
303 | int "Default FIFO threshold (EXPERIMENTAL)" | ||
304 | depends on FTAPE && EXPERIMENTAL | ||
305 | default "8" | ||
306 | help | ||
307 | Set the FIFO threshold of the FDC. If this is higher the DMA | ||
308 | controller may serve the FDC after a higher latency time. If this is | ||
309 | lower, fewer DMA transfers occur leading to less bus contention. | ||
310 | You may try to tune this if ftape annoys you with "reduced data | ||
311 | rate because of excessive overrun errors" messages. However, this | ||
312 | doesn't seem to have too much effect. | ||
313 | |||
314 | If unsure, don't touch the initial value, i.e. leave it at "8". | ||
315 | |||
316 | config FT_FDC_MAX_RATE | ||
317 | int "Maximal data rate to use (EXPERIMENTAL)" | ||
318 | depends on FTAPE && EXPERIMENTAL | ||
319 | default "2000" | ||
320 | ---help--- | ||
321 | With some motherboard/FDC combinations ftape will not be able to | ||
322 | run your FDC/tape drive combination at the highest available | ||
323 | speed. If this is the case you'll encounter "reduced data rate | ||
324 | because of excessive overrun errors" messages and lots of retries | ||
325 | before ftape finally decides to reduce the data rate. | ||
326 | |||
327 | In this case it might be desirable to tell ftape beforehand that | ||
328 | it need not try to run the tape drive at the highest available | ||
329 | speed. If unsure, leave this disabled, i.e. leave it at 2000 | ||
330 | bits/sec. | ||
331 | |||
332 | config FT_ALPHA_CLOCK | ||
333 | int "CPU clock frequency of your DEC Alpha" if ALPHA | ||
334 | depends on FTAPE | ||
335 | default "0" | ||
336 | help | ||
337 | On some DEC Alpha machines the CPU clock frequency cannot be | ||
338 | determined automatically, so you need to specify it here ONLY if | ||
339 | running a DEC Alpha, otherwise this setting has no effect. | ||
340 | |||
diff --git a/drivers/char/ftape/Makefile b/drivers/char/ftape/Makefile new file mode 100644 index 000000000000..0e67d2f8b7ec --- /dev/null +++ b/drivers/char/ftape/Makefile | |||
@@ -0,0 +1,28 @@ | |||
1 | # | ||
2 | # Copyright (C) 1997 Claus 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 | # $Source: /homes/cvs/ftape-stacked/ftape/Makefile,v $ | ||
19 | # $Revision: 1.4 $ | ||
20 | # $Date: 1997/10/05 19:17:56 $ | ||
21 | # | ||
22 | # Makefile for the QIC-40/80/3010/3020 floppy-tape driver for | ||
23 | # Linux. | ||
24 | # | ||
25 | |||
26 | obj-$(CONFIG_FTAPE) += lowlevel/ | ||
27 | obj-$(CONFIG_ZFTAPE) += zftape/ | ||
28 | obj-$(CONFIG_ZFT_COMPRESSOR) += compressor/ | ||
diff --git a/drivers/char/ftape/README.PCI b/drivers/char/ftape/README.PCI new file mode 100644 index 000000000000..18de159d36e0 --- /dev/null +++ b/drivers/char/ftape/README.PCI | |||
@@ -0,0 +1,81 @@ | |||
1 | Some notes for ftape users with PCI motherboards: | ||
2 | ================================================= | ||
3 | |||
4 | The problem: | ||
5 | ------------ | ||
6 | |||
7 | There have been some problem reports from people using PCI-bus based | ||
8 | systems getting overrun errors. | ||
9 | I wasn't able to reproduce these until I ran ftape on a Intel Plato | ||
10 | (Premiere PCI II) motherboard with bios version 1.00.08AX1. | ||
11 | It turned out that if GAT (Guaranteed Access Timing) is enabled (?) | ||
12 | ftape gets a lot of overrun errors. | ||
13 | The problem disappears when disabling GAT in the bios. | ||
14 | Note that Intel removed this setting (permanently disabled) from the | ||
15 | 1.00.10AX1 bios ! | ||
16 | |||
17 | It looks like that if GAT is enabled there are often large periods | ||
18 | (greater than 120 us !??) on the ISA bus that the DMA controller cannot | ||
19 | service the floppy disk controller. | ||
20 | I cannot imagine this being acceptable in a decent PCI implementation. | ||
21 | Maybe this is a `feature' of the chipset. I can only speculate why | ||
22 | Intel choose to remove the option from the latest Bios... | ||
23 | |||
24 | The lesson of this all is that there may be other motherboard | ||
25 | implementations having the same of similar problems. | ||
26 | If you experience a lot of overrun errors during a backup to tape, | ||
27 | see if there is some setting in the Bios that may influence the | ||
28 | bus timing. | ||
29 | |||
30 | I judge this a hardware problem and not a limitation of ftape ;-) | ||
31 | My DOS backup software seems to be suffering from the same problems | ||
32 | and even refuses to run at 1 Mbps ! | ||
33 | Ftape will reduce the data-rate from 1 Mbps to 500 Kbps if the number | ||
34 | of overrun errors on a track exceeds a threshold. | ||
35 | |||
36 | |||
37 | Possible solutions: | ||
38 | ------------------- | ||
39 | |||
40 | Some of the problems were solved by upgrading the (flash) bios. | ||
41 | Other suggest that it has to do with the FDC being on the PCI | ||
42 | bus, but that is not the case with the Intel Premiere II boards. | ||
43 | [If upgrading the bios doesn't solve the problem you could try | ||
44 | a floppy disk controller on the isa-bus]. | ||
45 | |||
46 | Here is a list of systems and recommended BIOS settings: | ||
47 | |||
48 | |||
49 | Intel Premiere PCI (Revenge): | ||
50 | |||
51 | Bios version 1.00.09.AF2 is reported to work. | ||
52 | |||
53 | |||
54 | |||
55 | Intel Premiere PCI II (Plato): | ||
56 | |||
57 | Bios version 1.00.10.AX1 and version 11 beta are ok. | ||
58 | If using version 1.00.08.AX1, GAT must be disabled ! | ||
59 | |||
60 | |||
61 | |||
62 | ASUS PCI/I-SP3G: | ||
63 | |||
64 | Preferred settings: ISA-GAT-mode : disabled | ||
65 | DMA-linebuffer-mode : standard | ||
66 | ISA-masterbuffer-mode : standard | ||
67 | |||
68 | |||
69 | DELL Dimension XPS P90 | ||
70 | |||
71 | Bios version A2 is reported to be broken, while bios version A5 works. | ||
72 | You can get a flash bios upgrade from http://www.dell.com | ||
73 | |||
74 | |||
75 | To see if you're having the GAT problem, try making a backup | ||
76 | under DOS. If it's very slow and often repositions you're | ||
77 | probably having this problem. | ||
78 | |||
79 | --//-- | ||
80 | LocalWords: ftape PCI bios GAT ISA DMA chipset Mbps Kbps FDC isa AF ok ASUS | ||
81 | LocalWords: SP linebuffer masterbuffer XPS http www com | ||
diff --git a/drivers/char/ftape/RELEASE-NOTES b/drivers/char/ftape/RELEASE-NOTES new file mode 100644 index 000000000000..03799dbc05a4 --- /dev/null +++ b/drivers/char/ftape/RELEASE-NOTES | |||
@@ -0,0 +1,966 @@ | |||
1 | Hey, Emacs, we're -*-Text-*- mode! | ||
2 | |||
3 | ===== Release notes for ftape-3.04d 25/11/97 ===== | ||
4 | - The correct pre-processor statement for "else if" is "#elif" not | ||
5 | "elsif". | ||
6 | - Need to call zft_reset_position() when overwriting cartridges | ||
7 | previously written with ftape-2.x, sftape, or ancient | ||
8 | (pre-ftape-3.x) versions of zftape. | ||
9 | |||
10 | ===== Release notes for ftape-3.04c 16/11/97 ===== | ||
11 | - fdc_probe() was calling DUMPREGS with a result length of "1" which | ||
12 | was just fine. Undo previous change. | ||
13 | |||
14 | ===== Release notes for ftape-3.04b 14/11/97 ===== | ||
15 | |||
16 | - patches/2.x.x/floppy.c.diff was somewhat broken, releasing i/o | ||
17 | regions it never had allocated. | ||
18 | - fdc_probe() was calling DUMPREGS with a result length of "1" instead | ||
19 | of "10" | ||
20 | - Writing deleted data marks if the first segents on track zero are | ||
21 | should work now. | ||
22 | - ftformat should now be able to handle those cases where the tape | ||
23 | drive sets the read only status bit (QIC-40/80 cartridges with | ||
24 | QIC-3010/3020 tape drives) because the header segment is damaged. | ||
25 | - the MTIOCFTCMD ioctl may now be issued by the superuser ONLY. | ||
26 | |||
27 | ===== Release notes for ftape-3.04a 12/11/97 ===== | ||
28 | - Fix an "infinite loop can't be killed by signal" bug in | ||
29 | ftape_get_drive_status(). Only relevant when trying to access | ||
30 | buggy/misconfigured hardware | ||
31 | - Try to compensate a bug in the HP Colorado T3000's firmware: it | ||
32 | doesn't set the write protect bit for QIC80/QIC40 cartridges. | ||
33 | |||
34 | ===== Release notes for ftape-3.04 06/11/97 ===== | ||
35 | - If positioning with fast seeking fails fall back to a slow seek | ||
36 | before giving up. | ||
37 | - (nearly) no retries on "no data errors" when verifying after | ||
38 | formatting. Improved tuning of the bad sector map after formatting. | ||
39 | - the directory layout has changed again to allow for easier kernel | ||
40 | integration | ||
41 | - Module parameter "ftape_tracing" now is called "ft_tracing" because | ||
42 | the "ftape_tracing" variable has the version checksum attached to it. | ||
43 | - `/proc/ftape' interface for 2.0.* kernels. `/proc/ftape' no longer | ||
44 | is a directory but a file that contains all the information formerly | ||
45 | provided in separate files under the `/proc/ftape/' directory. | ||
46 | - Most of the configuration options have been prefixed by "CONFIG_FT_" | ||
47 | in preparation of the kernel inclusion. The Makefiles under | ||
48 | "./ftape/" should be directly usable by the kernel. | ||
49 | - The MODVERSIONS stuff is now auto-detected. | ||
50 | - Broke backslashed multi line options in MCONFIG into separate lines | ||
51 | using GNU-make's "+=" feature. | ||
52 | - The html and dvi version of the manual is now installed under | ||
53 | '/usr/doc/ftape` with 'make install` | ||
54 | - New SMP define in MCONFIG. ftape works with SMP if this is defined. | ||
55 | - attempt to cope with "excessive overrun errors" by gradually | ||
56 | increasing FDC FIFO threshold. But this doesn't seem to have too | ||
57 | much an effect. | ||
58 | - New load time configuration parameter "ft_fdc_rate_limit". If you | ||
59 | encounter too many overrun errors with a 2Mb controller then you | ||
60 | might want to set this to 1000. | ||
61 | - overrun errors on the last sector in a segment sometimes result in | ||
62 | a zero DMA residue. Dunno why, but compensate for it. | ||
63 | - there were still fdc_read() timeout errors. I think I have fixed it | ||
64 | now, please FIXME. | ||
65 | - Sometimes ftape_write() failed to re-start the tape drive when a | ||
66 | segment without a good sector was reached ("wait for empty segment | ||
67 | failed"). This is fixed. Especially important for > QIC-3010. | ||
68 | - sftape (aka ftape-2.x) has vanished. I didn't work on it for | ||
69 | ages. It is probably still possible to use the old code with | ||
70 | ftape-3.04, if one really needs it (BUT RECOMPILE IT) | ||
71 | - zftape no longer alters the contents of already existing volume | ||
72 | table entries, which makes it possible to fill in missing fields, | ||
73 | like time stamps using some user space program. | ||
74 | - ./contrib/vtblc/ contains such a program. | ||
75 | - new perl script ./contrib/scripts/listtape that list the contents of a | ||
76 | floppy tape cartridge parsing the output of "mt volinfo" + "mt fsf" | ||
77 | - the MTWEOF implementation has changed a little bit (after I had a | ||
78 | look at amanda). Calling MTWEOF while the tape is still held open | ||
79 | after writing something to the tape now will terminate the current | ||
80 | volume, and start a new one at the current position. | ||
81 | - the volume table maintained by zftape now is a doubly linked list | ||
82 | that grows dynamically as needed. | ||
83 | |||
84 | formatting floppy tape cartridges | ||
85 | --------------------------------- | ||
86 | * there is a new user space formatting program that does most of the | ||
87 | dirty work in user space (auto-detect, computing the sector | ||
88 | coordinates, adjusting time stamps and statistics). It has a | ||
89 | simple command line interface. | ||
90 | * ftape-format.o has vanished, it has been folded into the low level | ||
91 | ftape.o module, and the ioctl interface into zftape.o. Most of the | ||
92 | complicated stuff has been moved to user space, so there was no | ||
93 | need for a separate module anymore. | ||
94 | * there is a new ioctl MTIOCFTCMD that sends a bare QIC-117 command | ||
95 | to the tape drive. | ||
96 | * there is a new mmap() feature to map the dma buffers into user | ||
97 | space to be used by the user level formatting program. | ||
98 | * Formatting of yet unformatted or totally degaussed cartridges | ||
99 | should be possible now. FIXME. | ||
100 | |||
101 | ===== Release notes for ftape-3.03b, <forgot the exact date> ==== | ||
102 | |||
103 | ftape-3.03b was released as a beta release only. Its main new feature | ||
104 | was support of the DITTO-2GB drive. This was made possible by reverse | ||
105 | engineering done by <fill in his name> after Iomega failed to support | ||
106 | ftape. Although they had promised to do so (this makes me feel a bit | ||
107 | sad and uncomfortable about Iomega). | ||
108 | |||
109 | ===== Release notes for ftape-3.03a, 22/05/97 ==== | ||
110 | |||
111 | - Finally fixed auto-un-loading of modules for kernels > 2.1.18 | ||
112 | - Add an "uninstall" target to the Makefile | ||
113 | - removed the kdtime hack | ||
114 | - texi2www didn't properly set the back-reference from a footnote back | ||
115 | to the regular text. | ||
116 | |||
117 | zftape specific | ||
118 | --------------- | ||
119 | * hide the old compression map volume. Taper doesn't accept the | ||
120 | presence of non-Taper volumes and Taper-written volume on the same | ||
121 | tape. | ||
122 | * EOD (End Of Data) handling was still broken: the expected behavior | ||
123 | is to return a zero byte count at the first attempt to read past | ||
124 | EOD, return a zero byte count at the second attempt to read past | ||
125 | EOD and THEN return -EIO. | ||
126 | |||
127 | ftape-format specific | ||
128 | --------------------- | ||
129 | * Detection of QIC-40 cartridges in select_tape_format() was broken | ||
130 | and made it impossible to format QIC-3010/3020 cartridges. | ||
131 | * There are strange "TR-1 Extra" cartridges out there which weren't | ||
132 | detected properly because the don't strictly conform to the | ||
133 | QIC-80, Rev. N, spec. | ||
134 | |||
135 | ===== Release notes for ftape-3.03, 30/04/97 ===== | ||
136 | |||
137 | - Removed kernel integration code from the package. I plan to provide | ||
138 | a package that can be integrated into the stock kernel separately | ||
139 | (hopefully soon). | ||
140 | As a result, a simple `make' command now will build everything. | ||
141 | - ALL compile time configuration options have been moved to the file | ||
142 | `MCONFIG'. | ||
143 | - Quite a few `low level' changes to allow formatting of cartridges. | ||
144 | - formatting is implemented as a separate module `ftape-format.o'. The | ||
145 | modified `mt' program contains sample code that shows how to use it. | ||
146 | - The VFS interface has been moved from the `ftape.o' module to the | ||
147 | high level modules `zftape.o' resp. `sftape.o'. `ftape.o' contains | ||
148 | the hardware support only. | ||
149 | - A bit of /proc support for kernels > 2.1.28 | ||
150 | - Moved documentation to Doc subdir. INSTALL now contains some real | ||
151 | installation notes. | ||
152 | - `install' target in Makefile. | ||
153 | |||
154 | zftape specific: | ||
155 | ---------------- | ||
156 | |||
157 | - zftape works for large cartridges now ( > 2^31 bytes) | ||
158 | - MTIOCVOLINFO and MTIOCGETSIZE now return the size in KILOBYTES, | ||
159 | NO LONGER in bytes. | ||
160 | |||
161 | - permissions for write access to a cartridge have changed: | ||
162 | * zftape now also takes the file access mode into account | ||
163 | * zftape no longer allows writing in the middle of the recorded | ||
164 | media. The tape has to be positioned at BOT or EOD for write | ||
165 | access. | ||
166 | |||
167 | - MTBSF has changed. It used to position at the beginning of the | ||
168 | previous file when called with count 1. This was different from the | ||
169 | expected behavior for other Un*x tape drivers (i.e. SCSI). MTBSF | ||
170 | with count 1 should merely position at the beginning of the current | ||
171 | volume. Fixed. As a result, `tar --verify' now produces the desired | ||
172 | result: it verifies the last written volume, not the pre-last | ||
173 | written volume. | ||
174 | |||
175 | - The compression map has vanished --> no need for `mt erase' any | ||
176 | more. Fast seeking in a compressed volume is still be possible, but | ||
177 | takes slightly longer. As a side effect, you may experience an | ||
178 | additional volume showing up in front of all others for old | ||
179 | cartridges. This is the tape volume that holds the compression map. | ||
180 | |||
181 | - The compression support for zftape has been moved to a separate | ||
182 | module `zft-compressor'. DON'T forget to load it before trying to | ||
183 | read back compressed volumes. The stock `zftape.o' module probes for | ||
184 | the module `zft-compressor' using the kerneld message channel; you | ||
185 | have to install `zft-compressor.o' in a place where modprobe can | ||
186 | find it if you want to use this. | ||
187 | |||
188 | - New experimental feature that tries to get the broken down GMT time | ||
189 | from user space via a kernel daemon message channel. You need to | ||
190 | compile and start the `kdtime' daemon contained in the contrib | ||
191 | directory to use it. Needed (?) for time stamps in the header | ||
192 | segments and the volume table. | ||
193 | |||
194 | - variable block size mode via MTSETBLK 0 | ||
195 | |||
196 | - keep modules locked in memory after the block size has been changed | ||
197 | |||
198 | sftape specific: | ||
199 | ---------------- | ||
200 | |||
201 | - end of tape handling should be fixed, i.e. multi volume archives | ||
202 | written with `afio' can be read back now. | ||
203 | |||
204 | |||
205 | ===== Release notes for ftape-3.02a, 09/01/97 ===== | ||
206 | |||
207 | No big news: | ||
208 | - call zft_init() resp. sft_init() when compiling the entire stuff | ||
209 | into the kernel image. | ||
210 | - fix bug in ftape-setup.c when NO_TRACE_AT_ALL was defined. | ||
211 | - fix bug in sftape-eof.c/zftape-eof.c for old kernels (1.2.*) | ||
212 | - add support for new module interface for recent kernels | ||
213 | |||
214 | ===== Release notes for ftape-3.02, 16/12/96 ===== | ||
215 | - Fixed the `FDC unlock command failed' bug in fdc-io.c. When the FIFO | ||
216 | was already locked when ftape was loaded, ftape failed to unlock it. | ||
217 | - Fixed compilation of `contrib/gnumt'. It now finds `mtio.h' even if | ||
218 | ftape is NOT included into the kernel source tree. | ||
219 | - fc-10.c: include <asm/io.h> for inb() and outb(). | ||
220 | - ftape/sftape/zftape: all global variable now have either a `ftape_', | ||
221 | a `ft_', `sft_', `zft_' or `qic_' prefix to prevent name clashes | ||
222 | with other parts of the kernel when including ftape into the kernel | ||
223 | source tree. | ||
224 | - Kerneld support has changed. `ftape' now searches for a module | ||
225 | `ftape-frontend' when none of the frontend (`sftape' or `zftape') is | ||
226 | loaded. Please refer to the `Installation/Loading ftape' section of | ||
227 | the TeXinfo manual. | ||
228 | - Add load resp. boot-time configuration of ftape. There are now | ||
229 | variables ft_fdc_base, ft_fdc_dma and ft_fdc_irq corresponding to | ||
230 | the former FDC_BASE etc. compile time definitions. One can also use | ||
231 | the kernel command line parameters to configure the driver if it is | ||
232 | compiled into the kernel. Also, the FC-10/FC-20 support is load-time | ||
233 | configurable now as well as the MACH-II hack (ft_probe_fc10, | ||
234 | resp. ft_mach2). Please refer to the section `Installation/Configure | ||
235 | ftape' of the TeXinfo manual. | ||
236 | - I removed the MODVERSIONS option from `Makefile.module'. Let me alone | ||
237 | with ftape and MODVERSIONS unless you include the ftape sources into | ||
238 | the kernel source tree. | ||
239 | - new vendors in `vendors.h': | ||
240 | * HP Colorado T3000 | ||
241 | * ComByte DoublePlay (including a bug fix for their broken | ||
242 | formatting software, thanks to whraven@njackn.com) | ||
243 | * Iomega DITTO 2GIG. NOTE: this drive cannot work with ftape because | ||
244 | the logical data layout of the cartridges used by this drive does | ||
245 | NOT conform to the QIC standards, it is a special Iomega specific | ||
246 | format. I've sent mail to Iomega but didn't receive an answer | ||
247 | yet. If you want this drive to be supported by ftape, ask Iomega | ||
248 | to give me information about it. | ||
249 | - zftape: | ||
250 | * re-introduced the MTIOC_ZFTAPE_GETBLKSZ ioctl for compatibility | ||
251 | with zftape 1.06a and earlier. Please don't use it when writing | ||
252 | new software, use the MTIOCVOLINFO ioctl instead. | ||
253 | * Major overhaul of the code that updates the header segments. Never | ||
254 | change the tape label unless erasing the tape. Thus we almost | ||
255 | never need to write the header segments, unless we would modify | ||
256 | the bad sector map which isn't done yet. Updating of volume table | ||
257 | and compression map more secure now although it takes a bit | ||
258 | longer. | ||
259 | * Fixed bug when aborting a write operation with a signal: zftape | ||
260 | now finishes the current volume (i.e. writes an eof marker) at the | ||
261 | current position. It didn't before which led to somehow *strange* | ||
262 | behavior in this cases. | ||
263 | * Keep module locked in memory when using it with the non-rewinding | ||
264 | devices and the tape is not logical at BOT. Needed for kerneld | ||
265 | support. | ||
266 | - sftape: | ||
267 | * Keep module locked in memory when using it with the non-rewinding | ||
268 | devices and the tape is not logical at BOT. Needed for kerneld | ||
269 | support. | ||
270 | |||
271 | ===== Release notes for ftape-3.01, 14/11/96 ===== | ||
272 | |||
273 | - Fixed silly bugs in ftape-3.00: | ||
274 | * MAKEDEV.ftape: major device number must be 27, not 23 | ||
275 | * sftape/sftape-read.c: sftape_read_header_segments() called | ||
276 | itself recursively instead of calling ftape_read_header_segment() | ||
277 | * zftape/qic-vtbl.h: conversion of ftape's file marks to zftape's | ||
278 | internal volume table was broken. | ||
279 | * patches/2.x.x/linux-2.0.21.dif: my RCS (resp. CVS) system replaced | ||
280 | the `$Revison:' etc. macros in the `ftape.h' concerning part of the | ||
281 | patch :-( Fixed. | ||
282 | * info/ftape.info: Fixed misspellings (`cp' <-> `cp -r' etc.) | ||
283 | * when ftape/sftape or ftape/zftape was compiled into the kernel the | ||
284 | variable ftape_status was declared twice. Fixed. | ||
285 | * removed reference to undeclared variable kernel_version when not | ||
286 | compiling as module | ||
287 | * fixed a bug introduced by the use of bit-fields for some flags | ||
288 | (i.e. write_protected, no_cartridge, formatted) | ||
289 | * flag `header_read' is now reset correctly to zero when tape is | ||
290 | removed. | ||
291 | - fixed a bug in sftape/sftape-eof.c that was already in the original | ||
292 | ftape code. MTFSF/BSF was not handled correctly when positioned | ||
293 | right before the file mark (think of tar) | ||
294 | - Changed TRACE macros (following a suggestion of Marcin Dalecki) to use | ||
295 | the predefined __FUNCTION__ macro of GCC. Spares about 4k of code. | ||
296 | - added new vendor id for Iomega DITTO 2GIG | ||
297 | - fixed a bug already present in zftape-1.06 when aborting a write | ||
298 | with a signal: we now finish the current volume at that | ||
299 | position. Header segments remain NOT up to date until an explicit call | ||
300 | to MTREW or MTOFFL is done. | ||
301 | |||
302 | ===== Release notes for ftape-3.00, 14/10/96 ===== | ||
303 | |||
304 | - Merged ftape with zftape. There are three modules now: | ||
305 | ftape for the hardware support, sftape for the implementation of the | ||
306 | original ftape eof mark stuff and zftape that implements zftape's way | ||
307 | of handling things (compression, volume table, tape blocks of | ||
308 | constant length) | ||
309 | - Documentation in TeXinfo format in the `info' subdirectory. | ||
310 | - New ioctls for zftape. See zftape/zftape.h | ||
311 | - Dummy formatting ioctl for ftape. See ftape.h | ||
312 | - Kernel patch files for the 2.*.* series to include ftape-3.00 in the | ||
313 | kernel source tree. These includes a kernel compatible Config.in | ||
314 | script and fairly large online information for the kernel configure | ||
315 | script. | ||
316 | - Support for compiling with Linux-1.2.13. | ||
317 | - Modified GNU mt from their cpio package that can handle the new | ||
318 | ioctls. | ||
319 | - ftape/sftape/zftape is kerneld save now! | ||
320 | |||
321 | Notes on sftape: | ||
322 | - sftape implements the eof handling code of the original ftape. If | ||
323 | you like to stick with the original ftape stuff, you have to use | ||
324 | this module, not zftape. | ||
325 | - sftape is kerneld save, unlike the original ftape. | ||
326 | - we keep the entire header segment now in memory, so no need to read | ||
327 | it before updating the header segments. Additional memory | ||
328 | consumption: 256 bytes. | ||
329 | |||
330 | Notes for zftape: | ||
331 | - zftape has support for tapes with format code 6 now, which use a | ||
332 | slightly different volume table format compared with other floppy | ||
333 | tapes. | ||
334 | - new ioctls for zftape. Have a look at zftape/zftape.h | ||
335 | - The internal volume table representation has changed for zftape. Old | ||
336 | cartridges are converted automatically. | ||
337 | - zftape no longer uses compression map segments, which have vanished | ||
338 | from the QIC specs, but creates volume table entry that reserves | ||
339 | enough space for the compression map. | ||
340 | - zftape is kerneld save now. | ||
341 | - we keep the entire header segment now in memory, so no need to read | ||
342 | it before updating the header segments. Additional memory | ||
343 | consumption: 256 bytes. | ||
344 | |||
345 | Notes for contrib/gnumt: | ||
346 | - modified mt from the GNU cpio package that supports all the new | ||
347 | ioctls of zftape. | ||
348 | Notes for contrib/swapout: | ||
349 | - This contains the swapout.c program that was written by Kai | ||
350 | Harrekilde-Pederson. I simply added a Makefile. | ||
351 | |||
352 | ===== Release notes for ftape-2.10, 14/10/96 ===== | ||
353 | |||
354 | The ftape maintainer has changed. | ||
355 | Kai Harrekilde-Petersen <khp@dolphinics.no> | ||
356 | has resigned from maintaining ftape, and I, | ||
357 | Claus-Justus Heine <claus@momo.math.rwth-aachen.de>, | ||
358 | have taken over. | ||
359 | |||
360 | - Added support for tapes with `format code 6', i.e. QIC-3020 tapes | ||
361 | with more than 2^16 segments. | ||
362 | - merged changes made by Bas Laarhoven with ftape-2.09. Refer | ||
363 | to his release notes below. I've included them into this | ||
364 | file unchanged for your reference. | ||
365 | - disabled call stack back trace for now. This new feature | ||
366 | introduced by the interim release 2.0.x still seems to | ||
367 | be buggy. | ||
368 | - Tried to minimize differences between the ftape version | ||
369 | to be included into the kernel source tree and the standalone | ||
370 | module version. | ||
371 | - Reintroduced support for Linux-1.2.13. Please refer to the | ||
372 | Install-guide. | ||
373 | |||
374 | ===== Release notes for ftape-2.09, 16/06/96 ===== | ||
375 | |||
376 | There aren't any really big news in this release, mostly just that I | ||
377 | (the maintainer) have changed my email address (due to a new job). My | ||
378 | new address is <khp@dolphinics.no> | ||
379 | |||
380 | - The CLK_48MHZ and FDC_82078SL options has gone (all 2Mbps cards seem | ||
381 | to use a 48MHz oscillator anyway and I haven't heard of an 'SL | ||
382 | chip out there). | ||
383 | - The S82078B has been `downgraded' to i82077AA compability. | ||
384 | - TESTING option revived. Right now, it'll enable the (seriously broken) | ||
385 | 2Mbps code. If you enable it, you'll experience a tape drive that's | ||
386 | *really* out to lunch! | ||
387 | - Some (bold) changes in the init code. Please notify me if they | ||
388 | break things for you. | ||
389 | |||
390 | ===== Release notes for ftape-2.08, 14/03/96 ===== | ||
391 | |||
392 | If you correct a problem with ftape, please send your patch to | ||
393 | khp@dolphinics.no too. | ||
394 | |||
395 | - Updated to reflect that NR_MEM_LISTS is gone in 1.3.74 | ||
396 | - Teac 700 added to list of known drives. | ||
397 | - The registered device name is now "ft" rather than "ftape". | ||
398 | |||
399 | ===== Release notes for ftape-2.07a, 14/03/96 ===== | ||
400 | |||
401 | Bugfixes by Marcin Dalecki <dalecki@namu03.gwdg.de>: | ||
402 | - In the last release it just compiled against 1.3.70; | ||
403 | now the params to request_irq() and free_irq are() are fixed, so it also | ||
404 | works in 1.3.73 :-) | ||
405 | - Support for modules is now correct for newer kernels. | ||
406 | |||
407 | ===== Release notes for ftape-2.07, 04/03/96 ===== | ||
408 | |||
409 | |||
410 | - ftape updated to compile against 1.3.70. | ||
411 | - Iomega 700 and Wangtek 3200 recognised. | ||
412 | |||
413 | |||
414 | ===== Release notes for ftape-2.06b, 13/02/96 ===== | ||
415 | |||
416 | Another simple bugfix version. | ||
417 | |||
418 | - Jumbo 700 recognised. | ||
419 | - Typo in vendors.h fixed. | ||
420 | |||
421 | |||
422 | ===== Release notes for ftape-2.06a, 10/02/96 ===== | ||
423 | |||
424 | This release is a simple bugfix version. | ||
425 | |||
426 | - Linux/SMP: ftape *should* work. | ||
427 | - FC-10/20: Only accepts IRQs 3-7, or 9. If IRQ 9, properly tell the card | ||
428 | to use IRQ 2. Thanks to Greg Crider (gcrider@iclnet.org) for finding and | ||
429 | locating this bug and testing the patch. | ||
430 | - Insight drive recognised correctly again. | ||
431 | - Motor-on wakeup version of the Iomega 250 drive added | ||
432 | |||
433 | |||
434 | ===== Release notes for ftape-2.06, 28/01/96 ===== | ||
435 | |||
436 | Special thanks go to Neal Friedman and Steven Sorbom for their | ||
437 | help in producing and testing this release. | ||
438 | |||
439 | I have continued to clean up the code, with an eye towards inclusion | ||
440 | of ftape in Linus' official kernel (In fact, as I type this, I am | ||
441 | running on a kernel with ftape support statically linked). I have | ||
442 | test-compiled ftape against my 1.2.13 tree without problems. | ||
443 | Hopefully, everything should be OK for the v1.2.x people. | ||
444 | |||
445 | WARNING! Alan Cox has mailed me that ftape does *NOT* work with | ||
446 | Linux/SMP. If you try to run ftape under Linux/SMP, it will cause a | ||
447 | kernel deadlock (which is worse than a panic). | ||
448 | |||
449 | - QIC-3020/TR-3: 1Mbps support works. Neal is capable of reading and | ||
450 | writing data to a tape. ftape will automatically detect the type of | ||
451 | tape (e.g. TR-3 vs QIC-80) and move the fdc in and out of | ||
452 | "perpendicular mode" as necessary. | ||
453 | - 2Mbps support is disabled by default, since it is not fully | ||
454 | debugged. If you are adventurous, remove -DFDC_82078SL in the | ||
455 | Makefile and see what happens :-) | ||
456 | - fdc detection: silly bugs removed (Only 2Mbps fdcs were affected) | ||
457 | and added detection of the National Semiconductors PC8744 fdc chip | ||
458 | (used in the PC873xx "super-IO" chips). | ||
459 | - Removed warning about incompatible types when compiling with Linux | ||
460 | 1.2.x. | ||
461 | - README.PCI updated with info about the DELL Dimension XPS P90. | ||
462 | - Connor TST3200R added to detected drives. | ||
463 | - `swapout' utility added to distribution. It will dirty 5Meg of | ||
464 | memory, trying to swap out other programs. Just say `make swapout' | ||
465 | to build it. ftape will do this automatically Real Soon Now (ie: | ||
466 | when I have found out which kernel memory alloc function to call). | ||
467 | |||
468 | |||
469 | ===== Release notes for ftape-2.05, 08/01/96 ===== | ||
470 | |||
471 | - For v1.2.x Kernels, you must apply the patch linux-1.2/ksyms.patch to | ||
472 | the kernel and rebuild it (it adds the __get_dma_pages symbol to | ||
473 | ksyms.c). | ||
474 | - Included new asm-i386/io.h file from v1.3.x kernel series, to enable | ||
475 | gcc v.2.7.[12] to compile v1.2.x kernels (linux-1.2/io.h). | ||
476 | - Module versions: If you wish to compile ftape as a versioned module, | ||
477 | you must first compile your kernel with CONFIG_MODVERSIONS=y. | ||
478 | Otherwise, you will get complaints that <linux/modversions.h> does not | ||
479 | exist (if that happens, a `touch modversions.h' will help you out). | ||
480 | - CLK_48MHZ: new define in the Makefile (default: non-zero). If you have | ||
481 | a tape controller card that uses the i82078(-1) chip, but cannot get | ||
482 | it to work with ftape, try set it to 0 (and please report this). | ||
483 | - QIC-3010/3020: Complete support is still missing, but will hopefully | ||
484 | come soon. Steven Sorbom has kindly provided me with hints about | ||
485 | this. Writing of QIC-3020 tapes definitely does NOT work (do not try | ||
486 | it! - the drive will not be in "perpendicular mode" and this will ruin | ||
487 | the formatting info on the tape). | ||
488 | - ftape_num_buffers is out of fashion: use NR_BUFFERS instead (and | ||
489 | recompile if you want to change it :-). | ||
490 | |||
491 | |||
492 | ===== Release notes for ftape-2.04, 01/01/96 ===== | ||
493 | |||
494 | This version by Kai Harrekilde-Petersen <khp@dolphinics.no> | ||
495 | |||
496 | - ALERT! Support for Kernels earlier then v1.1.85 is about to go away. | ||
497 | I intend to clean up some of the code (getting rid of an annoyingly | ||
498 | large numbers of #ifdef mostly), which means that support for | ||
499 | pre-1.1.85 kernels must go as well. | ||
500 | - NR_FTAPE_BUFFERS is gone; You can instead select the number of dma | ||
501 | buffers by saying `insmod ftape.o ftape_num_buffer=<n>' instead. | ||
502 | - Configure script gone. ftape will now automagically determine your | ||
503 | kernel version by /usr/include/linux/version.h instead. | ||
504 | - CONFIG_MODVERSIONS now work. All combinations of versioned / | ||
505 | unversioned kernel and ftape module works (at least with my 1.3.52 | ||
506 | kernel). | ||
507 | - If you have problems with inserting ftape into an old (1.2.x) | ||
508 | kernel (e.g. insmod says "1.2.8 does not match 1.2.8), recompile | ||
509 | your modules utilities with your new compiler. | ||
510 | - Reveal TB1400 drive added to vendors.h | ||
511 | - Support for the i82078-1 (2Mbps) chip is coming along. The | ||
512 | biggest problem is that I don't have such a card, which makes | ||
513 | testing / debugging somewhat problematic. The second biggest | ||
514 | problem is that I do not have the QIC-3010/3020 standards either. | ||
515 | Status right now is that the chip is detected, and it should be | ||
516 | possible to put it into 2Mbps mode. However, I do not know what | ||
517 | "extras" are needed to complete the support. Although putting the | ||
518 | i82078 into 1Mbps mode ought to work out of the box, it doesn't | ||
519 | (right now, ftape complains about id am errors). | ||
520 | |||
521 | |||
522 | ===== Release notes for ftape-2.04beta5, 29/12/95 ===== | ||
523 | |||
524 | Bas offline linux-tape | ||
525 | ---------------------- | ||
526 | For reasons only known to the majordomo mail list processor, Bas was | ||
527 | kicked off the linux-tape list sometime during the summer. Being | ||
528 | overworked at his for-pay job, he didn't notice it much. Instead I | ||
529 | (Kai, khp@dolphinics.no) has worked on ftape to produce the 2.04(beta) | ||
530 | version. | ||
531 | |||
532 | zftape | ||
533 | ------ | ||
534 | Note that there exists a much improved version of ftape, written by | ||
535 | Claus-Justus Heine <claus@willi.math.rwth-aachen.de> which is named | ||
536 | zftape, which conforms to the QIC-80 specs on how to mark backups, and | ||
537 | is capable of doing automatic compression. However, zftape makes | ||
538 | substantial changes to ftape, and I (Kai) have therefore declined to | ||
539 | integrate zftape into ftape. Hopefully, this will happen soon. | ||
540 | |||
541 | CONFIG_QIC117 removed from the kernel | ||
542 | ------------------------------------- | ||
543 | The biggest change of all is that ftape now will allocate its dma | ||
544 | buffers when it is inserted. The means that the CONFIG_QIC117 option | ||
545 | has disappeared from the Linux kernel as of v1.3.34. If you have an | ||
546 | earlier kernel, simply answer 'no' to the question will do the trick | ||
547 | (if you get complains about __get_free_pages() missing, contact the | ||
548 | linux-tape mailing list). | ||
549 | |||
550 | Note that ftape-2.04beta will work equally well on kernels with and | ||
551 | without `ftape support'. The only catch is, that you will waste | ||
552 | around 96-128Kb of precious DMA'able memory on a box that has ftape | ||
553 | support compiled in. | ||
554 | |||
555 | Now for the real changes: | ||
556 | |||
557 | - FC-20 can now use DMA channels 1, 2, and 3. Thanks to Daniel | ||
558 | Cohen, catman@wpi.edu. | ||
559 | - ftape no longer requires a (gigantic) 96Kb buffer to be statically | ||
560 | allocated by the kernel. | ||
561 | - Added new Iomega drive (8882) to vendors.h | ||
562 | - -fno-strength-reduce added to Makefile, since GCC is broken. | ||
563 | - i82078-1 (2Mbps) FDC support started. | ||
564 | |||
565 | |||
566 | ===== Release notes for ftape-2.03b, 27/05/95 ===== | ||
567 | |||
568 | - Prevented verify_area to return error if called with zero length. | ||
569 | - Fixed a bug in flush_buffers that caused too much padding to be | ||
570 | written when a final segment had bad sectors. | ||
571 | - Increased maximum fast-seek overshoot value from 5 to 10 segments. | ||
572 | - Breaking loop after 5 retries when positioning fails. | ||
573 | - Fixed wrong calculation of tape length for QIC-3010 and QIC-3020 | ||
574 | tapes (densities were swapped). | ||
575 | - Fixed wrong calculation of overshoot on seek_forward: Wrong sign | ||
576 | of error. | ||
577 | - Suppress (false) error message due to new tape loaded. | ||
578 | - Added two new CMS drives (11c3 and 11c5) to vendors.h. | ||
579 | |||
580 | |||
581 | ===== Release notes for ftape-2.03a, 09/05/95 ===== | ||
582 | |||
583 | - Fixed display of old error (even if already cleared) in ftape_open. | ||
584 | - Improved tape length detection, ioctls would fail for 425 ft tapes. | ||
585 | Until the tape length is calculated with data from the header | ||
586 | segment, we'll use worst-case values. | ||
587 | - Clear eof_mark after rewinding ioctls. | ||
588 | - Fixed wrong version message (2.03 had 2.02g id). | ||
589 | - Fixed bug that caused the fdc to be reset very frequently. | ||
590 | This shouldn't affect normal operation but the timing of the | ||
591 | report routines has changed again and that may cause problems. | ||
592 | We'll just have to find out.... | ||
593 | - Implemented correct write precompensation setting for QIC-3010/3020. | ||
594 | - Cleaned up fdc_interrupt_wait routine. Hope it still works :-) | ||
595 | - Finally removed (already disabled) special eof mark handling for | ||
596 | gnu tar. | ||
597 | - Changed order of get_dma_residue and disable_dma in fdc-isr.c | ||
598 | because the current order would fail on at least one system. | ||
599 | We're back to the original order again, hope (and expect) this | ||
600 | doesn't break any other system. | ||
601 | |||
602 | |||
603 | ===== Release notes for ftape-2.03, 07/05/95 ===== | ||
604 | |||
605 | (Changes refer to the first ftape-2.02 release) | ||
606 | |||
607 | Support for wide and extended length tapes | ||
608 | ------------------------------------------ | ||
609 | The Conner TSM 420 and 850 drives are reported to be working. | ||
610 | I haven't received any reports about other brands; the TSM 420 | ||
611 | and 850 seem to be the most widely used wide drives. | ||
612 | Extended length tapes (425 ft) with normal QIC-80 drives | ||
613 | are operating too (At least I've had no reports stating otherwise). | ||
614 | _Not_ yet completely supported (although they may work) are | ||
615 | QIC-3020 drives and 2 Mbps floppy disk controllers won't work at | ||
616 | the highest speed. | ||
617 | If someone is kind enough to send me one of these, I'll include | ||
618 | support for it too ;-) | ||
619 | |||
620 | Easier configuration | ||
621 | -------------------- | ||
622 | Problems due to wrong settings in the Makefile are prevented | ||
623 | by using a configuration script that sets the necessary (kernel | ||
624 | version dependent) compile time options. | ||
625 | This kernel version is now determined from the sources found | ||
626 | at /usr/src/linux, or if not found, the old way using | ||
627 | /proc/version. | ||
628 | Versioned modules will be used automatically when supported | ||
629 | by- and configured in- the kernel. | ||
630 | Note that the current modules code (1.1.87) is still broken | ||
631 | and _needs_ the fix included in the insmod directory. | ||
632 | Please don't send me any more Oops reports caused by insmod :-( | ||
633 | |||
634 | Reduced module size | ||
635 | ------------------- | ||
636 | The standard module size is much reduced and some compile time | ||
637 | options can even reduce it further. (I don't recommend this | ||
638 | for normal use but it can be handy for rescue diskettes) | ||
639 | |||
640 | Option: Approx. module size: | ||
641 | |||
642 | <standard> 150 Kb | ||
643 | NO_TRACE 125 Kb | ||
644 | NO_TRACE_AT_ALL 67 Kb | ||
645 | |||
646 | |||
647 | Much improved driver interruption | ||
648 | --------------------------------- | ||
649 | Most possible loops have been broken and signal detection | ||
650 | has been improved. | ||
651 | In most cases the driver can be aborted by ^C (SIGINT) and | ||
652 | SIGKILL (kill -9) will generate be a sure kill. | ||
653 | (Note that aborting a tape operation may damage the last | ||
654 | data written to tape) | ||
655 | |||
656 | Improved error recovery | ||
657 | ----------------------- | ||
658 | Ftape now returns an error (ENODATA) to the application if | ||
659 | a segment proves to be unrecoverable and then skips the | ||
660 | bad segment. | ||
661 | This causes most applications to continue to work (tar | ||
662 | and afio) loosing only a small amount (up to 29 Kb) of data. | ||
663 | Retried read operations will now be done slightly off-track | ||
664 | to improve the chance of success. Serious head off-track | ||
665 | errors will be detected. | ||
666 | |||
667 | FC-10 and FC-20 controllers | ||
668 | --------------------------- | ||
669 | Ftape now supports both the old CMS FC-10 and the newer FC-20 | ||
670 | controllers. | ||
671 | Because the operation of these cards is still undocumented, | ||
672 | thus far they will only work with the default settings (See | ||
673 | Makefile). Any feed-back on how to use them with other settings | ||
674 | will be welcome ! | ||
675 | Compilation will fail if one changes the settings to illegal | ||
676 | values. | ||
677 | |||
678 | Kernels and compilers | ||
679 | --------------------- | ||
680 | Ftape is currently being developed using the 2.5.8 compiler. | ||
681 | The older 2.4.5 probably works too (Set option in Makefile!). | ||
682 | I have no experience with any later compilers nor Elf support. | ||
683 | Any information on this is welcome. | ||
684 | The latest kernel I have tested ftape with is 1.2.6. | ||
685 | |||
686 | Compression | ||
687 | ----------- | ||
688 | An impressive collection of changes for ftape including | ||
689 | on-the-fly compression is still lying on my desk. | ||
690 | If 2.03 proves to be reliable I might start integrating these | ||
691 | but as usual, I'm short in time :-( | ||
692 | |||
693 | Formatting | ||
694 | ---------- | ||
695 | There is still no way to format tapes under Linux. As far as | ||
696 | I know all attempts to write such a program have died now. | ||
697 | Since formatted tapes are rather common now, I think all we | ||
698 | need is a utility that writes a worst case pattern and verifies | ||
699 | that with the drive put in verify mode, reducing margins. | ||
700 | Any takers ? | ||
701 | |||
702 | Furthermore | ||
703 | ----------- | ||
704 | Cleaned up messages. | ||
705 | Prepared to support multiple tape drives on one fdc. | ||
706 | Thanks to all the people who sent bug reports and helped me | ||
707 | improve the driver. Without trying to be complete I'll mention | ||
708 | Gary Anderson (without his accurate reports and unreliable | ||
709 | hardware there wouldn't be a 2.03), Stefan Kneifel (FC-20), | ||
710 | Robert Broughton (FC-20, you were almost there ;-), Bjorn | ||
711 | Ekwall (for the versioned modules and buggy insmod ;-), Peter | ||
712 | Fox, Christopher Oliver, Ralph Whittaker and not the least | ||
713 | Linus Torvalds (for Linux and keeping me busy because of | ||
714 | changes to the kernel ;-) | ||
715 | Thanks to anyone I forgot, for the bug reports, the ftape | ||
716 | bashing and the mental support... | ||
717 | |||
718 | |||
719 | That's it for now. Have Fun, | ||
720 | |||
721 | Bas. | ||
722 | |||
723 | |||
724 | ===== Release notes for ftape-2.02g, 06/05/95 ===== | ||
725 | |||
726 | - Added extra test to break read-id loop with signal. | ||
727 | - Changed rewind code to handle negative overshoot for drives | ||
728 | that take very long to start or stop. | ||
729 | - Let use of get/set i/o-regions depend on kernel version. | ||
730 | - Changed code to use a more general test for conditional | ||
731 | compilations depending on kernel version. | ||
732 | - Improved micro-step functionality to go off-track only | ||
733 | while reading (id & data). | ||
734 | - Added failure on tape-not-referenced bit in ftape_command. | ||
735 | - Added FOREVER option to read-wait routine. | ||
736 | - Changed read-id to use shorter timeout causing smaller | ||
737 | rewinds on timeout. | ||
738 | - Made kernel-interface functions static. | ||
739 | |||
740 | |||
741 | ===== Release notes for ftape-2.02f, 03/05/95 ===== | ||
742 | |||
743 | - Added support for dual tape drives on my system, extended Configure | ||
744 | script to detect host 'dodo'. | ||
745 | - Log media defect in history if ecc failed and no data was returned. | ||
746 | - Fixed Configure script that was failing for kernel versions with | ||
747 | double digit version or revision numbers. | ||
748 | |||
749 | |||
750 | ===== Release notes for ftape-2.02e, 01/05/95 ===== | ||
751 | |||
752 | - Fixed reposition loop at logical eot (failing read_id). | ||
753 | - Fixed 34 segment offset when rewinding. | ||
754 | - Added fast seek capability for more than 255 segments. | ||
755 | - Fixed wrong busy result from ftape_command causing reverse | ||
756 | seek to fail. | ||
757 | - Added breakout from infinite rewind loop (if something fails). | ||
758 | |||
759 | |||
760 | ===== Release notes for ftape-2.02d, 30/04/95 ===== | ||
761 | |||
762 | - Improved abortion on signals: Interrupt will make a graceful | ||
763 | exit, Kill will be less nice and should be used if everything | ||
764 | else fails. | ||
765 | - Included check for tape-head off track. | ||
766 | - Implemented exit from tape-start loop. | ||
767 | - Added kernel io-port registration. | ||
768 | - Implemented skip of failing segment (ENODATA) on ecc failure. | ||
769 | This allows afio and tar to continue when the tape is damaged. | ||
770 | - Made distinction between drive names with different codes. | ||
771 | |||
772 | |||
773 | ===== Release notes for ftape-2.02c, 22/04/95 ===== | ||
774 | |||
775 | - Fixed too tight command queueing after tape stop/pause command | ||
776 | issued from within interrupt service routine (Showed as timeout | ||
777 | on Acknowledge errors during retries on some systems) | ||
778 | - Tried to fix timeouts when using 425 ft tape because the extended | ||
779 | length doesn't seem to be detected by the hardware. | ||
780 | We now use the format code from the header segment so adjust the | ||
781 | timing after reading the header segment. | ||
782 | - Fixed some messages stating 'unexpected something...' being not | ||
783 | unexpected anymore. | ||
784 | - Started preparations for merge of dynamic buffer allocation and | ||
785 | compression code. | ||
786 | - Changed some debug messages to include relevant segment information | ||
787 | at level 4. | ||
788 | - Included early bail-out when drive offline, preventing a lot of | ||
789 | false messages. | ||
790 | - Moved ftape_parameter_xxx() offsets into function instead of in calls. | ||
791 | - Removed 'weird, drive busy but no data' error when caused by | ||
792 | an error during a read-id. | ||
793 | - Improved 'timeout on acknowledge' diagnostics. | ||
794 | - Moved MODULE option into Configure. | ||
795 | - Reduced code size when no tracing at all was set (Claus Heine). | ||
796 | - No longer log error code 0 (no error) as an error. | ||
797 | |||
798 | |||
799 | ===== Release notes for ftape-2.02b, 09/04/95 ===== | ||
800 | |||
801 | - Relaxed timing for status operation and displaying | ||
802 | abnormal results. Hopefully this shows what's going | ||
803 | wrong with the Conner TSM850R drives. | ||
804 | - Created script for configuration, using version number | ||
805 | of kernel source if available, otherwise /proc/version. | ||
806 | - Fixed conditionals in kernel-interface.c. | ||
807 | - Removed unavoidable TRACE output. | ||
808 | |||
809 | |||
810 | ===== Release notes for ftape-2.02a, 01/04/95 ===== | ||
811 | |||
812 | - Implemented `new-style' (versioned) modules support for new | ||
813 | kernels. | ||
814 | - Reduced size of module by moving static data to bss. | ||
815 | - Now using version number of kernel source instead of running | ||
816 | kernel for kernel versions >= 1.1.82 | ||
817 | - Added feedback on drive speeds to vendor information. | ||
818 | - Included fixed insmod sources to distribution (Let's hope | ||
819 | the modules distribution get fixed soon :-/). | ||
820 | |||
821 | Note that I haven't yet implemented any of the code extension I | ||
822 | received. I hope to find some time to do this soon. | ||
823 | |||
824 | |||
825 | ===== Release notes for ftape-2.02, 15/01/95 ===== | ||
826 | |||
827 | |||
828 | - Fixed failing repositioning when overshoot was incremented. | ||
829 | - Fixed rate selection: Because of a deficiency in the QIC-117 | ||
830 | specification one cannot distinguish between a not implemented | ||
831 | and a failing command. Therefor we now try to find out if the | ||
832 | drive does support this command before usage. | ||
833 | - Fixed error retry using wrong offset in fdc-isr. | ||
834 | - Improved retry code to retry only once on a single no-data | ||
835 | error in a segment. | ||
836 | - Validate sector number extracted from eof mark because an | ||
837 | invalid file mark (due to ???) could cause kernel panic. | ||
838 | - Split ftape-io.c into ftape-io.c and ftape-ctl.c files. | ||
839 | - Corrected too high media error count after writing to | ||
840 | a bad tape. | ||
841 | - Added #include <asm/segment.h> again because old kernel versions | ||
842 | need it. | ||
843 | - Fixed fdc not being disabled when open failed because no tape | ||
844 | drive was found. | ||
845 | - Fixed problem with soft error in sector 32 (shift operator with | ||
846 | shiftcount 32 is not defined). | ||
847 | |||
848 | |||
849 | ===== Release notes for ftape-2.01, 08/01/95 ===== | ||
850 | |||
851 | |||
852 | - Removed TESTING setting from distributed Makefile. | ||
853 | - Fixed `mt asf' failure: Rewind was deferred to close which | ||
854 | overruled the fsf ioctl. | ||
855 | - Prevented non-interruptible commands being interrupted. | ||
856 | - Added missing timeout.pause setting. | ||
857 | - Maximum tape speed read from drive type information table. | ||
858 | If the information is not in the table (0) the drive will | ||
859 | determine the speed itself and put a message in the logfile. | ||
860 | This information should then be added to the table in the | ||
861 | vendors.h file (and reported to me). | ||
862 | - Added call to ftape_init_drive after soft reset for those | ||
863 | (antique) drives that don't do an implicit seek_load_point | ||
864 | after a reset or power up. | ||
865 | - Don't try to set data rate if reset failed. | ||
866 | - Prevent update of seek variables when starting from the | ||
867 | beginning or the end of the tape. | ||
868 | - Fixed wrong adjustment of overshoot in seek_forward(). | ||
869 | - Added sync to Makefile (again). | ||
870 | - Added code to diagnose timer problems (calibr.c). | ||
871 | - Replaced time differences by timediff calls. | ||
872 | - Removed reference to do_floppy from object for recent kernels. | ||
873 | - Fixed wrong display of 'failing dma controller' message. | ||
874 | - Removed various no longer used #include statements. | ||
875 | - Added max. tape speed value to vendor-struct. | ||
876 | - Changed ftape-command to check pre-conditions and wait | ||
877 | if needed. | ||
878 | - Further updated qic117.h to rev G. | ||
879 | - Combined command name table and restrictions table to one. | ||
880 | Extended this table with some new fields. | ||
881 | - Increased timeout on Ack timer value and included code to | ||
882 | report out of spec behaviour. | ||
883 | - Increased rewind timeout margin to calculated + 20%. | ||
884 | - Improved data rate selection so it won't fail on some | ||
885 | older (pre standard) drives. | ||
886 | - Changed initialisation code so drive will be rewound if the | ||
887 | driver is reloaded and the tape is not at bot. | ||
888 | - Moved some of the flush operations from close to the ioctls. | ||
889 | - Added exit code value to failing verify area message. | ||
890 | - Loop until tape halted in smart-stop. | ||
891 | - Fast seek handled specially if located at bot or eot. | ||
892 | - Being more conservative on overshoot value. | ||
893 | |||
894 | |||
895 | ===== Release notes for ftape-2.00, 31/12/94 ===== | ||
896 | |||
897 | The Install-guide is completely rewritten and now also includes | ||
898 | some information on how to use the driver. If you're either new | ||
899 | to ftape or new to Unix tape devices make sure to read it ! | ||
900 | |||
901 | If you own a pci system and experience problems with the | ||
902 | ftape driver make sure to read the README.PCI file. It contains | ||
903 | some hints on how to fix your hardware. | ||
904 | |||
905 | For anybody who hasn't noticed: The version number of the | ||
906 | driver has been incremented (The latest released version has | ||
907 | been version 1.14d). | ||
908 | This has been done for two major reasons: | ||
909 | |||
910 | o A new (better) error recovery scheme is implemented. | ||
911 | o Support for new drive types has been added. | ||
912 | |||
913 | All these improvements/changes will probably include a couple | ||
914 | of new (and old?) bugs. If you encounter any problems that you think | ||
915 | I'm not yet aware of, feel free to send a report to <bas@vimec.nl>. | ||
916 | I recommend keeping a version of ftape-1.14d available, just | ||
917 | in case ;-) | ||
918 | |||
919 | This version should work with all kernel versions from 1.0.9 up | ||
920 | to 1.1.72 (and probably earlier and later versions too). | ||
921 | |||
922 | |||
923 | Major new features: | ||
924 | |||
925 | - Better handling of tapes with defects: When a sector repeatedly | ||
926 | (SOFT_RETRIES in ftape.h) cannot be written to or read from it is | ||
927 | marked as an hard error and gets skipped. | ||
928 | The error correction code can handle up to three of these hard | ||
929 | errors provided there are no other errors in that segment (32 Kb). | ||
930 | |||
931 | - Allows writing to tapes with defects (although the risk of loosing | ||
932 | data increases !) | ||
933 | Look for the media-defects entry printed with the statistics when | ||
934 | the tape is closed. A non-zero value here shows a bad tape. | ||
935 | [the actual count is wrong (too high), this is a known bug]. | ||
936 | |||
937 | - Use of backup header segment if first one is failing. | ||
938 | |||
939 | - Support for extended length tapes with QIC-80: both 425 and 1100 ft. | ||
940 | 0.25 inch tapes are now recognized and handled. | ||
941 | |||
942 | - Support for new QIC-80 drives with 8 mm `wide' tapes (e.g. Conner | ||
943 | TSM 420). | ||
944 | |||
945 | - Support for new QIC-3010 and QIC-3020 drives (experimental) with | ||
946 | both 0.25 inch and 8 mm tapes. | ||
947 | |||
948 | Some minor features were added, a couple of small bugs were fixed and | ||
949 | probably some new ones introduced ;-). | ||
950 | |||
951 | [lseek() didn't make it into this version] | ||
952 | |||
953 | Have fun, | ||
954 | |||
955 | Bas. | ||
956 | ---- | ||
957 | LocalWords: ftape MCONFIG mt VFS zftape resp sftape proc subdir MTIOCVOLINFO | ||
958 | LocalWords: MTIOCGETSIZE BOT EOD MTBSF zft kerneld modprobe kdtime contrib TR | ||
959 | LocalWords: MTSETBLK afio uninstall texi www EIO QIC init sft eof aka dma GB | ||
960 | LocalWords: SIGKILL MTIOCFTCMD mmap Iomega FDC fdc io gnumt mtio fc asm inb | ||
961 | LocalWords: outb ft qic frontend TeXinfo irq mach MODVERSIONS CONFIG html dvi | ||
962 | LocalWords: usr doc SMP Mb Dunno FIXME vtblc perl listtape volinfo fsf MTWEOF | ||
963 | LocalWords: amanda degaussed ComByte DoublePlay whraven njackn com MTIOC vtbl | ||
964 | LocalWords: GETBLKSZ MAKEDEV zftape's linux dif CVS Revison cp MTREW MTOFFL | ||
965 | LocalWords: MTFSF BSF Marcin Dalecki GCC Config cpio swapout Kai Harrekilde | ||
966 | LocalWords: Pederson khp dolphinics Justus claus momo rwth aachen Laarhoven | ||
diff --git a/drivers/char/ftape/compressor/Makefile b/drivers/char/ftape/compressor/Makefile new file mode 100644 index 000000000000..1fbd6c4019db --- /dev/null +++ b/drivers/char/ftape/compressor/Makefile | |||
@@ -0,0 +1,31 @@ | |||
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 | # $Source: /homes/cvs/ftape-stacked/ftape/compressor/Makefile,v $ | ||
19 | # $Revision: 1.1 $ | ||
20 | # $Date: 1997/10/05 19:12:28 $ | ||
21 | # | ||
22 | # Makefile for the optional compressor for th zftape VFS | ||
23 | # interface to the QIC-40/80/3010/3020 floppy-tape driver for | ||
24 | # Linux. | ||
25 | # | ||
26 | |||
27 | obj-$(CONFIG_ZFT_COMPRESSOR) += zft-compressor.o | ||
28 | |||
29 | zft-compressor-objs := zftape-compress.o lzrw3.o | ||
30 | |||
31 | CFLAGS_lzrw3.o := -O6 -funroll-all-loops | ||
diff --git a/drivers/char/ftape/compressor/lzrw3.c b/drivers/char/ftape/compressor/lzrw3.c new file mode 100644 index 000000000000..a032a0ee2a99 --- /dev/null +++ b/drivers/char/ftape/compressor/lzrw3.c | |||
@@ -0,0 +1,743 @@ | |||
1 | /* | ||
2 | * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.c,v $ | ||
3 | * $Revision: 1.1 $ | ||
4 | * $Date: 1997/10/05 19:12:29 $ | ||
5 | * | ||
6 | * Implementation of Ross Williams lzrw3 algorithm. Adaption for zftape. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include "../compressor/lzrw3.h" /* Defines single exported function "compress". */ | ||
11 | |||
12 | /******************************************************************************/ | ||
13 | /* */ | ||
14 | /* LZRW3.C */ | ||
15 | /* */ | ||
16 | /******************************************************************************/ | ||
17 | /* */ | ||
18 | /* Author : Ross Williams. */ | ||
19 | /* Date : 30-Jun-1991. */ | ||
20 | /* Release : 1. */ | ||
21 | /* */ | ||
22 | /******************************************************************************/ | ||
23 | /* */ | ||
24 | /* This file contains an implementation of the LZRW3 data compression */ | ||
25 | /* algorithm in C. */ | ||
26 | /* */ | ||
27 | /* The algorithm is a general purpose compression algorithm that runs fast */ | ||
28 | /* and gives reasonable compression. The algorithm is a member of the Lempel */ | ||
29 | /* Ziv family of algorithms and bases its compression on the presence in the */ | ||
30 | /* data of repeated substrings. */ | ||
31 | /* */ | ||
32 | /* This algorithm is unpatented and the code is public domain. As the */ | ||
33 | /* algorithm is based on the LZ77 class of algorithms, it is unlikely to be */ | ||
34 | /* the subject of a patent challenge. */ | ||
35 | /* */ | ||
36 | /* Unlike the LZRW1 and LZRW1-A algorithms, the LZRW3 algorithm is */ | ||
37 | /* deterministic and is guaranteed to yield the same compressed */ | ||
38 | /* representation for a given file each time it is run. */ | ||
39 | /* */ | ||
40 | /* The LZRW3 algorithm was originally designed and implemented */ | ||
41 | /* by Ross Williams on 31-Dec-1990. */ | ||
42 | /* */ | ||
43 | /* Here are the results of applying this code, compiled under THINK C 4.0 */ | ||
44 | /* and running on a Mac-SE (8MHz 68000), to the standard calgary corpus. */ | ||
45 | /* */ | ||
46 | /* +----------------------------------------------------------------+ */ | ||
47 | /* | DATA COMPRESSION TEST | */ | ||
48 | /* | ===================== | */ | ||
49 | /* | Time of run : Sun 30-Jun-1991 09:31PM | */ | ||
50 | /* | Timing accuracy : One part in 100 | */ | ||
51 | /* | Context length : 262144 bytes (= 256.0000K) | */ | ||
52 | /* | Test suite : Calgary Corpus Suite | */ | ||
53 | /* | Files in suite : 14 | */ | ||
54 | /* | Algorithm : LZRW3 | */ | ||
55 | /* | Note: All averages are calculated from the un-rounded values. | */ | ||
56 | /* +----------------------------------------------------------------+ */ | ||
57 | /* | File Name Length CxB ComLen %Remn Bits Com K/s Dec K/s | */ | ||
58 | /* | ---------- ------ --- ------ ----- ---- ------- ------- | */ | ||
59 | /* | rpus:Bib.D 111261 1 55033 49.5 3.96 19.46 32.27 | */ | ||
60 | /* | us:Book1.D 768771 3 467962 60.9 4.87 17.03 31.07 | */ | ||
61 | /* | us:Book2.D 610856 3 317102 51.9 4.15 19.39 34.15 | */ | ||
62 | /* | rpus:Geo.D 102400 1 82424 80.5 6.44 11.65 18.18 | */ | ||
63 | /* | pus:News.D 377109 2 205670 54.5 4.36 17.14 27.47 | */ | ||
64 | /* | pus:Obj1.D 21504 1 13027 60.6 4.85 13.40 18.95 | */ | ||
65 | /* | pus:Obj2.D 246814 1 116286 47.1 3.77 19.31 30.10 | */ | ||
66 | /* | s:Paper1.D 53161 1 27522 51.8 4.14 18.60 31.15 | */ | ||
67 | /* | s:Paper2.D 82199 1 45160 54.9 4.40 18.45 32.84 | */ | ||
68 | /* | rpus:Pic.D 513216 2 122388 23.8 1.91 35.29 51.05 | */ | ||
69 | /* | us:Progc.D 39611 1 19669 49.7 3.97 18.87 30.64 | */ | ||
70 | /* | us:Progl.D 71646 1 28247 39.4 3.15 24.34 40.66 | */ | ||
71 | /* | us:Progp.D 49379 1 19377 39.2 3.14 23.91 39.23 | */ | ||
72 | /* | us:Trans.D 93695 1 33481 35.7 2.86 25.48 40.37 | */ | ||
73 | /* +----------------------------------------------------------------+ */ | ||
74 | /* | Average 224401 1 110953 50.0 4.00 20.17 32.72 | */ | ||
75 | /* +----------------------------------------------------------------+ */ | ||
76 | /* */ | ||
77 | /******************************************************************************/ | ||
78 | |||
79 | /******************************************************************************/ | ||
80 | |||
81 | /* The following structure is returned by the "compress" function below when */ | ||
82 | /* the user asks the function to return identifying information. */ | ||
83 | /* The most important field in the record is the working memory field which */ | ||
84 | /* tells the calling program how much working memory should be passed to */ | ||
85 | /* "compress" when it is called to perform a compression or decompression. */ | ||
86 | /* LZRW3 uses the same amount of memory during compression and decompression. */ | ||
87 | /* For more information on this structure see "compress.h". */ | ||
88 | |||
89 | #define U(X) ((ULONG) X) | ||
90 | #define SIZE_P_BYTE (U(sizeof(UBYTE *))) | ||
91 | #define SIZE_WORD (U(sizeof(UWORD ))) | ||
92 | #define ALIGNMENT_FUDGE (U(16)) | ||
93 | #define MEM_REQ ( U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE ) | ||
94 | |||
95 | static struct compress_identity identity = | ||
96 | { | ||
97 | U(0x032DDEA8), /* Algorithm identification number. */ | ||
98 | MEM_REQ, /* Working memory (bytes) required. */ | ||
99 | "LZRW3", /* Name of algorithm. */ | ||
100 | "1.0", /* Version number of algorithm. */ | ||
101 | "31-Dec-1990", /* Date of algorithm. */ | ||
102 | "Public Domain", /* Copyright notice. */ | ||
103 | "Ross N. Williams", /* Author of algorithm. */ | ||
104 | "Renaissance Software", /* Affiliation of author. */ | ||
105 | "Public Domain" /* Vendor of algorithm. */ | ||
106 | }; | ||
107 | |||
108 | LOCAL void compress_compress (UBYTE *,UBYTE *,ULONG,UBYTE *, LONG *); | ||
109 | LOCAL void compress_decompress(UBYTE *,UBYTE *,LONG, UBYTE *, ULONG *); | ||
110 | |||
111 | /******************************************************************************/ | ||
112 | |||
113 | /* This function is the only function exported by this module. */ | ||
114 | /* Depending on its first parameter, the function can be requested to */ | ||
115 | /* compress a block of memory, decompress a block of memory, or to identify */ | ||
116 | /* itself. For more information, see the specification file "compress.h". */ | ||
117 | |||
118 | EXPORT void lzrw3_compress( | ||
119 | UWORD action, /* Action to be performed. */ | ||
120 | UBYTE *wrk_mem, /* Address of working memory we can use.*/ | ||
121 | UBYTE *src_adr, /* Address of input data. */ | ||
122 | LONG src_len, /* Length of input data. */ | ||
123 | UBYTE *dst_adr, /* Address to put output data. */ | ||
124 | void *p_dst_len /* Address of longword for length of output data.*/ | ||
125 | ) | ||
126 | { | ||
127 | switch (action) | ||
128 | { | ||
129 | case COMPRESS_ACTION_IDENTITY: | ||
130 | *((struct compress_identity **)p_dst_len)= &identity; | ||
131 | break; | ||
132 | case COMPRESS_ACTION_COMPRESS: | ||
133 | compress_compress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len); | ||
134 | break; | ||
135 | case COMPRESS_ACTION_DECOMPRESS: | ||
136 | compress_decompress(wrk_mem,src_adr,src_len,dst_adr,(LONG *)p_dst_len); | ||
137 | break; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /******************************************************************************/ | ||
142 | /* */ | ||
143 | /* BRIEF DESCRIPTION OF THE LZRW3 ALGORITHM */ | ||
144 | /* ======================================== */ | ||
145 | /* The LZRW3 algorithm is identical to the LZRW1-A algorithm except that */ | ||
146 | /* instead of transmitting history offsets, it transmits hash table indexes. */ | ||
147 | /* In order to decode the indexes, the decompressor must maintain an */ | ||
148 | /* identical hash table. Copy items are straightforward:when the decompressor */ | ||
149 | /* receives a copy item, it simply looks up the hash table to translate the */ | ||
150 | /* index into a pointer into the data already decompressed. To update the */ | ||
151 | /* hash table, it replaces the same table entry with a pointer to the start */ | ||
152 | /* of the newly decoded phrase. The tricky part is with literal items, for at */ | ||
153 | /* the time that the decompressor receives a literal item the decompressor */ | ||
154 | /* does not have the three bytes in the Ziv (that the compressor has) to */ | ||
155 | /* perform the three-byte hash. To solve this problem, in LZRW3, both the */ | ||
156 | /* compressor and decompressor are wired up so that they "buffer" these */ | ||
157 | /* literals and update their hash tables only when three bytes are available. */ | ||
158 | /* This makes the maximum buffering 2 bytes. */ | ||
159 | /* */ | ||
160 | /* Replacement of offsets by hash table indexes yields a few percent extra */ | ||
161 | /* compression at the cost of some speed. LZRW3 is slower than LZRW1, LZRW1-A */ | ||
162 | /* and LZRW2, but yields better compression. */ | ||
163 | /* */ | ||
164 | /* Extra compression could be obtained by using a hash table of depth two. */ | ||
165 | /* However, increasing the depth above one incurs a significant decrease in */ | ||
166 | /* compression speed which was not considered worthwhile. Another reason for */ | ||
167 | /* keeping the depth down to one was to allow easy comparison with the */ | ||
168 | /* LZRW1-A and LZRW2 algorithms so as to demonstrate the exact effect of the */ | ||
169 | /* use of direct hash indexes. */ | ||
170 | /* */ | ||
171 | /* +---+ */ | ||
172 | /* |___|4095 */ | ||
173 | /* |___| */ | ||
174 | /* +---------------------*_|<---+ /----+---\ */ | ||
175 | /* | |___| +---|Hash | */ | ||
176 | /* | |___| |Function| */ | ||
177 | /* | |___| \--------/ */ | ||
178 | /* | |___|0 ^ */ | ||
179 | /* | +---+ | */ | ||
180 | /* | Hash +-----+ */ | ||
181 | /* | Table | */ | ||
182 | /* | --- */ | ||
183 | /* v ^^^ */ | ||
184 | /* +-------------------------------------|----------------+ */ | ||
185 | /* |||||||||||||||||||||||||||||||||||||||||||||||||||||||| */ | ||
186 | /* +-------------------------------------|----------------+ */ | ||
187 | /* | |1......18| | */ | ||
188 | /* |<------- Lempel=History ------------>|<--Ziv-->| | */ | ||
189 | /* | (=bytes already processed) |<-Still to go-->| */ | ||
190 | /* |<-------------------- INPUT BLOCK ------------------->| */ | ||
191 | /* */ | ||
192 | /* The diagram above for LZRW3 looks almost identical to the diagram for */ | ||
193 | /* LZRW1. The difference is that in LZRW3, the compressor transmits hash */ | ||
194 | /* table indices instead of Lempel offsets. For this to work, the */ | ||
195 | /* decompressor must maintain a hash table as well as the compressor and both */ | ||
196 | /* compressor and decompressor must "buffer" literals, as the decompressor */ | ||
197 | /* cannot hash phrases commencing with a literal until another two bytes have */ | ||
198 | /* arrived. */ | ||
199 | /* */ | ||
200 | /* LZRW3 Algorithm Execution Summary */ | ||
201 | /* --------------------------------- */ | ||
202 | /* 1. Hash the first three bytes of the Ziv to yield a hash table index h. */ | ||
203 | /* 2. Look up the hash table yielding history pointer p. */ | ||
204 | /* 3. Match where p points with the Ziv. If there is a match of three or */ | ||
205 | /* more bytes, code those bytes (in the Ziv) as a copy item, otherwise */ | ||
206 | /* code the next byte in the Ziv as a literal item. */ | ||
207 | /* 4. Update the hash table as possible subject to the constraint that only */ | ||
208 | /* phrases commencing three bytes back from the Ziv can be hashed and */ | ||
209 | /* entered into the hash table. (This enables the decompressor to keep */ | ||
210 | /* pace). See the description and code for more details. */ | ||
211 | /* */ | ||
212 | /******************************************************************************/ | ||
213 | /* */ | ||
214 | /* DEFINITION OF COMPRESSED FILE FORMAT */ | ||
215 | /* ==================================== */ | ||
216 | /* * A compressed file consists of a COPY FLAG followed by a REMAINDER. */ | ||
217 | /* * The copy flag CF uses up four bytes with the first byte being the */ | ||
218 | /* least significant. */ | ||
219 | /* * If CF=1, then the compressed file represents the remainder of the file */ | ||
220 | /* exactly. Otherwise CF=0 and the remainder of the file consists of zero */ | ||
221 | /* or more GROUPS, each of which represents one or more bytes. */ | ||
222 | /* * Each group consists of two bytes of CONTROL information followed by */ | ||
223 | /* sixteen ITEMs except for the last group which can contain from one */ | ||
224 | /* to sixteen items. */ | ||
225 | /* * An item can be either a LITERAL item or a COPY item. */ | ||
226 | /* * Each item corresponds to a bit in the control bytes. */ | ||
227 | /* * The first control byte corresponds to the first 8 items in the group */ | ||
228 | /* with bit 0 corresponding to the first item in the group and bit 7 to */ | ||
229 | /* the eighth item in the group. */ | ||
230 | /* * The second control byte corresponds to the second 8 items in the group */ | ||
231 | /* with bit 0 corresponding to the ninth item in the group and bit 7 to */ | ||
232 | /* the sixteenth item in the group. */ | ||
233 | /* * A zero bit in a control word means that the corresponding item is a */ | ||
234 | /* literal item. A one bit corresponds to a copy item. */ | ||
235 | /* * A literal item consists of a single byte which represents itself. */ | ||
236 | /* * A copy item consists of two bytes that represent from 3 to 18 bytes. */ | ||
237 | /* * The first byte in a copy item will be denoted C1. */ | ||
238 | /* * The second byte in a copy item will be denoted C2. */ | ||
239 | /* * Bits will be selected using square brackets. */ | ||
240 | /* For example: C1[0..3] is the low nibble of the first control byte. */ | ||
241 | /* of copy item C1. */ | ||
242 | /* * The LENGTH of a copy item is defined to be C1[0..3]+3 which is a number */ | ||
243 | /* in the range [3,18]. */ | ||
244 | /* * The INDEX of a copy item is defined to be C1[4..7]*256+C2[0..8] which */ | ||
245 | /* is a number in the range [0,4095]. */ | ||
246 | /* * A copy item represents the sequence of bytes */ | ||
247 | /* text[POS-OFFSET..POS-OFFSET+LENGTH-1] where */ | ||
248 | /* text is the entire text of the uncompressed string. */ | ||
249 | /* POS is the index in the text of the character following the */ | ||
250 | /* string represented by all the items preceeding the item */ | ||
251 | /* being defined. */ | ||
252 | /* OFFSET is obtained from INDEX by looking up the hash table. */ | ||
253 | /* */ | ||
254 | /******************************************************************************/ | ||
255 | |||
256 | /* The following #define defines the length of the copy flag that appears at */ | ||
257 | /* the start of the compressed file. The value of four bytes was chosen */ | ||
258 | /* because the fast_copy routine on my Macintosh runs faster if the source */ | ||
259 | /* and destination blocks are relatively longword aligned. */ | ||
260 | /* The actual flag data appears in the first byte. The rest are zeroed so as */ | ||
261 | /* to normalize the compressed representation (i.e. not non-deterministic). */ | ||
262 | #define FLAG_BYTES 4 | ||
263 | |||
264 | /* The following #defines define the meaning of the values of the copy */ | ||
265 | /* flag at the start of the compressed file. */ | ||
266 | #define FLAG_COMPRESS 0 /* Signals that output was result of compression. */ | ||
267 | #define FLAG_COPY 1 /* Signals that output was simply copied over. */ | ||
268 | |||
269 | /* The 68000 microprocessor (on which this algorithm was originally developed */ | ||
270 | /* is fussy about non-aligned arrays of words. To avoid these problems the */ | ||
271 | /* following macro can be used to "waste" from 0 to 3 bytes so as to align */ | ||
272 | /* the argument pointer. */ | ||
273 | #define ULONG_ALIGN_UP(X) ((((ULONG)X)+sizeof(ULONG)-1)&~(sizeof(ULONG)-1)) | ||
274 | |||
275 | |||
276 | /* The following constant defines the maximum length of an uncompressed item. */ | ||
277 | /* This definition must not be changed; its value is hardwired into the code. */ | ||
278 | /* The longest number of bytes that can be spanned by a single item is 18 */ | ||
279 | /* for the longest copy item. */ | ||
280 | #define MAX_RAW_ITEM (18) | ||
281 | |||
282 | /* The following constant defines the maximum length of an uncompressed group.*/ | ||
283 | /* This definition must not be changed; its value is hardwired into the code. */ | ||
284 | /* A group contains at most 16 items which explains this definition. */ | ||
285 | #define MAX_RAW_GROUP (16*MAX_RAW_ITEM) | ||
286 | |||
287 | /* The following constant defines the maximum length of a compressed group. */ | ||
288 | /* This definition must not be changed; its value is hardwired into the code. */ | ||
289 | /* A compressed group consists of two control bytes followed by up to 16 */ | ||
290 | /* compressed items each of which can have a maximum length of two bytes. */ | ||
291 | #define MAX_CMP_GROUP (2+16*2) | ||
292 | |||
293 | /* The following constant defines the number of entries in the hash table. */ | ||
294 | /* This definition must not be changed; its value is hardwired into the code. */ | ||
295 | #define HASH_TABLE_LENGTH (4096) | ||
296 | |||
297 | /* LZRW3, unlike LZRW1(-A), must initialize its hash table so as to enable */ | ||
298 | /* the compressor and decompressor to stay in step maintaining identical hash */ | ||
299 | /* tables. In an early version of the algorithm, the tables were simply */ | ||
300 | /* initialized to zero and a check for zero was included just before the */ | ||
301 | /* matching code. However, this test costs time. A better solution is to */ | ||
302 | /* initialize all the entries in the hash table to point to a constant */ | ||
303 | /* string. The decompressor does the same. This solution requires no extra */ | ||
304 | /* test. The contents of the string do not matter so long as the string is */ | ||
305 | /* the same for the compressor and decompressor and contains at least */ | ||
306 | /* MAX_RAW_ITEM bytes. I chose consecutive decimal digits because they do not */ | ||
307 | /* have white space problems (e.g. there is no chance that the compiler will */ | ||
308 | /* replace more than one space by a TAB) and because they make the length of */ | ||
309 | /* the string obvious by inspection. */ | ||
310 | #define START_STRING_18 ((UBYTE *) "123456789012345678") | ||
311 | |||
312 | /* In this algorithm, hash values have to be calculated at more than one */ | ||
313 | /* point. The following macro neatens the code up for this. */ | ||
314 | #define HASH(PTR) \ | ||
315 | (((40543*(((*(PTR))<<8)^((*((PTR)+1))<<4)^(*((PTR)+2))))>>4) & 0xFFF) | ||
316 | |||
317 | /******************************************************************************/ | ||
318 | |||
319 | /* Input : Hand over the required amount of working memory in p_wrk_mem. */ | ||
320 | /* Input : Specify input block using p_src_first and src_len. */ | ||
321 | /* Input : Point p_dst_first to the start of the output zone (OZ). */ | ||
322 | /* Input : Point p_dst_len to a ULONG to receive the output length. */ | ||
323 | /* Input : Input block and output zone must not overlap. */ | ||
324 | /* Output : Length of output block written to *p_dst_len. */ | ||
325 | /* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. May */ | ||
326 | /* Output : write in OZ=Mem[p_dst_first..p_dst_first+src_len+MAX_CMP_GROUP-1].*/ | ||
327 | /* Output : Upon completion guaranteed *p_dst_len<=src_len+FLAG_BYTES. */ | ||
328 | LOCAL void compress_compress(UBYTE *p_wrk_mem, | ||
329 | UBYTE *p_src_first, ULONG src_len, | ||
330 | UBYTE *p_dst_first, LONG *p_dst_len) | ||
331 | { | ||
332 | /* p_src and p_dst step through the source and destination blocks. */ | ||
333 | register UBYTE *p_src = p_src_first; | ||
334 | register UBYTE *p_dst = p_dst_first; | ||
335 | |||
336 | /* The following variables are never modified and are used in the */ | ||
337 | /* calculations that determine when the main loop terminates. */ | ||
338 | UBYTE *p_src_post = p_src_first+src_len; | ||
339 | UBYTE *p_dst_post = p_dst_first+src_len; | ||
340 | UBYTE *p_src_max1 = p_src_first+src_len-MAX_RAW_ITEM; | ||
341 | UBYTE *p_src_max16 = p_src_first+src_len-MAX_RAW_ITEM*16; | ||
342 | |||
343 | /* The variables 'p_control' and 'control' are used to buffer control bits. */ | ||
344 | /* Before each group is processed, the next two bytes of the output block */ | ||
345 | /* are set aside for the control word for the group about to be processed. */ | ||
346 | /* 'p_control' is set to point to the first byte of that word. Meanwhile, */ | ||
347 | /* 'control' buffers the control bits being generated during the processing */ | ||
348 | /* of the group. Instead of having a counter to keep track of how many items */ | ||
349 | /* have been processed (=the number of bits in the control word), at the */ | ||
350 | /* start of each group, the top word of 'control' is filled with 1 bits. */ | ||
351 | /* As 'control' is shifted for each item, the 1 bits in the top word are */ | ||
352 | /* absorbed or destroyed. When they all run out (i.e. when the top word is */ | ||
353 | /* all zero bits, we know that we are at the end of a group. */ | ||
354 | # define TOPWORD 0xFFFF0000 | ||
355 | UBYTE *p_control; | ||
356 | register ULONG control=TOPWORD; | ||
357 | |||
358 | /* THe variable 'hash' always points to the first element of the hash table. */ | ||
359 | UBYTE **hash= (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem); | ||
360 | |||
361 | /* The following two variables represent the literal buffer. p_h1 points to */ | ||
362 | /* the hash table entry corresponding to the youngest literal. p_h2 points */ | ||
363 | /* to the hash table entry corresponding to the second youngest literal. */ | ||
364 | /* Note: p_h1=0=>p_h2=0 because zero values denote absence of a pending */ | ||
365 | /* literal. The variables are initialized to zero meaning an empty "buffer". */ | ||
366 | UBYTE **p_h1=NULL; | ||
367 | UBYTE **p_h2=NULL; | ||
368 | |||
369 | /* To start, we write the flag bytes. Being optimistic, we set the flag to */ | ||
370 | /* FLAG_COMPRESS. The remaining flag bytes are zeroed so as to keep the */ | ||
371 | /* algorithm deterministic. */ | ||
372 | *p_dst++=FLAG_COMPRESS; | ||
373 | {UWORD i; for (i=2;i<=FLAG_BYTES;i++) *p_dst++=0;} | ||
374 | |||
375 | /* Reserve the first word of output as the control word for the first group. */ | ||
376 | /* Note: This is undone at the end if the input block is empty. */ | ||
377 | p_control=p_dst; p_dst+=2; | ||
378 | |||
379 | /* Initialize all elements of the hash table to point to a constant string. */ | ||
380 | /* Use of an unrolled loop speeds this up considerably. */ | ||
381 | {UWORD i; UBYTE **p_h=hash; | ||
382 | # define ZH *p_h++=START_STRING_18 | ||
383 | for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */ | ||
384 | {ZH;ZH;ZH;ZH; | ||
385 | ZH;ZH;ZH;ZH; | ||
386 | ZH;ZH;ZH;ZH; | ||
387 | ZH;ZH;ZH;ZH;} | ||
388 | } | ||
389 | |||
390 | /* The main loop processes either 1 or 16 items per iteration. As its */ | ||
391 | /* termination logic is complicated, I have opted for an infinite loop */ | ||
392 | /* structure containing 'break' and 'goto' statements. */ | ||
393 | while (TRUE) | ||
394 | {/* Begin main processing loop. */ | ||
395 | |||
396 | /* Note: All the variables here except unroll should be defined within */ | ||
397 | /* the inner loop. Unfortunately the loop hasn't got a block. */ | ||
398 | register UBYTE *p; /* Scans through targ phrase during matching. */ | ||
399 | register UBYTE *p_ziv= NULL ; /* Points to first byte of current Ziv. */ | ||
400 | register UWORD unroll; /* Loop counter for unrolled inner loop. */ | ||
401 | register UWORD index; /* Index of current hash table entry. */ | ||
402 | register UBYTE **p_h0 = NULL ; /* Pointer to current hash table entry. */ | ||
403 | |||
404 | /* Test for overrun and jump to overrun code if necessary. */ | ||
405 | if (p_dst>p_dst_post) | ||
406 | goto overrun; | ||
407 | |||
408 | /* The following cascade of if statements efficiently catches and deals */ | ||
409 | /* with varying degrees of closeness to the end of the input block. */ | ||
410 | /* When we get very close to the end, we stop updating the table and */ | ||
411 | /* code the remaining bytes as literals. This makes the code simpler. */ | ||
412 | unroll=16; | ||
413 | if (p_src>p_src_max16) | ||
414 | { | ||
415 | unroll=1; | ||
416 | if (p_src>p_src_max1) | ||
417 | { | ||
418 | if (p_src==p_src_post) | ||
419 | break; | ||
420 | else | ||
421 | goto literal; | ||
422 | } | ||
423 | } | ||
424 | |||
425 | /* This inner unrolled loop processes 'unroll' (whose value is either 1 */ | ||
426 | /* or 16) items. I have chosen to implement this loop with labels and */ | ||
427 | /* gotos to heighten the ease with which the loop may be implemented with */ | ||
428 | /* a single decrement and branch instruction in assembly language and */ | ||
429 | /* also because the labels act as highly readable place markers. */ | ||
430 | /* (Also because we jump into the loop for endgame literals (see above)). */ | ||
431 | |||
432 | begin_unrolled_loop: | ||
433 | |||
434 | /* To process the next phrase, we hash the next three bytes and use */ | ||
435 | /* the resultant hash table index to look up the hash table. A pointer */ | ||
436 | /* to the entry is stored in p_h0 so as to avoid an array lookup. The */ | ||
437 | /* hash table entry *p_h0 is looked up yielding a pointer p to a */ | ||
438 | /* potential match of the Ziv in the history. */ | ||
439 | index=HASH(p_src); | ||
440 | p_h0=&hash[index]; | ||
441 | p=*p_h0; | ||
442 | |||
443 | /* Having looked up the candidate position, we are in a position to */ | ||
444 | /* attempt a match. The match loop has been unrolled using the PS */ | ||
445 | /* macro so that failure within the first three bytes automatically */ | ||
446 | /* results in the literal branch being taken. The coding is simple. */ | ||
447 | /* p_ziv saves p_src so we can let p_src wander. */ | ||
448 | # define PS *p++!=*p_src++ | ||
449 | p_ziv=p_src; | ||
450 | if (PS || PS || PS) | ||
451 | { | ||
452 | /* Literal. */ | ||
453 | |||
454 | /* Code the literal byte as itself and a zero control bit. */ | ||
455 | p_src=p_ziv; literal: *p_dst++=*p_src++; control&=0xFFFEFFFF; | ||
456 | |||
457 | /* We have just coded a literal. If we had two pending ones, that */ | ||
458 | /* makes three and we can update the hash table. */ | ||
459 | if (p_h2!=0) | ||
460 | {*p_h2=p_ziv-2;} | ||
461 | |||
462 | /* In any case, rotate the hash table pointers for next time. */ | ||
463 | p_h2=p_h1; p_h1=p_h0; | ||
464 | |||
465 | } | ||
466 | else | ||
467 | { | ||
468 | /* Copy */ | ||
469 | |||
470 | /* Match up to 15 remaining bytes using an unrolled loop and code. */ | ||
471 | #if 0 | ||
472 | PS || PS || PS || PS || PS || PS || PS || PS || | ||
473 | PS || PS || PS || PS || PS || PS || PS || p_src++; | ||
474 | #else | ||
475 | if ( | ||
476 | !( PS || PS || PS || PS || PS || PS || PS || PS || | ||
477 | PS || PS || PS || PS || PS || PS || PS ) | ||
478 | ) p_src++; | ||
479 | #endif | ||
480 | *p_dst++=((index&0xF00)>>4)|(--p_src-p_ziv-3); | ||
481 | *p_dst++=index&0xFF; | ||
482 | |||
483 | /* As we have just coded three bytes, we are now in a position to */ | ||
484 | /* update the hash table with the literal bytes that were pending */ | ||
485 | /* upon the arrival of extra context bytes. */ | ||
486 | if (p_h1!=0) | ||
487 | { | ||
488 | if (p_h2) | ||
489 | {*p_h2=p_ziv-2; p_h2=NULL;} | ||
490 | *p_h1=p_ziv-1; p_h1=NULL; | ||
491 | } | ||
492 | |||
493 | /* In any case, we can update the hash table based on the current */ | ||
494 | /* position as we just coded at least three bytes in a copy items. */ | ||
495 | *p_h0=p_ziv; | ||
496 | |||
497 | } | ||
498 | control>>=1; | ||
499 | |||
500 | /* This loop is all set up for a decrement and jump instruction! */ | ||
501 | #ifndef linux | ||
502 | ` end_unrolled_loop: if (--unroll) goto begin_unrolled_loop; | ||
503 | #else | ||
504 | /* end_unrolled_loop: */ if (--unroll) goto begin_unrolled_loop; | ||
505 | #endif | ||
506 | |||
507 | /* At this point it will nearly always be the end of a group in which */ | ||
508 | /* case, we have to do some control-word processing. However, near the */ | ||
509 | /* end of the input block, the inner unrolled loop is only executed once. */ | ||
510 | /* This necessitates the 'if' test. */ | ||
511 | if ((control&TOPWORD)==0) | ||
512 | { | ||
513 | /* Write the control word to the place we saved for it in the output. */ | ||
514 | *p_control++= control &0xFF; | ||
515 | *p_control = (control>>8) &0xFF; | ||
516 | |||
517 | /* Reserve the next word in the output block for the control word */ | ||
518 | /* for the group about to be processed. */ | ||
519 | p_control=p_dst; p_dst+=2; | ||
520 | |||
521 | /* Reset the control bits buffer. */ | ||
522 | control=TOPWORD; | ||
523 | } | ||
524 | |||
525 | } /* End main processing loop. */ | ||
526 | |||
527 | /* After the main processing loop has executed, all the input bytes have */ | ||
528 | /* been processed. However, the control word has still to be written to the */ | ||
529 | /* word reserved for it in the output at the start of the most recent group. */ | ||
530 | /* Before writing, the control word has to be shifted so that all the bits */ | ||
531 | /* are in the right place. The "empty" bit positions are filled with 1s */ | ||
532 | /* which partially fill the top word. */ | ||
533 | while(control&TOPWORD) control>>=1; | ||
534 | *p_control++= control &0xFF; | ||
535 | *p_control++=(control>>8) &0xFF; | ||
536 | |||
537 | /* If the last group contained no items, delete the control word too. */ | ||
538 | if (p_control==p_dst) p_dst-=2; | ||
539 | |||
540 | /* Write the length of the output block to the dst_len parameter and return. */ | ||
541 | *p_dst_len=p_dst-p_dst_first; | ||
542 | return; | ||
543 | |||
544 | /* Jump here as soon as an overrun is detected. An overrun is defined to */ | ||
545 | /* have occurred if p_dst>p_dst_first+src_len. That is, the moment the */ | ||
546 | /* length of the output written so far exceeds the length of the input block.*/ | ||
547 | /* The algorithm checks for overruns at least at the end of each group */ | ||
548 | /* which means that the maximum overrun is MAX_CMP_GROUP bytes. */ | ||
549 | /* Once an overrun occurs, the only thing to do is to set the copy flag and */ | ||
550 | /* copy the input over. */ | ||
551 | overrun: | ||
552 | #if 0 | ||
553 | *p_dst_first=FLAG_COPY; | ||
554 | fast_copy(p_src_first,p_dst_first+FLAG_BYTES,src_len); | ||
555 | *p_dst_len=src_len+FLAG_BYTES; | ||
556 | #else | ||
557 | fast_copy(p_src_first,p_dst_first,src_len); | ||
558 | *p_dst_len= -src_len; /* return a negative number to indicate uncompressed data */ | ||
559 | #endif | ||
560 | } | ||
561 | |||
562 | /******************************************************************************/ | ||
563 | |||
564 | /* Input : Hand over the required amount of working memory in p_wrk_mem. */ | ||
565 | /* Input : Specify input block using p_src_first and src_len. */ | ||
566 | /* Input : Point p_dst_first to the start of the output zone. */ | ||
567 | /* Input : Point p_dst_len to a ULONG to receive the output length. */ | ||
568 | /* Input : Input block and output zone must not overlap. User knows */ | ||
569 | /* Input : upperbound on output block length from earlier compression. */ | ||
570 | /* Input : In any case, maximum expansion possible is nine times. */ | ||
571 | /* Output : Length of output block written to *p_dst_len. */ | ||
572 | /* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */ | ||
573 | /* Output : Writes only in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */ | ||
574 | LOCAL void compress_decompress( UBYTE *p_wrk_mem, | ||
575 | UBYTE *p_src_first, LONG src_len, | ||
576 | UBYTE *p_dst_first, ULONG *p_dst_len) | ||
577 | { | ||
578 | /* Byte pointers p_src and p_dst scan through the input and output blocks. */ | ||
579 | register UBYTE *p_src = p_src_first+FLAG_BYTES; | ||
580 | register UBYTE *p_dst = p_dst_first; | ||
581 | /* we need to avoid a SEGV when trying to uncompress corrupt data */ | ||
582 | register UBYTE *p_dst_post = p_dst_first + *p_dst_len; | ||
583 | |||
584 | /* The following two variables are never modified and are used to control */ | ||
585 | /* the main loop. */ | ||
586 | UBYTE *p_src_post = p_src_first+src_len; | ||
587 | UBYTE *p_src_max16 = p_src_first+src_len-(MAX_CMP_GROUP-2); | ||
588 | |||
589 | /* The hash table is the only resident of the working memory. The hash table */ | ||
590 | /* contains HASH_TABLE_LENGTH=4096 pointers to positions in the history. To */ | ||
591 | /* keep Macintoshes happy, it is longword aligned. */ | ||
592 | UBYTE **hash = (UBYTE **) ULONG_ALIGN_UP(p_wrk_mem); | ||
593 | |||
594 | /* The variable 'control' is used to buffer the control bits which appear in */ | ||
595 | /* groups of 16 bits (control words) at the start of each compressed group. */ | ||
596 | /* When each group is read, bit 16 of the register is set to one. Whenever */ | ||
597 | /* a new bit is needed, the register is shifted right. When the value of the */ | ||
598 | /* register becomes 1, we know that we have reached the end of a group. */ | ||
599 | /* Initializing the register to 1 thus instructs the code to follow that it */ | ||
600 | /* should read a new control word immediately. */ | ||
601 | register ULONG control=1; | ||
602 | |||
603 | /* The value of 'literals' is always in the range 0..3. It is the number of */ | ||
604 | /* consecutive literal items just seen. We have to record this number so as */ | ||
605 | /* to know when to update the hash table. When literals gets to 3, there */ | ||
606 | /* have been three consecutive literals and we can update at the position of */ | ||
607 | /* the oldest of the three. */ | ||
608 | register UWORD literals=0; | ||
609 | |||
610 | /* Check the leading copy flag to see if the compressor chose to use a copy */ | ||
611 | /* operation instead of a compression operation. If a copy operation was */ | ||
612 | /* used, then all we need to do is copy the data over, set the output length */ | ||
613 | /* and return. */ | ||
614 | #if 0 | ||
615 | if (*p_src_first==FLAG_COPY) | ||
616 | { | ||
617 | fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES); | ||
618 | *p_dst_len=src_len-FLAG_BYTES; | ||
619 | return; | ||
620 | } | ||
621 | #else | ||
622 | if ( src_len < 0 ) | ||
623 | { | ||
624 | fast_copy(p_src_first,p_dst_first,-src_len ); | ||
625 | *p_dst_len = (ULONG)-src_len; | ||
626 | return; | ||
627 | } | ||
628 | #endif | ||
629 | |||
630 | /* Initialize all elements of the hash table to point to a constant string. */ | ||
631 | /* Use of an unrolled loop speeds this up considerably. */ | ||
632 | {UWORD i; UBYTE **p_h=hash; | ||
633 | # define ZJ *p_h++=START_STRING_18 | ||
634 | for (i=0;i<256;i++) /* 256=HASH_TABLE_LENGTH/16. */ | ||
635 | {ZJ;ZJ;ZJ;ZJ; | ||
636 | ZJ;ZJ;ZJ;ZJ; | ||
637 | ZJ;ZJ;ZJ;ZJ; | ||
638 | ZJ;ZJ;ZJ;ZJ;} | ||
639 | } | ||
640 | |||
641 | /* The outer loop processes either 1 or 16 items per iteration depending on */ | ||
642 | /* how close p_src is to the end of the input block. */ | ||
643 | while (p_src!=p_src_post) | ||
644 | {/* Start of outer loop */ | ||
645 | |||
646 | register UWORD unroll; /* Counts unrolled loop executions. */ | ||
647 | |||
648 | /* When 'control' has the value 1, it means that the 16 buffered control */ | ||
649 | /* bits that were read in at the start of the current group have all been */ | ||
650 | /* shifted out and that all that is left is the 1 bit that was injected */ | ||
651 | /* into bit 16 at the start of the current group. When we reach the end */ | ||
652 | /* of a group, we have to load a new control word and inject a new 1 bit. */ | ||
653 | if (control==1) | ||
654 | { | ||
655 | control=0x10000|*p_src++; | ||
656 | control|=(*p_src++)<<8; | ||
657 | } | ||
658 | |||
659 | /* If it is possible that we are within 16 groups from the end of the */ | ||
660 | /* input, execute the unrolled loop only once, else process a whole group */ | ||
661 | /* of 16 items by looping 16 times. */ | ||
662 | unroll= p_src<=p_src_max16 ? 16 : 1; | ||
663 | |||
664 | /* This inner loop processes one phrase (item) per iteration. */ | ||
665 | while (unroll--) | ||
666 | { /* Begin unrolled inner loop. */ | ||
667 | |||
668 | /* Process a literal or copy item depending on the next control bit. */ | ||
669 | if (control&1) | ||
670 | { | ||
671 | /* Copy item. */ | ||
672 | |||
673 | register UBYTE *p; /* Points to place from which to copy. */ | ||
674 | register UWORD lenmt; /* Length of copy item minus three. */ | ||
675 | register UBYTE **p_hte; /* Pointer to current hash table entry.*/ | ||
676 | register UBYTE *p_ziv=p_dst; /* Pointer to start of current Ziv. */ | ||
677 | |||
678 | /* Read and dismantle the copy word. Work out from where to copy. */ | ||
679 | lenmt=*p_src++; | ||
680 | p_hte=&hash[((lenmt&0xF0)<<4)|*p_src++]; | ||
681 | p=*p_hte; | ||
682 | lenmt&=0xF; | ||
683 | |||
684 | /* Now perform the copy using a half unrolled loop. */ | ||
685 | *p_dst++=*p++; | ||
686 | *p_dst++=*p++; | ||
687 | *p_dst++=*p++; | ||
688 | while (lenmt--) | ||
689 | *p_dst++=*p++; | ||
690 | |||
691 | /* Because we have just received 3 or more bytes in a copy item */ | ||
692 | /* (whose bytes we have just installed in the output), we are now */ | ||
693 | /* in a position to flush all the pending literal hashings that had */ | ||
694 | /* been postponed for lack of bytes. */ | ||
695 | if (literals>0) | ||
696 | { | ||
697 | register UBYTE *r=p_ziv-literals; | ||
698 | hash[HASH(r)]=r; | ||
699 | if (literals==2) | ||
700 | {r++; hash[HASH(r)]=r;} | ||
701 | literals=0; | ||
702 | } | ||
703 | |||
704 | /* In any case, we can immediately update the hash table with the */ | ||
705 | /* current position. We don't need to do a HASH(...) to work out */ | ||
706 | /* where to put the pointer, as the compressor just told us!!! */ | ||
707 | *p_hte=p_ziv; | ||
708 | |||
709 | } | ||
710 | else | ||
711 | { | ||
712 | /* Literal item. */ | ||
713 | |||
714 | /* Copy over the literal byte. */ | ||
715 | *p_dst++=*p_src++; | ||
716 | |||
717 | /* If we now have three literals waiting to be hashed into the hash */ | ||
718 | /* table, we can do one of them now (because there are three). */ | ||
719 | if (++literals == 3) | ||
720 | {register UBYTE *p=p_dst-3; hash[HASH(p)]=p; literals=2;} | ||
721 | } | ||
722 | |||
723 | /* Shift the control buffer so the next control bit is in bit 0. */ | ||
724 | control>>=1; | ||
725 | #if 1 | ||
726 | if (p_dst > p_dst_post) | ||
727 | { | ||
728 | /* Shit: we tried to decompress corrupt data */ | ||
729 | *p_dst_len = 0; | ||
730 | return; | ||
731 | } | ||
732 | #endif | ||
733 | } /* End unrolled inner loop. */ | ||
734 | |||
735 | } /* End of outer loop */ | ||
736 | |||
737 | /* Write the length of the decompressed data before returning. */ | ||
738 | *p_dst_len=p_dst-p_dst_first; | ||
739 | } | ||
740 | |||
741 | /******************************************************************************/ | ||
742 | /* End of LZRW3.C */ | ||
743 | /******************************************************************************/ | ||
diff --git a/drivers/char/ftape/compressor/lzrw3.h b/drivers/char/ftape/compressor/lzrw3.h new file mode 100644 index 000000000000..533feba47526 --- /dev/null +++ b/drivers/char/ftape/compressor/lzrw3.h | |||
@@ -0,0 +1,253 @@ | |||
1 | #ifndef _LZRW3_H | ||
2 | #define _LZRW3_H | ||
3 | /* | ||
4 | * $Source: /homes/cvs/ftape-stacked/ftape/compressor/lzrw3.h,v $ | ||
5 | * $Revision: 1.1 $ | ||
6 | * $Date: 1997/10/05 19:12:30 $ | ||
7 | * | ||
8 | * include files for lzrw3. Only slighty modified from the original | ||
9 | * version. Assembles the three include files compress.h, port.h and | ||
10 | * fastcopy.h from the original lzrw3 package. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/types.h> | ||
15 | #include <linux/string.h> | ||
16 | |||
17 | /******************************************************************************/ | ||
18 | /* */ | ||
19 | /* COMPRESS.H */ | ||
20 | /* */ | ||
21 | /******************************************************************************/ | ||
22 | /* */ | ||
23 | /* Author : Ross Williams. */ | ||
24 | /* Date : December 1989. */ | ||
25 | /* */ | ||
26 | /* This header file defines the interface to a set of functions called */ | ||
27 | /* 'compress', each member of which implements a particular data compression */ | ||
28 | /* algorithm. */ | ||
29 | /* */ | ||
30 | /* Normally in C programming, for each .H file, there is a corresponding .C */ | ||
31 | /* file that implements the functions promised in the .H file. */ | ||
32 | /* Here, there are many .C files corresponding to this header file. */ | ||
33 | /* Each comforming implementation file contains a single function */ | ||
34 | /* called 'compress' that implements a single data compression */ | ||
35 | /* algorithm that conforms with the interface specified in this header file. */ | ||
36 | /* Only one algorithm can be linked in at a time in this organization. */ | ||
37 | /* */ | ||
38 | /******************************************************************************/ | ||
39 | /* */ | ||
40 | /* DEFINITION OF FUNCTION COMPRESS */ | ||
41 | /* =============================== */ | ||
42 | /* */ | ||
43 | /* Summary of Function Compress */ | ||
44 | /* ---------------------------- */ | ||
45 | /* The action that 'compress' takes depends on its first argument called */ | ||
46 | /* 'action'. The function provides three actions: */ | ||
47 | /* */ | ||
48 | /* - Return information about the algorithm. */ | ||
49 | /* - Compress a block of memory. */ | ||
50 | /* - Decompress a block of memory. */ | ||
51 | /* */ | ||
52 | /* Parameters */ | ||
53 | /* ---------- */ | ||
54 | /* See the formal C definition later for a description of the parameters. */ | ||
55 | /* */ | ||
56 | /* Constants */ | ||
57 | /* --------- */ | ||
58 | /* COMPRESS_OVERRUN: The constant COMPRESS_OVERRUN defines by how many bytes */ | ||
59 | /* an algorithm is allowed to expand a block during a compression operation. */ | ||
60 | /* */ | ||
61 | /* Although compression algorithms usually compress data, there will always */ | ||
62 | /* be data that a given compressor will expand (this can be proven). */ | ||
63 | /* Fortunately, the degree of expansion can be limited to a single bit, by */ | ||
64 | /* copying over the input data if the data gets bigger during compression. */ | ||
65 | /* To allow for this possibility, the first bit of a compressed */ | ||
66 | /* representation can be used as a flag indicating whether the */ | ||
67 | /* input data was copied over, or truly compressed. In practice, the first */ | ||
68 | /* byte would be used to store this bit so as to maintain byte alignment. */ | ||
69 | /* */ | ||
70 | /* Unfortunately, in general, the only way to tell if an algorithm will */ | ||
71 | /* expand a particular block of data is to run the algorithm on the data. */ | ||
72 | /* If the algorithm does not continuously monitor how many output bytes it */ | ||
73 | /* has written, it might write an output block far larger than the input */ | ||
74 | /* block before realizing that it has done so. */ | ||
75 | /* On the other hand, continuous checks on output length are inefficient. */ | ||
76 | /* */ | ||
77 | /* To cater for all these problems, this interface definition: */ | ||
78 | /* > Allows a compression algorithm to return an output block that is up to */ | ||
79 | /* COMPRESS_OVERRUN bytes longer than the input block. */ | ||
80 | /* > Allows a compression algorithm to write up to COMPRESS_OVERRUN bytes */ | ||
81 | /* more than the length of the input block to the memory of the output */ | ||
82 | /* block regardless of the length of the output block eventually returned. */ | ||
83 | /* This allows an algorithm to overrun the length of the input block in the */ | ||
84 | /* output block by up to COMPRESS_OVERRUN bytes between expansion checks. */ | ||
85 | /* */ | ||
86 | /* The problem does not arise for decompression. */ | ||
87 | /* */ | ||
88 | /* Identity Action */ | ||
89 | /* --------------- */ | ||
90 | /* > action must be COMPRESS_ACTION_IDENTITY. */ | ||
91 | /* > p_dst_len must point to a longword to receive a longword address. */ | ||
92 | /* > The value of the other parameters does not matter. */ | ||
93 | /* > After execution, the longword that p_dst_len points to will be a pointer */ | ||
94 | /* to a structure of type compress_identity. */ | ||
95 | /* Thus, for example, after the call, (*p_dst_len)->memory will return the */ | ||
96 | /* number of bytes of working memory that the algorithm requires to run. */ | ||
97 | /* > The values of the identity structure returned are fixed constant */ | ||
98 | /* attributes of the algorithm and must not vary from call to call. */ | ||
99 | /* */ | ||
100 | /* Common Requirements for Compression and Decompression Actions */ | ||
101 | /* ------------------------------------------------------------- */ | ||
102 | /* > wrk_mem must point to an unused block of memory of a length specified in */ | ||
103 | /* the algorithm's identity block. The identity block can be obtained by */ | ||
104 | /* making a separate call to compress, specifying the identity action. */ | ||
105 | /* > The INPUT BLOCK is defined to be Memory[src_addr,src_addr+src_len-1]. */ | ||
106 | /* > dst_len will be used to denote *p_dst_len. */ | ||
107 | /* > dst_len is not read by compress, only written. */ | ||
108 | /* > The value of dst_len is defined only upon termination. */ | ||
109 | /* > The OUTPUT BLOCK is defined to be Memory[dst_addr,dst_addr+dst_len-1]. */ | ||
110 | /* */ | ||
111 | /* Compression Action */ | ||
112 | /* ------------------ */ | ||
113 | /* > action must be COMPRESS_ACTION_COMPRESS. */ | ||
114 | /* > src_len must be in the range [0,COMPRESS_MAX_ORG]. */ | ||
115 | /* > The OUTPUT ZONE is defined to be */ | ||
116 | /* Memory[dst_addr,dst_addr+src_len-1+COMPRESS_OVERRUN]. */ | ||
117 | /* > The function can modify any part of the output zone regardless of the */ | ||
118 | /* final length of the output block. */ | ||
119 | /* > The input block and the output zone must not overlap. */ | ||
120 | /* > dst_len will be in the range [0,src_len+COMPRESS_OVERRUN]. */ | ||
121 | /* > dst_len will be in the range [0,COMPRESS_MAX_COM] (from prev fact). */ | ||
122 | /* > The output block will consist of a representation of the input block. */ | ||
123 | /* */ | ||
124 | /* Decompression Action */ | ||
125 | /* -------------------- */ | ||
126 | /* > action must be COMPRESS_ACTION_DECOMPRESS. */ | ||
127 | /* > The input block must be the result of an earlier compression operation. */ | ||
128 | /* > If the previous fact is true, the following facts must also be true: */ | ||
129 | /* > src_len will be in the range [0,COMPRESS_MAX_COM]. */ | ||
130 | /* > dst_len will be in the range [0,COMPRESS_MAX_ORG]. */ | ||
131 | /* > The input and output blocks must not overlap. */ | ||
132 | /* > Only the output block is modified. */ | ||
133 | /* > Upon termination, the output block will consist of the bytes contained */ | ||
134 | /* in the input block passed to the earlier compression operation. */ | ||
135 | /* */ | ||
136 | /******************************************************************************/ | ||
137 | |||
138 | /******************************************************************************/ | ||
139 | /* */ | ||
140 | /* PORT.H */ | ||
141 | /* */ | ||
142 | /******************************************************************************/ | ||
143 | /* */ | ||
144 | /* This module contains macro definitions and types that are likely to */ | ||
145 | /* change between computers. */ | ||
146 | /* */ | ||
147 | /******************************************************************************/ | ||
148 | |||
149 | #ifndef DONE_PORT /* Only do this if not previously done. */ | ||
150 | |||
151 | #ifdef THINK_C | ||
152 | #define UBYTE unsigned char /* Unsigned byte */ | ||
153 | #define UWORD unsigned int /* Unsigned word (2 bytes) */ | ||
154 | #define ULONG unsigned long /* Unsigned word (4 bytes) */ | ||
155 | #define BOOL unsigned char /* Boolean */ | ||
156 | #define FOPEN_BINARY_READ "rb" /* Mode string for binary reading. */ | ||
157 | #define FOPEN_BINARY_WRITE "wb" /* Mode string for binary writing. */ | ||
158 | #define FOPEN_TEXT_APPEND "a" /* Mode string for text appending. */ | ||
159 | #define REAL double /* USed for floating point stuff. */ | ||
160 | #endif | ||
161 | #if defined(LINUX) || defined(linux) | ||
162 | #define UBYTE __u8 /* Unsigned byte */ | ||
163 | #define UWORD __u16 /* Unsigned word (2 bytes) */ | ||
164 | #define ULONG __u32 /* Unsigned word (4 bytes) */ | ||
165 | #define LONG __s32 /* Signed word (4 bytes) */ | ||
166 | #define BOOL is not used here /* Boolean */ | ||
167 | #define FOPEN_BINARY_READ not used /* Mode string for binary reading. */ | ||
168 | #define FOPEN_BINARY_WRITE not used /* Mode string for binary writing. */ | ||
169 | #define FOPEN_TEXT_APPEND not used /* Mode string for text appending. */ | ||
170 | #define REAL not used /* USed for floating point stuff. */ | ||
171 | #ifndef TRUE | ||
172 | #define TRUE 1 | ||
173 | #endif | ||
174 | #endif | ||
175 | |||
176 | #define DONE_PORT /* Don't do all this again. */ | ||
177 | #define MALLOC_FAIL NULL /* Failure status from malloc() */ | ||
178 | #define LOCAL static /* For non-exported routines. */ | ||
179 | #define EXPORT /* Signals exported function. */ | ||
180 | #define then /* Useful for aligning ifs. */ | ||
181 | |||
182 | #endif | ||
183 | |||
184 | /******************************************************************************/ | ||
185 | /* End of PORT.H */ | ||
186 | /******************************************************************************/ | ||
187 | |||
188 | #define COMPRESS_ACTION_IDENTITY 0 | ||
189 | #define COMPRESS_ACTION_COMPRESS 1 | ||
190 | #define COMPRESS_ACTION_DECOMPRESS 2 | ||
191 | |||
192 | #define COMPRESS_OVERRUN 1024 | ||
193 | #define COMPRESS_MAX_COM 0x70000000 | ||
194 | #define COMPRESS_MAX_ORG (COMPRESS_MAX_COM-COMPRESS_OVERRUN) | ||
195 | |||
196 | #define COMPRESS_MAX_STRLEN 255 | ||
197 | |||
198 | /* The following structure provides information about the algorithm. */ | ||
199 | /* > The top bit of id must be zero. The remaining bits must be chosen by */ | ||
200 | /* the author of the algorithm by tossing a coin 31 times. */ | ||
201 | /* > The amount of memory requested by the algorithm is specified in bytes */ | ||
202 | /* and must be in the range [0,0x70000000]. */ | ||
203 | /* > All strings s must be such that strlen(s)<=COMPRESS_MAX_STRLEN. */ | ||
204 | struct compress_identity | ||
205 | { | ||
206 | ULONG id; /* Identifying number of algorithm. */ | ||
207 | ULONG memory; /* Number of bytes of working memory required. */ | ||
208 | |||
209 | char *name; /* Name of algorithm. */ | ||
210 | char *version; /* Version number. */ | ||
211 | char *date; /* Date of release of this version. */ | ||
212 | char *copyright; /* Copyright message. */ | ||
213 | |||
214 | char *author; /* Author of algorithm. */ | ||
215 | char *affiliation; /* Affiliation of author. */ | ||
216 | char *vendor; /* Where the algorithm can be obtained. */ | ||
217 | }; | ||
218 | |||
219 | void lzrw3_compress( /* Single function interface to compression algorithm. */ | ||
220 | UWORD action, /* Action to be performed. */ | ||
221 | UBYTE *wrk_mem, /* Working memory temporarily given to routine to use. */ | ||
222 | UBYTE *src_adr, /* Address of input data. */ | ||
223 | LONG src_len, /* Length of input data. */ | ||
224 | UBYTE *dst_adr, /* Address of output data. */ | ||
225 | void *p_dst_len /* Pointer to a longword where routine will write: */ | ||
226 | /* If action=..IDENTITY => Adr of id structure. */ | ||
227 | /* If action=..COMPRESS => Length of output data. */ | ||
228 | /* If action=..DECOMPRESS => Length of output data. */ | ||
229 | ); | ||
230 | |||
231 | /******************************************************************************/ | ||
232 | /* End of COMPRESS.H */ | ||
233 | /******************************************************************************/ | ||
234 | |||
235 | |||
236 | /******************************************************************************/ | ||
237 | /* fast_copy.h */ | ||
238 | /******************************************************************************/ | ||
239 | |||
240 | /* This function copies a block of memory very quickly. */ | ||
241 | /* The exact speed depends on the relative alignment of the blocks of memory. */ | ||
242 | /* PRE : 0<=src_len<=(2^32)-1 . */ | ||
243 | /* PRE : Source and destination blocks must not overlap. */ | ||
244 | /* POST : MEM[dst_adr,dst_adr+src_len-1]=MEM[src_adr,src_adr+src_len-1]. */ | ||
245 | /* POST : MEM[dst_adr,dst_adr+src_len-1] is the only memory changed. */ | ||
246 | |||
247 | #define fast_copy(src,dst,len) memcpy(dst,src,len) | ||
248 | |||
249 | /******************************************************************************/ | ||
250 | /* End of fast_copy.h */ | ||
251 | /******************************************************************************/ | ||
252 | |||
253 | #endif | ||
diff --git a/drivers/char/ftape/compressor/zftape-compress.c b/drivers/char/ftape/compressor/zftape-compress.c new file mode 100644 index 000000000000..220a227e6061 --- /dev/null +++ b/drivers/char/ftape/compressor/zftape-compress.c | |||
@@ -0,0 +1,1203 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1994-1997 Claus-Justus Heine | ||
3 | |||
4 | This program is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 2, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | 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, | ||
17 | USA. | ||
18 | |||
19 | * | ||
20 | * This file implements a "generic" interface between the * | ||
21 | * zftape-driver and a compression-algorithm. The * | ||
22 | * compression-algorithm currently used is a LZ77. I use the * | ||
23 | * implementation lzrw3 by Ross N. Williams (Renaissance * | ||
24 | * Software). The compression program itself is in the file | ||
25 | * lzrw3.c * and lzrw3.h. To adopt another compression algorithm | ||
26 | * the functions * zft_compress() and zft_uncompress() must be | ||
27 | * changed * appropriately. See below. | ||
28 | */ | ||
29 | |||
30 | #include <linux/errno.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/module.h> | ||
33 | |||
34 | #include <linux/zftape.h> | ||
35 | |||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #include "../zftape/zftape-init.h" | ||
39 | #include "../zftape/zftape-eof.h" | ||
40 | #include "../zftape/zftape-ctl.h" | ||
41 | #include "../zftape/zftape-write.h" | ||
42 | #include "../zftape/zftape-read.h" | ||
43 | #include "../zftape/zftape-rw.h" | ||
44 | #include "../compressor/zftape-compress.h" | ||
45 | #include "../zftape/zftape-vtbl.h" | ||
46 | #include "../compressor/lzrw3.h" | ||
47 | |||
48 | /* | ||
49 | * global variables | ||
50 | */ | ||
51 | |||
52 | /* I handle the allocation of this buffer as a special case, because | ||
53 | * it's size varies depending on the tape length inserted. | ||
54 | */ | ||
55 | |||
56 | /* local variables | ||
57 | */ | ||
58 | static void *zftc_wrk_mem = NULL; | ||
59 | static __u8 *zftc_buf = NULL; | ||
60 | static void *zftc_scratch_buf = NULL; | ||
61 | |||
62 | /* compression statistics | ||
63 | */ | ||
64 | static unsigned int zftc_wr_uncompressed = 0; | ||
65 | static unsigned int zftc_wr_compressed = 0; | ||
66 | static unsigned int zftc_rd_uncompressed = 0; | ||
67 | static unsigned int zftc_rd_compressed = 0; | ||
68 | |||
69 | /* forward */ | ||
70 | static int zftc_write(int *write_cnt, | ||
71 | __u8 *dst_buf, const int seg_sz, | ||
72 | const __u8 __user *src_buf, const int req_len, | ||
73 | const zft_position *pos, const zft_volinfo *volume); | ||
74 | static int zftc_read(int *read_cnt, | ||
75 | __u8 __user *dst_buf, const int to_do, | ||
76 | const __u8 *src_buf, const int seg_sz, | ||
77 | const zft_position *pos, const zft_volinfo *volume); | ||
78 | static int zftc_seek(unsigned int new_block_pos, | ||
79 | zft_position *pos, const zft_volinfo *volume, | ||
80 | __u8 *buffer); | ||
81 | static void zftc_lock (void); | ||
82 | static void zftc_reset (void); | ||
83 | static void zftc_cleanup(void); | ||
84 | static void zftc_stats (void); | ||
85 | |||
86 | /* compressed segment. This conforms to QIC-80-MC, Revision K. | ||
87 | * | ||
88 | * Rev. K applies to tapes with `fixed length format' which is | ||
89 | * indicated by format code 2,3 and 5. See below for format code 4 and 6 | ||
90 | * | ||
91 | * 2 bytes: offset of compression segment structure | ||
92 | * 29k > offset >= 29k-18: data from previous segment ens in this | ||
93 | * segment and no compressed block starts | ||
94 | * in this segment | ||
95 | * offset == 0: data from previous segment occupies entire | ||
96 | * segment and continues in next segment | ||
97 | * n bytes: remainder from previous segment | ||
98 | * | ||
99 | * Rev. K: | ||
100 | * 4 bytes: 4 bytes: files set byte offset | ||
101 | * Post Rev. K and QIC-3020/3020: | ||
102 | * 8 bytes: 8 bytes: files set byte offset | ||
103 | * 2 bytes: byte count N (amount of data following) | ||
104 | * bit 15 is set if data is compressed, bit 15 is not | ||
105 | * set if data is uncompressed | ||
106 | * N bytes: data (as much as specified in the byte count) | ||
107 | * 2 bytes: byte count N_1 of next cluster | ||
108 | * N_1 bytes: data of next cluset | ||
109 | * 2 bytes: byte count N_2 of next cluster | ||
110 | * N_2 bytes: ... | ||
111 | * | ||
112 | * Note that the `N' byte count accounts only for the bytes that in the | ||
113 | * current segment if the cluster spans to the next segment. | ||
114 | */ | ||
115 | |||
116 | typedef struct | ||
117 | { | ||
118 | int cmpr_pos; /* actual position in compression buffer */ | ||
119 | int cmpr_sz; /* what is left in the compression buffer | ||
120 | * when copying the compressed data to the | ||
121 | * deblock buffer | ||
122 | */ | ||
123 | unsigned int first_block; /* location of header information in | ||
124 | * this segment | ||
125 | */ | ||
126 | unsigned int count; /* amount of data of current block | ||
127 | * contained in current segment | ||
128 | */ | ||
129 | unsigned int offset; /* offset in current segment */ | ||
130 | unsigned int spans:1; /* might continue in next segment */ | ||
131 | unsigned int uncmpr; /* 0x8000 if this block contains | ||
132 | * uncompressed data | ||
133 | */ | ||
134 | __s64 foffs; /* file set byte offset, same as in | ||
135 | * compression map segment | ||
136 | */ | ||
137 | } cmpr_info; | ||
138 | |||
139 | static cmpr_info cseg; /* static data. Must be kept uptodate and shared by | ||
140 | * read, write and seek functions | ||
141 | */ | ||
142 | |||
143 | #define DUMP_CMPR_INFO(level, msg, info) \ | ||
144 | TRACE(level, msg "\n" \ | ||
145 | KERN_INFO "cmpr_pos : %d\n" \ | ||
146 | KERN_INFO "cmpr_sz : %d\n" \ | ||
147 | KERN_INFO "first_block: %d\n" \ | ||
148 | KERN_INFO "count : %d\n" \ | ||
149 | KERN_INFO "offset : %d\n" \ | ||
150 | KERN_INFO "spans : %d\n" \ | ||
151 | KERN_INFO "uncmpr : 0x%04x\n" \ | ||
152 | KERN_INFO "foffs : " LL_X, \ | ||
153 | (info)->cmpr_pos, (info)->cmpr_sz, (info)->first_block, \ | ||
154 | (info)->count, (info)->offset, (info)->spans == 1, \ | ||
155 | (info)->uncmpr, LL((info)->foffs)) | ||
156 | |||
157 | /* dispatch compression segment info, return error code | ||
158 | * | ||
159 | * afterwards, cseg->offset points to start of data of the NEXT | ||
160 | * compressed block, and cseg->count contains the amount of data | ||
161 | * left in the actual compressed block. cseg->spans is set to 1 if | ||
162 | * the block is continued in the following segment. Otherwise it is | ||
163 | * set to 0. | ||
164 | */ | ||
165 | static int get_cseg (cmpr_info *cinfo, const __u8 *buff, | ||
166 | const unsigned int seg_sz, | ||
167 | const zft_volinfo *volume) | ||
168 | { | ||
169 | TRACE_FUN(ft_t_flow); | ||
170 | |||
171 | cinfo->first_block = GET2(buff, 0); | ||
172 | if (cinfo->first_block == 0) { /* data spans to next segment */ | ||
173 | cinfo->count = seg_sz - sizeof(__u16); | ||
174 | cinfo->offset = seg_sz; | ||
175 | cinfo->spans = 1; | ||
176 | } else { /* cluster definetely ends in this segment */ | ||
177 | if (cinfo->first_block > seg_sz) { | ||
178 | /* data corrupted */ | ||
179 | TRACE_ABORT(-EIO, ft_t_err, "corrupted data:\n" | ||
180 | KERN_INFO "segment size: %d\n" | ||
181 | KERN_INFO "first block : %d", | ||
182 | seg_sz, cinfo->first_block); | ||
183 | } | ||
184 | cinfo->count = cinfo->first_block - sizeof(__u16); | ||
185 | cinfo->offset = cinfo->first_block; | ||
186 | cinfo->spans = 0; | ||
187 | } | ||
188 | /* now get the offset the first block should have in the | ||
189 | * uncompressed data stream. | ||
190 | * | ||
191 | * For this magic `18' refer to CRF-3 standard or QIC-80MC, | ||
192 | * Rev. K. | ||
193 | */ | ||
194 | if ((seg_sz - cinfo->offset) > 18) { | ||
195 | if (volume->qic113) { /* > revision K */ | ||
196 | TRACE(ft_t_data_flow, "New QIC-113 compliance"); | ||
197 | cinfo->foffs = GET8(buff, cinfo->offset); | ||
198 | cinfo->offset += sizeof(__s64); | ||
199 | } else { | ||
200 | TRACE(/* ft_t_data_flow */ ft_t_noise, "pre QIC-113 version"); | ||
201 | cinfo->foffs = (__s64)GET4(buff, cinfo->offset); | ||
202 | cinfo->offset += sizeof(__u32); | ||
203 | } | ||
204 | } | ||
205 | if (cinfo->foffs > volume->size) { | ||
206 | TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n" | ||
207 | KERN_INFO "offset in current volume: %d\n" | ||
208 | KERN_INFO "size of current volume : %d", | ||
209 | (int)(cinfo->foffs>>10), (int)(volume->size>>10)); | ||
210 | } | ||
211 | if (cinfo->cmpr_pos + cinfo->count > volume->blk_sz) { | ||
212 | TRACE_ABORT(-EIO, ft_t_err, "Inconsistency:\n" | ||
213 | KERN_INFO "block size : %d\n" | ||
214 | KERN_INFO "data record: %d", | ||
215 | volume->blk_sz, cinfo->cmpr_pos + cinfo->count); | ||
216 | } | ||
217 | DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", cinfo); | ||
218 | TRACE_EXIT 0; | ||
219 | } | ||
220 | |||
221 | /* This one is called, when a new cluster starts in same segment. | ||
222 | * | ||
223 | * Note: if this is the first cluster in the current segment, we must | ||
224 | * not check whether there are more than 18 bytes available because | ||
225 | * this have already been done in get_cseg() and there may be less | ||
226 | * than 18 bytes available due to header information. | ||
227 | * | ||
228 | */ | ||
229 | static void get_next_cluster(cmpr_info *cluster, const __u8 *buff, | ||
230 | const int seg_sz, const int finish) | ||
231 | { | ||
232 | TRACE_FUN(ft_t_flow); | ||
233 | |||
234 | if (seg_sz - cluster->offset > 18 || cluster->foffs != 0) { | ||
235 | cluster->count = GET2(buff, cluster->offset); | ||
236 | cluster->uncmpr = cluster->count & 0x8000; | ||
237 | cluster->count -= cluster->uncmpr; | ||
238 | cluster->offset += sizeof(__u16); | ||
239 | cluster->foffs = 0; | ||
240 | if ((cluster->offset + cluster->count) < seg_sz) { | ||
241 | cluster->spans = 0; | ||
242 | } else if (cluster->offset + cluster->count == seg_sz) { | ||
243 | cluster->spans = !finish; | ||
244 | } else { | ||
245 | /* either an error or a volume written by an | ||
246 | * old version. If this is a data error, then we'll | ||
247 | * catch it later. | ||
248 | */ | ||
249 | TRACE(ft_t_data_flow, "Either error or old volume"); | ||
250 | cluster->spans = 1; | ||
251 | cluster->count = seg_sz - cluster->offset; | ||
252 | } | ||
253 | } else { | ||
254 | cluster->count = 0; | ||
255 | cluster->spans = 0; | ||
256 | cluster->foffs = 0; | ||
257 | } | ||
258 | DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */ , "", cluster); | ||
259 | TRACE_EXIT; | ||
260 | } | ||
261 | |||
262 | static void zftc_lock(void) | ||
263 | { | ||
264 | } | ||
265 | |||
266 | /* this function is needed for zftape_reset_position in zftape-io.c | ||
267 | */ | ||
268 | static void zftc_reset(void) | ||
269 | { | ||
270 | TRACE_FUN(ft_t_flow); | ||
271 | |||
272 | memset((void *)&cseg, '\0', sizeof(cseg)); | ||
273 | zftc_stats(); | ||
274 | TRACE_EXIT; | ||
275 | } | ||
276 | |||
277 | static int cmpr_mem_initialized = 0; | ||
278 | static unsigned int alloc_blksz = 0; | ||
279 | |||
280 | static int zft_allocate_cmpr_mem(unsigned int blksz) | ||
281 | { | ||
282 | TRACE_FUN(ft_t_flow); | ||
283 | |||
284 | if (cmpr_mem_initialized && blksz == alloc_blksz) { | ||
285 | TRACE_EXIT 0; | ||
286 | } | ||
287 | TRACE_CATCH(zft_vmalloc_once(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE), | ||
288 | zftc_cleanup()); | ||
289 | TRACE_CATCH(zft_vmalloc_always(&zftc_buf, blksz + CMPR_OVERRUN), | ||
290 | zftc_cleanup()); | ||
291 | alloc_blksz = blksz; | ||
292 | TRACE_CATCH(zft_vmalloc_always(&zftc_scratch_buf, blksz+CMPR_OVERRUN), | ||
293 | zftc_cleanup()); | ||
294 | cmpr_mem_initialized = 1; | ||
295 | TRACE_EXIT 0; | ||
296 | } | ||
297 | |||
298 | static void zftc_cleanup(void) | ||
299 | { | ||
300 | TRACE_FUN(ft_t_flow); | ||
301 | |||
302 | zft_vfree(&zftc_wrk_mem, CMPR_WRK_MEM_SIZE); | ||
303 | zft_vfree(&zftc_buf, alloc_blksz + CMPR_OVERRUN); | ||
304 | zft_vfree(&zftc_scratch_buf, alloc_blksz + CMPR_OVERRUN); | ||
305 | cmpr_mem_initialized = alloc_blksz = 0; | ||
306 | TRACE_EXIT; | ||
307 | } | ||
308 | |||
309 | /***************************************************************************** | ||
310 | * * | ||
311 | * The following two functions "ftape_compress()" and * | ||
312 | * "ftape_uncompress()" are the interface to the actual compression * | ||
313 | * algorithm (i.e. they are calling the "compress()" function from * | ||
314 | * the lzrw3 package for now). These routines could quite easily be * | ||
315 | * changed to adopt another compression algorithm instead of lzrw3, * | ||
316 | * which currently is used. * | ||
317 | * * | ||
318 | *****************************************************************************/ | ||
319 | |||
320 | /* called by zft_compress_write() to perform the compression. Must | ||
321 | * return the size of the compressed data. | ||
322 | * | ||
323 | * NOTE: The size of the compressed data should not exceed the size of | ||
324 | * the uncompressed data. Most compression algorithms have means | ||
325 | * to store data unchanged if the "compressed" data amount would | ||
326 | * exceed the original one. Mostly this is done by storing some | ||
327 | * flag-bytes in front of the compressed data to indicate if it | ||
328 | * is compressed or not. Thus the worst compression result | ||
329 | * length is the original length plus those flag-bytes. | ||
330 | * | ||
331 | * We don't want that, as the QIC-80 standard provides a means | ||
332 | * of marking uncompressed blocks by simply setting bit 15 of | ||
333 | * the compressed block's length. Thus a compessed block can | ||
334 | * have at most a length of 2^15-1 bytes. The QIC-80 standard | ||
335 | * restricts the block-length even further, allowing only 29k - | ||
336 | * 6 bytes. | ||
337 | * | ||
338 | * Currently, the maximum blocksize used by zftape is 28k. | ||
339 | * | ||
340 | * In short: don't exceed the length of the input-package, set | ||
341 | * bit 15 of the compressed size to 1 if you have copied data | ||
342 | * instead of compressing it. | ||
343 | */ | ||
344 | static int zft_compress(__u8 *in_buffer, unsigned int in_sz, __u8 *out_buffer) | ||
345 | { | ||
346 | __s32 compressed_sz; | ||
347 | TRACE_FUN(ft_t_flow); | ||
348 | |||
349 | |||
350 | lzrw3_compress(COMPRESS_ACTION_COMPRESS, zftc_wrk_mem, | ||
351 | in_buffer, in_sz, out_buffer, &compressed_sz); | ||
352 | if (TRACE_LEVEL >= ft_t_info) { | ||
353 | /* the compiler will optimize this away when | ||
354 | * compiled with NO_TRACE_AT_ALL option | ||
355 | */ | ||
356 | TRACE(ft_t_data_flow, "\n" | ||
357 | KERN_INFO "before compression: %d bytes\n" | ||
358 | KERN_INFO "after compresison : %d bytes", | ||
359 | in_sz, | ||
360 | (int)(compressed_sz < 0 | ||
361 | ? -compressed_sz : compressed_sz)); | ||
362 | /* for statistical purposes | ||
363 | */ | ||
364 | zftc_wr_compressed += (compressed_sz < 0 | ||
365 | ? -compressed_sz : compressed_sz); | ||
366 | zftc_wr_uncompressed += in_sz; | ||
367 | } | ||
368 | TRACE_EXIT (int)compressed_sz; | ||
369 | } | ||
370 | |||
371 | /* called by zft_compress_read() to decompress the data. Must | ||
372 | * return the size of the decompressed data for sanity checks | ||
373 | * (compared with zft_blk_sz) | ||
374 | * | ||
375 | * NOTE: Read the note for zft_compress() above! If bit 15 of the | ||
376 | * parameter in_sz is set, then the data in in_buffer isn't | ||
377 | * compressed, which must be handled by the un-compression | ||
378 | * algorithm. (I changed lzrw3 to handle this.) | ||
379 | * | ||
380 | * The parameter max_out_sz is needed to prevent buffer overruns when | ||
381 | * uncompressing corrupt data. | ||
382 | */ | ||
383 | static unsigned int zft_uncompress(__u8 *in_buffer, | ||
384 | int in_sz, | ||
385 | __u8 *out_buffer, | ||
386 | unsigned int max_out_sz) | ||
387 | { | ||
388 | TRACE_FUN(ft_t_flow); | ||
389 | |||
390 | lzrw3_compress(COMPRESS_ACTION_DECOMPRESS, zftc_wrk_mem, | ||
391 | in_buffer, (__s32)in_sz, | ||
392 | out_buffer, (__u32 *)&max_out_sz); | ||
393 | |||
394 | if (TRACE_LEVEL >= ft_t_info) { | ||
395 | TRACE(ft_t_data_flow, "\n" | ||
396 | KERN_INFO "before decompression: %d bytes\n" | ||
397 | KERN_INFO "after decompression : %d bytes", | ||
398 | in_sz < 0 ? -in_sz : in_sz,(int)max_out_sz); | ||
399 | /* for statistical purposes | ||
400 | */ | ||
401 | zftc_rd_compressed += in_sz < 0 ? -in_sz : in_sz; | ||
402 | zftc_rd_uncompressed += max_out_sz; | ||
403 | } | ||
404 | TRACE_EXIT (unsigned int)max_out_sz; | ||
405 | } | ||
406 | |||
407 | /* print some statistics about the efficiency of the compression to | ||
408 | * the kernel log | ||
409 | */ | ||
410 | static void zftc_stats(void) | ||
411 | { | ||
412 | TRACE_FUN(ft_t_flow); | ||
413 | |||
414 | if (TRACE_LEVEL < ft_t_info) { | ||
415 | TRACE_EXIT; | ||
416 | } | ||
417 | if (zftc_wr_uncompressed != 0) { | ||
418 | if (zftc_wr_compressed > (1<<14)) { | ||
419 | TRACE(ft_t_info, "compression statistics (writing):\n" | ||
420 | KERN_INFO " compr./uncmpr. : %3d %%", | ||
421 | (((zftc_wr_compressed>>10) * 100) | ||
422 | / (zftc_wr_uncompressed>>10))); | ||
423 | } else { | ||
424 | TRACE(ft_t_info, "compression statistics (writing):\n" | ||
425 | KERN_INFO " compr./uncmpr. : %3d %%", | ||
426 | ((zftc_wr_compressed * 100) | ||
427 | / zftc_wr_uncompressed)); | ||
428 | } | ||
429 | } | ||
430 | if (zftc_rd_uncompressed != 0) { | ||
431 | if (zftc_rd_compressed > (1<<14)) { | ||
432 | TRACE(ft_t_info, "compression statistics (reading):\n" | ||
433 | KERN_INFO " compr./uncmpr. : %3d %%", | ||
434 | (((zftc_rd_compressed>>10) * 100) | ||
435 | / (zftc_rd_uncompressed>>10))); | ||
436 | } else { | ||
437 | TRACE(ft_t_info, "compression statistics (reading):\n" | ||
438 | KERN_INFO " compr./uncmpr. : %3d %%", | ||
439 | ((zftc_rd_compressed * 100) | ||
440 | / zftc_rd_uncompressed)); | ||
441 | } | ||
442 | } | ||
443 | /* only print it once: */ | ||
444 | zftc_wr_uncompressed = | ||
445 | zftc_wr_compressed = | ||
446 | zftc_rd_uncompressed = | ||
447 | zftc_rd_compressed = 0; | ||
448 | TRACE_EXIT; | ||
449 | } | ||
450 | |||
451 | /* start new compressed block | ||
452 | */ | ||
453 | static int start_new_cseg(cmpr_info *cluster, | ||
454 | char *dst_buf, | ||
455 | const zft_position *pos, | ||
456 | const unsigned int blk_sz, | ||
457 | const char *src_buf, | ||
458 | const int this_segs_sz, | ||
459 | const int qic113) | ||
460 | { | ||
461 | int size_left; | ||
462 | int cp_cnt; | ||
463 | int buf_pos; | ||
464 | TRACE_FUN(ft_t_flow); | ||
465 | |||
466 | size_left = this_segs_sz - sizeof(__u16) - cluster->cmpr_sz; | ||
467 | TRACE(ft_t_data_flow,"\n" | ||
468 | KERN_INFO "segment size : %d\n" | ||
469 | KERN_INFO "compressed_sz: %d\n" | ||
470 | KERN_INFO "size_left : %d", | ||
471 | this_segs_sz, cluster->cmpr_sz, size_left); | ||
472 | if (size_left > 18) { /* start a new cluseter */ | ||
473 | cp_cnt = cluster->cmpr_sz; | ||
474 | cluster->cmpr_sz = 0; | ||
475 | buf_pos = cp_cnt + sizeof(__u16); | ||
476 | PUT2(dst_buf, 0, buf_pos); | ||
477 | |||
478 | if (qic113) { | ||
479 | __s64 foffs = pos->volume_pos; | ||
480 | if (cp_cnt) foffs += (__s64)blk_sz; | ||
481 | |||
482 | TRACE(ft_t_data_flow, "new style QIC-113 header"); | ||
483 | PUT8(dst_buf, buf_pos, foffs); | ||
484 | buf_pos += sizeof(__s64); | ||
485 | } else { | ||
486 | __u32 foffs = (__u32)pos->volume_pos; | ||
487 | if (cp_cnt) foffs += (__u32)blk_sz; | ||
488 | |||
489 | TRACE(ft_t_data_flow, "old style QIC-80MC header"); | ||
490 | PUT4(dst_buf, buf_pos, foffs); | ||
491 | buf_pos += sizeof(__u32); | ||
492 | } | ||
493 | } else if (size_left >= 0) { | ||
494 | cp_cnt = cluster->cmpr_sz; | ||
495 | cluster->cmpr_sz = 0; | ||
496 | buf_pos = cp_cnt + sizeof(__u16); | ||
497 | PUT2(dst_buf, 0, buf_pos); | ||
498 | /* zero unused part of segment. */ | ||
499 | memset(dst_buf + buf_pos, '\0', size_left); | ||
500 | buf_pos = this_segs_sz; | ||
501 | } else { /* need entire segment and more space */ | ||
502 | PUT2(dst_buf, 0, 0); | ||
503 | cp_cnt = this_segs_sz - sizeof(__u16); | ||
504 | cluster->cmpr_sz -= cp_cnt; | ||
505 | buf_pos = this_segs_sz; | ||
506 | } | ||
507 | memcpy(dst_buf + sizeof(__u16), src_buf + cluster->cmpr_pos, cp_cnt); | ||
508 | cluster->cmpr_pos += cp_cnt; | ||
509 | TRACE_EXIT buf_pos; | ||
510 | } | ||
511 | |||
512 | /* return-value: the number of bytes removed from the user-buffer | ||
513 | * `src_buf' or error code | ||
514 | * | ||
515 | * int *write_cnt : how much actually has been moved to the | ||
516 | * dst_buf. Need not be initialized when | ||
517 | * function returns with an error code | ||
518 | * (negativ return value) | ||
519 | * __u8 *dst_buf : kernel space buffer where the has to be | ||
520 | * copied to. The contents of this buffers | ||
521 | * goes to a specific segment. | ||
522 | * const int seg_sz : the size of the segment dst_buf will be | ||
523 | * copied to. | ||
524 | * const zft_position *pos : struct containing the coordinates in | ||
525 | * the current volume (byte position, | ||
526 | * segment id of current segment etc) | ||
527 | * const zft_volinfo *volume: information about the current volume, | ||
528 | * size etc. | ||
529 | * const __u8 *src_buf : user space buffer that contains the | ||
530 | * data the user wants to be written to | ||
531 | * tape. | ||
532 | * const int req_len : the amount of data the user wants to be | ||
533 | * written to tape. | ||
534 | */ | ||
535 | static int zftc_write(int *write_cnt, | ||
536 | __u8 *dst_buf, const int seg_sz, | ||
537 | const __u8 __user *src_buf, const int req_len, | ||
538 | const zft_position *pos, const zft_volinfo *volume) | ||
539 | { | ||
540 | int req_len_left = req_len; | ||
541 | int result; | ||
542 | int len_left; | ||
543 | int buf_pos_write = pos->seg_byte_pos; | ||
544 | TRACE_FUN(ft_t_flow); | ||
545 | |||
546 | /* Note: we do not unlock the module because | ||
547 | * there are some values cached in that `cseg' variable. We | ||
548 | * don't don't want to use this information when being | ||
549 | * unloaded by kerneld even when the tape is full or when we | ||
550 | * cannot allocate enough memory. | ||
551 | */ | ||
552 | if (pos->tape_pos > (volume->size-volume->blk_sz-ZFT_CMPR_OVERHEAD)) { | ||
553 | TRACE_EXIT -ENOSPC; | ||
554 | } | ||
555 | if (zft_allocate_cmpr_mem(volume->blk_sz) < 0) { | ||
556 | /* should we unlock the module? But it shouldn't | ||
557 | * be locked anyway ... | ||
558 | */ | ||
559 | TRACE_EXIT -ENOMEM; | ||
560 | } | ||
561 | if (buf_pos_write == 0) { /* fill a new segment */ | ||
562 | *write_cnt = buf_pos_write = start_new_cseg(&cseg, | ||
563 | dst_buf, | ||
564 | pos, | ||
565 | volume->blk_sz, | ||
566 | zftc_buf, | ||
567 | seg_sz, | ||
568 | volume->qic113); | ||
569 | if (cseg.cmpr_sz == 0 && cseg.cmpr_pos != 0) { | ||
570 | req_len_left -= result = volume->blk_sz; | ||
571 | cseg.cmpr_pos = 0; | ||
572 | } else { | ||
573 | result = 0; | ||
574 | } | ||
575 | } else { | ||
576 | *write_cnt = result = 0; | ||
577 | } | ||
578 | |||
579 | len_left = seg_sz - buf_pos_write; | ||
580 | while ((req_len_left > 0) && (len_left > 18)) { | ||
581 | /* now we have some size left for a new compressed | ||
582 | * block. We know, that the compression buffer is | ||
583 | * empty (else there wouldn't be any space left). | ||
584 | */ | ||
585 | if (copy_from_user(zftc_scratch_buf, src_buf + result, | ||
586 | volume->blk_sz) != 0) { | ||
587 | TRACE_EXIT -EFAULT; | ||
588 | } | ||
589 | req_len_left -= volume->blk_sz; | ||
590 | cseg.cmpr_sz = zft_compress(zftc_scratch_buf, volume->blk_sz, | ||
591 | zftc_buf); | ||
592 | if (cseg.cmpr_sz < 0) { | ||
593 | cseg.uncmpr = 0x8000; | ||
594 | cseg.cmpr_sz = -cseg.cmpr_sz; | ||
595 | } else { | ||
596 | cseg.uncmpr = 0; | ||
597 | } | ||
598 | /* increment "result" iff we copied the entire | ||
599 | * compressed block to the zft_deblock_buf | ||
600 | */ | ||
601 | len_left -= sizeof(__u16); | ||
602 | if (len_left >= cseg.cmpr_sz) { | ||
603 | len_left -= cseg.count = cseg.cmpr_sz; | ||
604 | cseg.cmpr_pos = cseg.cmpr_sz = 0; | ||
605 | result += volume->blk_sz; | ||
606 | } else { | ||
607 | cseg.cmpr_sz -= | ||
608 | cseg.cmpr_pos = | ||
609 | cseg.count = len_left; | ||
610 | len_left = 0; | ||
611 | } | ||
612 | PUT2(dst_buf, buf_pos_write, cseg.uncmpr | cseg.count); | ||
613 | buf_pos_write += sizeof(__u16); | ||
614 | memcpy(dst_buf + buf_pos_write, zftc_buf, cseg.count); | ||
615 | buf_pos_write += cseg.count; | ||
616 | *write_cnt += cseg.count + sizeof(__u16); | ||
617 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
618 | } | ||
619 | /* erase the remainder of the segment if less than 18 bytes | ||
620 | * left (18 bytes is due to the QIC-80 standard) | ||
621 | */ | ||
622 | if (len_left <= 18) { | ||
623 | memset(dst_buf + buf_pos_write, '\0', len_left); | ||
624 | (*write_cnt) += len_left; | ||
625 | } | ||
626 | TRACE(ft_t_data_flow, "returning %d", result); | ||
627 | TRACE_EXIT result; | ||
628 | } | ||
629 | |||
630 | /* out: | ||
631 | * | ||
632 | * int *read_cnt: the number of bytes we removed from the zft_deblock_buf | ||
633 | * (result) | ||
634 | * int *to_do : the remaining size of the read-request. | ||
635 | * | ||
636 | * in: | ||
637 | * | ||
638 | * char *buff : buff is the address of the upper part of the user | ||
639 | * buffer, that hasn't been filled with data yet. | ||
640 | |||
641 | * int buf_pos_read : copy of from _ftape_read() | ||
642 | * int buf_len_read : copy of buf_len_rd from _ftape_read() | ||
643 | * char *zft_deblock_buf: zft_deblock_buf | ||
644 | * unsigned short blk_sz: the block size valid for this volume, may differ | ||
645 | * from zft_blk_sz. | ||
646 | * int finish: if != 0 means that this is the last segment belonging | ||
647 | * to this volume | ||
648 | * returns the amount of data actually copied to the user-buffer | ||
649 | * | ||
650 | * to_do MUST NOT SHRINK except to indicate an EOF. In this case *to_do has to | ||
651 | * be set to 0 | ||
652 | */ | ||
653 | static int zftc_read (int *read_cnt, | ||
654 | __u8 __user *dst_buf, const int to_do, | ||
655 | const __u8 *src_buf, const int seg_sz, | ||
656 | const zft_position *pos, const zft_volinfo *volume) | ||
657 | { | ||
658 | int uncompressed_sz; | ||
659 | int result = 0; | ||
660 | int remaining = to_do; | ||
661 | TRACE_FUN(ft_t_flow); | ||
662 | |||
663 | TRACE_CATCH(zft_allocate_cmpr_mem(volume->blk_sz),); | ||
664 | if (pos->seg_byte_pos == 0) { | ||
665 | /* new segment just read | ||
666 | */ | ||
667 | TRACE_CATCH(get_cseg(&cseg, src_buf, seg_sz, volume), | ||
668 | *read_cnt = 0); | ||
669 | memcpy(zftc_buf + cseg.cmpr_pos, src_buf + sizeof(__u16), | ||
670 | cseg.count); | ||
671 | cseg.cmpr_pos += cseg.count; | ||
672 | *read_cnt = cseg.offset; | ||
673 | DUMP_CMPR_INFO(ft_t_noise /* ft_t_any */, "", &cseg); | ||
674 | } else { | ||
675 | *read_cnt = 0; | ||
676 | } | ||
677 | /* loop and uncompress until user buffer full or | ||
678 | * deblock-buffer empty | ||
679 | */ | ||
680 | TRACE(ft_t_data_flow, "compressed_sz: %d, compos : %d, *read_cnt: %d", | ||
681 | cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt); | ||
682 | while ((cseg.spans == 0) && (remaining > 0)) { | ||
683 | if (cseg.cmpr_pos != 0) { /* cmpr buf is not empty */ | ||
684 | uncompressed_sz = | ||
685 | zft_uncompress(zftc_buf, | ||
686 | cseg.uncmpr == 0x8000 ? | ||
687 | -cseg.cmpr_pos : cseg.cmpr_pos, | ||
688 | zftc_scratch_buf, | ||
689 | volume->blk_sz); | ||
690 | if (uncompressed_sz != volume->blk_sz) { | ||
691 | *read_cnt = 0; | ||
692 | TRACE_ABORT(-EIO, ft_t_warn, | ||
693 | "Uncompressed blk (%d) != blk size (%d)", | ||
694 | uncompressed_sz, volume->blk_sz); | ||
695 | } | ||
696 | if (copy_to_user(dst_buf + result, | ||
697 | zftc_scratch_buf, | ||
698 | uncompressed_sz) != 0 ) { | ||
699 | TRACE_EXIT -EFAULT; | ||
700 | } | ||
701 | remaining -= uncompressed_sz; | ||
702 | result += uncompressed_sz; | ||
703 | cseg.cmpr_pos = 0; | ||
704 | } | ||
705 | if (remaining > 0) { | ||
706 | get_next_cluster(&cseg, src_buf, seg_sz, | ||
707 | volume->end_seg == pos->seg_pos); | ||
708 | if (cseg.count != 0) { | ||
709 | memcpy(zftc_buf, src_buf + cseg.offset, | ||
710 | cseg.count); | ||
711 | cseg.cmpr_pos = cseg.count; | ||
712 | cseg.offset += cseg.count; | ||
713 | *read_cnt += cseg.count + sizeof(__u16); | ||
714 | } else { | ||
715 | remaining = 0; | ||
716 | } | ||
717 | } | ||
718 | TRACE(ft_t_data_flow, "\n" | ||
719 | KERN_INFO "compressed_sz: %d\n" | ||
720 | KERN_INFO "compos : %d\n" | ||
721 | KERN_INFO "*read_cnt : %d", | ||
722 | cseg.cmpr_sz, cseg.cmpr_pos, *read_cnt); | ||
723 | } | ||
724 | if (seg_sz - cseg.offset <= 18) { | ||
725 | *read_cnt += seg_sz - cseg.offset; | ||
726 | TRACE(ft_t_data_flow, "expanding read cnt to: %d", *read_cnt); | ||
727 | } | ||
728 | TRACE(ft_t_data_flow, "\n" | ||
729 | KERN_INFO "segment size : %d\n" | ||
730 | KERN_INFO "read count : %d\n" | ||
731 | KERN_INFO "buf_pos_read : %d\n" | ||
732 | KERN_INFO "remaining : %d", | ||
733 | seg_sz, *read_cnt, pos->seg_byte_pos, | ||
734 | seg_sz - *read_cnt - pos->seg_byte_pos); | ||
735 | TRACE(ft_t_data_flow, "returning: %d", result); | ||
736 | TRACE_EXIT result; | ||
737 | } | ||
738 | |||
739 | /* seeks to the new data-position. Reads sometimes a segment. | ||
740 | * | ||
741 | * start_seg and end_seg give the boundaries of the current volume | ||
742 | * blk_sz is the blk_sz of the current volume as stored in the | ||
743 | * volume label | ||
744 | * | ||
745 | * We don't allow blocksizes less than 1024 bytes, therefore we don't need | ||
746 | * a 64 bit argument for new_block_pos. | ||
747 | */ | ||
748 | |||
749 | static int seek_in_segment(const unsigned int to_do, cmpr_info *c_info, | ||
750 | const char *src_buf, const int seg_sz, | ||
751 | const int seg_pos, const zft_volinfo *volume); | ||
752 | static int slow_seek_forward_until_error(const unsigned int distance, | ||
753 | cmpr_info *c_info, zft_position *pos, | ||
754 | const zft_volinfo *volume, __u8 *buf); | ||
755 | static int search_valid_segment(unsigned int segment, | ||
756 | const unsigned int end_seg, | ||
757 | const unsigned int max_foffs, | ||
758 | zft_position *pos, cmpr_info *c_info, | ||
759 | const zft_volinfo *volume, __u8 *buf); | ||
760 | static int slow_seek_forward(unsigned int dest, cmpr_info *c_info, | ||
761 | zft_position *pos, const zft_volinfo *volume, | ||
762 | __u8 *buf); | ||
763 | static int compute_seg_pos(unsigned int dest, zft_position *pos, | ||
764 | const zft_volinfo *volume); | ||
765 | |||
766 | #define ZFT_SLOW_SEEK_THRESHOLD 10 /* segments */ | ||
767 | #define ZFT_FAST_SEEK_MAX_TRIALS 10 /* times */ | ||
768 | #define ZFT_FAST_SEEK_BACKUP 10 /* segments */ | ||
769 | |||
770 | static int zftc_seek(unsigned int new_block_pos, | ||
771 | zft_position *pos, const zft_volinfo *volume, __u8 *buf) | ||
772 | { | ||
773 | unsigned int dest; | ||
774 | int limit; | ||
775 | int distance; | ||
776 | int result = 0; | ||
777 | int seg_dist; | ||
778 | int new_seg; | ||
779 | int old_seg = 0; | ||
780 | int fast_seek_trials = 0; | ||
781 | TRACE_FUN(ft_t_flow); | ||
782 | |||
783 | if (new_block_pos == 0) { | ||
784 | pos->seg_pos = volume->start_seg; | ||
785 | pos->seg_byte_pos = 0; | ||
786 | pos->volume_pos = 0; | ||
787 | zftc_reset(); | ||
788 | TRACE_EXIT 0; | ||
789 | } | ||
790 | dest = new_block_pos * (volume->blk_sz >> 10); | ||
791 | distance = dest - (pos->volume_pos >> 10); | ||
792 | while (distance != 0) { | ||
793 | seg_dist = compute_seg_pos(dest, pos, volume); | ||
794 | TRACE(ft_t_noise, "\n" | ||
795 | KERN_INFO "seg_dist: %d\n" | ||
796 | KERN_INFO "distance: %d\n" | ||
797 | KERN_INFO "dest : %d\n" | ||
798 | KERN_INFO "vpos : %d\n" | ||
799 | KERN_INFO "seg_pos : %d\n" | ||
800 | KERN_INFO "trials : %d", | ||
801 | seg_dist, distance, dest, | ||
802 | (unsigned int)(pos->volume_pos>>10), pos->seg_pos, | ||
803 | fast_seek_trials); | ||
804 | if (distance > 0) { | ||
805 | if (seg_dist < 0) { | ||
806 | TRACE(ft_t_bug, "BUG: distance %d > 0, " | ||
807 | "segment difference %d < 0", | ||
808 | distance, seg_dist); | ||
809 | result = -EIO; | ||
810 | break; | ||
811 | } | ||
812 | new_seg = pos->seg_pos + seg_dist; | ||
813 | if (new_seg > volume->end_seg) { | ||
814 | new_seg = volume->end_seg; | ||
815 | } | ||
816 | if (old_seg == new_seg || /* loop */ | ||
817 | seg_dist <= ZFT_SLOW_SEEK_THRESHOLD || | ||
818 | fast_seek_trials >= ZFT_FAST_SEEK_MAX_TRIALS) { | ||
819 | TRACE(ft_t_noise, "starting slow seek:\n" | ||
820 | KERN_INFO "fast seek failed too often: %s\n" | ||
821 | KERN_INFO "near target position : %s\n" | ||
822 | KERN_INFO "looping between two segs : %s", | ||
823 | (fast_seek_trials >= | ||
824 | ZFT_FAST_SEEK_MAX_TRIALS) | ||
825 | ? "yes" : "no", | ||
826 | (seg_dist <= ZFT_SLOW_SEEK_THRESHOLD) | ||
827 | ? "yes" : "no", | ||
828 | (old_seg == new_seg) | ||
829 | ? "yes" : "no"); | ||
830 | result = slow_seek_forward(dest, &cseg, | ||
831 | pos, volume, buf); | ||
832 | break; | ||
833 | } | ||
834 | old_seg = new_seg; | ||
835 | limit = volume->end_seg; | ||
836 | fast_seek_trials ++; | ||
837 | for (;;) { | ||
838 | result = search_valid_segment(new_seg, limit, | ||
839 | volume->size, | ||
840 | pos, &cseg, | ||
841 | volume, buf); | ||
842 | if (result == 0 || result == -EINTR) { | ||
843 | break; | ||
844 | } | ||
845 | if (new_seg == volume->start_seg) { | ||
846 | result = -EIO; /* set errror | ||
847 | * condition | ||
848 | */ | ||
849 | break; | ||
850 | } | ||
851 | limit = new_seg; | ||
852 | new_seg -= ZFT_FAST_SEEK_BACKUP; | ||
853 | if (new_seg < volume->start_seg) { | ||
854 | new_seg = volume->start_seg; | ||
855 | } | ||
856 | } | ||
857 | if (result < 0) { | ||
858 | TRACE(ft_t_warn, | ||
859 | "Couldn't find a readable segment"); | ||
860 | break; | ||
861 | } | ||
862 | } else /* if (distance < 0) */ { | ||
863 | if (seg_dist > 0) { | ||
864 | TRACE(ft_t_bug, "BUG: distance %d < 0, " | ||
865 | "segment difference %d >0", | ||
866 | distance, seg_dist); | ||
867 | result = -EIO; | ||
868 | break; | ||
869 | } | ||
870 | new_seg = pos->seg_pos + seg_dist; | ||
871 | if (fast_seek_trials > 0 && seg_dist == 0) { | ||
872 | /* this avoids sticking to the same | ||
873 | * segment all the time. On the other hand: | ||
874 | * if we got here for the first time, and the | ||
875 | * deblock_buffer still contains a valid | ||
876 | * segment, then there is no need to skip to | ||
877 | * the previous segment if the desired position | ||
878 | * is inside this segment. | ||
879 | */ | ||
880 | new_seg --; | ||
881 | } | ||
882 | if (new_seg < volume->start_seg) { | ||
883 | new_seg = volume->start_seg; | ||
884 | } | ||
885 | limit = pos->seg_pos; | ||
886 | fast_seek_trials ++; | ||
887 | for (;;) { | ||
888 | result = search_valid_segment(new_seg, limit, | ||
889 | pos->volume_pos, | ||
890 | pos, &cseg, | ||
891 | volume, buf); | ||
892 | if (result == 0 || result == -EINTR) { | ||
893 | break; | ||
894 | } | ||
895 | if (new_seg == volume->start_seg) { | ||
896 | result = -EIO; /* set errror | ||
897 | * condition | ||
898 | */ | ||
899 | break; | ||
900 | } | ||
901 | limit = new_seg; | ||
902 | new_seg -= ZFT_FAST_SEEK_BACKUP; | ||
903 | if (new_seg < volume->start_seg) { | ||
904 | new_seg = volume->start_seg; | ||
905 | } | ||
906 | } | ||
907 | if (result < 0) { | ||
908 | TRACE(ft_t_warn, | ||
909 | "Couldn't find a readable segment"); | ||
910 | break; | ||
911 | } | ||
912 | } | ||
913 | distance = dest - (pos->volume_pos >> 10); | ||
914 | } | ||
915 | TRACE_EXIT result; | ||
916 | } | ||
917 | |||
918 | |||
919 | /* advance inside the given segment at most to_do bytes. | ||
920 | * of kilobytes moved | ||
921 | */ | ||
922 | |||
923 | static int seek_in_segment(const unsigned int to_do, | ||
924 | cmpr_info *c_info, | ||
925 | const char *src_buf, | ||
926 | const int seg_sz, | ||
927 | const int seg_pos, | ||
928 | const zft_volinfo *volume) | ||
929 | { | ||
930 | int result = 0; | ||
931 | int blk_sz = volume->blk_sz >> 10; | ||
932 | int remaining = to_do; | ||
933 | TRACE_FUN(ft_t_flow); | ||
934 | |||
935 | if (c_info->offset == 0) { | ||
936 | /* new segment just read | ||
937 | */ | ||
938 | TRACE_CATCH(get_cseg(c_info, src_buf, seg_sz, volume),); | ||
939 | c_info->cmpr_pos += c_info->count; | ||
940 | DUMP_CMPR_INFO(ft_t_noise, "", c_info); | ||
941 | } | ||
942 | /* loop and uncompress until user buffer full or | ||
943 | * deblock-buffer empty | ||
944 | */ | ||
945 | TRACE(ft_t_noise, "compressed_sz: %d, compos : %d", | ||
946 | c_info->cmpr_sz, c_info->cmpr_pos); | ||
947 | while (c_info->spans == 0 && remaining > 0) { | ||
948 | if (c_info->cmpr_pos != 0) { /* cmpr buf is not empty */ | ||
949 | result += blk_sz; | ||
950 | remaining -= blk_sz; | ||
951 | c_info->cmpr_pos = 0; | ||
952 | } | ||
953 | if (remaining > 0) { | ||
954 | get_next_cluster(c_info, src_buf, seg_sz, | ||
955 | volume->end_seg == seg_pos); | ||
956 | if (c_info->count != 0) { | ||
957 | c_info->cmpr_pos = c_info->count; | ||
958 | c_info->offset += c_info->count; | ||
959 | } else { | ||
960 | break; | ||
961 | } | ||
962 | } | ||
963 | /* Allow escape from this loop on signal! | ||
964 | */ | ||
965 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
966 | DUMP_CMPR_INFO(ft_t_noise, "", c_info); | ||
967 | TRACE(ft_t_noise, "to_do: %d", remaining); | ||
968 | } | ||
969 | if (seg_sz - c_info->offset <= 18) { | ||
970 | c_info->offset = seg_sz; | ||
971 | } | ||
972 | TRACE(ft_t_noise, "\n" | ||
973 | KERN_INFO "segment size : %d\n" | ||
974 | KERN_INFO "buf_pos_read : %d\n" | ||
975 | KERN_INFO "remaining : %d", | ||
976 | seg_sz, c_info->offset, | ||
977 | seg_sz - c_info->offset); | ||
978 | TRACE_EXIT result; | ||
979 | } | ||
980 | |||
981 | static int slow_seek_forward_until_error(const unsigned int distance, | ||
982 | cmpr_info *c_info, | ||
983 | zft_position *pos, | ||
984 | const zft_volinfo *volume, | ||
985 | __u8 *buf) | ||
986 | { | ||
987 | unsigned int remaining = distance; | ||
988 | int seg_sz; | ||
989 | int seg_pos; | ||
990 | int result; | ||
991 | TRACE_FUN(ft_t_flow); | ||
992 | |||
993 | seg_pos = pos->seg_pos; | ||
994 | do { | ||
995 | TRACE_CATCH(seg_sz = zft_fetch_segment(seg_pos, buf, | ||
996 | FT_RD_AHEAD),); | ||
997 | /* now we have the contents of the actual segment in | ||
998 | * the deblock buffer | ||
999 | */ | ||
1000 | TRACE_CATCH(result = seek_in_segment(remaining, c_info, buf, | ||
1001 | seg_sz, seg_pos,volume),); | ||
1002 | remaining -= result; | ||
1003 | pos->volume_pos += result<<10; | ||
1004 | pos->seg_pos = seg_pos; | ||
1005 | pos->seg_byte_pos = c_info->offset; | ||
1006 | seg_pos ++; | ||
1007 | if (seg_pos <= volume->end_seg && c_info->offset == seg_sz) { | ||
1008 | pos->seg_pos ++; | ||
1009 | pos->seg_byte_pos = 0; | ||
1010 | c_info->offset = 0; | ||
1011 | } | ||
1012 | /* Allow escape from this loop on signal! | ||
1013 | */ | ||
1014 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
1015 | TRACE(ft_t_noise, "\n" | ||
1016 | KERN_INFO "remaining: %d\n" | ||
1017 | KERN_INFO "seg_pos: %d\n" | ||
1018 | KERN_INFO "end_seg: %d\n" | ||
1019 | KERN_INFO "result: %d", | ||
1020 | remaining, seg_pos, volume->end_seg, result); | ||
1021 | } while (remaining > 0 && seg_pos <= volume->end_seg); | ||
1022 | TRACE_EXIT 0; | ||
1023 | } | ||
1024 | |||
1025 | /* return segment id of next segment containing valid data, -EIO otherwise | ||
1026 | */ | ||
1027 | static int search_valid_segment(unsigned int segment, | ||
1028 | const unsigned int end_seg, | ||
1029 | const unsigned int max_foffs, | ||
1030 | zft_position *pos, | ||
1031 | cmpr_info *c_info, | ||
1032 | const zft_volinfo *volume, | ||
1033 | __u8 *buf) | ||
1034 | { | ||
1035 | cmpr_info tmp_info; | ||
1036 | int seg_sz; | ||
1037 | TRACE_FUN(ft_t_flow); | ||
1038 | |||
1039 | memset(&tmp_info, 0, sizeof(cmpr_info)); | ||
1040 | while (segment <= end_seg) { | ||
1041 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
1042 | TRACE(ft_t_noise, | ||
1043 | "Searching readable segment between %d and %d", | ||
1044 | segment, end_seg); | ||
1045 | seg_sz = zft_fetch_segment(segment, buf, FT_RD_AHEAD); | ||
1046 | if ((seg_sz > 0) && | ||
1047 | (get_cseg (&tmp_info, buf, seg_sz, volume) >= 0) && | ||
1048 | (tmp_info.foffs != 0 || segment == volume->start_seg)) { | ||
1049 | if ((tmp_info.foffs>>10) > max_foffs) { | ||
1050 | TRACE_ABORT(-EIO, ft_t_noise, "\n" | ||
1051 | KERN_INFO "cseg.foff: %d\n" | ||
1052 | KERN_INFO "dest : %d", | ||
1053 | (int)(tmp_info.foffs >> 10), | ||
1054 | max_foffs); | ||
1055 | } | ||
1056 | DUMP_CMPR_INFO(ft_t_noise, "", &tmp_info); | ||
1057 | *c_info = tmp_info; | ||
1058 | pos->seg_pos = segment; | ||
1059 | pos->volume_pos = c_info->foffs; | ||
1060 | pos->seg_byte_pos = c_info->offset; | ||
1061 | TRACE(ft_t_noise, "found segment at %d", segment); | ||
1062 | TRACE_EXIT 0; | ||
1063 | } | ||
1064 | segment++; | ||
1065 | } | ||
1066 | TRACE_EXIT -EIO; | ||
1067 | } | ||
1068 | |||
1069 | static int slow_seek_forward(unsigned int dest, | ||
1070 | cmpr_info *c_info, | ||
1071 | zft_position *pos, | ||
1072 | const zft_volinfo *volume, | ||
1073 | __u8 *buf) | ||
1074 | { | ||
1075 | unsigned int distance; | ||
1076 | int result = 0; | ||
1077 | TRACE_FUN(ft_t_flow); | ||
1078 | |||
1079 | distance = dest - (pos->volume_pos >> 10); | ||
1080 | while ((distance > 0) && | ||
1081 | (result = slow_seek_forward_until_error(distance, | ||
1082 | c_info, | ||
1083 | pos, | ||
1084 | volume, | ||
1085 | buf)) < 0) { | ||
1086 | if (result == -EINTR) { | ||
1087 | break; | ||
1088 | } | ||
1089 | TRACE(ft_t_noise, "seg_pos: %d", pos->seg_pos); | ||
1090 | /* the failing segment is either pos->seg_pos or | ||
1091 | * pos->seg_pos + 1. There is no need to further try | ||
1092 | * that segment, because ftape_read_segment() already | ||
1093 | * has tried very much to read it. So we start with | ||
1094 | * following segment, which is pos->seg_pos + 1 | ||
1095 | */ | ||
1096 | if(search_valid_segment(pos->seg_pos+1, volume->end_seg, dest, | ||
1097 | pos, c_info, | ||
1098 | volume, buf) < 0) { | ||
1099 | TRACE(ft_t_noise, "search_valid_segment() failed"); | ||
1100 | result = -EIO; | ||
1101 | break; | ||
1102 | } | ||
1103 | distance = dest - (pos->volume_pos >> 10); | ||
1104 | result = 0; | ||
1105 | TRACE(ft_t_noise, "segment: %d", pos->seg_pos); | ||
1106 | /* found valid segment, retry the seek */ | ||
1107 | } | ||
1108 | TRACE_EXIT result; | ||
1109 | } | ||
1110 | |||
1111 | static int compute_seg_pos(const unsigned int dest, | ||
1112 | zft_position *pos, | ||
1113 | const zft_volinfo *volume) | ||
1114 | { | ||
1115 | int segment; | ||
1116 | int distance = dest - (pos->volume_pos >> 10); | ||
1117 | unsigned int raw_size; | ||
1118 | unsigned int virt_size; | ||
1119 | unsigned int factor; | ||
1120 | TRACE_FUN(ft_t_flow); | ||
1121 | |||
1122 | if (distance >= 0) { | ||
1123 | raw_size = volume->end_seg - pos->seg_pos + 1; | ||
1124 | virt_size = ((unsigned int)(volume->size>>10) | ||
1125 | - (unsigned int)(pos->volume_pos>>10) | ||
1126 | + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1); | ||
1127 | virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS; | ||
1128 | if (virt_size == 0 || raw_size == 0) { | ||
1129 | TRACE_EXIT 0; | ||
1130 | } | ||
1131 | if (raw_size >= (1<<25)) { | ||
1132 | factor = raw_size/(virt_size>>7); | ||
1133 | } else { | ||
1134 | factor = (raw_size<<7)/virt_size; | ||
1135 | } | ||
1136 | segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS); | ||
1137 | segment = (segment * factor)>>7; | ||
1138 | } else { | ||
1139 | raw_size = pos->seg_pos - volume->start_seg + 1; | ||
1140 | virt_size = ((unsigned int)(pos->volume_pos>>10) | ||
1141 | + FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS - 1); | ||
1142 | virt_size /= FT_SECTORS_PER_SEGMENT - FT_ECC_SECTORS; | ||
1143 | if (virt_size == 0 || raw_size == 0) { | ||
1144 | TRACE_EXIT 0; | ||
1145 | } | ||
1146 | if (raw_size >= (1<<25)) { | ||
1147 | factor = raw_size/(virt_size>>7); | ||
1148 | } else { | ||
1149 | factor = (raw_size<<7)/virt_size; | ||
1150 | } | ||
1151 | segment = distance/(FT_SECTORS_PER_SEGMENT-FT_ECC_SECTORS); | ||
1152 | } | ||
1153 | TRACE(ft_t_noise, "factor: %d/%d", factor, 1<<7); | ||
1154 | TRACE_EXIT segment; | ||
1155 | } | ||
1156 | |||
1157 | static struct zft_cmpr_ops cmpr_ops = { | ||
1158 | zftc_write, | ||
1159 | zftc_read, | ||
1160 | zftc_seek, | ||
1161 | zftc_lock, | ||
1162 | zftc_reset, | ||
1163 | zftc_cleanup | ||
1164 | }; | ||
1165 | |||
1166 | int zft_compressor_init(void) | ||
1167 | { | ||
1168 | TRACE_FUN(ft_t_flow); | ||
1169 | |||
1170 | #ifdef MODULE | ||
1171 | printk(KERN_INFO "zftape compressor v1.00a 970514 for " FTAPE_VERSION "\n"); | ||
1172 | if (TRACE_LEVEL >= ft_t_info) { | ||
1173 | printk( | ||
1174 | KERN_INFO "(c) 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" | ||
1175 | KERN_INFO "Compressor for zftape (lzrw3 algorithm)\n"); | ||
1176 | } | ||
1177 | #else /* !MODULE */ | ||
1178 | /* print a short no-nonsense boot message */ | ||
1179 | printk("zftape compressor v1.00a 970514\n"); | ||
1180 | printk("For use with " FTAPE_VERSION "\n"); | ||
1181 | #endif /* MODULE */ | ||
1182 | TRACE(ft_t_info, "zft_compressor_init @ 0x%p", zft_compressor_init); | ||
1183 | TRACE(ft_t_info, "installing compressor for zftape ..."); | ||
1184 | TRACE_CATCH(zft_cmpr_register(&cmpr_ops),); | ||
1185 | TRACE_EXIT 0; | ||
1186 | } | ||
1187 | |||
1188 | #ifdef MODULE | ||
1189 | |||
1190 | MODULE_AUTHOR( | ||
1191 | "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de"); | ||
1192 | MODULE_DESCRIPTION( | ||
1193 | "Compression routines for zftape. Uses the lzrw3 algorithm by Ross Williams"); | ||
1194 | MODULE_LICENSE("GPL"); | ||
1195 | |||
1196 | /* Called by modules package when installing the driver | ||
1197 | */ | ||
1198 | int init_module(void) | ||
1199 | { | ||
1200 | return zft_compressor_init(); | ||
1201 | } | ||
1202 | |||
1203 | #endif /* MODULE */ | ||
diff --git a/drivers/char/ftape/compressor/zftape-compress.h b/drivers/char/ftape/compressor/zftape-compress.h new file mode 100644 index 000000000000..f200741e33bf --- /dev/null +++ b/drivers/char/ftape/compressor/zftape-compress.h | |||
@@ -0,0 +1,83 @@ | |||
1 | #ifndef _ZFTAPE_COMPRESS_H | ||
2 | #define _ZFTAPE_COMPRESS_H | ||
3 | /* | ||
4 | * Copyright (c) 1994-1997 Claus-Justus Heine | ||
5 | |||
6 | This program is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU General Public License as | ||
8 | published by the Free Software Foundation; either version 2, or (at | ||
9 | your option) any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, but | ||
12 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; see the file COPYING. If not, write to | ||
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
19 | USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/compressor/zftape-compress.h,v $ | ||
23 | * $Revision: 1.1 $ | ||
24 | * $Date: 1997/10/05 19:12:32 $ | ||
25 | * | ||
26 | * This file contains macros and definitions for zftape's | ||
27 | * builtin compression code. | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include "../zftape/zftape-buffers.h" | ||
32 | #include "../zftape/zftape-vtbl.h" | ||
33 | #include "../compressor/lzrw3.h" | ||
34 | |||
35 | /* CMPR_WRK_MEM_SIZE gives the size of the compression wrk_mem */ | ||
36 | /* I got these out of lzrw3.c */ | ||
37 | #define U(X) ((__u32) X) | ||
38 | #define SIZE_P_BYTE (U(sizeof(__u8 *))) | ||
39 | #define ALIGNMENT_FUDGE (U(16)) | ||
40 | |||
41 | #define CMPR_WRK_MEM_SIZE (U(4096)*(SIZE_P_BYTE) + ALIGNMENT_FUDGE) | ||
42 | |||
43 | /* the maximum number of bytes the size of the "compressed" data can | ||
44 | * exceed the uncompressed data. As it is quite useless to compress | ||
45 | * data twice it is sometimes the case that it is more efficient to | ||
46 | * copy a block of data but to feed it to the "compression" | ||
47 | * algorithm. In this case there are some flag bytes or the like | ||
48 | * proceding the "compressed" data. THAT MUST NOT BE THE CASE for the | ||
49 | * algorithm we use for this driver. Instead, the high bit 15 of | ||
50 | * compressed_size: | ||
51 | * | ||
52 | * compressed_size = ftape_compress() | ||
53 | * | ||
54 | * must be set in such a case. | ||
55 | * | ||
56 | * Nevertheless, it might also be as for lzrw3 that there is an | ||
57 | * "intermediate" overrun that exceeds the amount of the compressed | ||
58 | * data that is actually produced. During the algorithm we need in the | ||
59 | * worst case MAX_CMP_GROUP bytes more than the input-size. | ||
60 | */ | ||
61 | #define MAX_CMP_GROUP (2+16*2) /* from lzrw3.c */ | ||
62 | |||
63 | #define CMPR_OVERRUN MAX_CMP_GROUP /* during compression */ | ||
64 | |||
65 | /****************************************************/ | ||
66 | |||
67 | #define CMPR_BUFFER_SIZE (MAX_BLOCK_SIZE + CMPR_OVERRUN) | ||
68 | |||
69 | /* the compression map stores the byte offset compressed blocks within | ||
70 | * the current volume for catridges with format code 2,3 and 5 | ||
71 | * (and old versions of zftape) and the offset measured in kilobytes for | ||
72 | * format code 4 and 6. This gives us a possible max. size of a | ||
73 | * compressed volume of 1024*4GIG which should be enough. | ||
74 | */ | ||
75 | typedef __u32 CmprMap; | ||
76 | |||
77 | /* globals | ||
78 | */ | ||
79 | |||
80 | /* exported functions | ||
81 | */ | ||
82 | |||
83 | #endif /* _ZFTAPE_COMPRESS_H */ | ||
diff --git a/drivers/char/ftape/lowlevel/Makefile b/drivers/char/ftape/lowlevel/Makefile new file mode 100644 index 000000000000..febab07ba427 --- /dev/null +++ b/drivers/char/ftape/lowlevel/Makefile | |||
@@ -0,0 +1,43 @@ | |||
1 | # | ||
2 | # Copyright (C) 1996, 1997 Clau-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 | # $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/Makefile,v $ | ||
19 | # $Revision: 1.4 $ | ||
20 | # $Date: 1997/10/07 09:26:02 $ | ||
21 | # | ||
22 | # Makefile for the lowlevel part QIC-40/80/3010/3020 floppy-tape | ||
23 | # driver for Linux. | ||
24 | # | ||
25 | |||
26 | obj-$(CONFIG_FTAPE) += ftape.o | ||
27 | |||
28 | ftape-objs := ftape-init.o fdc-io.o fdc-isr.o \ | ||
29 | ftape-bsm.o ftape-ctl.o ftape-read.o ftape-rw.o \ | ||
30 | ftape-write.o ftape-io.o ftape-calibr.o ftape-ecc.o fc-10.o \ | ||
31 | ftape-buffer.o ftape-format.o ftape_syms.o | ||
32 | |||
33 | ifeq ($(CONFIG_FTAPE),y) | ||
34 | ftape-objs += ftape-setup.o | ||
35 | endif | ||
36 | |||
37 | ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||
38 | ftape-objs += ftape-tracing.o | ||
39 | endif | ||
40 | |||
41 | ifeq ($(CONFIG_FT_PROC_FS),y) | ||
42 | ftape-objs += ftape-proc.o | ||
43 | endif | ||
diff --git a/drivers/char/ftape/lowlevel/fc-10.c b/drivers/char/ftape/lowlevel/fc-10.c new file mode 100644 index 000000000000..9bc1cddade76 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fc-10.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * | ||
3 | |||
4 | Copyright (C) 1993,1994 Jon Tombs. | ||
5 | |||
6 | This program is distributed in the hope that it will be useful, | ||
7 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | GNU General Public License for more details. | ||
10 | |||
11 | The entire guts of this program was written by dosemu, modified to | ||
12 | record reads and writes to the ports in the 0x180-0x188 address space, | ||
13 | while running the CMS program TAPE.EXE V2.0.5 supplied with the drive. | ||
14 | |||
15 | Modified to use an array of addresses and generally cleaned up (made | ||
16 | much shorter) 4 June 94, dosemu isn't that good at writing short code it | ||
17 | would seem :-). Made independent of 0x180, but I doubt it will work | ||
18 | at any other address. | ||
19 | |||
20 | Modified for distribution with ftape source. 21 June 94, SJL. | ||
21 | |||
22 | Modifications on 20 October 95, by Daniel Cohen (catman@wpi.edu): | ||
23 | Modified to support different DMA, IRQ, and IO Ports. Borland's | ||
24 | Turbo Debugger in virtual 8086 mode (TD386.EXE with hardware breakpoints | ||
25 | provided by the TDH386.SYS Device Driver) was used on the CMS program | ||
26 | TAPE V4.0.5. I set breakpoints on I/O to ports 0x180-0x187. Note that | ||
27 | CMS's program will not successfully configure the tape drive if you set | ||
28 | breakpoints on IO Reads, but you can set them on IO Writes without problems. | ||
29 | Known problems: | ||
30 | - You can not use DMA Channels 5 or 7. | ||
31 | |||
32 | Modification on 29 January 96, by Daniel Cohen (catman@wpi.edu): | ||
33 | Modified to only accept IRQs 3 - 7, or 9. Since we can only send a 3 bit | ||
34 | number representing the IRQ to the card, special handling is required when | ||
35 | IRQ 9 is selected. IRQ 2 and 9 are the same, and we should request IRQ 9 | ||
36 | from the kernel while telling the card to use IRQ 2. Thanks to Greg | ||
37 | Crider (gcrider@iclnet.org) for finding and locating this bug, as well as | ||
38 | testing the patch. | ||
39 | |||
40 | Modification on 11 December 96, by Claus Heine (claus@momo.math.rwth-aachen.de): | ||
41 | Modified a little to use variahle ft_fdc_base, ft_fdc_irq, ft_fdc_dma | ||
42 | instead of preprocessor symbols. Thus we can compile this into the module | ||
43 | or kernel and let the user specify the options as command line arguments. | ||
44 | |||
45 | * | ||
46 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.c,v $ | ||
47 | * $Revision: 1.2 $ | ||
48 | * $Date: 1997/10/05 19:18:04 $ | ||
49 | * | ||
50 | * This file contains code for the CMS FC-10/FC-20 card. | ||
51 | */ | ||
52 | |||
53 | #include <asm/io.h> | ||
54 | #include <linux/ftape.h> | ||
55 | #include "../lowlevel/ftape-tracing.h" | ||
56 | #include "../lowlevel/fdc-io.h" | ||
57 | #include "../lowlevel/fc-10.h" | ||
58 | |||
59 | static __u16 inbs_magic[] = { | ||
60 | 0x3, 0x3, 0x0, 0x4, 0x7, 0x2, 0x5, 0x3, 0x1, 0x4, | ||
61 | 0x3, 0x5, 0x2, 0x0, 0x3, 0x7, 0x4, 0x2, | ||
62 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 | ||
63 | }; | ||
64 | |||
65 | static __u16 fc10_ports[] = { | ||
66 | 0x180, 0x210, 0x2A0, 0x300, 0x330, 0x340, 0x370 | ||
67 | }; | ||
68 | |||
69 | int fc10_enable(void) | ||
70 | { | ||
71 | int i; | ||
72 | __u8 cardConfig = 0x00; | ||
73 | __u8 x; | ||
74 | TRACE_FUN(ft_t_flow); | ||
75 | |||
76 | /* This code will only work if the FC-10 (or FC-20) is set to | ||
77 | * use DMA channels 1, 2, or 3. DMA channels 5 and 7 seem to be | ||
78 | * initialized by the same command as channels 1 and 3, respectively. | ||
79 | */ | ||
80 | if (ft_fdc_dma > 3) { | ||
81 | TRACE_ABORT(0, ft_t_err, | ||
82 | "Error: The FC-10/20 must be set to use DMA channels 1, 2, or 3!"); | ||
83 | } | ||
84 | /* Only allow the FC-10/20 to use IRQ 3-7, or 9. Note that CMS's program | ||
85 | * only accepts IRQ's 2-7, but in linux, IRQ 2 is the same as IRQ 9. | ||
86 | */ | ||
87 | if (ft_fdc_irq < 3 || ft_fdc_irq == 8 || ft_fdc_irq > 9) { | ||
88 | TRACE_ABORT(0, ft_t_err, | ||
89 | "Error: The FC-10/20 must be set to use IRQ levels 3 - 7, or 9!\n" | ||
90 | KERN_INFO "Note: IRQ 9 is the same as IRQ 2"); | ||
91 | } | ||
92 | /* Clear state machine ??? | ||
93 | */ | ||
94 | for (i = 0; i < NR_ITEMS(inbs_magic); i++) { | ||
95 | inb(ft_fdc_base + inbs_magic[i]); | ||
96 | } | ||
97 | outb(0x0, ft_fdc_base); | ||
98 | |||
99 | x = inb(ft_fdc_base); | ||
100 | if (x == 0x13 || x == 0x93) { | ||
101 | for (i = 1; i < 8; i++) { | ||
102 | if (inb(ft_fdc_base + i) != x) { | ||
103 | TRACE_EXIT 0; | ||
104 | } | ||
105 | } | ||
106 | } else { | ||
107 | TRACE_EXIT 0; | ||
108 | } | ||
109 | |||
110 | outb(0x8, ft_fdc_base); | ||
111 | |||
112 | for (i = 0; i < 8; i++) { | ||
113 | if (inb(ft_fdc_base + i) != 0x0) { | ||
114 | TRACE_EXIT 0; | ||
115 | } | ||
116 | } | ||
117 | outb(0x10, ft_fdc_base); | ||
118 | |||
119 | for (i = 0; i < 8; i++) { | ||
120 | if (inb(ft_fdc_base + i) != 0xff) { | ||
121 | TRACE_EXIT 0; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | /* Okay, we found a FC-10 card ! ??? | ||
126 | */ | ||
127 | outb(0x0, fdc.ccr); | ||
128 | |||
129 | /* Clear state machine again ??? | ||
130 | */ | ||
131 | for (i = 0; i < NR_ITEMS(inbs_magic); i++) { | ||
132 | inb(ft_fdc_base + inbs_magic[i]); | ||
133 | } | ||
134 | /* Send io port */ | ||
135 | for (i = 0; i < NR_ITEMS(fc10_ports); i++) | ||
136 | if (ft_fdc_base == fc10_ports[i]) | ||
137 | cardConfig = i + 1; | ||
138 | if (cardConfig == 0) { | ||
139 | TRACE_EXIT 0; /* Invalid I/O Port */ | ||
140 | } | ||
141 | /* and IRQ - If using IRQ 9, tell the FC card it is actually IRQ 2 */ | ||
142 | if (ft_fdc_irq != 9) | ||
143 | cardConfig |= ft_fdc_irq << 3; | ||
144 | else | ||
145 | cardConfig |= 2 << 3; | ||
146 | |||
147 | /* and finally DMA Channel */ | ||
148 | cardConfig |= ft_fdc_dma << 6; | ||
149 | outb(cardConfig, ft_fdc_base); /* DMA [2 bits]/IRQ [3 bits]/BASE [3 bits] */ | ||
150 | |||
151 | /* Enable FC-10 ??? | ||
152 | */ | ||
153 | outb(0, fdc.ccr); | ||
154 | outb(0, fdc.dor2); | ||
155 | outb(FDC_DMA_MODE /* 8 */, fdc.dor); | ||
156 | outb(FDC_DMA_MODE /* 8 */, fdc.dor); | ||
157 | outb(1, fdc.dor2); | ||
158 | |||
159 | /************************************* | ||
160 | * | ||
161 | * cH: why the hell should this be necessary? This is done | ||
162 | * by fdc_reset()!!! | ||
163 | * | ||
164 | *************************************/ | ||
165 | /* Initialize fdc, select drive B: | ||
166 | */ | ||
167 | outb(FDC_DMA_MODE, fdc.dor); /* assert reset, dma & irq enabled */ | ||
168 | /* 0x08 */ | ||
169 | outb(FDC_DMA_MODE|FDC_RESET_NOT, fdc.dor); /* release reset */ | ||
170 | /* 0x08 | 0x04 = 0x0c */ | ||
171 | outb(FDC_DMA_MODE|FDC_RESET_NOT|FDC_MOTOR_1|FTAPE_SEL_B, fdc.dor); | ||
172 | /* 0x08 | 0x04 | 0x20 | 0x01 = 0x2d */ | ||
173 | /* select drive 1 */ /* why not drive 0 ???? */ | ||
174 | TRACE_EXIT (x == 0x93) ? 2 : 1; | ||
175 | } | ||
diff --git a/drivers/char/ftape/lowlevel/fc-10.h b/drivers/char/ftape/lowlevel/fc-10.h new file mode 100644 index 000000000000..da7b88bca889 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fc-10.h | |||
@@ -0,0 +1,39 @@ | |||
1 | #ifndef _FC_10_H | ||
2 | #define _FC_10_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1996 Bas Laarhoven. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fc-10.h,v $ | ||
23 | * $Revision: 1.1 $ | ||
24 | * $Date: 1997/09/19 09:05:22 $ | ||
25 | * | ||
26 | * This file contains definitions for the FC-10 code | ||
27 | * of the QIC-40/80 floppy-tape driver for Linux. | ||
28 | */ | ||
29 | |||
30 | /* | ||
31 | * fc-10.c defined global vars. | ||
32 | */ | ||
33 | |||
34 | /* | ||
35 | * fc-10.c defined global functions. | ||
36 | */ | ||
37 | extern int fc10_enable(void); | ||
38 | |||
39 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/fdc-io.c b/drivers/char/ftape/lowlevel/fdc-io.c new file mode 100644 index 000000000000..1704a2a57048 --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-io.c | |||
@@ -0,0 +1,1352 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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/fdc-io.c,v $ | ||
21 | * $Revision: 1.7.4.2 $ | ||
22 | * $Date: 1997/11/16 14:48:17 $ | ||
23 | * | ||
24 | * This file contains the low-level floppy disk interface code | ||
25 | * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for | ||
26 | * Linux. | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> /* for CONFIG_FT_* */ | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/ioport.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/kernel.h> | ||
35 | #include <asm/system.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <asm/dma.h> | ||
38 | #include <asm/irq.h> | ||
39 | |||
40 | #include <linux/ftape.h> | ||
41 | #include <linux/qic117.h> | ||
42 | #include "../lowlevel/ftape-tracing.h" | ||
43 | #include "../lowlevel/fdc-io.h" | ||
44 | #include "../lowlevel/fdc-isr.h" | ||
45 | #include "../lowlevel/ftape-io.h" | ||
46 | #include "../lowlevel/ftape-rw.h" | ||
47 | #include "../lowlevel/ftape-ctl.h" | ||
48 | #include "../lowlevel/ftape-calibr.h" | ||
49 | #include "../lowlevel/fc-10.h" | ||
50 | |||
51 | /* Global vars. | ||
52 | */ | ||
53 | static int ftape_motor; | ||
54 | volatile int ftape_current_cylinder = -1; | ||
55 | volatile fdc_mode_enum fdc_mode = fdc_idle; | ||
56 | fdc_config_info fdc; | ||
57 | DECLARE_WAIT_QUEUE_HEAD(ftape_wait_intr); | ||
58 | |||
59 | unsigned int ft_fdc_base = CONFIG_FT_FDC_BASE; | ||
60 | unsigned int ft_fdc_irq = CONFIG_FT_FDC_IRQ; | ||
61 | unsigned int ft_fdc_dma = CONFIG_FT_FDC_DMA; | ||
62 | unsigned int ft_fdc_threshold = CONFIG_FT_FDC_THR; /* bytes */ | ||
63 | unsigned int ft_fdc_rate_limit = CONFIG_FT_FDC_MAX_RATE; /* bits/sec */ | ||
64 | int ft_probe_fc10 = CONFIG_FT_PROBE_FC10; | ||
65 | int ft_mach2 = CONFIG_FT_MACH2; | ||
66 | |||
67 | /* Local vars. | ||
68 | */ | ||
69 | static spinlock_t fdc_io_lock; | ||
70 | static unsigned int fdc_calibr_count; | ||
71 | static unsigned int fdc_calibr_time; | ||
72 | static int fdc_status; | ||
73 | volatile __u8 fdc_head; /* FDC head from sector id */ | ||
74 | volatile __u8 fdc_cyl; /* FDC track from sector id */ | ||
75 | volatile __u8 fdc_sect; /* FDC sector from sector id */ | ||
76 | static int fdc_data_rate = 500; /* data rate (Kbps) */ | ||
77 | static int fdc_rate_code; /* data rate code (0 == 500 Kbps) */ | ||
78 | static int fdc_seek_rate = 2; /* step rate (msec) */ | ||
79 | static void (*do_ftape) (void); | ||
80 | static int fdc_fifo_state; /* original fifo setting - fifo enabled */ | ||
81 | static int fdc_fifo_thr; /* original fifo setting - threshold */ | ||
82 | static int fdc_lock_state; /* original lock setting - locked */ | ||
83 | static int fdc_fifo_locked; /* has fifo && lock set ? */ | ||
84 | static __u8 fdc_precomp; /* default precomp. value (nsec) */ | ||
85 | static __u8 fdc_prec_code; /* fdc precomp. select code */ | ||
86 | |||
87 | static char ftape_id[] = "ftape"; /* used by request irq and free irq */ | ||
88 | |||
89 | static int fdc_set_seek_rate(int seek_rate); | ||
90 | |||
91 | void fdc_catch_stray_interrupts(int count) | ||
92 | { | ||
93 | unsigned long flags; | ||
94 | |||
95 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
96 | if (count == 0) { | ||
97 | ft_expected_stray_interrupts = 0; | ||
98 | } else { | ||
99 | ft_expected_stray_interrupts += count; | ||
100 | } | ||
101 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
102 | } | ||
103 | |||
104 | /* Wait during a timeout period for a given FDC status. | ||
105 | * If usecs == 0 then just test status, else wait at least for usecs. | ||
106 | * Returns -ETIME on timeout. Function must be calibrated first ! | ||
107 | */ | ||
108 | static int fdc_wait(unsigned int usecs, __u8 mask, __u8 state) | ||
109 | { | ||
110 | int count_1 = (fdc_calibr_count * usecs + | ||
111 | fdc_calibr_count - 1) / fdc_calibr_time; | ||
112 | |||
113 | do { | ||
114 | fdc_status = inb_p(fdc.msr); | ||
115 | if ((fdc_status & mask) == state) { | ||
116 | return 0; | ||
117 | } | ||
118 | } while (count_1-- >= 0); | ||
119 | return -ETIME; | ||
120 | } | ||
121 | |||
122 | int fdc_ready_wait(unsigned int usecs) | ||
123 | { | ||
124 | return fdc_wait(usecs, FDC_DATA_READY | FDC_BUSY, FDC_DATA_READY); | ||
125 | } | ||
126 | |||
127 | /* Why can't we just use udelay()? | ||
128 | */ | ||
129 | static void fdc_usec_wait(unsigned int usecs) | ||
130 | { | ||
131 | fdc_wait(usecs, 0, 1); /* will always timeout ! */ | ||
132 | } | ||
133 | |||
134 | static int fdc_ready_out_wait(unsigned int usecs) | ||
135 | { | ||
136 | fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ | ||
137 | return fdc_wait(usecs, FDC_DATA_OUT_READY, FDC_DATA_OUT_READY); | ||
138 | } | ||
139 | |||
140 | void fdc_wait_calibrate(void) | ||
141 | { | ||
142 | ftape_calibrate("fdc_wait", | ||
143 | fdc_usec_wait, &fdc_calibr_count, &fdc_calibr_time); | ||
144 | } | ||
145 | |||
146 | /* Wait for a (short) while for the FDC to become ready | ||
147 | * and transfer the next command byte. | ||
148 | * Return -ETIME on timeout on getting ready (depends on hardware!). | ||
149 | */ | ||
150 | static int fdc_write(const __u8 data) | ||
151 | { | ||
152 | fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ | ||
153 | if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_IN_READY) < 0) { | ||
154 | return -ETIME; | ||
155 | } else { | ||
156 | outb(data, fdc.fifo); | ||
157 | return 0; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | /* Wait for a (short) while for the FDC to become ready | ||
162 | * and transfer the next result byte. | ||
163 | * Return -ETIME if timeout on getting ready (depends on hardware!). | ||
164 | */ | ||
165 | static int fdc_read(__u8 * data) | ||
166 | { | ||
167 | fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ | ||
168 | if (fdc_wait(150, FDC_DATA_READY_MASK, FDC_DATA_OUT_READY) < 0) { | ||
169 | return -ETIME; | ||
170 | } else { | ||
171 | *data = inb(fdc.fifo); | ||
172 | return 0; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | /* Output a cmd_len long command string to the FDC. | ||
177 | * The FDC should be ready to receive a new command or | ||
178 | * an error (EBUSY or ETIME) will occur. | ||
179 | */ | ||
180 | int fdc_command(const __u8 * cmd_data, int cmd_len) | ||
181 | { | ||
182 | int result = 0; | ||
183 | unsigned long flags; | ||
184 | int count = cmd_len; | ||
185 | int retry = 0; | ||
186 | #ifdef TESTING | ||
187 | static unsigned int last_time; | ||
188 | unsigned int time; | ||
189 | #endif | ||
190 | TRACE_FUN(ft_t_any); | ||
191 | |||
192 | fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ | ||
193 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
194 | if (!in_interrupt()) | ||
195 | /* Yes, I know, too much comments inside this function | ||
196 | * ... | ||
197 | * | ||
198 | * Yet another bug in the original driver. All that | ||
199 | * havoc is caused by the fact that the isr() sends | ||
200 | * itself a command to the floppy tape driver (pause, | ||
201 | * micro step pause). Now, the problem is that | ||
202 | * commands are transmitted via the fdc_seek | ||
203 | * command. But: the fdc performs seeks in the | ||
204 | * background i.e. it doesn't signal busy while | ||
205 | * sending the step pulses to the drive. Therefore the | ||
206 | * non-interrupt level driver has no chance to tell | ||
207 | * whether the isr() just has issued a seek. Therefore | ||
208 | * we HAVE TO have a look at the ft_hide_interrupt | ||
209 | * flag: it signals the non-interrupt level part of | ||
210 | * the driver that it has to wait for the fdc until it | ||
211 | * has completet seeking. | ||
212 | * | ||
213 | * THIS WAS PRESUMABLY THE REASON FOR ALL THAT | ||
214 | * "fdc_read timeout" errors, I HOPE :-) | ||
215 | */ | ||
216 | if (ft_hide_interrupt) { | ||
217 | restore_flags(flags); | ||
218 | TRACE(ft_t_info, | ||
219 | "Waiting for the isr() completing fdc_seek()"); | ||
220 | if (fdc_interrupt_wait(2 * FT_SECOND) < 0) { | ||
221 | TRACE(ft_t_warn, | ||
222 | "Warning: timeout waiting for isr() seek to complete"); | ||
223 | } | ||
224 | if (ft_hide_interrupt || !ft_seek_completed) { | ||
225 | /* There cannot be another | ||
226 | * interrupt. The isr() only stops | ||
227 | * the tape and the next interrupt | ||
228 | * won't come until we have send our | ||
229 | * command to the drive. | ||
230 | */ | ||
231 | TRACE_ABORT(-EIO, ft_t_bug, | ||
232 | "BUG? isr() is still seeking?\n" | ||
233 | KERN_INFO "hide: %d\n" | ||
234 | KERN_INFO "seek: %d", | ||
235 | ft_hide_interrupt, | ||
236 | ft_seek_completed); | ||
237 | |||
238 | } | ||
239 | fdc_usec_wait(FT_RQM_DELAY); /* wait for valid RQM status */ | ||
240 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
241 | } | ||
242 | fdc_status = inb(fdc.msr); | ||
243 | if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_IN_READY) { | ||
244 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
245 | TRACE_ABORT(-EBUSY, ft_t_err, "fdc not ready"); | ||
246 | } | ||
247 | fdc_mode = *cmd_data; /* used by isr */ | ||
248 | #ifdef TESTING | ||
249 | if (fdc_mode == FDC_SEEK) { | ||
250 | time = ftape_timediff(last_time, ftape_timestamp()); | ||
251 | if (time < 6000) { | ||
252 | TRACE(ft_t_bug,"Warning: short timeout between seek commands: %d", | ||
253 | time); | ||
254 | } | ||
255 | } | ||
256 | #endif | ||
257 | if (!in_interrupt()) { | ||
258 | /* shouldn't be cleared if called from isr | ||
259 | */ | ||
260 | ft_interrupt_seen = 0; | ||
261 | } | ||
262 | while (count) { | ||
263 | result = fdc_write(*cmd_data); | ||
264 | if (result < 0) { | ||
265 | TRACE(ft_t_fdc_dma, | ||
266 | "fdc_mode = %02x, status = %02x at index %d", | ||
267 | (int) fdc_mode, (int) fdc_status, | ||
268 | cmd_len - count); | ||
269 | if (++retry <= 3) { | ||
270 | TRACE(ft_t_warn, "fdc_write timeout, retry"); | ||
271 | } else { | ||
272 | TRACE(ft_t_err, "fdc_write timeout, fatal"); | ||
273 | /* recover ??? */ | ||
274 | break; | ||
275 | } | ||
276 | } else { | ||
277 | --count; | ||
278 | ++cmd_data; | ||
279 | } | ||
280 | } | ||
281 | #ifdef TESTING | ||
282 | if (fdc_mode == FDC_SEEK) { | ||
283 | last_time = ftape_timestamp(); | ||
284 | } | ||
285 | #endif | ||
286 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
287 | TRACE_EXIT result; | ||
288 | } | ||
289 | |||
290 | /* Input a res_len long result string from the FDC. | ||
291 | * The FDC should be ready to send the result or an error | ||
292 | * (EBUSY or ETIME) will occur. | ||
293 | */ | ||
294 | int fdc_result(__u8 * res_data, int res_len) | ||
295 | { | ||
296 | int result = 0; | ||
297 | unsigned long flags; | ||
298 | int count = res_len; | ||
299 | int retry = 0; | ||
300 | TRACE_FUN(ft_t_any); | ||
301 | |||
302 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
303 | fdc_status = inb(fdc.msr); | ||
304 | if ((fdc_status & FDC_DATA_READY_MASK) != FDC_DATA_OUT_READY) { | ||
305 | TRACE(ft_t_err, "fdc not ready"); | ||
306 | result = -EBUSY; | ||
307 | } else while (count) { | ||
308 | if (!(fdc_status & FDC_BUSY)) { | ||
309 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
310 | TRACE_ABORT(-EIO, ft_t_err, "premature end of result phase"); | ||
311 | } | ||
312 | result = fdc_read(res_data); | ||
313 | if (result < 0) { | ||
314 | TRACE(ft_t_fdc_dma, | ||
315 | "fdc_mode = %02x, status = %02x at index %d", | ||
316 | (int) fdc_mode, | ||
317 | (int) fdc_status, | ||
318 | res_len - count); | ||
319 | if (++retry <= 3) { | ||
320 | TRACE(ft_t_warn, "fdc_read timeout, retry"); | ||
321 | } else { | ||
322 | TRACE(ft_t_err, "fdc_read timeout, fatal"); | ||
323 | /* recover ??? */ | ||
324 | break; | ||
325 | ++retry; | ||
326 | } | ||
327 | } else { | ||
328 | --count; | ||
329 | ++res_data; | ||
330 | } | ||
331 | } | ||
332 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
333 | fdc_usec_wait(FT_RQM_DELAY); /* allow FDC to negate BSY */ | ||
334 | TRACE_EXIT result; | ||
335 | } | ||
336 | |||
337 | /* Handle command and result phases for | ||
338 | * commands without data phase. | ||
339 | */ | ||
340 | static int fdc_issue_command(const __u8 * out_data, int out_count, | ||
341 | __u8 * in_data, int in_count) | ||
342 | { | ||
343 | TRACE_FUN(ft_t_any); | ||
344 | |||
345 | if (out_count > 0) { | ||
346 | TRACE_CATCH(fdc_command(out_data, out_count),); | ||
347 | } | ||
348 | /* will take 24 - 30 usec for fdc_sense_drive_status and | ||
349 | * fdc_sense_interrupt_status commands. | ||
350 | * 35 fails sometimes (5/9/93 SJL) | ||
351 | * On a loaded system it incidentally takes longer than | ||
352 | * this for the fdc to get ready ! ?????? WHY ?????? | ||
353 | * So until we know what's going on use a very long timeout. | ||
354 | */ | ||
355 | TRACE_CATCH(fdc_ready_out_wait(500 /* usec */),); | ||
356 | if (in_count > 0) { | ||
357 | TRACE_CATCH(fdc_result(in_data, in_count), | ||
358 | TRACE(ft_t_err, "result phase aborted")); | ||
359 | } | ||
360 | TRACE_EXIT 0; | ||
361 | } | ||
362 | |||
363 | /* Wait for FDC interrupt with timeout (in milliseconds). | ||
364 | * Signals are blocked so the wait will not be aborted. | ||
365 | * Note: interrupts must be enabled ! (23/05/93 SJL) | ||
366 | */ | ||
367 | int fdc_interrupt_wait(unsigned int time) | ||
368 | { | ||
369 | DECLARE_WAITQUEUE(wait,current); | ||
370 | sigset_t old_sigmask; | ||
371 | static int resetting; | ||
372 | long timeout; | ||
373 | |||
374 | TRACE_FUN(ft_t_fdc_dma); | ||
375 | |||
376 | if (waitqueue_active(&ftape_wait_intr)) { | ||
377 | TRACE_ABORT(-EIO, ft_t_err, "error: nested call"); | ||
378 | } | ||
379 | /* timeout time will be up to USPT microseconds too long ! */ | ||
380 | timeout = (1000 * time + FT_USPT - 1) / FT_USPT; | ||
381 | |||
382 | spin_lock_irq(¤t->sighand->siglock); | ||
383 | old_sigmask = current->blocked; | ||
384 | sigfillset(¤t->blocked); | ||
385 | recalc_sigpending(); | ||
386 | spin_unlock_irq(¤t->sighand->siglock); | ||
387 | |||
388 | set_current_state(TASK_INTERRUPTIBLE); | ||
389 | add_wait_queue(&ftape_wait_intr, &wait); | ||
390 | while (!ft_interrupt_seen && timeout) { | ||
391 | set_current_state(TASK_INTERRUPTIBLE); | ||
392 | timeout = schedule_timeout(timeout); | ||
393 | } | ||
394 | |||
395 | spin_lock_irq(¤t->sighand->siglock); | ||
396 | current->blocked = old_sigmask; | ||
397 | recalc_sigpending(); | ||
398 | spin_unlock_irq(¤t->sighand->siglock); | ||
399 | |||
400 | remove_wait_queue(&ftape_wait_intr, &wait); | ||
401 | /* the following IS necessary. True: as well | ||
402 | * wake_up_interruptible() as the schedule() set TASK_RUNNING | ||
403 | * when they wakeup a task, BUT: it may very well be that | ||
404 | * ft_interrupt_seen is already set to 1 when we enter here | ||
405 | * in which case schedule() gets never called, and | ||
406 | * TASK_RUNNING never set. This has the funny effect that we | ||
407 | * execute all the code until we leave kernel space, but then | ||
408 | * the task is stopped (a task CANNOT be preempted while in | ||
409 | * kernel mode. Sending a pair of SIGSTOP/SIGCONT to the | ||
410 | * tasks wakes it up again. Funny! :-) | ||
411 | */ | ||
412 | current->state = TASK_RUNNING; | ||
413 | if (ft_interrupt_seen) { /* woken up by interrupt */ | ||
414 | ft_interrupt_seen = 0; | ||
415 | TRACE_EXIT 0; | ||
416 | } | ||
417 | /* Original comment: | ||
418 | * In first instance, next statement seems unnecessary since | ||
419 | * it will be cleared in fdc_command. However, a small part of | ||
420 | * the software seems to rely on this being cleared here | ||
421 | * (ftape_close might fail) so stick to it until things get fixed ! | ||
422 | */ | ||
423 | /* My deeply sought of knowledge: | ||
424 | * Behold NO! It is obvious. fdc_reset() doesn't call fdc_command() | ||
425 | * but nevertheless uses fdc_interrupt_wait(). OF COURSE this needs to | ||
426 | * be reset here. | ||
427 | */ | ||
428 | ft_interrupt_seen = 0; /* clear for next call */ | ||
429 | if (!resetting) { | ||
430 | resetting = 1; /* break infinite recursion if reset fails */ | ||
431 | TRACE(ft_t_any, "cleanup reset"); | ||
432 | fdc_reset(); | ||
433 | resetting = 0; | ||
434 | } | ||
435 | TRACE_EXIT (signal_pending(current)) ? -EINTR : -ETIME; | ||
436 | } | ||
437 | |||
438 | /* Start/stop drive motor. Enable DMA mode. | ||
439 | */ | ||
440 | void fdc_motor(int motor) | ||
441 | { | ||
442 | int unit = ft_drive_sel; | ||
443 | int data = unit | FDC_RESET_NOT | FDC_DMA_MODE; | ||
444 | TRACE_FUN(ft_t_any); | ||
445 | |||
446 | ftape_motor = motor; | ||
447 | if (ftape_motor) { | ||
448 | data |= FDC_MOTOR_0 << unit; | ||
449 | TRACE(ft_t_noise, "turning motor %d on", unit); | ||
450 | } else { | ||
451 | TRACE(ft_t_noise, "turning motor %d off", unit); | ||
452 | } | ||
453 | if (ft_mach2) { | ||
454 | outb_p(data, fdc.dor2); | ||
455 | } else { | ||
456 | outb_p(data, fdc.dor); | ||
457 | } | ||
458 | ftape_sleep(10 * FT_MILLISECOND); | ||
459 | TRACE_EXIT; | ||
460 | } | ||
461 | |||
462 | static void fdc_update_dsr(void) | ||
463 | { | ||
464 | TRACE_FUN(ft_t_any); | ||
465 | |||
466 | TRACE(ft_t_flow, "rate = %d Kbps, precomp = %d ns", | ||
467 | fdc_data_rate, fdc_precomp); | ||
468 | if (fdc.type >= i82077) { | ||
469 | outb_p((fdc_rate_code & 0x03) | fdc_prec_code, fdc.dsr); | ||
470 | } else { | ||
471 | outb_p(fdc_rate_code & 0x03, fdc.ccr); | ||
472 | } | ||
473 | TRACE_EXIT; | ||
474 | } | ||
475 | |||
476 | void fdc_set_write_precomp(int precomp) | ||
477 | { | ||
478 | TRACE_FUN(ft_t_any); | ||
479 | |||
480 | TRACE(ft_t_noise, "New precomp: %d nsec", precomp); | ||
481 | fdc_precomp = precomp; | ||
482 | /* write precompensation can be set in multiples of 41.67 nsec. | ||
483 | * round the parameter to the nearest multiple and convert it | ||
484 | * into a fdc setting. Note that 0 means default to the fdc, | ||
485 | * 7 is used instead of that. | ||
486 | */ | ||
487 | fdc_prec_code = ((fdc_precomp + 21) / 42) << 2; | ||
488 | if (fdc_prec_code == 0 || fdc_prec_code > (6 << 2)) { | ||
489 | fdc_prec_code = 7 << 2; | ||
490 | } | ||
491 | fdc_update_dsr(); | ||
492 | TRACE_EXIT; | ||
493 | } | ||
494 | |||
495 | /* Reprogram the 82078 registers to use Data Rate Table 1 on all drives. | ||
496 | */ | ||
497 | static void fdc_set_drive_specs(void) | ||
498 | { | ||
499 | __u8 cmd[] = { FDC_DRIVE_SPEC, 0x00, 0x00, 0x00, 0x00, 0xc0}; | ||
500 | int result; | ||
501 | TRACE_FUN(ft_t_any); | ||
502 | |||
503 | TRACE(ft_t_flow, "Setting of drive specs called"); | ||
504 | if (fdc.type >= i82078_1) { | ||
505 | cmd[1] = (0 << 5) | (2 << 2); | ||
506 | cmd[2] = (1 << 5) | (2 << 2); | ||
507 | cmd[3] = (2 << 5) | (2 << 2); | ||
508 | cmd[4] = (3 << 5) | (2 << 2); | ||
509 | result = fdc_command(cmd, NR_ITEMS(cmd)); | ||
510 | if (result < 0) { | ||
511 | TRACE(ft_t_err, "Setting of drive specs failed"); | ||
512 | } | ||
513 | } | ||
514 | TRACE_EXIT; | ||
515 | } | ||
516 | |||
517 | /* Select clock for fdc, must correspond with tape drive setting ! | ||
518 | * This also influences the fdc timing so we must adjust some values. | ||
519 | */ | ||
520 | int fdc_set_data_rate(int rate) | ||
521 | { | ||
522 | int bad_rate = 0; | ||
523 | TRACE_FUN(ft_t_any); | ||
524 | |||
525 | /* Select clock for fdc, must correspond with tape drive setting ! | ||
526 | * This also influences the fdc timing so we must adjust some values. | ||
527 | */ | ||
528 | TRACE(ft_t_fdc_dma, "new rate = %d", rate); | ||
529 | switch (rate) { | ||
530 | case 250: | ||
531 | fdc_rate_code = fdc_data_rate_250; | ||
532 | break; | ||
533 | case 500: | ||
534 | fdc_rate_code = fdc_data_rate_500; | ||
535 | break; | ||
536 | case 1000: | ||
537 | if (fdc.type < i82077) { | ||
538 | bad_rate = 1; | ||
539 | } else { | ||
540 | fdc_rate_code = fdc_data_rate_1000; | ||
541 | } | ||
542 | break; | ||
543 | case 2000: | ||
544 | if (fdc.type < i82078_1) { | ||
545 | bad_rate = 1; | ||
546 | } else { | ||
547 | fdc_rate_code = fdc_data_rate_2000; | ||
548 | } | ||
549 | break; | ||
550 | default: | ||
551 | bad_rate = 1; | ||
552 | } | ||
553 | if (bad_rate) { | ||
554 | TRACE_ABORT(-EIO, | ||
555 | ft_t_fdc_dma, "%d is not a valid data rate", rate); | ||
556 | } | ||
557 | fdc_data_rate = rate; | ||
558 | fdc_update_dsr(); | ||
559 | fdc_set_seek_rate(fdc_seek_rate); /* clock changed! */ | ||
560 | ftape_udelay(1000); | ||
561 | TRACE_EXIT 0; | ||
562 | } | ||
563 | |||
564 | /* keep the unit select if keep_select is != 0, | ||
565 | */ | ||
566 | static void fdc_dor_reset(int keep_select) | ||
567 | { | ||
568 | __u8 fdc_ctl = ft_drive_sel; | ||
569 | |||
570 | if (keep_select != 0) { | ||
571 | fdc_ctl |= FDC_DMA_MODE; | ||
572 | if (ftape_motor) { | ||
573 | fdc_ctl |= FDC_MOTOR_0 << ft_drive_sel; | ||
574 | } | ||
575 | } | ||
576 | ftape_udelay(10); /* ??? but seems to be necessary */ | ||
577 | if (ft_mach2) { | ||
578 | outb_p(fdc_ctl & 0x0f, fdc.dor); | ||
579 | outb_p(fdc_ctl, fdc.dor2); | ||
580 | } else { | ||
581 | outb_p(fdc_ctl, fdc.dor); | ||
582 | } | ||
583 | fdc_usec_wait(10); /* delay >= 14 fdc clocks */ | ||
584 | if (keep_select == 0) { | ||
585 | fdc_ctl = 0; | ||
586 | } | ||
587 | fdc_ctl |= FDC_RESET_NOT; | ||
588 | if (ft_mach2) { | ||
589 | outb_p(fdc_ctl & 0x0f, fdc.dor); | ||
590 | outb_p(fdc_ctl, fdc.dor2); | ||
591 | } else { | ||
592 | outb_p(fdc_ctl, fdc.dor); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | /* Reset the floppy disk controller. Leave the ftape_unit selected. | ||
597 | */ | ||
598 | void fdc_reset(void) | ||
599 | { | ||
600 | int st0; | ||
601 | int i; | ||
602 | int dummy; | ||
603 | unsigned long flags; | ||
604 | TRACE_FUN(ft_t_any); | ||
605 | |||
606 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
607 | |||
608 | fdc_dor_reset(1); /* keep unit selected */ | ||
609 | |||
610 | fdc_mode = fdc_idle; | ||
611 | |||
612 | /* maybe the cli()/sti() pair is not necessary, BUT: | ||
613 | * the following line MUST be here. Otherwise fdc_interrupt_wait() | ||
614 | * won't wait. Note that fdc_reset() is called from | ||
615 | * ftape_dumb_stop() when the fdc is busy transferring data. In this | ||
616 | * case fdc_isr() MOST PROBABLY sets ft_interrupt_seen, and tries | ||
617 | * to get the result bytes from the fdc etc. CLASH. | ||
618 | */ | ||
619 | ft_interrupt_seen = 0; | ||
620 | |||
621 | /* Program data rate | ||
622 | */ | ||
623 | fdc_update_dsr(); /* restore data rate and precomp */ | ||
624 | |||
625 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
626 | |||
627 | /* | ||
628 | * Wait for first polling cycle to complete | ||
629 | */ | ||
630 | if (fdc_interrupt_wait(1 * FT_SECOND) < 0) { | ||
631 | TRACE(ft_t_err, "no drive polling interrupt!"); | ||
632 | } else { /* clear all disk-changed statuses */ | ||
633 | for (i = 0; i < 4; ++i) { | ||
634 | if(fdc_sense_interrupt_status(&st0, &dummy) != 0) { | ||
635 | TRACE(ft_t_err, "sense failed for %d", i); | ||
636 | } | ||
637 | if (i == ft_drive_sel) { | ||
638 | ftape_current_cylinder = dummy; | ||
639 | } | ||
640 | } | ||
641 | TRACE(ft_t_noise, "drive polling completed"); | ||
642 | } | ||
643 | /* | ||
644 | * SPECIFY COMMAND | ||
645 | */ | ||
646 | fdc_set_seek_rate(fdc_seek_rate); | ||
647 | /* | ||
648 | * DRIVE SPECIFICATION COMMAND (if fdc type known) | ||
649 | */ | ||
650 | if (fdc.type >= i82078_1) { | ||
651 | fdc_set_drive_specs(); | ||
652 | } | ||
653 | TRACE_EXIT; | ||
654 | } | ||
655 | |||
656 | #if !defined(CLK_48MHZ) | ||
657 | # define CLK_48MHZ 1 | ||
658 | #endif | ||
659 | |||
660 | /* When we're done, put the fdc into reset mode so that the regular | ||
661 | * floppy disk driver will figure out that something is wrong and | ||
662 | * initialize the controller the way it wants. | ||
663 | */ | ||
664 | void fdc_disable(void) | ||
665 | { | ||
666 | __u8 cmd1[] = {FDC_CONFIGURE, 0x00, 0x00, 0x00}; | ||
667 | __u8 cmd2[] = {FDC_LOCK}; | ||
668 | __u8 cmd3[] = {FDC_UNLOCK}; | ||
669 | __u8 stat[1]; | ||
670 | TRACE_FUN(ft_t_flow); | ||
671 | |||
672 | if (!fdc_fifo_locked) { | ||
673 | fdc_reset(); | ||
674 | TRACE_EXIT; | ||
675 | } | ||
676 | if (fdc_issue_command(cmd3, 1, stat, 1) < 0 || stat[0] != 0x00) { | ||
677 | fdc_dor_reset(0); | ||
678 | TRACE_ABORT(/**/, ft_t_bug, | ||
679 | "couldn't unlock fifo, configuration remains changed"); | ||
680 | } | ||
681 | fdc_fifo_locked = 0; | ||
682 | if (CLK_48MHZ && fdc.type >= i82078) { | ||
683 | cmd1[0] |= FDC_CLK48_BIT; | ||
684 | } | ||
685 | cmd1[2] = ((fdc_fifo_state) ? 0 : 0x20) + (fdc_fifo_thr - 1); | ||
686 | if (fdc_command(cmd1, NR_ITEMS(cmd1)) < 0) { | ||
687 | fdc_dor_reset(0); | ||
688 | TRACE_ABORT(/**/, ft_t_bug, | ||
689 | "couldn't reconfigure fifo to old state"); | ||
690 | } | ||
691 | if (fdc_lock_state && | ||
692 | fdc_issue_command(cmd2, 1, stat, 1) < 0) { | ||
693 | fdc_dor_reset(0); | ||
694 | TRACE_ABORT(/**/, ft_t_bug, "couldn't lock old state again"); | ||
695 | } | ||
696 | TRACE(ft_t_noise, "fifo restored: %sabled, thr. %d, %slocked", | ||
697 | fdc_fifo_state ? "en" : "dis", | ||
698 | fdc_fifo_thr, (fdc_lock_state) ? "" : "not "); | ||
699 | fdc_dor_reset(0); | ||
700 | TRACE_EXIT; | ||
701 | } | ||
702 | |||
703 | /* Specify FDC seek-rate (milliseconds) | ||
704 | */ | ||
705 | static int fdc_set_seek_rate(int seek_rate) | ||
706 | { | ||
707 | /* set step rate, dma mode, and minimal head load and unload times | ||
708 | */ | ||
709 | __u8 in[3] = { FDC_SPECIFY, 1, (1 << 1)}; | ||
710 | |||
711 | fdc_seek_rate = seek_rate; | ||
712 | in[1] |= (16 - (fdc_data_rate * fdc_seek_rate) / 500) << 4; | ||
713 | |||
714 | return fdc_command(in, 3); | ||
715 | } | ||
716 | |||
717 | /* Sense drive status: get unit's drive status (ST3) | ||
718 | */ | ||
719 | int fdc_sense_drive_status(int *st3) | ||
720 | { | ||
721 | __u8 out[2]; | ||
722 | __u8 in[1]; | ||
723 | TRACE_FUN(ft_t_any); | ||
724 | |||
725 | out[0] = FDC_SENSED; | ||
726 | out[1] = ft_drive_sel; | ||
727 | TRACE_CATCH(fdc_issue_command(out, 2, in, 1),); | ||
728 | *st3 = in[0]; | ||
729 | TRACE_EXIT 0; | ||
730 | } | ||
731 | |||
732 | /* Sense Interrupt Status command: | ||
733 | * should be issued at the end of each seek. | ||
734 | * get ST0 and current cylinder. | ||
735 | */ | ||
736 | int fdc_sense_interrupt_status(int *st0, int *current_cylinder) | ||
737 | { | ||
738 | __u8 out[1]; | ||
739 | __u8 in[2]; | ||
740 | TRACE_FUN(ft_t_any); | ||
741 | |||
742 | out[0] = FDC_SENSEI; | ||
743 | TRACE_CATCH(fdc_issue_command(out, 1, in, 2),); | ||
744 | *st0 = in[0]; | ||
745 | *current_cylinder = in[1]; | ||
746 | TRACE_EXIT 0; | ||
747 | } | ||
748 | |||
749 | /* step to track | ||
750 | */ | ||
751 | int fdc_seek(int track) | ||
752 | { | ||
753 | __u8 out[3]; | ||
754 | int st0, pcn; | ||
755 | #ifdef TESTING | ||
756 | unsigned int time; | ||
757 | #endif | ||
758 | TRACE_FUN(ft_t_any); | ||
759 | |||
760 | out[0] = FDC_SEEK; | ||
761 | out[1] = ft_drive_sel; | ||
762 | out[2] = track; | ||
763 | #ifdef TESTING | ||
764 | time = ftape_timestamp(); | ||
765 | #endif | ||
766 | /* We really need this command to work ! | ||
767 | */ | ||
768 | ft_seek_completed = 0; | ||
769 | TRACE_CATCH(fdc_command(out, 3), | ||
770 | fdc_reset(); | ||
771 | TRACE(ft_t_noise, "destination was: %d, resetting FDC...", | ||
772 | track)); | ||
773 | /* Handle interrupts until ft_seek_completed or timeout. | ||
774 | */ | ||
775 | for (;;) { | ||
776 | TRACE_CATCH(fdc_interrupt_wait(2 * FT_SECOND),); | ||
777 | if (ft_seek_completed) { | ||
778 | TRACE_CATCH(fdc_sense_interrupt_status(&st0, &pcn),); | ||
779 | if ((st0 & ST0_SEEK_END) == 0) { | ||
780 | TRACE_ABORT(-EIO, ft_t_err, | ||
781 | "no seek-end after seek completion !??"); | ||
782 | } | ||
783 | break; | ||
784 | } | ||
785 | } | ||
786 | #ifdef TESTING | ||
787 | time = ftape_timediff(time, ftape_timestamp()) / abs(track - ftape_current_cylinder); | ||
788 | if ((time < 900 || time > 3100) && abs(track - ftape_current_cylinder) > 5) { | ||
789 | TRACE(ft_t_warn, "Wrong FDC STEP interval: %d usecs (%d)", | ||
790 | time, track - ftape_current_cylinder); | ||
791 | } | ||
792 | #endif | ||
793 | /* Verify whether we issued the right tape command. | ||
794 | */ | ||
795 | /* Verify that we seek to the proper track. */ | ||
796 | if (pcn != track) { | ||
797 | TRACE_ABORT(-EIO, ft_t_err, "bad seek.."); | ||
798 | } | ||
799 | ftape_current_cylinder = track; | ||
800 | TRACE_EXIT 0; | ||
801 | } | ||
802 | |||
803 | static int perpend_mode; /* set if fdc is in perpendicular mode */ | ||
804 | |||
805 | static int perpend_off(void) | ||
806 | { | ||
807 | __u8 perpend[] = {FDC_PERPEND, 0x00}; | ||
808 | TRACE_FUN(ft_t_any); | ||
809 | |||
810 | if (perpend_mode) { | ||
811 | /* Turn off perpendicular mode */ | ||
812 | perpend[1] = 0x80; | ||
813 | TRACE_CATCH(fdc_command(perpend, 2), | ||
814 | TRACE(ft_t_err,"Perpendicular mode exit failed!")); | ||
815 | perpend_mode = 0; | ||
816 | } | ||
817 | TRACE_EXIT 0; | ||
818 | } | ||
819 | |||
820 | static int handle_perpend(int segment_id) | ||
821 | { | ||
822 | __u8 perpend[] = {FDC_PERPEND, 0x00}; | ||
823 | TRACE_FUN(ft_t_any); | ||
824 | |||
825 | /* When writing QIC-3020 tapes, turn on perpendicular mode | ||
826 | * if tape is moving in forward direction (even tracks). | ||
827 | */ | ||
828 | if (ft_qic_std == QIC_TAPE_QIC3020 && | ||
829 | ((segment_id / ft_segments_per_track) & 1) == 0) { | ||
830 | /* FIXME: some i82077 seem to support perpendicular mode as | ||
831 | * well. | ||
832 | */ | ||
833 | #if 0 | ||
834 | if (fdc.type < i82077AA) {} | ||
835 | #else | ||
836 | if (fdc.type < i82077 && ft_data_rate < 1000) { | ||
837 | #endif | ||
838 | /* fdc does not support perpendicular mode: complain | ||
839 | */ | ||
840 | TRACE_ABORT(-EIO, ft_t_err, | ||
841 | "Your FDC does not support QIC-3020."); | ||
842 | } | ||
843 | perpend[1] = 0x03 /* 0x83 + (0x4 << ft_drive_sel) */ ; | ||
844 | TRACE_CATCH(fdc_command(perpend, 2), | ||
845 | TRACE(ft_t_err,"Perpendicular mode entry failed!")); | ||
846 | TRACE(ft_t_flow, "Perpendicular mode set"); | ||
847 | perpend_mode = 1; | ||
848 | TRACE_EXIT 0; | ||
849 | } | ||
850 | TRACE_EXIT perpend_off(); | ||
851 | } | ||
852 | |||
853 | static inline void fdc_setup_dma(char mode, | ||
854 | volatile void *addr, unsigned int count) | ||
855 | { | ||
856 | /* Program the DMA controller. | ||
857 | */ | ||
858 | disable_dma(fdc.dma); | ||
859 | clear_dma_ff(fdc.dma); | ||
860 | set_dma_mode(fdc.dma, mode); | ||
861 | set_dma_addr(fdc.dma, virt_to_bus((void*)addr)); | ||
862 | set_dma_count(fdc.dma, count); | ||
863 | enable_dma(fdc.dma); | ||
864 | } | ||
865 | |||
866 | /* Setup fdc and dma for formatting the next segment | ||
867 | */ | ||
868 | int fdc_setup_formatting(buffer_struct * buff) | ||
869 | { | ||
870 | unsigned long flags; | ||
871 | __u8 out[6] = { | ||
872 | FDC_FORMAT, 0x00, 3, 4 * FT_SECTORS_PER_SEGMENT, 0x00, 0x6b | ||
873 | }; | ||
874 | TRACE_FUN(ft_t_any); | ||
875 | |||
876 | TRACE_CATCH(handle_perpend(buff->segment_id),); | ||
877 | /* Program the DMA controller. | ||
878 | */ | ||
879 | TRACE(ft_t_fdc_dma, | ||
880 | "phys. addr. = %lx", virt_to_bus((void*) buff->ptr)); | ||
881 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
882 | fdc_setup_dma(DMA_MODE_WRITE, buff->ptr, FT_SECTORS_PER_SEGMENT * 4); | ||
883 | /* Issue FDC command to start reading/writing. | ||
884 | */ | ||
885 | out[1] = ft_drive_sel; | ||
886 | out[4] = buff->gap3; | ||
887 | TRACE_CATCH(fdc_setup_error = fdc_command(out, sizeof(out)), | ||
888 | restore_flags(flags); fdc_mode = fdc_idle); | ||
889 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
890 | TRACE_EXIT 0; | ||
891 | } | ||
892 | |||
893 | |||
894 | /* Setup Floppy Disk Controller and DMA to read or write the next cluster | ||
895 | * of good sectors from or to the current segment. | ||
896 | */ | ||
897 | int fdc_setup_read_write(buffer_struct * buff, __u8 operation) | ||
898 | { | ||
899 | unsigned long flags; | ||
900 | __u8 out[9]; | ||
901 | int dma_mode; | ||
902 | TRACE_FUN(ft_t_any); | ||
903 | |||
904 | switch(operation) { | ||
905 | case FDC_VERIFY: | ||
906 | if (fdc.type < i82077) { | ||
907 | operation = FDC_READ; | ||
908 | } | ||
909 | case FDC_READ: | ||
910 | case FDC_READ_DELETED: | ||
911 | dma_mode = DMA_MODE_READ; | ||
912 | TRACE(ft_t_fdc_dma, "xfer %d sectors to 0x%p", | ||
913 | buff->sector_count, buff->ptr); | ||
914 | TRACE_CATCH(perpend_off(),); | ||
915 | break; | ||
916 | case FDC_WRITE_DELETED: | ||
917 | TRACE(ft_t_noise, "deleting segment %d", buff->segment_id); | ||
918 | case FDC_WRITE: | ||
919 | dma_mode = DMA_MODE_WRITE; | ||
920 | /* When writing QIC-3020 tapes, turn on perpendicular mode | ||
921 | * if tape is moving in forward direction (even tracks). | ||
922 | */ | ||
923 | TRACE_CATCH(handle_perpend(buff->segment_id),); | ||
924 | TRACE(ft_t_fdc_dma, "xfer %d sectors from 0x%p", | ||
925 | buff->sector_count, buff->ptr); | ||
926 | break; | ||
927 | default: | ||
928 | TRACE_ABORT(-EIO, | ||
929 | ft_t_bug, "bug: invalid operation parameter"); | ||
930 | } | ||
931 | TRACE(ft_t_fdc_dma, "phys. addr. = %lx",virt_to_bus((void*)buff->ptr)); | ||
932 | spin_lock_irqsave(&fdc_io_lock, flags); | ||
933 | if (operation != FDC_VERIFY) { | ||
934 | fdc_setup_dma(dma_mode, buff->ptr, | ||
935 | FT_SECTOR_SIZE * buff->sector_count); | ||
936 | } | ||
937 | /* Issue FDC command to start reading/writing. | ||
938 | */ | ||
939 | out[0] = operation; | ||
940 | out[1] = ft_drive_sel; | ||
941 | out[2] = buff->cyl; | ||
942 | out[3] = buff->head; | ||
943 | out[4] = buff->sect + buff->sector_offset; | ||
944 | out[5] = 3; /* Sector size of 1K. */ | ||
945 | out[6] = out[4] + buff->sector_count - 1; /* last sector */ | ||
946 | out[7] = 109; /* Gap length. */ | ||
947 | out[8] = 0xff; /* No limit to transfer size. */ | ||
948 | TRACE(ft_t_fdc_dma, "C: 0x%02x, H: 0x%02x, R: 0x%02x, cnt: 0x%02x", | ||
949 | out[2], out[3], out[4], out[6] - out[4] + 1); | ||
950 | spin_unlock_irqrestore(&fdc_io_lock, flags); | ||
951 | TRACE_CATCH(fdc_setup_error = fdc_command(out, 9),fdc_mode = fdc_idle); | ||
952 | TRACE_EXIT 0; | ||
953 | } | ||
954 | |||
955 | int fdc_fifo_threshold(__u8 threshold, | ||
956 | int *fifo_state, int *lock_state, int *fifo_thr) | ||
957 | { | ||
958 | const __u8 cmd0[] = {FDC_DUMPREGS}; | ||
959 | __u8 cmd1[] = {FDC_CONFIGURE, 0, (0x0f & (threshold - 1)), 0}; | ||
960 | const __u8 cmd2[] = {FDC_LOCK}; | ||
961 | const __u8 cmd3[] = {FDC_UNLOCK}; | ||
962 | __u8 reg[10]; | ||
963 | __u8 stat; | ||
964 | int i; | ||
965 | int result; | ||
966 | TRACE_FUN(ft_t_any); | ||
967 | |||
968 | if (CLK_48MHZ && fdc.type >= i82078) { | ||
969 | cmd1[0] |= FDC_CLK48_BIT; | ||
970 | } | ||
971 | /* Dump fdc internal registers for examination | ||
972 | */ | ||
973 | TRACE_CATCH(fdc_command(cmd0, NR_ITEMS(cmd0)), | ||
974 | TRACE(ft_t_warn, "dumpreg cmd failed, fifo unchanged")); | ||
975 | /* Now read fdc internal registers from fifo | ||
976 | */ | ||
977 | for (i = 0; i < (int)NR_ITEMS(reg); ++i) { | ||
978 | fdc_read(®[i]); | ||
979 | TRACE(ft_t_fdc_dma, "Register %d = 0x%02x", i, reg[i]); | ||
980 | } | ||
981 | if (fifo_state && lock_state && fifo_thr) { | ||
982 | *fifo_state = (reg[8] & 0x20) == 0; | ||
983 | *lock_state = reg[7] & 0x80; | ||
984 | *fifo_thr = 1 + (reg[8] & 0x0f); | ||
985 | } | ||
986 | TRACE(ft_t_noise, | ||
987 | "original fifo state: %sabled, threshold %d, %slocked", | ||
988 | ((reg[8] & 0x20) == 0) ? "en" : "dis", | ||
989 | 1 + (reg[8] & 0x0f), (reg[7] & 0x80) ? "" : "not "); | ||
990 | /* If fdc is already locked, unlock it first ! */ | ||
991 | if (reg[7] & 0x80) { | ||
992 | fdc_ready_wait(100); | ||
993 | TRACE_CATCH(fdc_issue_command(cmd3, NR_ITEMS(cmd3), &stat, 1), | ||
994 | TRACE(ft_t_bug, "FDC unlock command failed, " | ||
995 | "configuration unchanged")); | ||
996 | } | ||
997 | fdc_fifo_locked = 0; | ||
998 | /* Enable fifo and set threshold at xx bytes to allow a | ||
999 | * reasonably large latency and reduce number of dma bursts. | ||
1000 | */ | ||
1001 | fdc_ready_wait(100); | ||
1002 | if ((result = fdc_command(cmd1, NR_ITEMS(cmd1))) < 0) { | ||
1003 | TRACE(ft_t_bug, "configure cmd failed, fifo unchanged"); | ||
1004 | } | ||
1005 | /* Now lock configuration so reset will not change it | ||
1006 | */ | ||
1007 | if(fdc_issue_command(cmd2, NR_ITEMS(cmd2), &stat, 1) < 0 || | ||
1008 | stat != 0x10) { | ||
1009 | TRACE_ABORT(-EIO, ft_t_bug, | ||
1010 | "FDC lock command failed, stat = 0x%02x", stat); | ||
1011 | } | ||
1012 | fdc_fifo_locked = 1; | ||
1013 | TRACE_EXIT result; | ||
1014 | } | ||
1015 | |||
1016 | static int fdc_fifo_enable(void) | ||
1017 | { | ||
1018 | TRACE_FUN(ft_t_any); | ||
1019 | |||
1020 | if (fdc_fifo_locked) { | ||
1021 | TRACE_ABORT(0, ft_t_warn, "Fifo not enabled because locked"); | ||
1022 | } | ||
1023 | TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, | ||
1024 | &fdc_fifo_state, | ||
1025 | &fdc_lock_state, | ||
1026 | &fdc_fifo_thr),); | ||
1027 | TRACE_CATCH(fdc_fifo_threshold(ft_fdc_threshold /* bytes */, | ||
1028 | NULL, NULL, NULL),); | ||
1029 | TRACE_EXIT 0; | ||
1030 | } | ||
1031 | |||
1032 | /* Determine fd controller type | ||
1033 | */ | ||
1034 | static __u8 fdc_save_state[2]; | ||
1035 | |||
1036 | static int fdc_probe(void) | ||
1037 | { | ||
1038 | __u8 cmd[1]; | ||
1039 | __u8 stat[16]; /* must be able to hold dumpregs & save results */ | ||
1040 | int i; | ||
1041 | TRACE_FUN(ft_t_any); | ||
1042 | |||
1043 | /* Try to find out what kind of fd controller we have to deal with | ||
1044 | * Scheme borrowed from floppy driver: | ||
1045 | * first try if FDC_DUMPREGS command works | ||
1046 | * (this indicates that we have a 82072 or better) | ||
1047 | * then try the FDC_VERSION command (82072 doesn't support this) | ||
1048 | * then try the FDC_UNLOCK command (some older 82077's don't support this) | ||
1049 | * then try the FDC_PARTID command (82078's support this) | ||
1050 | */ | ||
1051 | cmd[0] = FDC_DUMPREGS; | ||
1052 | if (fdc_issue_command(cmd, 1, stat, 1) != 0) { | ||
1053 | TRACE_ABORT(no_fdc, ft_t_bug, "No FDC found"); | ||
1054 | } | ||
1055 | if (stat[0] == 0x80) { | ||
1056 | /* invalid command: must be pre 82072 */ | ||
1057 | TRACE_ABORT(i8272, | ||
1058 | ft_t_warn, "Type 8272A/765A compatible FDC found"); | ||
1059 | } | ||
1060 | fdc_result(&stat[1], 9); | ||
1061 | fdc_save_state[0] = stat[7]; | ||
1062 | fdc_save_state[1] = stat[8]; | ||
1063 | cmd[0] = FDC_VERSION; | ||
1064 | if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { | ||
1065 | TRACE_ABORT(i8272, ft_t_warn, "Type 82072 FDC found"); | ||
1066 | } | ||
1067 | if (*stat != 0x90) { | ||
1068 | TRACE_ABORT(i8272, ft_t_warn, "Unknown FDC found"); | ||
1069 | } | ||
1070 | cmd[0] = FDC_UNLOCK; | ||
1071 | if(fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] != 0x00) { | ||
1072 | TRACE_ABORT(i8272, ft_t_warn, | ||
1073 | "Type pre-1991 82077 FDC found, " | ||
1074 | "treating it like a 82072"); | ||
1075 | } | ||
1076 | if (fdc_save_state[0] & 0x80) { /* was locked */ | ||
1077 | cmd[0] = FDC_LOCK; /* restore lock */ | ||
1078 | (void)fdc_issue_command(cmd, 1, stat, 1); | ||
1079 | TRACE(ft_t_warn, "FDC is already locked"); | ||
1080 | } | ||
1081 | /* Test for a i82078 FDC */ | ||
1082 | cmd[0] = FDC_PARTID; | ||
1083 | if (fdc_issue_command(cmd, 1, stat, 1) < 0 || stat[0] == 0x80) { | ||
1084 | /* invalid command: not a i82078xx type FDC */ | ||
1085 | for (i = 0; i < 4; ++i) { | ||
1086 | outb_p(i, fdc.tdr); | ||
1087 | if ((inb_p(fdc.tdr) & 0x03) != i) { | ||
1088 | TRACE_ABORT(i82077, | ||
1089 | ft_t_warn, "Type 82077 FDC found"); | ||
1090 | } | ||
1091 | } | ||
1092 | TRACE_ABORT(i82077AA, ft_t_warn, "Type 82077AA FDC found"); | ||
1093 | } | ||
1094 | /* FDC_PARTID cmd succeeded */ | ||
1095 | switch (stat[0] >> 5) { | ||
1096 | case 0x0: | ||
1097 | /* i82078SL or i82078-1. The SL part cannot run at | ||
1098 | * 2Mbps (the SL and -1 dies are identical; they are | ||
1099 | * speed graded after production, according to Intel). | ||
1100 | * Some SL's can be detected by doing a SAVE cmd and | ||
1101 | * look at bit 7 of the first byte (the SEL3V# bit). | ||
1102 | * If it is 0, the part runs off 3Volts, and hence it | ||
1103 | * is a SL. | ||
1104 | */ | ||
1105 | cmd[0] = FDC_SAVE; | ||
1106 | if(fdc_issue_command(cmd, 1, stat, 16) < 0) { | ||
1107 | TRACE(ft_t_err, "FDC_SAVE failed. Dunno why"); | ||
1108 | /* guess we better claim the fdc to be a i82078 */ | ||
1109 | TRACE_ABORT(i82078, | ||
1110 | ft_t_warn, | ||
1111 | "Type i82078 FDC (i suppose) found"); | ||
1112 | } | ||
1113 | if ((stat[0] & FDC_SEL3V_BIT)) { | ||
1114 | /* fdc running off 5Volts; Pray that it's a i82078-1 | ||
1115 | */ | ||
1116 | TRACE_ABORT(i82078_1, ft_t_warn, | ||
1117 | "Type i82078-1 or 5Volt i82078SL FDC found"); | ||
1118 | } | ||
1119 | TRACE_ABORT(i82078, ft_t_warn, | ||
1120 | "Type 3Volt i82078SL FDC (1Mbps) found"); | ||
1121 | case 0x1: | ||
1122 | case 0x2: /* S82078B */ | ||
1123 | /* The '78B isn't '78 compatible. Detect it as a '77AA */ | ||
1124 | TRACE_ABORT(i82077AA, ft_t_warn, "Type i82077AA FDC found"); | ||
1125 | case 0x3: /* NSC PC8744 core; used in several super-IO chips */ | ||
1126 | TRACE_ABORT(i82077AA, | ||
1127 | ft_t_warn, "Type 82077AA compatible FDC found"); | ||
1128 | default: | ||
1129 | TRACE(ft_t_warn, "A previously undetected FDC found"); | ||
1130 | TRACE_ABORT(i82077AA, ft_t_warn, | ||
1131 | "Treating it as a 82077AA. Please report partid= %d", | ||
1132 | stat[0]); | ||
1133 | } /* switch(stat[ 0] >> 5) */ | ||
1134 | TRACE_EXIT no_fdc; | ||
1135 | } | ||
1136 | |||
1137 | static int fdc_request_regions(void) | ||
1138 | { | ||
1139 | TRACE_FUN(ft_t_flow); | ||
1140 | |||
1141 | if (ft_mach2 || ft_probe_fc10) { | ||
1142 | if (!request_region(fdc.sra, 8, "fdc (ft)")) { | ||
1143 | #ifndef BROKEN_FLOPPY_DRIVER | ||
1144 | TRACE_EXIT -EBUSY; | ||
1145 | #else | ||
1146 | TRACE(ft_t_warn, | ||
1147 | "address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra); | ||
1148 | #endif | ||
1149 | } | ||
1150 | } else { | ||
1151 | if (!request_region(fdc.sra, 6, "fdc (ft)")) { | ||
1152 | #ifndef BROKEN_FLOPPY_DRIVER | ||
1153 | TRACE_EXIT -EBUSY; | ||
1154 | #else | ||
1155 | TRACE(ft_t_warn, | ||
1156 | "address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra); | ||
1157 | #endif | ||
1158 | } | ||
1159 | if (!request_region(fdc.sra + 7, 1, "fdc (ft)")) { | ||
1160 | #ifndef BROKEN_FLOPPY_DRIVER | ||
1161 | release_region(fdc.sra, 6); | ||
1162 | TRACE_EXIT -EBUSY; | ||
1163 | #else | ||
1164 | TRACE(ft_t_warn, | ||
1165 | "address 0x%03x occupied (by floppy driver?), using it anyway", fdc.sra + 7); | ||
1166 | #endif | ||
1167 | } | ||
1168 | } | ||
1169 | TRACE_EXIT 0; | ||
1170 | } | ||
1171 | |||
1172 | void fdc_release_regions(void) | ||
1173 | { | ||
1174 | TRACE_FUN(ft_t_flow); | ||
1175 | |||
1176 | if (fdc.sra != 0) { | ||
1177 | if (fdc.dor2 != 0) { | ||
1178 | release_region(fdc.sra, 8); | ||
1179 | } else { | ||
1180 | release_region(fdc.sra, 6); | ||
1181 | release_region(fdc.dir, 1); | ||
1182 | } | ||
1183 | } | ||
1184 | TRACE_EXIT; | ||
1185 | } | ||
1186 | |||
1187 | static int fdc_config_regs(unsigned int fdc_base, | ||
1188 | unsigned int fdc_irq, | ||
1189 | unsigned int fdc_dma) | ||
1190 | { | ||
1191 | TRACE_FUN(ft_t_flow); | ||
1192 | |||
1193 | fdc.irq = fdc_irq; | ||
1194 | fdc.dma = fdc_dma; | ||
1195 | fdc.sra = fdc_base; | ||
1196 | fdc.srb = fdc_base + 1; | ||
1197 | fdc.dor = fdc_base + 2; | ||
1198 | fdc.tdr = fdc_base + 3; | ||
1199 | fdc.msr = fdc.dsr = fdc_base + 4; | ||
1200 | fdc.fifo = fdc_base + 5; | ||
1201 | fdc.dir = fdc.ccr = fdc_base + 7; | ||
1202 | fdc.dor2 = (ft_mach2 || ft_probe_fc10) ? fdc_base + 6 : 0; | ||
1203 | TRACE_CATCH(fdc_request_regions(), fdc.sra = 0); | ||
1204 | TRACE_EXIT 0; | ||
1205 | } | ||
1206 | |||
1207 | static int fdc_config(void) | ||
1208 | { | ||
1209 | static int already_done; | ||
1210 | TRACE_FUN(ft_t_any); | ||
1211 | |||
1212 | if (already_done) { | ||
1213 | TRACE_CATCH(fdc_request_regions(),); | ||
1214 | *(fdc.hook) = fdc_isr; /* hook our handler in */ | ||
1215 | TRACE_EXIT 0; | ||
1216 | } | ||
1217 | if (ft_probe_fc10) { | ||
1218 | int fc_type; | ||
1219 | |||
1220 | TRACE_CATCH(fdc_config_regs(ft_fdc_base, | ||
1221 | ft_fdc_irq, ft_fdc_dma),); | ||
1222 | fc_type = fc10_enable(); | ||
1223 | if (fc_type != 0) { | ||
1224 | TRACE(ft_t_warn, "FC-%c0 controller found", '0' + fc_type); | ||
1225 | fdc.type = fc10; | ||
1226 | fdc.hook = &do_ftape; | ||
1227 | *(fdc.hook) = fdc_isr; /* hook our handler in */ | ||
1228 | already_done = 1; | ||
1229 | TRACE_EXIT 0; | ||
1230 | } else { | ||
1231 | TRACE(ft_t_warn, "FC-10/20 controller not found"); | ||
1232 | fdc_release_regions(); | ||
1233 | fdc.type = no_fdc; | ||
1234 | ft_probe_fc10 = 0; | ||
1235 | ft_fdc_base = 0x3f0; | ||
1236 | ft_fdc_irq = 6; | ||
1237 | ft_fdc_dma = 2; | ||
1238 | } | ||
1239 | } | ||
1240 | TRACE(ft_t_warn, "fdc base: 0x%x, irq: %d, dma: %d", | ||
1241 | ft_fdc_base, ft_fdc_irq, ft_fdc_dma); | ||
1242 | TRACE_CATCH(fdc_config_regs(ft_fdc_base, ft_fdc_irq, ft_fdc_dma),); | ||
1243 | fdc.hook = &do_ftape; | ||
1244 | *(fdc.hook) = fdc_isr; /* hook our handler in */ | ||
1245 | already_done = 1; | ||
1246 | TRACE_EXIT 0; | ||
1247 | } | ||
1248 | |||
1249 | static irqreturn_t ftape_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
1250 | { | ||
1251 | void (*handler) (void) = *fdc.hook; | ||
1252 | int handled = 0; | ||
1253 | TRACE_FUN(ft_t_any); | ||
1254 | |||
1255 | *fdc.hook = NULL; | ||
1256 | if (handler) { | ||
1257 | handled = 1; | ||
1258 | handler(); | ||
1259 | } else { | ||
1260 | TRACE(ft_t_bug, "Unexpected ftape interrupt"); | ||
1261 | } | ||
1262 | TRACE_EXIT IRQ_RETVAL(handled); | ||
1263 | } | ||
1264 | |||
1265 | static int fdc_grab_irq_and_dma(void) | ||
1266 | { | ||
1267 | TRACE_FUN(ft_t_any); | ||
1268 | |||
1269 | if (fdc.hook == &do_ftape) { | ||
1270 | /* Get fast interrupt handler. | ||
1271 | */ | ||
1272 | if (request_irq(fdc.irq, ftape_interrupt, | ||
1273 | SA_INTERRUPT, "ft", ftape_id)) { | ||
1274 | TRACE_ABORT(-EIO, ft_t_bug, | ||
1275 | "Unable to grab IRQ%d for ftape driver", | ||
1276 | fdc.irq); | ||
1277 | } | ||
1278 | if (request_dma(fdc.dma, ftape_id)) { | ||
1279 | free_irq(fdc.irq, ftape_id); | ||
1280 | TRACE_ABORT(-EIO, ft_t_bug, | ||
1281 | "Unable to grab DMA%d for ftape driver", | ||
1282 | fdc.dma); | ||
1283 | } | ||
1284 | } | ||
1285 | if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { | ||
1286 | /* Using same dma channel or irq as standard fdc, need | ||
1287 | * to disable the dma-gate on the std fdc. This | ||
1288 | * couldn't be done in the floppy driver as some | ||
1289 | * laptops are using the dma-gate to enter a low power | ||
1290 | * or even suspended state :-( | ||
1291 | */ | ||
1292 | outb_p(FDC_RESET_NOT, 0x3f2); | ||
1293 | TRACE(ft_t_noise, "DMA-gate on standard fdc disabled"); | ||
1294 | } | ||
1295 | TRACE_EXIT 0; | ||
1296 | } | ||
1297 | |||
1298 | int fdc_release_irq_and_dma(void) | ||
1299 | { | ||
1300 | TRACE_FUN(ft_t_any); | ||
1301 | |||
1302 | if (fdc.hook == &do_ftape) { | ||
1303 | disable_dma(fdc.dma); /* just in case... */ | ||
1304 | free_dma(fdc.dma); | ||
1305 | free_irq(fdc.irq, ftape_id); | ||
1306 | } | ||
1307 | if (ft_fdc_base != 0x3f0 && (ft_fdc_dma == 2 || ft_fdc_irq == 6)) { | ||
1308 | /* Using same dma channel as standard fdc, need to | ||
1309 | * disable the dma-gate on the std fdc. This couldn't | ||
1310 | * be done in the floppy driver as some laptops are | ||
1311 | * using the dma-gate to enter a low power or even | ||
1312 | * suspended state :-( | ||
1313 | */ | ||
1314 | outb_p(FDC_RESET_NOT | FDC_DMA_MODE, 0x3f2); | ||
1315 | TRACE(ft_t_noise, "DMA-gate on standard fdc enabled again"); | ||
1316 | } | ||
1317 | TRACE_EXIT 0; | ||
1318 | } | ||
1319 | |||
1320 | int fdc_init(void) | ||
1321 | { | ||
1322 | TRACE_FUN(ft_t_any); | ||
1323 | |||
1324 | /* find a FDC to use */ | ||
1325 | TRACE_CATCH(fdc_config(),); | ||
1326 | TRACE_CATCH(fdc_grab_irq_and_dma(), fdc_release_regions()); | ||
1327 | ftape_motor = 0; | ||
1328 | fdc_catch_stray_interrupts(0); /* clear number of awainted | ||
1329 | * stray interrupte | ||
1330 | */ | ||
1331 | fdc_catch_stray_interrupts(1); /* one always comes (?) */ | ||
1332 | TRACE(ft_t_flow, "resetting fdc"); | ||
1333 | fdc_set_seek_rate(2); /* use nominal QIC step rate */ | ||
1334 | fdc_reset(); /* init fdc & clear track counters */ | ||
1335 | if (fdc.type == no_fdc) { /* no FC-10 or FC-20 found */ | ||
1336 | fdc.type = fdc_probe(); | ||
1337 | fdc_reset(); /* update with new knowledge */ | ||
1338 | } | ||
1339 | if (fdc.type == no_fdc) { | ||
1340 | fdc_release_irq_and_dma(); | ||
1341 | fdc_release_regions(); | ||
1342 | TRACE_EXIT -ENXIO; | ||
1343 | } | ||
1344 | if (fdc.type >= i82077) { | ||
1345 | if (fdc_fifo_enable() < 0) { | ||
1346 | TRACE(ft_t_warn, "couldn't enable fdc fifo !"); | ||
1347 | } else { | ||
1348 | TRACE(ft_t_flow, "fdc fifo enabled and locked"); | ||
1349 | } | ||
1350 | } | ||
1351 | TRACE_EXIT 0; | ||
1352 | } | ||
diff --git a/drivers/char/ftape/lowlevel/fdc-io.h b/drivers/char/ftape/lowlevel/fdc-io.h new file mode 100644 index 000000000000..7ec3c72178bb --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-io.h | |||
@@ -0,0 +1,252 @@ | |||
1 | #ifndef _FDC_IO_H | ||
2 | #define _FDC_IO_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-io.h,v $ | ||
24 | * $Revision: 1.3 $ | ||
25 | * $Date: 1997/10/05 19:18:06 $ | ||
26 | * | ||
27 | * This file contains the declarations for the low level | ||
28 | * functions that communicate with the floppy disk controller, | ||
29 | * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for | ||
30 | * Linux. | ||
31 | */ | ||
32 | |||
33 | #include <linux/fdreg.h> | ||
34 | |||
35 | #include "../lowlevel/ftape-bsm.h" | ||
36 | |||
37 | #define FDC_SK_BIT (0x20) | ||
38 | #define FDC_MT_BIT (0x80) | ||
39 | |||
40 | #define FDC_READ (FD_READ & ~(FDC_SK_BIT | FDC_MT_BIT)) | ||
41 | #define FDC_WRITE (FD_WRITE & ~FDC_MT_BIT) | ||
42 | #define FDC_READ_DELETED (0x4c) | ||
43 | #define FDC_WRITE_DELETED (0x49) | ||
44 | #define FDC_VERIFY (0x56) | ||
45 | #define FDC_READID (0x4a) | ||
46 | #define FDC_SENSED (0x04) | ||
47 | #define FDC_SENSEI (FD_SENSEI) | ||
48 | #define FDC_FORMAT (FD_FORMAT) | ||
49 | #define FDC_RECAL (FD_RECALIBRATE) | ||
50 | #define FDC_SEEK (FD_SEEK) | ||
51 | #define FDC_SPECIFY (FD_SPECIFY) | ||
52 | #define FDC_RECALIBR (FD_RECALIBRATE) | ||
53 | #define FDC_VERSION (FD_VERSION) | ||
54 | #define FDC_PERPEND (FD_PERPENDICULAR) | ||
55 | #define FDC_DUMPREGS (FD_DUMPREGS) | ||
56 | #define FDC_LOCK (FD_LOCK) | ||
57 | #define FDC_UNLOCK (FD_UNLOCK) | ||
58 | #define FDC_CONFIGURE (FD_CONFIGURE) | ||
59 | #define FDC_DRIVE_SPEC (0x8e) /* i82078 has this (any others?) */ | ||
60 | #define FDC_PARTID (0x18) /* i82078 has this */ | ||
61 | #define FDC_SAVE (0x2e) /* i82078 has this (any others?) */ | ||
62 | #define FDC_RESTORE (0x4e) /* i82078 has this (any others?) */ | ||
63 | |||
64 | #define FDC_STATUS_MASK (STATUS_BUSY | STATUS_DMA | STATUS_DIR | STATUS_READY) | ||
65 | #define FDC_DATA_READY (STATUS_READY) | ||
66 | #define FDC_DATA_OUTPUT (STATUS_DIR) | ||
67 | #define FDC_DATA_READY_MASK (STATUS_READY | STATUS_DIR) | ||
68 | #define FDC_DATA_OUT_READY (STATUS_READY | STATUS_DIR) | ||
69 | #define FDC_DATA_IN_READY (STATUS_READY) | ||
70 | #define FDC_BUSY (STATUS_BUSY) | ||
71 | #define FDC_CLK48_BIT (0x80) | ||
72 | #define FDC_SEL3V_BIT (0x40) | ||
73 | |||
74 | #define ST0_INT_MASK (ST0_INTR) | ||
75 | #define FDC_INT_NORMAL (ST0_INTR & 0x00) | ||
76 | #define FDC_INT_ABNORMAL (ST0_INTR & 0x40) | ||
77 | #define FDC_INT_INVALID (ST0_INTR & 0x80) | ||
78 | #define FDC_INT_READYCH (ST0_INTR & 0xC0) | ||
79 | #define ST0_SEEK_END (ST0_SE) | ||
80 | #define ST3_TRACK_0 (ST3_TZ) | ||
81 | |||
82 | #define FDC_RESET_NOT (0x04) | ||
83 | #define FDC_DMA_MODE (0x08) | ||
84 | #define FDC_MOTOR_0 (0x10) | ||
85 | #define FDC_MOTOR_1 (0x20) | ||
86 | |||
87 | typedef struct { | ||
88 | void (**hook) (void); /* our wedge into the isr */ | ||
89 | enum { | ||
90 | no_fdc, i8272, i82077, i82077AA, fc10, | ||
91 | i82078, i82078_1 | ||
92 | } type; /* FDC type */ | ||
93 | unsigned int irq; /* FDC irq nr */ | ||
94 | unsigned int dma; /* FDC dma channel nr */ | ||
95 | __u16 sra; /* Status register A (PS/2 only) */ | ||
96 | __u16 srb; /* Status register B (PS/2 only) */ | ||
97 | __u16 dor; /* Digital output register */ | ||
98 | __u16 tdr; /* Tape Drive Register (82077SL-1 & | ||
99 | 82078 only) */ | ||
100 | __u16 msr; /* Main Status Register */ | ||
101 | __u16 dsr; /* Datarate Select Register (8207x only) */ | ||
102 | __u16 fifo; /* Data register / Fifo on 8207x */ | ||
103 | __u16 dir; /* Digital Input Register */ | ||
104 | __u16 ccr; /* Configuration Control Register */ | ||
105 | __u16 dor2; /* Alternate dor on MACH-2 controller, | ||
106 | also used with FC-10, meaning unknown */ | ||
107 | } fdc_config_info; | ||
108 | |||
109 | typedef enum { | ||
110 | fdc_data_rate_250 = 2, | ||
111 | fdc_data_rate_300 = 1, /* any fdc in default configuration */ | ||
112 | fdc_data_rate_500 = 0, | ||
113 | fdc_data_rate_1000 = 3, | ||
114 | fdc_data_rate_2000 = 1, /* i82078-1: when using Data Rate Table #2 */ | ||
115 | } fdc_data_rate_type; | ||
116 | |||
117 | typedef enum { | ||
118 | fdc_idle = 0, | ||
119 | fdc_reading_data = FDC_READ, | ||
120 | fdc_seeking = FDC_SEEK, | ||
121 | fdc_writing_data = FDC_WRITE, | ||
122 | fdc_deleting = FDC_WRITE_DELETED, | ||
123 | fdc_reading_id = FDC_READID, | ||
124 | fdc_recalibrating = FDC_RECAL, | ||
125 | fdc_formatting = FDC_FORMAT, | ||
126 | fdc_verifying = FDC_VERIFY | ||
127 | } fdc_mode_enum; | ||
128 | |||
129 | typedef enum { | ||
130 | waiting = 0, | ||
131 | reading, | ||
132 | writing, | ||
133 | formatting, | ||
134 | verifying, | ||
135 | deleting, | ||
136 | done, | ||
137 | error, | ||
138 | mmapped, | ||
139 | } buffer_state_enum; | ||
140 | |||
141 | typedef struct { | ||
142 | __u8 *address; | ||
143 | volatile buffer_state_enum status; | ||
144 | volatile __u8 *ptr; | ||
145 | volatile unsigned int bytes; | ||
146 | volatile unsigned int segment_id; | ||
147 | |||
148 | /* bitmap for remainder of segment not yet handled. | ||
149 | * one bit set for each bad sector that must be skipped. | ||
150 | */ | ||
151 | volatile SectorMap bad_sector_map; | ||
152 | |||
153 | /* bitmap with bad data blocks in data buffer. | ||
154 | * the errors in this map may be retried. | ||
155 | */ | ||
156 | volatile SectorMap soft_error_map; | ||
157 | |||
158 | /* bitmap with bad data blocks in data buffer | ||
159 | * the errors in this map may not be retried. | ||
160 | */ | ||
161 | volatile SectorMap hard_error_map; | ||
162 | |||
163 | /* retry counter for soft errors. | ||
164 | */ | ||
165 | volatile int retry; | ||
166 | |||
167 | /* sectors to skip on retry ??? | ||
168 | */ | ||
169 | volatile unsigned int skip; | ||
170 | |||
171 | /* nr of data blocks in data buffer | ||
172 | */ | ||
173 | volatile unsigned int data_offset; | ||
174 | |||
175 | /* offset in segment for first sector to be handled. | ||
176 | */ | ||
177 | volatile unsigned int sector_offset; | ||
178 | |||
179 | /* size of cluster of good sectors to be handled. | ||
180 | */ | ||
181 | volatile unsigned int sector_count; | ||
182 | |||
183 | /* size of remaining part of segment to be handled. | ||
184 | */ | ||
185 | volatile unsigned int remaining; | ||
186 | |||
187 | /* points to next segment (contiguous) to be handled, | ||
188 | * or is zero if no read-ahead is allowed. | ||
189 | */ | ||
190 | volatile unsigned int next_segment; | ||
191 | |||
192 | /* flag being set if deleted data was read. | ||
193 | */ | ||
194 | volatile int deleted; | ||
195 | |||
196 | /* floppy coordinates of first sector in segment */ | ||
197 | volatile __u8 head; | ||
198 | volatile __u8 cyl; | ||
199 | volatile __u8 sect; | ||
200 | |||
201 | /* gap to use when formatting */ | ||
202 | __u8 gap3; | ||
203 | /* flag set when buffer is mmaped */ | ||
204 | int mmapped; | ||
205 | } buffer_struct; | ||
206 | |||
207 | /* | ||
208 | * fdc-io.c defined public variables | ||
209 | */ | ||
210 | extern volatile fdc_mode_enum fdc_mode; | ||
211 | extern int fdc_setup_error; /* outdated ??? */ | ||
212 | extern wait_queue_head_t ftape_wait_intr; | ||
213 | extern volatile int ftape_current_cylinder; /* track nr FDC thinks we're on */ | ||
214 | extern volatile __u8 fdc_head; /* FDC head */ | ||
215 | extern volatile __u8 fdc_cyl; /* FDC track */ | ||
216 | extern volatile __u8 fdc_sect; /* FDC sector */ | ||
217 | extern fdc_config_info fdc; /* FDC hardware configuration */ | ||
218 | |||
219 | extern unsigned int ft_fdc_base; | ||
220 | extern unsigned int ft_fdc_irq; | ||
221 | extern unsigned int ft_fdc_dma; | ||
222 | extern unsigned int ft_fdc_threshold; | ||
223 | extern unsigned int ft_fdc_rate_limit; | ||
224 | extern int ft_probe_fc10; | ||
225 | extern int ft_mach2; | ||
226 | /* | ||
227 | * fdc-io.c defined public functions | ||
228 | */ | ||
229 | extern void fdc_catch_stray_interrupts(int count); | ||
230 | extern int fdc_ready_wait(unsigned int timeout); | ||
231 | extern int fdc_command(const __u8 * cmd_data, int cmd_len); | ||
232 | extern int fdc_result(__u8 * res_data, int res_len); | ||
233 | extern int fdc_interrupt_wait(unsigned int time); | ||
234 | extern int fdc_seek(int track); | ||
235 | extern int fdc_sense_drive_status(int *st3); | ||
236 | extern void fdc_motor(int motor); | ||
237 | extern void fdc_reset(void); | ||
238 | extern void fdc_disable(void); | ||
239 | extern int fdc_fifo_threshold(__u8 threshold, | ||
240 | int *fifo_state, int *lock_state, int *fifo_thr); | ||
241 | extern void fdc_wait_calibrate(void); | ||
242 | extern int fdc_sense_interrupt_status(int *st0, int *current_cylinder); | ||
243 | extern void fdc_save_drive_specs(void); | ||
244 | extern void fdc_restore_drive_specs(void); | ||
245 | extern int fdc_set_data_rate(int rate); | ||
246 | extern void fdc_set_write_precomp(int precomp); | ||
247 | extern int fdc_release_irq_and_dma(void); | ||
248 | extern void fdc_release_regions(void); | ||
249 | extern int fdc_init(void); | ||
250 | extern int fdc_setup_read_write(buffer_struct * buff, __u8 operation); | ||
251 | extern int fdc_setup_formatting(buffer_struct * buff); | ||
252 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.c b/drivers/char/ftape/lowlevel/fdc-isr.c new file mode 100644 index 000000000000..ad2bc733ae1b --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-isr.c | |||
@@ -0,0 +1,1170 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1994-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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/fdc-isr.c,v $ | ||
21 | * $Revision: 1.9 $ | ||
22 | * $Date: 1997/10/17 23:01:53 $ | ||
23 | * | ||
24 | * This file contains the interrupt service routine and | ||
25 | * associated code for the QIC-40/80/3010/3020 floppy-tape driver | ||
26 | * "ftape" for Linux. | ||
27 | */ | ||
28 | |||
29 | #include <asm/io.h> | ||
30 | #include <asm/dma.h> | ||
31 | |||
32 | #define volatile /* */ | ||
33 | |||
34 | #include <linux/ftape.h> | ||
35 | #include <linux/qic117.h> | ||
36 | #include "../lowlevel/ftape-tracing.h" | ||
37 | #include "../lowlevel/fdc-isr.h" | ||
38 | #include "../lowlevel/fdc-io.h" | ||
39 | #include "../lowlevel/ftape-ctl.h" | ||
40 | #include "../lowlevel/ftape-rw.h" | ||
41 | #include "../lowlevel/ftape-io.h" | ||
42 | #include "../lowlevel/ftape-calibr.h" | ||
43 | #include "../lowlevel/ftape-bsm.h" | ||
44 | |||
45 | /* Global vars. | ||
46 | */ | ||
47 | volatile int ft_expected_stray_interrupts; | ||
48 | volatile int ft_interrupt_seen; | ||
49 | volatile int ft_seek_completed; | ||
50 | volatile int ft_hide_interrupt; | ||
51 | /* Local vars. | ||
52 | */ | ||
53 | typedef enum { | ||
54 | no_error = 0, id_am_error = 0x01, id_crc_error = 0x02, | ||
55 | data_am_error = 0x04, data_crc_error = 0x08, | ||
56 | no_data_error = 0x10, overrun_error = 0x20, | ||
57 | } error_cause; | ||
58 | static int stop_read_ahead; | ||
59 | |||
60 | |||
61 | static void print_error_cause(int cause) | ||
62 | { | ||
63 | TRACE_FUN(ft_t_any); | ||
64 | |||
65 | switch (cause) { | ||
66 | case no_data_error: | ||
67 | TRACE(ft_t_noise, "no data error"); | ||
68 | break; | ||
69 | case id_am_error: | ||
70 | TRACE(ft_t_noise, "id am error"); | ||
71 | break; | ||
72 | case id_crc_error: | ||
73 | TRACE(ft_t_noise, "id crc error"); | ||
74 | break; | ||
75 | case data_am_error: | ||
76 | TRACE(ft_t_noise, "data am error"); | ||
77 | break; | ||
78 | case data_crc_error: | ||
79 | TRACE(ft_t_noise, "data crc error"); | ||
80 | break; | ||
81 | case overrun_error: | ||
82 | TRACE(ft_t_noise, "overrun error"); | ||
83 | break; | ||
84 | default:; | ||
85 | } | ||
86 | TRACE_EXIT; | ||
87 | } | ||
88 | |||
89 | static char *fdc_mode_txt(fdc_mode_enum mode) | ||
90 | { | ||
91 | switch (mode) { | ||
92 | case fdc_idle: | ||
93 | return "fdc_idle"; | ||
94 | case fdc_reading_data: | ||
95 | return "fdc_reading_data"; | ||
96 | case fdc_seeking: | ||
97 | return "fdc_seeking"; | ||
98 | case fdc_writing_data: | ||
99 | return "fdc_writing_data"; | ||
100 | case fdc_reading_id: | ||
101 | return "fdc_reading_id"; | ||
102 | case fdc_recalibrating: | ||
103 | return "fdc_recalibrating"; | ||
104 | case fdc_formatting: | ||
105 | return "fdc_formatting"; | ||
106 | case fdc_verifying: | ||
107 | return "fdc_verifying"; | ||
108 | default: | ||
109 | return "unknown"; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static inline error_cause decode_irq_cause(fdc_mode_enum mode, __u8 st[]) | ||
114 | { | ||
115 | error_cause cause = no_error; | ||
116 | TRACE_FUN(ft_t_any); | ||
117 | |||
118 | /* Valid st[], decode cause of interrupt. | ||
119 | */ | ||
120 | switch (st[0] & ST0_INT_MASK) { | ||
121 | case FDC_INT_NORMAL: | ||
122 | TRACE(ft_t_fdc_dma,"normal completion: %s",fdc_mode_txt(mode)); | ||
123 | break; | ||
124 | case FDC_INT_ABNORMAL: | ||
125 | TRACE(ft_t_flow, "abnormal completion %s", fdc_mode_txt(mode)); | ||
126 | TRACE(ft_t_fdc_dma, "ST0: 0x%02x, ST1: 0x%02x, ST2: 0x%02x", | ||
127 | st[0], st[1], st[2]); | ||
128 | TRACE(ft_t_fdc_dma, | ||
129 | "C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x", | ||
130 | st[3], st[4], st[5], st[6]); | ||
131 | if (st[1] & 0x01) { | ||
132 | if (st[2] & 0x01) { | ||
133 | cause = data_am_error; | ||
134 | } else { | ||
135 | cause = id_am_error; | ||
136 | } | ||
137 | } else if (st[1] & 0x20) { | ||
138 | if (st[2] & 0x20) { | ||
139 | cause = data_crc_error; | ||
140 | } else { | ||
141 | cause = id_crc_error; | ||
142 | } | ||
143 | } else if (st[1] & 0x04) { | ||
144 | cause = no_data_error; | ||
145 | } else if (st[1] & 0x10) { | ||
146 | cause = overrun_error; | ||
147 | } | ||
148 | print_error_cause(cause); | ||
149 | break; | ||
150 | case FDC_INT_INVALID: | ||
151 | TRACE(ft_t_flow, "invalid completion %s", fdc_mode_txt(mode)); | ||
152 | break; | ||
153 | case FDC_INT_READYCH: | ||
154 | if (st[0] & ST0_SEEK_END) { | ||
155 | TRACE(ft_t_flow, "drive poll completed"); | ||
156 | } else { | ||
157 | TRACE(ft_t_flow, "ready change %s",fdc_mode_txt(mode)); | ||
158 | } | ||
159 | break; | ||
160 | default: | ||
161 | break; | ||
162 | } | ||
163 | TRACE_EXIT cause; | ||
164 | } | ||
165 | |||
166 | static void update_history(error_cause cause) | ||
167 | { | ||
168 | switch (cause) { | ||
169 | case id_am_error: | ||
170 | ft_history.id_am_errors++; | ||
171 | break; | ||
172 | case id_crc_error: | ||
173 | ft_history.id_crc_errors++; | ||
174 | break; | ||
175 | case data_am_error: | ||
176 | ft_history.data_am_errors++; | ||
177 | break; | ||
178 | case data_crc_error: | ||
179 | ft_history.data_crc_errors++; | ||
180 | break; | ||
181 | case overrun_error: | ||
182 | ft_history.overrun_errors++; | ||
183 | break; | ||
184 | case no_data_error: | ||
185 | ft_history.no_data_errors++; | ||
186 | break; | ||
187 | default:; | ||
188 | } | ||
189 | } | ||
190 | |||
191 | static void skip_bad_sector(buffer_struct * buff) | ||
192 | { | ||
193 | TRACE_FUN(ft_t_any); | ||
194 | |||
195 | /* Mark sector as soft error and skip it | ||
196 | */ | ||
197 | if (buff->remaining > 0) { | ||
198 | ++buff->sector_offset; | ||
199 | ++buff->data_offset; | ||
200 | --buff->remaining; | ||
201 | buff->ptr += FT_SECTOR_SIZE; | ||
202 | buff->bad_sector_map >>= 1; | ||
203 | } else { | ||
204 | /* Hey, what is this????????????? C code: if we shift | ||
205 | * more than 31 bits, we get no shift. That's bad!!!!!! | ||
206 | */ | ||
207 | ++buff->sector_offset; /* hack for error maps */ | ||
208 | TRACE(ft_t_warn, "skipping last sector in segment"); | ||
209 | } | ||
210 | TRACE_EXIT; | ||
211 | } | ||
212 | |||
213 | static void update_error_maps(buffer_struct * buff, unsigned int error_offset) | ||
214 | { | ||
215 | int hard = 0; | ||
216 | TRACE_FUN(ft_t_any); | ||
217 | |||
218 | if (buff->retry < FT_SOFT_RETRIES) { | ||
219 | buff->soft_error_map |= (1 << error_offset); | ||
220 | } else { | ||
221 | buff->hard_error_map |= (1 << error_offset); | ||
222 | buff->soft_error_map &= ~buff->hard_error_map; | ||
223 | buff->retry = -1; /* will be set to 0 in setup_segment */ | ||
224 | hard = 1; | ||
225 | } | ||
226 | TRACE(ft_t_noise, "sector %d : %s error\n" | ||
227 | KERN_INFO "hard map: 0x%08lx\n" | ||
228 | KERN_INFO "soft map: 0x%08lx", | ||
229 | FT_SECTOR(error_offset), hard ? "hard" : "soft", | ||
230 | (long) buff->hard_error_map, (long) buff->soft_error_map); | ||
231 | TRACE_EXIT; | ||
232 | } | ||
233 | |||
234 | static void print_progress(buffer_struct *buff, error_cause cause) | ||
235 | { | ||
236 | TRACE_FUN(ft_t_any); | ||
237 | |||
238 | switch (cause) { | ||
239 | case no_error: | ||
240 | TRACE(ft_t_flow,"%d Sector(s) transferred", buff->sector_count); | ||
241 | break; | ||
242 | case no_data_error: | ||
243 | TRACE(ft_t_flow, "Sector %d not found", | ||
244 | FT_SECTOR(buff->sector_offset)); | ||
245 | break; | ||
246 | case overrun_error: | ||
247 | /* got an overrun error on the first byte, must be a | ||
248 | * hardware problem | ||
249 | */ | ||
250 | TRACE(ft_t_bug, | ||
251 | "Unexpected error: failing DMA or FDC controller ?"); | ||
252 | break; | ||
253 | case data_crc_error: | ||
254 | TRACE(ft_t_flow, "Error in sector %d", | ||
255 | FT_SECTOR(buff->sector_offset - 1)); | ||
256 | break; | ||
257 | case id_crc_error: | ||
258 | case id_am_error: | ||
259 | case data_am_error: | ||
260 | TRACE(ft_t_flow, "Error in sector %d", | ||
261 | FT_SECTOR(buff->sector_offset)); | ||
262 | break; | ||
263 | default: | ||
264 | TRACE(ft_t_flow, "Unexpected error at sector %d", | ||
265 | FT_SECTOR(buff->sector_offset)); | ||
266 | break; | ||
267 | } | ||
268 | TRACE_EXIT; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * Error cause: Amount xferred: Action: | ||
273 | * | ||
274 | * id_am_error 0 mark bad and skip | ||
275 | * id_crc_error 0 mark bad and skip | ||
276 | * data_am_error 0 mark bad and skip | ||
277 | * data_crc_error % 1024 mark bad and skip | ||
278 | * no_data_error 0 retry on write | ||
279 | * mark bad and skip on read | ||
280 | * overrun_error [ 0..all-1 ] mark bad and skip | ||
281 | * no_error all continue | ||
282 | */ | ||
283 | |||
284 | /* the arg `sector' is returned by the fdc and tells us at which sector we | ||
285 | * are positioned at (relative to starting sector of segment) | ||
286 | */ | ||
287 | static void determine_verify_progress(buffer_struct *buff, | ||
288 | error_cause cause, | ||
289 | __u8 sector) | ||
290 | { | ||
291 | TRACE_FUN(ft_t_any); | ||
292 | |||
293 | if (cause == no_error && sector == 1) { | ||
294 | buff->sector_offset = FT_SECTORS_PER_SEGMENT; | ||
295 | buff->remaining = 0; | ||
296 | if (TRACE_LEVEL >= ft_t_flow) { | ||
297 | print_progress(buff, cause); | ||
298 | } | ||
299 | } else { | ||
300 | buff->sector_offset = sector - buff->sect; | ||
301 | buff->remaining = FT_SECTORS_PER_SEGMENT - buff->sector_offset; | ||
302 | TRACE(ft_t_noise, "%ssector offset: 0x%04x", | ||
303 | (cause == no_error) ? "unexpected " : "", | ||
304 | buff->sector_offset); | ||
305 | switch (cause) { | ||
306 | case overrun_error: | ||
307 | break; | ||
308 | #if 0 | ||
309 | case no_data_error: | ||
310 | buff->retry = FT_SOFT_RETRIES; | ||
311 | if (buff->hard_error_map && | ||
312 | buff->sector_offset > 1 && | ||
313 | (buff->hard_error_map & | ||
314 | (1 << (buff->sector_offset-2)))) { | ||
315 | buff->retry --; | ||
316 | } | ||
317 | break; | ||
318 | #endif | ||
319 | default: | ||
320 | buff->retry = FT_SOFT_RETRIES; | ||
321 | break; | ||
322 | } | ||
323 | if (TRACE_LEVEL >= ft_t_flow) { | ||
324 | print_progress(buff, cause); | ||
325 | } | ||
326 | /* Sector_offset points to the problem area Now adjust | ||
327 | * sector_offset so it always points one past he failing | ||
328 | * sector. I.e. skip the bad sector. | ||
329 | */ | ||
330 | ++buff->sector_offset; | ||
331 | --buff->remaining; | ||
332 | update_error_maps(buff, buff->sector_offset - 1); | ||
333 | } | ||
334 | TRACE_EXIT; | ||
335 | } | ||
336 | |||
337 | static void determine_progress(buffer_struct *buff, | ||
338 | error_cause cause, | ||
339 | __u8 sector) | ||
340 | { | ||
341 | unsigned int dma_residue; | ||
342 | TRACE_FUN(ft_t_any); | ||
343 | |||
344 | /* Using less preferred order of disable_dma and | ||
345 | * get_dma_residue because this seems to fail on at least one | ||
346 | * system if reversed! | ||
347 | */ | ||
348 | dma_residue = get_dma_residue(fdc.dma); | ||
349 | disable_dma(fdc.dma); | ||
350 | if (cause != no_error || dma_residue != 0) { | ||
351 | TRACE(ft_t_noise, "%sDMA residue: 0x%04x", | ||
352 | (cause == no_error) ? "unexpected " : "", | ||
353 | dma_residue); | ||
354 | /* adjust to actual value: */ | ||
355 | if (dma_residue == 0) { | ||
356 | /* this happens sometimes with overrun errors. | ||
357 | * I don't know whether we could ignore the | ||
358 | * overrun error. Play save. | ||
359 | */ | ||
360 | buff->sector_count --; | ||
361 | } else { | ||
362 | buff->sector_count -= ((dma_residue + | ||
363 | (FT_SECTOR_SIZE - 1)) / | ||
364 | FT_SECTOR_SIZE); | ||
365 | } | ||
366 | } | ||
367 | /* Update var's influenced by the DMA operation. | ||
368 | */ | ||
369 | if (buff->sector_count > 0) { | ||
370 | buff->sector_offset += buff->sector_count; | ||
371 | buff->data_offset += buff->sector_count; | ||
372 | buff->ptr += (buff->sector_count * | ||
373 | FT_SECTOR_SIZE); | ||
374 | buff->remaining -= buff->sector_count; | ||
375 | buff->bad_sector_map >>= buff->sector_count; | ||
376 | } | ||
377 | if (TRACE_LEVEL >= ft_t_flow) { | ||
378 | print_progress(buff, cause); | ||
379 | } | ||
380 | if (cause != no_error) { | ||
381 | if (buff->remaining == 0) { | ||
382 | TRACE(ft_t_warn, "foo?\n" | ||
383 | KERN_INFO "count : %d\n" | ||
384 | KERN_INFO "offset: %d\n" | ||
385 | KERN_INFO "soft : %08x\n" | ||
386 | KERN_INFO "hard : %08x", | ||
387 | buff->sector_count, | ||
388 | buff->sector_offset, | ||
389 | buff->soft_error_map, | ||
390 | buff->hard_error_map); | ||
391 | } | ||
392 | /* Sector_offset points to the problem area, except if we got | ||
393 | * a data_crc_error. In that case it points one past the | ||
394 | * failing sector. | ||
395 | * | ||
396 | * Now adjust sector_offset so it always points one past he | ||
397 | * failing sector. I.e. skip the bad sector. | ||
398 | */ | ||
399 | if (cause != data_crc_error) { | ||
400 | skip_bad_sector(buff); | ||
401 | } | ||
402 | update_error_maps(buff, buff->sector_offset - 1); | ||
403 | } | ||
404 | TRACE_EXIT; | ||
405 | } | ||
406 | |||
407 | static int calc_steps(int cmd) | ||
408 | { | ||
409 | if (ftape_current_cylinder > cmd) { | ||
410 | return ftape_current_cylinder - cmd; | ||
411 | } else { | ||
412 | return ftape_current_cylinder + cmd; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | static void pause_tape(int retry, int mode) | ||
417 | { | ||
418 | int result; | ||
419 | __u8 out[3] = {FDC_SEEK, ft_drive_sel, 0}; | ||
420 | TRACE_FUN(ft_t_any); | ||
421 | |||
422 | /* We'll use a raw seek command to get the tape to rewind and | ||
423 | * stop for a retry. | ||
424 | */ | ||
425 | ++ft_history.rewinds; | ||
426 | if (qic117_cmds[ftape_current_command].non_intr) { | ||
427 | TRACE(ft_t_warn, "motion command may be issued too soon"); | ||
428 | } | ||
429 | if (retry && (mode == fdc_reading_data || | ||
430 | mode == fdc_reading_id || | ||
431 | mode == fdc_verifying)) { | ||
432 | ftape_current_command = QIC_MICRO_STEP_PAUSE; | ||
433 | ftape_might_be_off_track = 1; | ||
434 | } else { | ||
435 | ftape_current_command = QIC_PAUSE; | ||
436 | } | ||
437 | out[2] = calc_steps(ftape_current_command); | ||
438 | result = fdc_command(out, 3); /* issue QIC_117 command */ | ||
439 | ftape_current_cylinder = out[ 2]; | ||
440 | if (result < 0) { | ||
441 | TRACE(ft_t_noise, "qic-pause failed, status = %d", result); | ||
442 | } else { | ||
443 | ft_location.known = 0; | ||
444 | ft_runner_status = idle; | ||
445 | ft_hide_interrupt = 1; | ||
446 | ftape_tape_running = 0; | ||
447 | } | ||
448 | TRACE_EXIT; | ||
449 | } | ||
450 | |||
451 | static void continue_xfer(buffer_struct *buff, | ||
452 | fdc_mode_enum mode, | ||
453 | unsigned int skip) | ||
454 | { | ||
455 | int write = 0; | ||
456 | TRACE_FUN(ft_t_any); | ||
457 | |||
458 | if (mode == fdc_writing_data || mode == fdc_deleting) { | ||
459 | write = 1; | ||
460 | } | ||
461 | /* This part can be removed if it never happens | ||
462 | */ | ||
463 | if (skip > 0 && | ||
464 | (ft_runner_status != running || | ||
465 | (write && (buff->status != writing)) || | ||
466 | (!write && (buff->status != reading && | ||
467 | buff->status != verifying)))) { | ||
468 | TRACE(ft_t_err, "unexpected runner/buffer state %d/%d", | ||
469 | ft_runner_status, buff->status); | ||
470 | buff->status = error; | ||
471 | /* finish this buffer: */ | ||
472 | (void)ftape_next_buffer(ft_queue_head); | ||
473 | ft_runner_status = aborting; | ||
474 | fdc_mode = fdc_idle; | ||
475 | } else if (buff->remaining > 0 && ftape_calc_next_cluster(buff) > 0) { | ||
476 | /* still sectors left in current segment, continue | ||
477 | * with this segment | ||
478 | */ | ||
479 | if (fdc_setup_read_write(buff, mode) < 0) { | ||
480 | /* failed, abort operation | ||
481 | */ | ||
482 | buff->bytes = buff->ptr - buff->address; | ||
483 | buff->status = error; | ||
484 | /* finish this buffer: */ | ||
485 | (void)ftape_next_buffer(ft_queue_head); | ||
486 | ft_runner_status = aborting; | ||
487 | fdc_mode = fdc_idle; | ||
488 | } | ||
489 | } else { | ||
490 | /* current segment completed | ||
491 | */ | ||
492 | unsigned int last_segment = buff->segment_id; | ||
493 | int eot = ((last_segment + 1) % ft_segments_per_track) == 0; | ||
494 | unsigned int next = buff->next_segment; /* 0 means stop ! */ | ||
495 | |||
496 | buff->bytes = buff->ptr - buff->address; | ||
497 | buff->status = done; | ||
498 | buff = ftape_next_buffer(ft_queue_head); | ||
499 | if (eot) { | ||
500 | /* finished last segment on current track, | ||
501 | * can't continue | ||
502 | */ | ||
503 | ft_runner_status = logical_eot; | ||
504 | fdc_mode = fdc_idle; | ||
505 | TRACE_EXIT; | ||
506 | } | ||
507 | if (next <= 0) { | ||
508 | /* don't continue with next segment | ||
509 | */ | ||
510 | TRACE(ft_t_noise, "no %s allowed, stopping tape", | ||
511 | (write) ? "write next" : "read ahead"); | ||
512 | pause_tape(0, mode); | ||
513 | ft_runner_status = idle; /* not quite true until | ||
514 | * next irq | ||
515 | */ | ||
516 | TRACE_EXIT; | ||
517 | } | ||
518 | /* continue with next segment | ||
519 | */ | ||
520 | if (buff->status != waiting) { | ||
521 | TRACE(ft_t_noise, "all input buffers %s, pausing tape", | ||
522 | (write) ? "empty" : "full"); | ||
523 | pause_tape(0, mode); | ||
524 | ft_runner_status = idle; /* not quite true until | ||
525 | * next irq | ||
526 | */ | ||
527 | TRACE_EXIT; | ||
528 | } | ||
529 | if (write && next != buff->segment_id) { | ||
530 | TRACE(ft_t_noise, | ||
531 | "segments out of order, aborting write"); | ||
532 | ft_runner_status = do_abort; | ||
533 | fdc_mode = fdc_idle; | ||
534 | TRACE_EXIT; | ||
535 | } | ||
536 | ftape_setup_new_segment(buff, next, 0); | ||
537 | if (stop_read_ahead) { | ||
538 | buff->next_segment = 0; | ||
539 | stop_read_ahead = 0; | ||
540 | } | ||
541 | if (ftape_calc_next_cluster(buff) == 0 || | ||
542 | fdc_setup_read_write(buff, mode) != 0) { | ||
543 | TRACE(ft_t_err, "couldn't start %s-ahead", | ||
544 | write ? "write" : "read"); | ||
545 | ft_runner_status = do_abort; | ||
546 | fdc_mode = fdc_idle; | ||
547 | } else { | ||
548 | /* keep on going */ | ||
549 | switch (ft_driver_state) { | ||
550 | case reading: buff->status = reading; break; | ||
551 | case verifying: buff->status = verifying; break; | ||
552 | case writing: buff->status = writing; break; | ||
553 | case deleting: buff->status = deleting; break; | ||
554 | default: | ||
555 | TRACE(ft_t_err, | ||
556 | "BUG: ft_driver_state %d should be one out of " | ||
557 | "{reading, writing, verifying, deleting}", | ||
558 | ft_driver_state); | ||
559 | buff->status = write ? writing : reading; | ||
560 | break; | ||
561 | } | ||
562 | } | ||
563 | } | ||
564 | TRACE_EXIT; | ||
565 | } | ||
566 | |||
567 | static void retry_sector(buffer_struct *buff, | ||
568 | int mode, | ||
569 | unsigned int skip) | ||
570 | { | ||
571 | TRACE_FUN(ft_t_any); | ||
572 | |||
573 | TRACE(ft_t_noise, "%s error, will retry", | ||
574 | (mode == fdc_writing_data || mode == fdc_deleting) ? "write" : "read"); | ||
575 | pause_tape(1, mode); | ||
576 | ft_runner_status = aborting; | ||
577 | buff->status = error; | ||
578 | buff->skip = skip; | ||
579 | TRACE_EXIT; | ||
580 | } | ||
581 | |||
582 | static unsigned int find_resume_point(buffer_struct *buff) | ||
583 | { | ||
584 | int i = 0; | ||
585 | SectorMap mask; | ||
586 | SectorMap map; | ||
587 | TRACE_FUN(ft_t_any); | ||
588 | |||
589 | /* This function is to be called after all variables have been | ||
590 | * updated to point past the failing sector. | ||
591 | * If there are any soft errors before the failing sector, | ||
592 | * find the first soft error and return the sector offset. | ||
593 | * Otherwise find the last hard error. | ||
594 | * Note: there should always be at least one hard or soft error ! | ||
595 | */ | ||
596 | if (buff->sector_offset < 1 || buff->sector_offset > 32) { | ||
597 | TRACE(ft_t_bug, "BUG: sector_offset = %d", | ||
598 | buff->sector_offset); | ||
599 | TRACE_EXIT 0; | ||
600 | } | ||
601 | if (buff->sector_offset >= 32) { /* C-limitation on shift ! */ | ||
602 | mask = 0xffffffff; | ||
603 | } else { | ||
604 | mask = (1 << buff->sector_offset) - 1; | ||
605 | } | ||
606 | map = buff->soft_error_map & mask; | ||
607 | if (map) { | ||
608 | while ((map & (1 << i)) == 0) { | ||
609 | ++i; | ||
610 | } | ||
611 | TRACE(ft_t_noise, "at sector %d", FT_SECTOR(i)); | ||
612 | } else { | ||
613 | map = buff->hard_error_map & mask; | ||
614 | i = buff->sector_offset - 1; | ||
615 | if (map) { | ||
616 | while ((map & (1 << i)) == 0) { | ||
617 | --i; | ||
618 | } | ||
619 | TRACE(ft_t_noise, "after sector %d", FT_SECTOR(i)); | ||
620 | ++i; /* first sector after last hard error */ | ||
621 | } else { | ||
622 | TRACE(ft_t_bug, "BUG: no soft or hard errors"); | ||
623 | } | ||
624 | } | ||
625 | TRACE_EXIT i; | ||
626 | } | ||
627 | |||
628 | /* check possible dma residue when formatting, update position record in | ||
629 | * buffer struct. This is, of course, modelled after determine_progress(), but | ||
630 | * we don't need to set up for retries because the format process cannot be | ||
631 | * interrupted (except at the end of the tape track). | ||
632 | */ | ||
633 | static int determine_fmt_progress(buffer_struct *buff, error_cause cause) | ||
634 | { | ||
635 | unsigned int dma_residue; | ||
636 | TRACE_FUN(ft_t_any); | ||
637 | |||
638 | /* Using less preferred order of disable_dma and | ||
639 | * get_dma_residue because this seems to fail on at least one | ||
640 | * system if reversed! | ||
641 | */ | ||
642 | dma_residue = get_dma_residue(fdc.dma); | ||
643 | disable_dma(fdc.dma); | ||
644 | if (cause != no_error || dma_residue != 0) { | ||
645 | TRACE(ft_t_info, "DMA residue = 0x%04x", dma_residue); | ||
646 | fdc_mode = fdc_idle; | ||
647 | switch(cause) { | ||
648 | case no_error: | ||
649 | ft_runner_status = aborting; | ||
650 | buff->status = idle; | ||
651 | break; | ||
652 | case overrun_error: | ||
653 | /* got an overrun error on the first byte, must be a | ||
654 | * hardware problem | ||
655 | */ | ||
656 | TRACE(ft_t_bug, | ||
657 | "Unexpected error: failing DMA controller ?"); | ||
658 | ft_runner_status = do_abort; | ||
659 | buff->status = error; | ||
660 | break; | ||
661 | default: | ||
662 | TRACE(ft_t_noise, "Unexpected error at segment %d", | ||
663 | buff->segment_id); | ||
664 | ft_runner_status = do_abort; | ||
665 | buff->status = error; | ||
666 | break; | ||
667 | } | ||
668 | TRACE_EXIT -EIO; /* can only retry entire track in format mode | ||
669 | */ | ||
670 | } | ||
671 | /* Update var's influenced by the DMA operation. | ||
672 | */ | ||
673 | buff->ptr += FT_SECTORS_PER_SEGMENT * 4; | ||
674 | buff->bytes -= FT_SECTORS_PER_SEGMENT * 4; | ||
675 | buff->remaining -= FT_SECTORS_PER_SEGMENT; | ||
676 | buff->segment_id ++; /* done with segment */ | ||
677 | TRACE_EXIT 0; | ||
678 | } | ||
679 | |||
680 | /* | ||
681 | * Continue formatting, switch buffers if there is no data left in | ||
682 | * current buffer. This is, of course, modelled after | ||
683 | * continue_xfer(), but we don't need to set up for retries because | ||
684 | * the format process cannot be interrupted (except at the end of the | ||
685 | * tape track). | ||
686 | */ | ||
687 | static void continue_formatting(buffer_struct *buff) | ||
688 | { | ||
689 | TRACE_FUN(ft_t_any); | ||
690 | |||
691 | if (buff->remaining <= 0) { /* no space left in dma buffer */ | ||
692 | unsigned int next = buff->next_segment; | ||
693 | |||
694 | if (next == 0) { /* end of tape track */ | ||
695 | buff->status = done; | ||
696 | ft_runner_status = logical_eot; | ||
697 | fdc_mode = fdc_idle; | ||
698 | TRACE(ft_t_noise, "Done formatting track %d", | ||
699 | ft_location.track); | ||
700 | TRACE_EXIT; | ||
701 | } | ||
702 | /* | ||
703 | * switch to next buffer! | ||
704 | */ | ||
705 | buff->status = done; | ||
706 | buff = ftape_next_buffer(ft_queue_head); | ||
707 | |||
708 | if (buff->status != waiting || next != buff->segment_id) { | ||
709 | goto format_setup_error; | ||
710 | } | ||
711 | } | ||
712 | if (fdc_setup_formatting(buff) < 0) { | ||
713 | goto format_setup_error; | ||
714 | } | ||
715 | buff->status = formatting; | ||
716 | TRACE(ft_t_fdc_dma, "Formatting segment %d on track %d", | ||
717 | buff->segment_id, ft_location.track); | ||
718 | TRACE_EXIT; | ||
719 | format_setup_error: | ||
720 | ft_runner_status = do_abort; | ||
721 | fdc_mode = fdc_idle; | ||
722 | buff->status = error; | ||
723 | TRACE(ft_t_err, "Error setting up for segment %d on track %d", | ||
724 | buff->segment_id, ft_location.track); | ||
725 | TRACE_EXIT; | ||
726 | |||
727 | } | ||
728 | |||
729 | /* this handles writing, read id, reading and formatting | ||
730 | */ | ||
731 | static void handle_fdc_busy(buffer_struct *buff) | ||
732 | { | ||
733 | static int no_data_error_count; | ||
734 | int retry = 0; | ||
735 | error_cause cause; | ||
736 | __u8 in[7]; | ||
737 | int skip; | ||
738 | fdc_mode_enum fmode = fdc_mode; | ||
739 | TRACE_FUN(ft_t_any); | ||
740 | |||
741 | if (fdc_result(in, 7) < 0) { /* better get it fast ! */ | ||
742 | TRACE(ft_t_err, | ||
743 | "Probably fatal error during FDC Result Phase\n" | ||
744 | KERN_INFO | ||
745 | "drive may hang until (power on) reset :-("); | ||
746 | /* what to do next ???? | ||
747 | */ | ||
748 | TRACE_EXIT; | ||
749 | } | ||
750 | cause = decode_irq_cause(fdc_mode, in); | ||
751 | #ifdef TESTING | ||
752 | { int i; | ||
753 | for (i = 0; i < (int)ft_nr_buffers; ++i) | ||
754 | TRACE(ft_t_any, "buffer[%d] status: %d, segment_id: %d", | ||
755 | i, ft_buffer[i]->status, ft_buffer[i]->segment_id); | ||
756 | } | ||
757 | #endif | ||
758 | if (fmode == fdc_reading_data && ft_driver_state == verifying) { | ||
759 | fmode = fdc_verifying; | ||
760 | } | ||
761 | switch (fmode) { | ||
762 | case fdc_verifying: | ||
763 | if (ft_runner_status == aborting || | ||
764 | ft_runner_status == do_abort) { | ||
765 | TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); | ||
766 | break; | ||
767 | } | ||
768 | if (buff->retry > 0) { | ||
769 | TRACE(ft_t_flow, "this is retry nr %d", buff->retry); | ||
770 | } | ||
771 | switch (cause) { | ||
772 | case no_error: | ||
773 | no_data_error_count = 0; | ||
774 | determine_verify_progress(buff, cause, in[5]); | ||
775 | if (in[2] & 0x40) { | ||
776 | /* This should not happen when verifying | ||
777 | */ | ||
778 | TRACE(ft_t_warn, | ||
779 | "deleted data in segment %d/%d", | ||
780 | buff->segment_id, | ||
781 | FT_SECTOR(buff->sector_offset - 1)); | ||
782 | buff->remaining = 0; /* abort transfer */ | ||
783 | buff->hard_error_map = EMPTY_SEGMENT; | ||
784 | skip = 1; | ||
785 | } else { | ||
786 | skip = 0; | ||
787 | } | ||
788 | continue_xfer(buff, fdc_mode, skip); | ||
789 | break; | ||
790 | case no_data_error: | ||
791 | no_data_error_count ++; | ||
792 | case overrun_error: | ||
793 | retry ++; | ||
794 | case id_am_error: | ||
795 | case id_crc_error: | ||
796 | case data_am_error: | ||
797 | case data_crc_error: | ||
798 | determine_verify_progress(buff, cause, in[5]); | ||
799 | if (cause == no_data_error) { | ||
800 | if (no_data_error_count >= 2) { | ||
801 | TRACE(ft_t_warn, | ||
802 | "retrying because of successive " | ||
803 | "no data errors"); | ||
804 | no_data_error_count = 0; | ||
805 | } else { | ||
806 | retry --; | ||
807 | } | ||
808 | } else { | ||
809 | no_data_error_count = 0; | ||
810 | } | ||
811 | if (retry) { | ||
812 | skip = find_resume_point(buff); | ||
813 | } else { | ||
814 | skip = buff->sector_offset; | ||
815 | } | ||
816 | if (retry && skip < 32) { | ||
817 | retry_sector(buff, fdc_mode, skip); | ||
818 | } else { | ||
819 | continue_xfer(buff, fdc_mode, skip); | ||
820 | } | ||
821 | update_history(cause); | ||
822 | break; | ||
823 | default: | ||
824 | /* Don't know why this could happen | ||
825 | * but find out. | ||
826 | */ | ||
827 | determine_verify_progress(buff, cause, in[5]); | ||
828 | retry_sector(buff, fdc_mode, 0); | ||
829 | TRACE(ft_t_err, "Error: unexpected error"); | ||
830 | break; | ||
831 | } | ||
832 | break; | ||
833 | case fdc_reading_data: | ||
834 | #ifdef TESTING | ||
835 | /* I'm sorry, but: NOBODY ever used this trace | ||
836 | * messages for ages. I guess that Bas was the last person | ||
837 | * that ever really used this (thank you, between the lines) | ||
838 | */ | ||
839 | if (cause == no_error) { | ||
840 | TRACE(ft_t_flow,"reading segment %d",buff->segment_id); | ||
841 | } else { | ||
842 | TRACE(ft_t_noise, "error reading segment %d", | ||
843 | buff->segment_id); | ||
844 | TRACE(ft_t_noise, "\n" | ||
845 | KERN_INFO | ||
846 | "IRQ:C: 0x%02x, H: 0x%02x, R: 0x%02x, N: 0x%02x\n" | ||
847 | KERN_INFO | ||
848 | "BUF:C: 0x%02x, H: 0x%02x, R: 0x%02x", | ||
849 | in[3], in[4], in[5], in[6], | ||
850 | buff->cyl, buff->head, buff->sect); | ||
851 | } | ||
852 | #endif | ||
853 | if (ft_runner_status == aborting || | ||
854 | ft_runner_status == do_abort) { | ||
855 | TRACE(ft_t_noise,"aborting %s",fdc_mode_txt(fdc_mode)); | ||
856 | break; | ||
857 | } | ||
858 | if (buff->bad_sector_map == FAKE_SEGMENT) { | ||
859 | /* This condition occurs when reading a `fake' | ||
860 | * sector that's not accessible. Doesn't | ||
861 | * really matter as we would have ignored it | ||
862 | * anyway ! | ||
863 | * | ||
864 | * Chance is that we're past the next segment | ||
865 | * now, so the next operation may fail and | ||
866 | * result in a retry. | ||
867 | */ | ||
868 | buff->remaining = 0; /* skip failing sector */ | ||
869 | /* buff->ptr = buff->address; */ | ||
870 | /* fake success: */ | ||
871 | continue_xfer(buff, fdc_mode, 1); | ||
872 | /* trace calls are expensive: place them AFTER | ||
873 | * the real stuff has been done. | ||
874 | * | ||
875 | */ | ||
876 | TRACE(ft_t_noise, "skipping empty segment %d (read), size? %d", | ||
877 | buff->segment_id, buff->ptr - buff->address); | ||
878 | TRACE_EXIT; | ||
879 | } | ||
880 | if (buff->retry > 0) { | ||
881 | TRACE(ft_t_flow, "this is retry nr %d", buff->retry); | ||
882 | } | ||
883 | switch (cause) { | ||
884 | case no_error: | ||
885 | determine_progress(buff, cause, in[5]); | ||
886 | if (in[2] & 0x40) { | ||
887 | /* Handle deleted data in header segments. | ||
888 | * Skip segment and force read-ahead. | ||
889 | */ | ||
890 | TRACE(ft_t_warn, | ||
891 | "deleted data in segment %d/%d", | ||
892 | buff->segment_id, | ||
893 | FT_SECTOR(buff->sector_offset - 1)); | ||
894 | buff->deleted = 1; | ||
895 | buff->remaining = 0;/*abort transfer */ | ||
896 | buff->soft_error_map |= | ||
897 | (-1L << buff->sector_offset); | ||
898 | if (buff->segment_id == 0) { | ||
899 | /* stop on next segment */ | ||
900 | stop_read_ahead = 1; | ||
901 | } | ||
902 | /* force read-ahead: */ | ||
903 | buff->next_segment = | ||
904 | buff->segment_id + 1; | ||
905 | skip = (FT_SECTORS_PER_SEGMENT - | ||
906 | buff->sector_offset); | ||
907 | } else { | ||
908 | skip = 0; | ||
909 | } | ||
910 | continue_xfer(buff, fdc_mode, skip); | ||
911 | break; | ||
912 | case no_data_error: | ||
913 | /* Tape started too far ahead of or behind the | ||
914 | * right sector. This may also happen in the | ||
915 | * middle of a segment ! | ||
916 | * | ||
917 | * Handle no-data as soft error. If next | ||
918 | * sector fails too, a retry (with needed | ||
919 | * reposition) will follow. | ||
920 | */ | ||
921 | retry ++; | ||
922 | case id_am_error: | ||
923 | case id_crc_error: | ||
924 | case data_am_error: | ||
925 | case data_crc_error: | ||
926 | case overrun_error: | ||
927 | retry += (buff->soft_error_map != 0 || | ||
928 | buff->hard_error_map != 0); | ||
929 | determine_progress(buff, cause, in[5]); | ||
930 | #if 1 || defined(TESTING) | ||
931 | if (cause == overrun_error) retry ++; | ||
932 | #endif | ||
933 | if (retry) { | ||
934 | skip = find_resume_point(buff); | ||
935 | } else { | ||
936 | skip = buff->sector_offset; | ||
937 | } | ||
938 | /* Try to resume with next sector on single | ||
939 | * errors (let ecc correct it), but retry on | ||
940 | * no_data (we'll be past the target when we | ||
941 | * get here so we cannot retry) or on | ||
942 | * multiple errors (reduce chance on ecc | ||
943 | * failure). | ||
944 | */ | ||
945 | /* cH: 23/02/97: if the last sector in the | ||
946 | * segment was a hard error, then there is | ||
947 | * no sense in a retry. This occasion seldom | ||
948 | * occurs but ... @:³²¸`@%&§$ | ||
949 | */ | ||
950 | if (retry && skip < 32) { | ||
951 | retry_sector(buff, fdc_mode, skip); | ||
952 | } else { | ||
953 | continue_xfer(buff, fdc_mode, skip); | ||
954 | } | ||
955 | update_history(cause); | ||
956 | break; | ||
957 | default: | ||
958 | /* Don't know why this could happen | ||
959 | * but find out. | ||
960 | */ | ||
961 | determine_progress(buff, cause, in[5]); | ||
962 | retry_sector(buff, fdc_mode, 0); | ||
963 | TRACE(ft_t_err, "Error: unexpected error"); | ||
964 | break; | ||
965 | } | ||
966 | break; | ||
967 | case fdc_reading_id: | ||
968 | if (cause == no_error) { | ||
969 | fdc_cyl = in[3]; | ||
970 | fdc_head = in[4]; | ||
971 | fdc_sect = in[5]; | ||
972 | TRACE(ft_t_fdc_dma, | ||
973 | "id read: C: 0x%02x, H: 0x%02x, R: 0x%02x", | ||
974 | fdc_cyl, fdc_head, fdc_sect); | ||
975 | } else { /* no valid information, use invalid sector */ | ||
976 | fdc_cyl = fdc_head = fdc_sect = 0; | ||
977 | TRACE(ft_t_flow, "Didn't find valid sector Id"); | ||
978 | } | ||
979 | fdc_mode = fdc_idle; | ||
980 | break; | ||
981 | case fdc_deleting: | ||
982 | case fdc_writing_data: | ||
983 | #ifdef TESTING | ||
984 | if (cause == no_error) { | ||
985 | TRACE(ft_t_flow, "writing segment %d", buff->segment_id); | ||
986 | } else { | ||
987 | TRACE(ft_t_noise, "error writing segment %d", | ||
988 | buff->segment_id); | ||
989 | } | ||
990 | #endif | ||
991 | if (ft_runner_status == aborting || | ||
992 | ft_runner_status == do_abort) { | ||
993 | TRACE(ft_t_flow, "aborting %s",fdc_mode_txt(fdc_mode)); | ||
994 | break; | ||
995 | } | ||
996 | if (buff->retry > 0) { | ||
997 | TRACE(ft_t_flow, "this is retry nr %d", buff->retry); | ||
998 | } | ||
999 | if (buff->bad_sector_map == FAKE_SEGMENT) { | ||
1000 | /* This condition occurs when trying to write to a | ||
1001 | * `fake' sector that's not accessible. Doesn't really | ||
1002 | * matter as it isn't used anyway ! Might be located | ||
1003 | * at wrong segment, then we'll fail on the next | ||
1004 | * segment. | ||
1005 | */ | ||
1006 | TRACE(ft_t_noise, "skipping empty segment (write)"); | ||
1007 | buff->remaining = 0; /* skip failing sector */ | ||
1008 | /* fake success: */ | ||
1009 | continue_xfer(buff, fdc_mode, 1); | ||
1010 | break; | ||
1011 | } | ||
1012 | switch (cause) { | ||
1013 | case no_error: | ||
1014 | determine_progress(buff, cause, in[5]); | ||
1015 | continue_xfer(buff, fdc_mode, 0); | ||
1016 | break; | ||
1017 | case no_data_error: | ||
1018 | case id_am_error: | ||
1019 | case id_crc_error: | ||
1020 | case data_am_error: | ||
1021 | case overrun_error: | ||
1022 | update_history(cause); | ||
1023 | determine_progress(buff, cause, in[5]); | ||
1024 | skip = find_resume_point(buff); | ||
1025 | retry_sector(buff, fdc_mode, skip); | ||
1026 | break; | ||
1027 | default: | ||
1028 | if (in[1] & 0x02) { | ||
1029 | TRACE(ft_t_err, "media not writable"); | ||
1030 | } else { | ||
1031 | TRACE(ft_t_bug, "unforeseen write error"); | ||
1032 | } | ||
1033 | fdc_mode = fdc_idle; | ||
1034 | break; | ||
1035 | } | ||
1036 | break; /* fdc_deleting || fdc_writing_data */ | ||
1037 | case fdc_formatting: | ||
1038 | /* The interrupt comes after formatting a segment. We then | ||
1039 | * have to set up QUICKLY for the next segment. But | ||
1040 | * afterwards, there is plenty of time. | ||
1041 | */ | ||
1042 | switch (cause) { | ||
1043 | case no_error: | ||
1044 | /* would like to keep most of the formatting stuff | ||
1045 | * outside the isr code, but timing is too critical | ||
1046 | */ | ||
1047 | if (determine_fmt_progress(buff, cause) >= 0) { | ||
1048 | continue_formatting(buff); | ||
1049 | } | ||
1050 | break; | ||
1051 | case no_data_error: | ||
1052 | case id_am_error: | ||
1053 | case id_crc_error: | ||
1054 | case data_am_error: | ||
1055 | case overrun_error: | ||
1056 | default: | ||
1057 | determine_fmt_progress(buff, cause); | ||
1058 | update_history(cause); | ||
1059 | if (in[1] & 0x02) { | ||
1060 | TRACE(ft_t_err, "media not writable"); | ||
1061 | } else { | ||
1062 | TRACE(ft_t_bug, "unforeseen write error"); | ||
1063 | } | ||
1064 | break; | ||
1065 | } /* cause */ | ||
1066 | break; | ||
1067 | default: | ||
1068 | TRACE(ft_t_warn, "Warning: unexpected irq during: %s", | ||
1069 | fdc_mode_txt(fdc_mode)); | ||
1070 | fdc_mode = fdc_idle; | ||
1071 | break; | ||
1072 | } | ||
1073 | TRACE_EXIT; | ||
1074 | } | ||
1075 | |||
1076 | /* FDC interrupt service routine. | ||
1077 | */ | ||
1078 | void fdc_isr(void) | ||
1079 | { | ||
1080 | static int isr_active; | ||
1081 | #ifdef TESTING | ||
1082 | unsigned int t0 = ftape_timestamp(); | ||
1083 | #endif | ||
1084 | TRACE_FUN(ft_t_any); | ||
1085 | |||
1086 | if (isr_active++) { | ||
1087 | --isr_active; | ||
1088 | TRACE(ft_t_bug, "BUG: nested interrupt, not good !"); | ||
1089 | *fdc.hook = fdc_isr; /* hook our handler into the fdc | ||
1090 | * code again | ||
1091 | */ | ||
1092 | TRACE_EXIT; | ||
1093 | } | ||
1094 | sti(); | ||
1095 | if (inb_p(fdc.msr) & FDC_BUSY) { /* Entering Result Phase */ | ||
1096 | ft_hide_interrupt = 0; | ||
1097 | handle_fdc_busy(ftape_get_buffer(ft_queue_head)); | ||
1098 | if (ft_runner_status == do_abort) { | ||
1099 | /* cease operation, remember tape position | ||
1100 | */ | ||
1101 | TRACE(ft_t_flow, "runner aborting"); | ||
1102 | ft_runner_status = aborting; | ||
1103 | ++ft_expected_stray_interrupts; | ||
1104 | } | ||
1105 | } else { /* !FDC_BUSY */ | ||
1106 | /* clear interrupt, cause should be gotten by issuing | ||
1107 | * a Sense Interrupt Status command. | ||
1108 | */ | ||
1109 | if (fdc_mode == fdc_recalibrating || fdc_mode == fdc_seeking) { | ||
1110 | if (ft_hide_interrupt) { | ||
1111 | int st0; | ||
1112 | int pcn; | ||
1113 | |||
1114 | if (fdc_sense_interrupt_status(&st0, &pcn) < 0) | ||
1115 | TRACE(ft_t_err, | ||
1116 | "sense interrupt status failed"); | ||
1117 | ftape_current_cylinder = pcn; | ||
1118 | TRACE(ft_t_flow, "handled hidden interrupt"); | ||
1119 | } | ||
1120 | ft_seek_completed = 1; | ||
1121 | fdc_mode = fdc_idle; | ||
1122 | } else if (!waitqueue_active(&ftape_wait_intr)) { | ||
1123 | if (ft_expected_stray_interrupts == 0) { | ||
1124 | TRACE(ft_t_warn, "unexpected stray interrupt"); | ||
1125 | } else { | ||
1126 | TRACE(ft_t_flow, "expected stray interrupt"); | ||
1127 | --ft_expected_stray_interrupts; | ||
1128 | } | ||
1129 | } else { | ||
1130 | if (fdc_mode == fdc_reading_data || | ||
1131 | fdc_mode == fdc_verifying || | ||
1132 | fdc_mode == fdc_writing_data || | ||
1133 | fdc_mode == fdc_deleting || | ||
1134 | fdc_mode == fdc_formatting || | ||
1135 | fdc_mode == fdc_reading_id) { | ||
1136 | if (inb_p(fdc.msr) & FDC_BUSY) { | ||
1137 | TRACE(ft_t_bug, | ||
1138 | "***** FDC failure, busy too late"); | ||
1139 | } else { | ||
1140 | TRACE(ft_t_bug, | ||
1141 | "***** FDC failure, no busy"); | ||
1142 | } | ||
1143 | } else { | ||
1144 | TRACE(ft_t_fdc_dma, "awaited stray interrupt"); | ||
1145 | } | ||
1146 | } | ||
1147 | ft_hide_interrupt = 0; | ||
1148 | } | ||
1149 | /* Handle sleep code. | ||
1150 | */ | ||
1151 | if (!ft_hide_interrupt) { | ||
1152 | ft_interrupt_seen ++; | ||
1153 | if (waitqueue_active(&ftape_wait_intr)) { | ||
1154 | wake_up_interruptible(&ftape_wait_intr); | ||
1155 | } | ||
1156 | } else { | ||
1157 | TRACE(ft_t_flow, "hiding interrupt while %s", | ||
1158 | waitqueue_active(&ftape_wait_intr) ? "waiting":"active"); | ||
1159 | } | ||
1160 | #ifdef TESTING | ||
1161 | t0 = ftape_timediff(t0, ftape_timestamp()); | ||
1162 | if (t0 >= 1000) { | ||
1163 | /* only tell us about long calls */ | ||
1164 | TRACE(ft_t_noise, "isr() duration: %5d usec", t0); | ||
1165 | } | ||
1166 | #endif | ||
1167 | *fdc.hook = fdc_isr; /* hook our handler into the fdc code again */ | ||
1168 | --isr_active; | ||
1169 | TRACE_EXIT; | ||
1170 | } | ||
diff --git a/drivers/char/ftape/lowlevel/fdc-isr.h b/drivers/char/ftape/lowlevel/fdc-isr.h new file mode 100644 index 000000000000..065aa978942d --- /dev/null +++ b/drivers/char/ftape/lowlevel/fdc-isr.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef _FDC_ISR_H | ||
2 | #define _FDC_ISR_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/fdc-isr.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:07 $ | ||
26 | * | ||
27 | * This file declares the global variables necessary to | ||
28 | * synchronize the interrupt service routine (isr) with the | ||
29 | * remainder of the QIC-40/80/3010/3020 floppy-tape driver | ||
30 | * "ftape" for Linux. | ||
31 | */ | ||
32 | |||
33 | /* | ||
34 | * fdc-isr.c defined public variables | ||
35 | */ | ||
36 | extern volatile int ft_expected_stray_interrupts; /* masks stray interrupts */ | ||
37 | extern volatile int ft_seek_completed; /* flag set by isr */ | ||
38 | extern volatile int ft_interrupt_seen; /* flag set by isr */ | ||
39 | extern volatile int ft_hide_interrupt; /* flag set by isr */ | ||
40 | |||
41 | /* | ||
42 | * fdc-io.c defined public functions | ||
43 | */ | ||
44 | extern void fdc_isr(void); | ||
45 | |||
46 | /* | ||
47 | * A kernel hook that steals one interrupt from the floppy | ||
48 | * driver (Should be fixed when the new fdc driver gets ready) | ||
49 | * See the linux kernel source files: | ||
50 | * drivers/block/floppy.c & drivers/block/blk.h | ||
51 | * for the details. | ||
52 | */ | ||
53 | extern void (*do_floppy) (void); | ||
54 | |||
55 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.c b/drivers/char/ftape/lowlevel/ftape-bsm.c new file mode 100644 index 000000000000..d1a301cc344f --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-bsm.c | |||
@@ -0,0 +1,491 @@ | |||
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 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-bsm.h b/drivers/char/ftape/lowlevel/ftape-bsm.h new file mode 100644 index 000000000000..ed45465af4d4 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-bsm.h | |||
@@ -0,0 +1,66 @@ | |||
1 | #ifndef _FTAPE_BSM_H | ||
2 | #define _FTAPE_BSM_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-bsm.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:07 $ | ||
26 | * | ||
27 | * This file contains definitions for the bad sector map handling | ||
28 | * routines for the QIC-117 floppy-tape driver for Linux. | ||
29 | */ | ||
30 | |||
31 | #include <linux/ftape.h> | ||
32 | #include <linux/ftape-header-segment.h> | ||
33 | |||
34 | #define EMPTY_SEGMENT (0xffffffff) | ||
35 | #define FAKE_SEGMENT (0xfffffffe) | ||
36 | |||
37 | /* maximum (format code 4) bad sector map size (bytes). | ||
38 | */ | ||
39 | #define BAD_SECTOR_MAP_SIZE (29 * SECTOR_SIZE - 256) | ||
40 | |||
41 | /* format code 4 bad sector entry, ftape uses this | ||
42 | * internally for all format codes | ||
43 | */ | ||
44 | typedef __u32 SectorMap; | ||
45 | /* variable and 1100 ft bad sector map entry. These three bytes represent | ||
46 | * a single sector address measured from BOT. | ||
47 | */ | ||
48 | typedef struct NewSectorMap { | ||
49 | __u8 bytes[3]; | ||
50 | } SectorCount; | ||
51 | |||
52 | |||
53 | /* | ||
54 | * ftape-bsm.c defined global vars. | ||
55 | */ | ||
56 | |||
57 | /* | ||
58 | * ftape-bsm.c defined global functions. | ||
59 | */ | ||
60 | extern void update_bad_sector_map(__u8 * buffer); | ||
61 | extern void ftape_extract_bad_sector_map(__u8 * buffer); | ||
62 | extern SectorMap ftape_get_bad_sector_entry(int segment_id); | ||
63 | extern __u8 *ftape_find_end_of_bsm_list(__u8 * address); | ||
64 | extern void ftape_init_bsm(void); | ||
65 | |||
66 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.c b/drivers/char/ftape/lowlevel/ftape-buffer.c new file mode 100644 index 000000000000..54af20cd9a2c --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-buffer.c | |||
@@ -0,0 +1,129 @@ | |||
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-buffer.c,v $ | ||
20 | * $Revision: 1.3 $ | ||
21 | * $Date: 1997/10/16 23:33:11 $ | ||
22 | * | ||
23 | * This file contains the allocator/dealloctor for ftape's dynamic dma | ||
24 | * buffer. | ||
25 | */ | ||
26 | |||
27 | #include <linux/slab.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/mman.h> | ||
30 | #include <asm/dma.h> | ||
31 | |||
32 | #include <linux/ftape.h> | ||
33 | #include "../lowlevel/ftape-rw.h" | ||
34 | #include "../lowlevel/ftape-read.h" | ||
35 | #include "../lowlevel/ftape-tracing.h" | ||
36 | |||
37 | /* DMA'able memory allocation stuff. | ||
38 | */ | ||
39 | |||
40 | static inline void *dmaalloc(size_t size) | ||
41 | { | ||
42 | unsigned long addr; | ||
43 | |||
44 | if (size == 0) { | ||
45 | return NULL; | ||
46 | } | ||
47 | addr = __get_dma_pages(GFP_KERNEL, get_order(size)); | ||
48 | if (addr) { | ||
49 | struct page *page; | ||
50 | |||
51 | for (page = virt_to_page(addr); page < virt_to_page(addr+size); page++) | ||
52 | SetPageReserved(page); | ||
53 | } | ||
54 | return (void *)addr; | ||
55 | } | ||
56 | |||
57 | static inline void dmafree(void *addr, size_t size) | ||
58 | { | ||
59 | if (size > 0) { | ||
60 | struct page *page; | ||
61 | |||
62 | for (page = virt_to_page((unsigned long)addr); | ||
63 | page < virt_to_page((unsigned long)addr+size); page++) | ||
64 | ClearPageReserved(page); | ||
65 | free_pages((unsigned long) addr, get_order(size)); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | static int add_one_buffer(void) | ||
70 | { | ||
71 | TRACE_FUN(ft_t_flow); | ||
72 | |||
73 | if (ft_nr_buffers >= FT_MAX_NR_BUFFERS) { | ||
74 | TRACE_EXIT -ENOMEM; | ||
75 | } | ||
76 | ft_buffer[ft_nr_buffers] = kmalloc(sizeof(buffer_struct), GFP_KERNEL); | ||
77 | if (ft_buffer[ft_nr_buffers] == NULL) { | ||
78 | TRACE_EXIT -ENOMEM; | ||
79 | } | ||
80 | memset(ft_buffer[ft_nr_buffers], 0, sizeof(buffer_struct)); | ||
81 | ft_buffer[ft_nr_buffers]->address = dmaalloc(FT_BUFF_SIZE); | ||
82 | if (ft_buffer[ft_nr_buffers]->address == NULL) { | ||
83 | kfree(ft_buffer[ft_nr_buffers]); | ||
84 | ft_buffer[ft_nr_buffers] = NULL; | ||
85 | TRACE_EXIT -ENOMEM; | ||
86 | } | ||
87 | ft_nr_buffers ++; | ||
88 | TRACE(ft_t_info, "buffer nr #%d @ %p, dma area @ %p", | ||
89 | ft_nr_buffers, | ||
90 | ft_buffer[ft_nr_buffers-1], | ||
91 | ft_buffer[ft_nr_buffers-1]->address); | ||
92 | TRACE_EXIT 0; | ||
93 | } | ||
94 | |||
95 | static void del_one_buffer(void) | ||
96 | { | ||
97 | TRACE_FUN(ft_t_flow); | ||
98 | if (ft_nr_buffers > 0) { | ||
99 | TRACE(ft_t_info, "releasing buffer nr #%d @ %p, dma area @ %p", | ||
100 | ft_nr_buffers, | ||
101 | ft_buffer[ft_nr_buffers-1], | ||
102 | ft_buffer[ft_nr_buffers-1]->address); | ||
103 | ft_nr_buffers --; | ||
104 | dmafree(ft_buffer[ft_nr_buffers]->address, FT_BUFF_SIZE); | ||
105 | kfree(ft_buffer[ft_nr_buffers]); | ||
106 | ft_buffer[ft_nr_buffers] = NULL; | ||
107 | } | ||
108 | TRACE_EXIT; | ||
109 | } | ||
110 | |||
111 | int ftape_set_nr_buffers(int cnt) | ||
112 | { | ||
113 | int delta = cnt - ft_nr_buffers; | ||
114 | TRACE_FUN(ft_t_flow); | ||
115 | |||
116 | if (delta > 0) { | ||
117 | while (delta--) { | ||
118 | if (add_one_buffer() < 0) { | ||
119 | TRACE_EXIT -ENOMEM; | ||
120 | } | ||
121 | } | ||
122 | } else if (delta < 0) { | ||
123 | while (delta++) { | ||
124 | del_one_buffer(); | ||
125 | } | ||
126 | } | ||
127 | ftape_zap_read_buffers(); | ||
128 | TRACE_EXIT 0; | ||
129 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-buffer.h b/drivers/char/ftape/lowlevel/ftape-buffer.h new file mode 100644 index 000000000000..eec99cee8f82 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-buffer.h | |||
@@ -0,0 +1,32 @@ | |||
1 | #ifndef _FTAPE_BUFFER_H | ||
2 | #define _FTAPE_BUFFER_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1997 Claus-Justus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-buffer.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:18:08 $ | ||
25 | * | ||
26 | * This file contains the allocator/dealloctor for ftape's dynamic dma | ||
27 | * buffer. | ||
28 | */ | ||
29 | |||
30 | extern int ftape_set_nr_buffers(int cnt); | ||
31 | |||
32 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.c b/drivers/char/ftape/lowlevel/ftape-calibr.c new file mode 100644 index 000000000000..956b2586e138 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-calibr.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven. | ||
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-calibr.c,v $ | ||
20 | * $Revision: 1.2 $ | ||
21 | * $Date: 1997/10/05 19:18:08 $ | ||
22 | * | ||
23 | * GP calibration routine for processor speed dependent | ||
24 | * functions. | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/jiffies.h> | ||
30 | #include <asm/system.h> | ||
31 | #include <asm/io.h> | ||
32 | #if defined(__alpha__) | ||
33 | # include <asm/hwrpb.h> | ||
34 | #elif defined(__x86_64__) | ||
35 | # include <asm/msr.h> | ||
36 | # include <asm/timex.h> | ||
37 | #elif defined(__i386__) | ||
38 | # include <linux/timex.h> | ||
39 | #endif | ||
40 | #include <linux/ftape.h> | ||
41 | #include "../lowlevel/ftape-tracing.h" | ||
42 | #include "../lowlevel/ftape-calibr.h" | ||
43 | #include "../lowlevel/fdc-io.h" | ||
44 | |||
45 | #undef DEBUG | ||
46 | |||
47 | #if !defined(__alpha__) && !defined(__i386__) && !defined(__x86_64__) | ||
48 | # error Ftape is not implemented for this architecture! | ||
49 | #endif | ||
50 | |||
51 | #if defined(__alpha__) || defined(__x86_64__) | ||
52 | static unsigned long ps_per_cycle = 0; | ||
53 | #endif | ||
54 | |||
55 | static spinlock_t calibr_lock; | ||
56 | |||
57 | /* | ||
58 | * Note: On Intel PCs, the clock ticks at 100 Hz (HZ==100) which is | ||
59 | * too slow for certain timeouts (and that clock doesn't even tick | ||
60 | * when interrupts are disabled). For that reason, the 8254 timer is | ||
61 | * used directly to implement fine-grained timeouts. However, on | ||
62 | * Alpha PCs, the 8254 is *not* used to implement the clock tick | ||
63 | * (which is 1024 Hz, normally) and the 8254 timer runs at some | ||
64 | * "random" frequency (it seems to run at 18Hz, but it's not safe to | ||
65 | * rely on this value). Instead, we use the Alpha's "rpcc" | ||
66 | * instruction to read cycle counts. As this is a 32 bit counter, | ||
67 | * it will overflow only once per 30 seconds (on a 200MHz machine), | ||
68 | * which is plenty. | ||
69 | */ | ||
70 | |||
71 | unsigned int ftape_timestamp(void) | ||
72 | { | ||
73 | #if defined(__alpha__) | ||
74 | unsigned long r; | ||
75 | |||
76 | asm volatile ("rpcc %0" : "=r" (r)); | ||
77 | return r; | ||
78 | #elif defined(__x86_64__) | ||
79 | unsigned long r; | ||
80 | rdtscl(r); | ||
81 | return r; | ||
82 | #elif defined(__i386__) | ||
83 | |||
84 | /* | ||
85 | * Note that there is some time between counter underflowing and jiffies | ||
86 | * increasing, so the code below won't always give correct output. | ||
87 | * -Vojtech | ||
88 | */ | ||
89 | |||
90 | unsigned long flags; | ||
91 | __u16 lo; | ||
92 | __u16 hi; | ||
93 | |||
94 | spin_lock_irqsave(&calibr_lock, flags); | ||
95 | outb_p(0x00, 0x43); /* latch the count ASAP */ | ||
96 | lo = inb_p(0x40); /* read the latched count */ | ||
97 | lo |= inb(0x40) << 8; | ||
98 | hi = jiffies; | ||
99 | spin_unlock_irqrestore(&calibr_lock, flags); | ||
100 | return ((hi + 1) * (unsigned int) LATCH) - lo; /* downcounter ! */ | ||
101 | #endif | ||
102 | } | ||
103 | |||
104 | static unsigned int short_ftape_timestamp(void) | ||
105 | { | ||
106 | #if defined(__alpha__) || defined(__x86_64__) | ||
107 | return ftape_timestamp(); | ||
108 | #elif defined(__i386__) | ||
109 | unsigned int count; | ||
110 | unsigned long flags; | ||
111 | |||
112 | spin_lock_irqsave(&calibr_lock, flags); | ||
113 | outb_p(0x00, 0x43); /* latch the count ASAP */ | ||
114 | count = inb_p(0x40); /* read the latched count */ | ||
115 | count |= inb(0x40) << 8; | ||
116 | spin_unlock_irqrestore(&calibr_lock, flags); | ||
117 | return (LATCH - count); /* normal: downcounter */ | ||
118 | #endif | ||
119 | } | ||
120 | |||
121 | static unsigned int diff(unsigned int t0, unsigned int t1) | ||
122 | { | ||
123 | #if defined(__alpha__) || defined(__x86_64__) | ||
124 | return (t1 - t0); | ||
125 | #elif defined(__i386__) | ||
126 | /* | ||
127 | * This is tricky: to work for both short and full ftape_timestamps | ||
128 | * we'll have to discriminate between these. | ||
129 | * If it _looks_ like short stamps with wrapping around we'll | ||
130 | * asume it are. This will generate a small error if it really | ||
131 | * was a (very large) delta from full ftape_timestamps. | ||
132 | */ | ||
133 | return (t1 <= t0 && t0 <= LATCH) ? t1 + LATCH - t0 : t1 - t0; | ||
134 | #endif | ||
135 | } | ||
136 | |||
137 | static unsigned int usecs(unsigned int count) | ||
138 | { | ||
139 | #if defined(__alpha__) || defined(__x86_64__) | ||
140 | return (ps_per_cycle * count) / 1000000UL; | ||
141 | #elif defined(__i386__) | ||
142 | return (10000 * count) / ((CLOCK_TICK_RATE + 50) / 100); | ||
143 | #endif | ||
144 | } | ||
145 | |||
146 | unsigned int ftape_timediff(unsigned int t0, unsigned int t1) | ||
147 | { | ||
148 | /* | ||
149 | * Calculate difference in usec for ftape_timestamp results t0 & t1. | ||
150 | * Note that on the i386 platform with short time-stamps, the | ||
151 | * maximum allowed timespan is 1/HZ or we'll lose ticks! | ||
152 | */ | ||
153 | return usecs(diff(t0, t1)); | ||
154 | } | ||
155 | |||
156 | /* To get an indication of the I/O performance, | ||
157 | * measure the duration of the inb() function. | ||
158 | */ | ||
159 | static void time_inb(void) | ||
160 | { | ||
161 | int i; | ||
162 | int t0, t1; | ||
163 | unsigned long flags; | ||
164 | int status; | ||
165 | TRACE_FUN(ft_t_any); | ||
166 | |||
167 | spin_lock_irqsave(&calibr_lock, flags); | ||
168 | t0 = short_ftape_timestamp(); | ||
169 | for (i = 0; i < 1000; ++i) { | ||
170 | status = inb(fdc.msr); | ||
171 | } | ||
172 | t1 = short_ftape_timestamp(); | ||
173 | spin_unlock_irqrestore(&calibr_lock, flags); | ||
174 | TRACE(ft_t_info, "inb() duration: %d nsec", ftape_timediff(t0, t1)); | ||
175 | TRACE_EXIT; | ||
176 | } | ||
177 | |||
178 | static void init_clock(void) | ||
179 | { | ||
180 | TRACE_FUN(ft_t_any); | ||
181 | |||
182 | #if defined(__x86_64__) | ||
183 | ps_per_cycle = 1000000000UL / cpu_khz; | ||
184 | #elif defined(__alpha__) | ||
185 | extern struct hwrpb_struct *hwrpb; | ||
186 | ps_per_cycle = (1000*1000*1000*1000UL) / hwrpb->cycle_freq; | ||
187 | #endif | ||
188 | TRACE_EXIT; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Input: function taking int count as parameter. | ||
193 | * pointers to calculated calibration variables. | ||
194 | */ | ||
195 | void ftape_calibrate(char *name, | ||
196 | void (*fun) (unsigned int), | ||
197 | unsigned int *calibr_count, | ||
198 | unsigned int *calibr_time) | ||
199 | { | ||
200 | static int first_time = 1; | ||
201 | int i; | ||
202 | unsigned int tc = 0; | ||
203 | unsigned int count; | ||
204 | unsigned int time; | ||
205 | #if defined(__i386__) | ||
206 | unsigned int old_tc = 0; | ||
207 | unsigned int old_count = 1; | ||
208 | unsigned int old_time = 1; | ||
209 | #endif | ||
210 | TRACE_FUN(ft_t_flow); | ||
211 | |||
212 | if (first_time) { /* get idea of I/O performance */ | ||
213 | init_clock(); | ||
214 | time_inb(); | ||
215 | first_time = 0; | ||
216 | } | ||
217 | /* value of timeout must be set so that on very slow systems | ||
218 | * it will give a time less than one jiffy, and on | ||
219 | * very fast systems it'll give reasonable precision. | ||
220 | */ | ||
221 | |||
222 | count = 40; | ||
223 | for (i = 0; i < 15; ++i) { | ||
224 | unsigned int t0; | ||
225 | unsigned int t1; | ||
226 | unsigned int once; | ||
227 | unsigned int multiple; | ||
228 | unsigned long flags; | ||
229 | |||
230 | *calibr_count = | ||
231 | *calibr_time = count; /* set TC to 1 */ | ||
232 | spin_lock_irqsave(&calibr_lock, flags); | ||
233 | fun(0); /* dummy, get code into cache */ | ||
234 | t0 = short_ftape_timestamp(); | ||
235 | fun(0); /* overhead + one test */ | ||
236 | t1 = short_ftape_timestamp(); | ||
237 | once = diff(t0, t1); | ||
238 | t0 = short_ftape_timestamp(); | ||
239 | fun(count); /* overhead + count tests */ | ||
240 | t1 = short_ftape_timestamp(); | ||
241 | multiple = diff(t0, t1); | ||
242 | spin_unlock_irqrestore(&calibr_lock, flags); | ||
243 | time = ftape_timediff(0, multiple - once); | ||
244 | tc = (1000 * time) / (count - 1); | ||
245 | TRACE(ft_t_any, "once:%3d us,%6d times:%6d us, TC:%5d ns", | ||
246 | usecs(once), count - 1, usecs(multiple), tc); | ||
247 | #if defined(__alpha__) || defined(__x86_64__) | ||
248 | /* | ||
249 | * Increase the calibration count exponentially until the | ||
250 | * calibration time exceeds 100 ms. | ||
251 | */ | ||
252 | if (time >= 100*1000) { | ||
253 | break; | ||
254 | } | ||
255 | #elif defined(__i386__) | ||
256 | /* | ||
257 | * increase the count until the resulting time nears 2/HZ, | ||
258 | * then the tc will drop sharply because we lose LATCH counts. | ||
259 | */ | ||
260 | if (tc <= old_tc / 2) { | ||
261 | time = old_time; | ||
262 | count = old_count; | ||
263 | break; | ||
264 | } | ||
265 | old_tc = tc; | ||
266 | old_count = count; | ||
267 | old_time = time; | ||
268 | #endif | ||
269 | count *= 2; | ||
270 | } | ||
271 | *calibr_count = count - 1; | ||
272 | *calibr_time = time; | ||
273 | TRACE(ft_t_info, "TC for `%s()' = %d nsec (at %d counts)", | ||
274 | name, (1000 * *calibr_time) / *calibr_count, *calibr_count); | ||
275 | TRACE_EXIT; | ||
276 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-calibr.h b/drivers/char/ftape/lowlevel/ftape-calibr.h new file mode 100644 index 000000000000..0c7e75246c7d --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-calibr.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef _FTAPE_CALIBR_H | ||
2 | #define _FTAPE_CALIBR_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-calibr.h,v $ | ||
23 | * $Revision: 1.1 $ | ||
24 | * $Date: 1997/09/19 09:05:26 $ | ||
25 | * | ||
26 | * This file contains a gp calibration routine for | ||
27 | * hardware dependent timeout functions. | ||
28 | */ | ||
29 | |||
30 | extern void ftape_calibrate(char *name, | ||
31 | void (*fun) (unsigned int), | ||
32 | unsigned int *calibr_count, | ||
33 | unsigned int *calibr_time); | ||
34 | extern unsigned int ftape_timestamp(void); | ||
35 | extern unsigned int ftape_timediff(unsigned int t0, unsigned int t1); | ||
36 | |||
37 | #endif /* _FTAPE_CALIBR_H */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.c b/drivers/char/ftape/lowlevel/ftape-ctl.c new file mode 100644 index 000000000000..32e043911790 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ctl.c | |||
@@ -0,0 +1,897 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * 1996-1997 Claus-Justus 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-ctl.c,v $ | ||
21 | * $Revision: 1.4 $ | ||
22 | * $Date: 1997/11/11 14:37:44 $ | ||
23 | * | ||
24 | * This file contains the non-read/write ftape functions for the | ||
25 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
26 | */ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <linux/mm.h> | ||
31 | #include <linux/mman.h> | ||
32 | |||
33 | #include <linux/ftape.h> | ||
34 | #include <linux/qic117.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/io.h> | ||
37 | |||
38 | /* ease porting between pre-2.4.x and later kernels */ | ||
39 | #define vma_get_pgoff(v) ((v)->vm_pgoff) | ||
40 | |||
41 | #include "../lowlevel/ftape-tracing.h" | ||
42 | #include "../lowlevel/ftape-io.h" | ||
43 | #include "../lowlevel/ftape-ctl.h" | ||
44 | #include "../lowlevel/ftape-write.h" | ||
45 | #include "../lowlevel/ftape-read.h" | ||
46 | #include "../lowlevel/ftape-rw.h" | ||
47 | #include "../lowlevel/ftape-bsm.h" | ||
48 | |||
49 | /* Global vars. | ||
50 | */ | ||
51 | ftape_info ftape_status = { | ||
52 | /* vendor information */ | ||
53 | { 0, }, /* drive type */ | ||
54 | /* data rates */ | ||
55 | 500, /* used data rate */ | ||
56 | 500, /* drive max rate */ | ||
57 | 500, /* fdc max rate */ | ||
58 | /* drive selection, either FTAPE_SEL_A/B/C/D */ | ||
59 | -1, /* drive selection */ | ||
60 | /* flags set after decode the drive and tape status */ | ||
61 | 0, /* formatted */ | ||
62 | 1, /* no tape */ | ||
63 | 1, /* write protected */ | ||
64 | 1, /* new tape */ | ||
65 | /* values of last queried drive/tape status and error */ | ||
66 | {{0,}}, /* last error code */ | ||
67 | {{0,}}, /* drive status, configuration, tape status */ | ||
68 | /* cartridge geometry */ | ||
69 | 20, /* tracks_per_tape */ | ||
70 | 102, /* segments_per_track */ | ||
71 | /* location of header segments, etc. */ | ||
72 | -1, /* used_header_segment */ | ||
73 | -1, /* header_segment_1 */ | ||
74 | -1, /* header_segment_2 */ | ||
75 | -1, /* first_data_segment */ | ||
76 | -1, /* last_data_segment */ | ||
77 | /* the format code as stored in the header segment */ | ||
78 | fmt_normal, /* format code */ | ||
79 | /* the default for the qic std: unknown */ | ||
80 | -1, | ||
81 | /* is tape running? */ | ||
82 | idle, /* runner_state */ | ||
83 | /* is tape reading/writing/verifying/formatting/deleting */ | ||
84 | idle, /* driver state */ | ||
85 | /* flags fatal hardware error */ | ||
86 | 1, /* failure */ | ||
87 | /* history record */ | ||
88 | { 0, } /* history record */ | ||
89 | }; | ||
90 | |||
91 | int ftape_segments_per_head = 1020; | ||
92 | int ftape_segments_per_cylinder = 4; | ||
93 | int ftape_init_drive_needed = 1; /* need to be global for ftape_reset_drive() | ||
94 | * in ftape-io.c | ||
95 | */ | ||
96 | |||
97 | /* Local vars. | ||
98 | */ | ||
99 | static const vendor_struct vendors[] = QIC117_VENDORS; | ||
100 | static const wakeup_method methods[] = WAKEUP_METHODS; | ||
101 | |||
102 | const ftape_info *ftape_get_status(void) | ||
103 | { | ||
104 | #if defined(STATUS_PARANOYA) | ||
105 | static ftape_info get_status; | ||
106 | |||
107 | get_status = ftape_status; | ||
108 | return &get_status; | ||
109 | #else | ||
110 | return &ftape_status; /* maybe return only a copy of it to assure | ||
111 | * read only access | ||
112 | */ | ||
113 | #endif | ||
114 | } | ||
115 | |||
116 | static int ftape_not_operational(int status) | ||
117 | { | ||
118 | /* return true if status indicates tape can not be used. | ||
119 | */ | ||
120 | return ((status ^ QIC_STATUS_CARTRIDGE_PRESENT) & | ||
121 | (QIC_STATUS_ERROR | | ||
122 | QIC_STATUS_CARTRIDGE_PRESENT | | ||
123 | QIC_STATUS_NEW_CARTRIDGE)); | ||
124 | } | ||
125 | |||
126 | int ftape_seek_to_eot(void) | ||
127 | { | ||
128 | int status; | ||
129 | TRACE_FUN(ft_t_any); | ||
130 | |||
131 | TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),); | ||
132 | while ((status & QIC_STATUS_AT_EOT) == 0) { | ||
133 | if (ftape_not_operational(status)) { | ||
134 | TRACE_EXIT -EIO; | ||
135 | } | ||
136 | TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_FORWARD, | ||
137 | ftape_timeout.rewind,&status),); | ||
138 | } | ||
139 | TRACE_EXIT 0; | ||
140 | } | ||
141 | |||
142 | int ftape_seek_to_bot(void) | ||
143 | { | ||
144 | int status; | ||
145 | TRACE_FUN(ft_t_any); | ||
146 | |||
147 | TRACE_CATCH(ftape_ready_wait(ftape_timeout.pause, &status),); | ||
148 | while ((status & QIC_STATUS_AT_BOT) == 0) { | ||
149 | if (ftape_not_operational(status)) { | ||
150 | TRACE_EXIT -EIO; | ||
151 | } | ||
152 | TRACE_CATCH(ftape_command_wait(QIC_PHYSICAL_REVERSE, | ||
153 | ftape_timeout.rewind,&status),); | ||
154 | } | ||
155 | TRACE_EXIT 0; | ||
156 | } | ||
157 | |||
158 | static int ftape_new_cartridge(void) | ||
159 | { | ||
160 | ft_location.track = -1; /* force seek on first access */ | ||
161 | ftape_zap_read_buffers(); | ||
162 | ftape_zap_write_buffers(); | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | int ftape_abort_operation(void) | ||
167 | { | ||
168 | int result = 0; | ||
169 | int status; | ||
170 | TRACE_FUN(ft_t_flow); | ||
171 | |||
172 | if (ft_runner_status == running) { | ||
173 | TRACE(ft_t_noise, "aborting runner, waiting"); | ||
174 | |||
175 | ft_runner_status = do_abort; | ||
176 | /* set timeout so that the tape will run to logical EOT | ||
177 | * if we missed the last sector and there are no queue pulses. | ||
178 | */ | ||
179 | result = ftape_dumb_stop(); | ||
180 | } | ||
181 | if (ft_runner_status != idle) { | ||
182 | if (ft_runner_status == do_abort) { | ||
183 | TRACE(ft_t_noise, "forcing runner abort"); | ||
184 | } | ||
185 | TRACE(ft_t_noise, "stopping tape"); | ||
186 | result = ftape_stop_tape(&status); | ||
187 | ft_location.known = 0; | ||
188 | ft_runner_status = idle; | ||
189 | } | ||
190 | ftape_reset_buffer(); | ||
191 | ftape_zap_read_buffers(); | ||
192 | ftape_set_state(idle); | ||
193 | TRACE_EXIT result; | ||
194 | } | ||
195 | |||
196 | static int lookup_vendor_id(unsigned int vendor_id) | ||
197 | { | ||
198 | int i = 0; | ||
199 | |||
200 | while (vendors[i].vendor_id != vendor_id) { | ||
201 | if (++i >= NR_ITEMS(vendors)) { | ||
202 | return -1; | ||
203 | } | ||
204 | } | ||
205 | return i; | ||
206 | } | ||
207 | |||
208 | static void ftape_detach_drive(void) | ||
209 | { | ||
210 | TRACE_FUN(ft_t_any); | ||
211 | |||
212 | TRACE(ft_t_flow, "disabling tape drive and fdc"); | ||
213 | ftape_put_drive_to_sleep(ft_drive_type.wake_up); | ||
214 | fdc_catch_stray_interrupts(1); /* one always comes */ | ||
215 | fdc_disable(); | ||
216 | fdc_release_irq_and_dma(); | ||
217 | fdc_release_regions(); | ||
218 | TRACE_EXIT; | ||
219 | } | ||
220 | |||
221 | static void clear_history(void) | ||
222 | { | ||
223 | ft_history.used = 0; | ||
224 | ft_history.id_am_errors = | ||
225 | ft_history.id_crc_errors = | ||
226 | ft_history.data_am_errors = | ||
227 | ft_history.data_crc_errors = | ||
228 | ft_history.overrun_errors = | ||
229 | ft_history.no_data_errors = | ||
230 | ft_history.retries = | ||
231 | ft_history.crc_errors = | ||
232 | ft_history.crc_failures = | ||
233 | ft_history.ecc_failures = | ||
234 | ft_history.corrected = | ||
235 | ft_history.defects = | ||
236 | ft_history.rewinds = 0; | ||
237 | } | ||
238 | |||
239 | static int ftape_activate_drive(vendor_struct * drive_type) | ||
240 | { | ||
241 | int result = 0; | ||
242 | TRACE_FUN(ft_t_flow); | ||
243 | |||
244 | /* If we already know the drive type, wake it up. | ||
245 | * Else try to find out what kind of drive is attached. | ||
246 | */ | ||
247 | if (drive_type->wake_up != unknown_wake_up) { | ||
248 | TRACE(ft_t_flow, "enabling tape drive and fdc"); | ||
249 | result = ftape_wakeup_drive(drive_type->wake_up); | ||
250 | if (result < 0) { | ||
251 | TRACE(ft_t_err, "known wakeup method failed"); | ||
252 | } | ||
253 | } else { | ||
254 | wake_up_types method; | ||
255 | const ft_trace_t old_tracing = TRACE_LEVEL; | ||
256 | if (TRACE_LEVEL < ft_t_flow) { | ||
257 | SET_TRACE_LEVEL(ft_t_bug); | ||
258 | } | ||
259 | |||
260 | /* Try to awaken the drive using all known methods. | ||
261 | * Lower tracing for a while. | ||
262 | */ | ||
263 | for (method=no_wake_up; method < NR_ITEMS(methods); ++method) { | ||
264 | drive_type->wake_up = method; | ||
265 | #ifdef CONFIG_FT_TWO_DRIVES | ||
266 | /* Test setup for dual drive configuration. | ||
267 | * /dev/rft2 uses mountain wakeup | ||
268 | * /dev/rft3 uses colorado wakeup | ||
269 | * Other systems will use the normal scheme. | ||
270 | */ | ||
271 | if ((ft_drive_sel < 2) || | ||
272 | (ft_drive_sel == 2 && method == FT_WAKE_UP_1) || | ||
273 | (ft_drive_sel == 3 && method == FT_WAKE_UP_2)) { | ||
274 | result=ftape_wakeup_drive(drive_type->wake_up); | ||
275 | } else { | ||
276 | result = -EIO; | ||
277 | } | ||
278 | #else | ||
279 | result = ftape_wakeup_drive(drive_type->wake_up); | ||
280 | #endif | ||
281 | if (result >= 0) { | ||
282 | TRACE(ft_t_warn, "drive wakeup method: %s", | ||
283 | methods[drive_type->wake_up].name); | ||
284 | break; | ||
285 | } | ||
286 | } | ||
287 | SET_TRACE_LEVEL(old_tracing); | ||
288 | |||
289 | if (method >= NR_ITEMS(methods)) { | ||
290 | /* no response at all, cannot open this drive */ | ||
291 | drive_type->wake_up = unknown_wake_up; | ||
292 | TRACE(ft_t_err, "no tape drive found !"); | ||
293 | result = -ENODEV; | ||
294 | } | ||
295 | } | ||
296 | TRACE_EXIT result; | ||
297 | } | ||
298 | |||
299 | static int ftape_get_drive_status(void) | ||
300 | { | ||
301 | int result; | ||
302 | int status; | ||
303 | TRACE_FUN(ft_t_flow); | ||
304 | |||
305 | ft_no_tape = ft_write_protected = 0; | ||
306 | /* Tape drive is activated now. | ||
307 | * First clear error status if present. | ||
308 | */ | ||
309 | do { | ||
310 | result = ftape_ready_wait(ftape_timeout.reset, &status); | ||
311 | if (result < 0) { | ||
312 | if (result == -ETIME) { | ||
313 | TRACE(ft_t_err, "ftape_ready_wait timeout"); | ||
314 | } else if (result == -EINTR) { | ||
315 | TRACE(ft_t_err, "ftape_ready_wait aborted"); | ||
316 | } else { | ||
317 | TRACE(ft_t_err, "ftape_ready_wait failed"); | ||
318 | } | ||
319 | TRACE_EXIT -EIO; | ||
320 | } | ||
321 | /* Clear error condition (drive is ready !) | ||
322 | */ | ||
323 | if (status & QIC_STATUS_ERROR) { | ||
324 | unsigned int error; | ||
325 | qic117_cmd_t command; | ||
326 | |||
327 | TRACE(ft_t_err, "error status set"); | ||
328 | result = ftape_report_error(&error, &command, 1); | ||
329 | if (result < 0) { | ||
330 | TRACE(ft_t_err, | ||
331 | "report_error_code failed: %d", result); | ||
332 | /* hope it's working next time */ | ||
333 | ftape_reset_drive(); | ||
334 | TRACE_EXIT -EIO; | ||
335 | } else if (error != 0) { | ||
336 | TRACE(ft_t_noise, "error code : %d", error); | ||
337 | TRACE(ft_t_noise, "error command: %d", command); | ||
338 | } | ||
339 | } | ||
340 | if (status & QIC_STATUS_NEW_CARTRIDGE) { | ||
341 | unsigned int error; | ||
342 | qic117_cmd_t command; | ||
343 | const ft_trace_t old_tracing = TRACE_LEVEL; | ||
344 | SET_TRACE_LEVEL(ft_t_bug); | ||
345 | |||
346 | /* Undocumented feature: Must clear (not present!) | ||
347 | * error here or we'll fail later. | ||
348 | */ | ||
349 | ftape_report_error(&error, &command, 1); | ||
350 | |||
351 | SET_TRACE_LEVEL(old_tracing); | ||
352 | TRACE(ft_t_info, "status: new cartridge"); | ||
353 | ft_new_tape = 1; | ||
354 | } else { | ||
355 | ft_new_tape = 0; | ||
356 | } | ||
357 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
358 | } while (status & QIC_STATUS_ERROR); | ||
359 | |||
360 | ft_no_tape = !(status & QIC_STATUS_CARTRIDGE_PRESENT); | ||
361 | ft_write_protected = (status & QIC_STATUS_WRITE_PROTECT) != 0; | ||
362 | if (ft_no_tape) { | ||
363 | TRACE(ft_t_warn, "no cartridge present"); | ||
364 | } else { | ||
365 | if (ft_write_protected) { | ||
366 | TRACE(ft_t_noise, "Write protected cartridge"); | ||
367 | } | ||
368 | } | ||
369 | TRACE_EXIT 0; | ||
370 | } | ||
371 | |||
372 | static void ftape_log_vendor_id(void) | ||
373 | { | ||
374 | int vendor_index; | ||
375 | TRACE_FUN(ft_t_flow); | ||
376 | |||
377 | ftape_report_vendor_id(&ft_drive_type.vendor_id); | ||
378 | vendor_index = lookup_vendor_id(ft_drive_type.vendor_id); | ||
379 | if (ft_drive_type.vendor_id == UNKNOWN_VENDOR && | ||
380 | ft_drive_type.wake_up == wake_up_colorado) { | ||
381 | vendor_index = 0; | ||
382 | /* hack to get rid of all this mail */ | ||
383 | ft_drive_type.vendor_id = 0; | ||
384 | } | ||
385 | if (vendor_index < 0) { | ||
386 | /* Unknown vendor id, first time opening device. The | ||
387 | * drive_type remains set to type found at wakeup | ||
388 | * time, this will probably keep the driver operating | ||
389 | * for this new vendor. | ||
390 | */ | ||
391 | TRACE(ft_t_warn, "\n" | ||
392 | KERN_INFO "============ unknown vendor id ===========\n" | ||
393 | KERN_INFO "A new, yet unsupported tape drive is found\n" | ||
394 | KERN_INFO "Please report the following values:\n" | ||
395 | KERN_INFO " Vendor id : 0x%04x\n" | ||
396 | KERN_INFO " Wakeup method : %s\n" | ||
397 | KERN_INFO "And a description of your tape drive\n" | ||
398 | KERN_INFO "to "THE_FTAPE_MAINTAINER"\n" | ||
399 | KERN_INFO "==========================================", | ||
400 | ft_drive_type.vendor_id, | ||
401 | methods[ft_drive_type.wake_up].name); | ||
402 | ft_drive_type.speed = 0; /* unknown */ | ||
403 | } else { | ||
404 | ft_drive_type.name = vendors[vendor_index].name; | ||
405 | ft_drive_type.speed = vendors[vendor_index].speed; | ||
406 | TRACE(ft_t_info, "tape drive type: %s", ft_drive_type.name); | ||
407 | /* scan all methods for this vendor_id in table */ | ||
408 | while(ft_drive_type.wake_up != vendors[vendor_index].wake_up) { | ||
409 | if (vendor_index < NR_ITEMS(vendors) - 1 && | ||
410 | vendors[vendor_index + 1].vendor_id | ||
411 | == | ||
412 | ft_drive_type.vendor_id) { | ||
413 | ++vendor_index; | ||
414 | } else { | ||
415 | break; | ||
416 | } | ||
417 | } | ||
418 | if (ft_drive_type.wake_up != vendors[vendor_index].wake_up) { | ||
419 | TRACE(ft_t_warn, "\n" | ||
420 | KERN_INFO "==========================================\n" | ||
421 | KERN_INFO "wakeup type mismatch:\n" | ||
422 | KERN_INFO "found: %s, expected: %s\n" | ||
423 | KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n" | ||
424 | KERN_INFO "==========================================", | ||
425 | methods[ft_drive_type.wake_up].name, | ||
426 | methods[vendors[vendor_index].wake_up].name); | ||
427 | } | ||
428 | } | ||
429 | TRACE_EXIT; | ||
430 | } | ||
431 | |||
432 | void ftape_calc_timeouts(unsigned int qic_std, | ||
433 | unsigned int data_rate, | ||
434 | unsigned int tape_len) | ||
435 | { | ||
436 | int speed; /* deci-ips ! */ | ||
437 | int ff_speed; | ||
438 | int length; | ||
439 | TRACE_FUN(ft_t_any); | ||
440 | |||
441 | /* tape transport speed | ||
442 | * data rate: QIC-40 QIC-80 QIC-3010 QIC-3020 | ||
443 | * | ||
444 | * 250 Kbps 25 ips n/a n/a n/a | ||
445 | * 500 Kbps 50 ips 34 ips 22.6 ips n/a | ||
446 | * 1 Mbps n/a 68 ips 45.2 ips 22.6 ips | ||
447 | * 2 Mbps n/a n/a n/a 45.2 ips | ||
448 | * | ||
449 | * fast tape transport speed is at least 68 ips. | ||
450 | */ | ||
451 | switch (qic_std) { | ||
452 | case QIC_TAPE_QIC40: | ||
453 | speed = (data_rate == 250) ? 250 : 500; | ||
454 | break; | ||
455 | case QIC_TAPE_QIC80: | ||
456 | speed = (data_rate == 500) ? 340 : 680; | ||
457 | break; | ||
458 | case QIC_TAPE_QIC3010: | ||
459 | speed = (data_rate == 500) ? 226 : 452; | ||
460 | break; | ||
461 | case QIC_TAPE_QIC3020: | ||
462 | speed = (data_rate == 1000) ? 226 : 452; | ||
463 | break; | ||
464 | default: | ||
465 | TRACE(ft_t_bug, "Unknown qic_std (bug) ?"); | ||
466 | speed = 500; | ||
467 | break; | ||
468 | } | ||
469 | if (ft_drive_type.speed == 0) { | ||
470 | unsigned long t0; | ||
471 | static int dt = 0; /* keep gcc from complaining */ | ||
472 | static int first_time = 1; | ||
473 | |||
474 | /* Measure the time it takes to wind to EOT and back to BOT. | ||
475 | * If the tape length is known, calculate the rewind speed. | ||
476 | * Else keep the time value for calculation of the rewind | ||
477 | * speed later on, when the length _is_ known. | ||
478 | * Ask for a report only when length and speed are both known. | ||
479 | */ | ||
480 | if (first_time) { | ||
481 | ftape_seek_to_bot(); | ||
482 | t0 = jiffies; | ||
483 | ftape_seek_to_eot(); | ||
484 | ftape_seek_to_bot(); | ||
485 | dt = (int) (((jiffies - t0) * FT_USPT) / 1000); | ||
486 | if (dt < 1) { | ||
487 | dt = 1; /* prevent div by zero on failures */ | ||
488 | } | ||
489 | first_time = 0; | ||
490 | TRACE(ft_t_info, | ||
491 | "trying to determine seek timeout, got %d msec", | ||
492 | dt); | ||
493 | } | ||
494 | if (tape_len != 0) { | ||
495 | ft_drive_type.speed = | ||
496 | (2 * 12 * tape_len * 1000) / dt; | ||
497 | TRACE(ft_t_warn, "\n" | ||
498 | KERN_INFO "==========================================\n" | ||
499 | KERN_INFO "drive type: %s\n" | ||
500 | KERN_INFO "delta time = %d ms, length = %d ft\n" | ||
501 | KERN_INFO "has a maximum tape speed of %d ips\n" | ||
502 | KERN_INFO "please report this to "THE_FTAPE_MAINTAINER"\n" | ||
503 | KERN_INFO "==========================================", | ||
504 | ft_drive_type.name, dt, tape_len, | ||
505 | ft_drive_type.speed); | ||
506 | } | ||
507 | } | ||
508 | /* Handle unknown length tapes as very long ones. We'll | ||
509 | * determine the actual length from a header segment later. | ||
510 | * This is normal for all modern (Wide,TR1/2/3) formats. | ||
511 | */ | ||
512 | if (tape_len <= 0) { | ||
513 | TRACE(ft_t_noise, | ||
514 | "Unknown tape length, using maximal timeouts"); | ||
515 | length = QIC_TOP_TAPE_LEN; /* use worst case values */ | ||
516 | } else { | ||
517 | length = tape_len; /* use actual values */ | ||
518 | } | ||
519 | if (ft_drive_type.speed == 0) { | ||
520 | ff_speed = speed; | ||
521 | } else { | ||
522 | ff_speed = ft_drive_type.speed; | ||
523 | } | ||
524 | /* time to go from bot to eot at normal speed (data rate): | ||
525 | * time = (1+delta) * length (ft) * 12 (inch/ft) / speed (ips) | ||
526 | * delta = 10 % for seek speed, 20 % for rewind speed. | ||
527 | */ | ||
528 | ftape_timeout.seek = (length * 132 * FT_SECOND) / speed; | ||
529 | ftape_timeout.rewind = (length * 144 * FT_SECOND) / (10 * ff_speed); | ||
530 | ftape_timeout.reset = 20 * FT_SECOND + ftape_timeout.rewind; | ||
531 | TRACE(ft_t_noise, "timeouts for speed = %d, length = %d\n" | ||
532 | KERN_INFO "seek timeout : %d sec\n" | ||
533 | KERN_INFO "rewind timeout: %d sec\n" | ||
534 | KERN_INFO "reset timeout : %d sec", | ||
535 | speed, length, | ||
536 | (ftape_timeout.seek + 500) / 1000, | ||
537 | (ftape_timeout.rewind + 500) / 1000, | ||
538 | (ftape_timeout.reset + 500) / 1000); | ||
539 | TRACE_EXIT; | ||
540 | } | ||
541 | |||
542 | /* This function calibrates the datarate (i.e. determines the maximal | ||
543 | * usable data rate) and sets the global variable ft_qic_std to qic_std | ||
544 | * | ||
545 | */ | ||
546 | int ftape_calibrate_data_rate(unsigned int qic_std) | ||
547 | { | ||
548 | int rate = ft_fdc_rate_limit; | ||
549 | int result; | ||
550 | TRACE_FUN(ft_t_flow); | ||
551 | |||
552 | ft_qic_std = qic_std; | ||
553 | |||
554 | if (ft_qic_std == -1) { | ||
555 | TRACE_ABORT(-EIO, ft_t_err, | ||
556 | "Unable to determine data rate if QIC standard is unknown"); | ||
557 | } | ||
558 | |||
559 | /* Select highest rate supported by both fdc and drive. | ||
560 | * Start with highest rate supported by the fdc. | ||
561 | */ | ||
562 | while (fdc_set_data_rate(rate) < 0 && rate > 250) { | ||
563 | rate /= 2; | ||
564 | } | ||
565 | TRACE(ft_t_info, | ||
566 | "Highest FDC supported data rate: %d Kbps", rate); | ||
567 | ft_fdc_max_rate = rate; | ||
568 | do { | ||
569 | result = ftape_set_data_rate(rate, ft_qic_std); | ||
570 | } while (result == -EINVAL && (rate /= 2) > 250); | ||
571 | if (result < 0) { | ||
572 | TRACE_ABORT(-EIO, ft_t_err, "set datarate failed"); | ||
573 | } | ||
574 | ft_data_rate = rate; | ||
575 | TRACE_EXIT 0; | ||
576 | } | ||
577 | |||
578 | static int ftape_init_drive(void) | ||
579 | { | ||
580 | int status; | ||
581 | qic_model model; | ||
582 | unsigned int qic_std; | ||
583 | unsigned int data_rate; | ||
584 | TRACE_FUN(ft_t_flow); | ||
585 | |||
586 | ftape_init_drive_needed = 0; /* don't retry if this fails ? */ | ||
587 | TRACE_CATCH(ftape_report_raw_drive_status(&status),); | ||
588 | if (status & QIC_STATUS_CARTRIDGE_PRESENT) { | ||
589 | if (!(status & QIC_STATUS_AT_BOT)) { | ||
590 | /* Antique drives will get here after a soft reset, | ||
591 | * modern ones only if the driver is loaded when the | ||
592 | * tape wasn't rewound properly. | ||
593 | */ | ||
594 | /* Tape should be at bot if new cartridge ! */ | ||
595 | ftape_seek_to_bot(); | ||
596 | } | ||
597 | if (!(status & QIC_STATUS_REFERENCED)) { | ||
598 | TRACE(ft_t_flow, "starting seek_load_point"); | ||
599 | TRACE_CATCH(ftape_command_wait(QIC_SEEK_LOAD_POINT, | ||
600 | ftape_timeout.reset, | ||
601 | &status),); | ||
602 | } | ||
603 | } | ||
604 | ft_formatted = (status & QIC_STATUS_REFERENCED) != 0; | ||
605 | if (!ft_formatted) { | ||
606 | TRACE(ft_t_warn, "Warning: tape is not formatted !"); | ||
607 | } | ||
608 | |||
609 | /* report configuration aborts when ftape_tape_len == -1 | ||
610 | * unknown qic_std is okay if not formatted. | ||
611 | */ | ||
612 | TRACE_CATCH(ftape_report_configuration(&model, | ||
613 | &data_rate, | ||
614 | &qic_std, | ||
615 | &ftape_tape_len),); | ||
616 | |||
617 | /* Maybe add the following to the /proc entry | ||
618 | */ | ||
619 | TRACE(ft_t_info, "%s drive @ %d Kbps", | ||
620 | (model == prehistoric) ? "prehistoric" : | ||
621 | ((model == pre_qic117c) ? "pre QIC-117C" : | ||
622 | ((model == post_qic117b) ? "post QIC-117B" : | ||
623 | "post QIC-117D")), data_rate); | ||
624 | |||
625 | if (ft_formatted) { | ||
626 | /* initialize ft_used_data_rate to maximum value | ||
627 | * and set ft_qic_std | ||
628 | */ | ||
629 | TRACE_CATCH(ftape_calibrate_data_rate(qic_std),); | ||
630 | if (ftape_tape_len == 0) { | ||
631 | TRACE(ft_t_info, "unknown length QIC-%s tape", | ||
632 | (ft_qic_std == QIC_TAPE_QIC40) ? "40" : | ||
633 | ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : | ||
634 | ((ft_qic_std == QIC_TAPE_QIC3010) | ||
635 | ? "3010" : "3020"))); | ||
636 | } else { | ||
637 | TRACE(ft_t_info, "%d ft. QIC-%s tape", ftape_tape_len, | ||
638 | (ft_qic_std == QIC_TAPE_QIC40) ? "40" : | ||
639 | ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : | ||
640 | ((ft_qic_std == QIC_TAPE_QIC3010) | ||
641 | ? "3010" : "3020"))); | ||
642 | } | ||
643 | ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); | ||
644 | /* soft write-protect QIC-40/QIC-80 cartridges used with a | ||
645 | * Colorado T3000 drive. Buggy hardware! | ||
646 | */ | ||
647 | if ((ft_drive_type.vendor_id == 0x011c6) && | ||
648 | ((ft_qic_std == QIC_TAPE_QIC40 || | ||
649 | ft_qic_std == QIC_TAPE_QIC80) && | ||
650 | !ft_write_protected)) { | ||
651 | TRACE(ft_t_warn, "\n" | ||
652 | KERN_INFO "The famous Colorado T3000 bug:\n" | ||
653 | KERN_INFO "%s drives can't write QIC40 and QIC80\n" | ||
654 | KERN_INFO "cartridges but don't set the write-protect flag!", | ||
655 | ft_drive_type.name); | ||
656 | ft_write_protected = 1; | ||
657 | } | ||
658 | } else { | ||
659 | /* Doesn't make too much sense to set the data rate | ||
660 | * because we don't know what to use for the write | ||
661 | * precompensation. | ||
662 | * Need to do this again when formatting the cartridge. | ||
663 | */ | ||
664 | ft_data_rate = data_rate; | ||
665 | ftape_calc_timeouts(QIC_TAPE_QIC40, | ||
666 | data_rate, | ||
667 | ftape_tape_len); | ||
668 | } | ||
669 | ftape_new_cartridge(); | ||
670 | TRACE_EXIT 0; | ||
671 | } | ||
672 | |||
673 | static void ftape_munmap(void) | ||
674 | { | ||
675 | int i; | ||
676 | TRACE_FUN(ft_t_flow); | ||
677 | |||
678 | for (i = 0; i < ft_nr_buffers; i++) { | ||
679 | ft_buffer[i]->mmapped = 0; | ||
680 | } | ||
681 | TRACE_EXIT; | ||
682 | } | ||
683 | |||
684 | /* Map the dma buffers into the virtual address range given by vma. | ||
685 | * We only check the caller doesn't map non-existent buffers. We | ||
686 | * don't check for multiple mappings. | ||
687 | */ | ||
688 | int ftape_mmap(struct vm_area_struct *vma) | ||
689 | { | ||
690 | int num_buffers; | ||
691 | int i; | ||
692 | TRACE_FUN(ft_t_flow); | ||
693 | |||
694 | if (ft_failure) { | ||
695 | TRACE_EXIT -ENODEV; | ||
696 | } | ||
697 | if (!(vma->vm_flags & (VM_READ|VM_WRITE))) { | ||
698 | TRACE_ABORT(-EINVAL, ft_t_err, "Undefined mmap() access"); | ||
699 | } | ||
700 | if (vma_get_pgoff(vma) != 0) { | ||
701 | TRACE_ABORT(-EINVAL, ft_t_err, "page offset must be 0"); | ||
702 | } | ||
703 | if ((vma->vm_end - vma->vm_start) % FT_BUFF_SIZE != 0) { | ||
704 | TRACE_ABORT(-EINVAL, ft_t_err, | ||
705 | "size = %ld, should be a multiple of %d", | ||
706 | vma->vm_end - vma->vm_start, | ||
707 | FT_BUFF_SIZE); | ||
708 | } | ||
709 | num_buffers = (vma->vm_end - vma->vm_start) / FT_BUFF_SIZE; | ||
710 | if (num_buffers > ft_nr_buffers) { | ||
711 | TRACE_ABORT(-EINVAL, | ||
712 | ft_t_err, "size = %ld, should be less than %d", | ||
713 | vma->vm_end - vma->vm_start, | ||
714 | ft_nr_buffers * FT_BUFF_SIZE); | ||
715 | } | ||
716 | if (ft_driver_state != idle) { | ||
717 | /* this also clears the buffer states | ||
718 | */ | ||
719 | ftape_abort_operation(); | ||
720 | } else { | ||
721 | ftape_reset_buffer(); | ||
722 | } | ||
723 | for (i = 0; i < num_buffers; i++) { | ||
724 | unsigned long pfn; | ||
725 | |||
726 | pfn = virt_to_phys(ft_buffer[i]->address) >> PAGE_SHIFT; | ||
727 | TRACE_CATCH(remap_pfn_range(vma, vma->vm_start + | ||
728 | i * FT_BUFF_SIZE, | ||
729 | pfn, | ||
730 | FT_BUFF_SIZE, | ||
731 | vma->vm_page_prot), | ||
732 | _res = -EAGAIN); | ||
733 | TRACE(ft_t_noise, "remapped dma buffer @ %p to location @ %p", | ||
734 | ft_buffer[i]->address, | ||
735 | (void *)(vma->vm_start + i * FT_BUFF_SIZE)); | ||
736 | } | ||
737 | for (i = 0; i < num_buffers; i++) { | ||
738 | memset(ft_buffer[i]->address, 0xAA, FT_BUFF_SIZE); | ||
739 | ft_buffer[i]->mmapped++; | ||
740 | } | ||
741 | TRACE_EXIT 0; | ||
742 | } | ||
743 | |||
744 | static void ftape_init_driver(void); /* forward declaration */ | ||
745 | |||
746 | /* OPEN routine called by kernel-interface code | ||
747 | */ | ||
748 | int ftape_enable(int drive_selection) | ||
749 | { | ||
750 | TRACE_FUN(ft_t_any); | ||
751 | |||
752 | if (ft_drive_sel == -1 || ft_drive_sel != drive_selection) { | ||
753 | /* Other selection than last time | ||
754 | */ | ||
755 | ftape_init_driver(); | ||
756 | } | ||
757 | ft_drive_sel = FTAPE_SEL(drive_selection); | ||
758 | ft_failure = 0; | ||
759 | TRACE_CATCH(fdc_init(),); /* init & detect fdc */ | ||
760 | TRACE_CATCH(ftape_activate_drive(&ft_drive_type), | ||
761 | fdc_disable(); | ||
762 | fdc_release_irq_and_dma(); | ||
763 | fdc_release_regions()); | ||
764 | TRACE_CATCH(ftape_get_drive_status(), ftape_detach_drive()); | ||
765 | if (ft_drive_type.vendor_id == UNKNOWN_VENDOR) { | ||
766 | ftape_log_vendor_id(); | ||
767 | } | ||
768 | if (ft_new_tape) { | ||
769 | ftape_init_drive_needed = 1; | ||
770 | } | ||
771 | if (!ft_no_tape && ftape_init_drive_needed) { | ||
772 | TRACE_CATCH(ftape_init_drive(), ftape_detach_drive()); | ||
773 | } | ||
774 | ftape_munmap(); /* clear the mmap flag */ | ||
775 | clear_history(); | ||
776 | TRACE_EXIT 0; | ||
777 | } | ||
778 | |||
779 | /* release routine called by the high level interface modules | ||
780 | * zftape or sftape. | ||
781 | */ | ||
782 | void ftape_disable(void) | ||
783 | { | ||
784 | int i; | ||
785 | TRACE_FUN(ft_t_any); | ||
786 | |||
787 | for (i = 0; i < ft_nr_buffers; i++) { | ||
788 | if (ft_buffer[i]->mmapped) { | ||
789 | TRACE(ft_t_noise, "first byte of buffer %d: 0x%02x", | ||
790 | i, *ft_buffer[i]->address); | ||
791 | } | ||
792 | } | ||
793 | if (sigtestsetmask(¤t->pending.signal, _DONT_BLOCK) && | ||
794 | !(sigtestsetmask(¤t->pending.signal, _NEVER_BLOCK)) && | ||
795 | ftape_tape_running) { | ||
796 | TRACE(ft_t_warn, | ||
797 | "Interrupted by fatal signal and tape still running"); | ||
798 | ftape_dumb_stop(); | ||
799 | ftape_abort_operation(); /* it's annoying */ | ||
800 | } else { | ||
801 | ftape_set_state(idle); | ||
802 | } | ||
803 | ftape_detach_drive(); | ||
804 | if (ft_history.used) { | ||
805 | TRACE(ft_t_info, "== Non-fatal errors this run: =="); | ||
806 | TRACE(ft_t_info, "fdc isr statistics:\n" | ||
807 | KERN_INFO " id_am_errors : %3d\n" | ||
808 | KERN_INFO " id_crc_errors : %3d\n" | ||
809 | KERN_INFO " data_am_errors : %3d\n" | ||
810 | KERN_INFO " data_crc_errors : %3d\n" | ||
811 | KERN_INFO " overrun_errors : %3d\n" | ||
812 | KERN_INFO " no_data_errors : %3d\n" | ||
813 | KERN_INFO " retries : %3d", | ||
814 | ft_history.id_am_errors, ft_history.id_crc_errors, | ||
815 | ft_history.data_am_errors, ft_history.data_crc_errors, | ||
816 | ft_history.overrun_errors, ft_history.no_data_errors, | ||
817 | ft_history.retries); | ||
818 | if (ft_history.used & 1) { | ||
819 | TRACE(ft_t_info, "ecc statistics:\n" | ||
820 | KERN_INFO " crc_errors : %3d\n" | ||
821 | KERN_INFO " crc_failures : %3d\n" | ||
822 | KERN_INFO " ecc_failures : %3d\n" | ||
823 | KERN_INFO " sectors corrected: %3d", | ||
824 | ft_history.crc_errors, ft_history.crc_failures, | ||
825 | ft_history.ecc_failures, ft_history.corrected); | ||
826 | } | ||
827 | if (ft_history.defects > 0) { | ||
828 | TRACE(ft_t_warn, "Warning: %d media defects!", | ||
829 | ft_history.defects); | ||
830 | } | ||
831 | if (ft_history.rewinds > 0) { | ||
832 | TRACE(ft_t_info, "tape motion statistics:\n" | ||
833 | KERN_INFO "repositions : %3d", | ||
834 | ft_history.rewinds); | ||
835 | } | ||
836 | } | ||
837 | ft_failure = 1; | ||
838 | TRACE_EXIT; | ||
839 | } | ||
840 | |||
841 | static void ftape_init_driver(void) | ||
842 | { | ||
843 | TRACE_FUN(ft_t_flow); | ||
844 | |||
845 | ft_drive_type.vendor_id = UNKNOWN_VENDOR; | ||
846 | ft_drive_type.speed = 0; | ||
847 | ft_drive_type.wake_up = unknown_wake_up; | ||
848 | ft_drive_type.name = "Unknown"; | ||
849 | |||
850 | ftape_timeout.seek = 650 * FT_SECOND; | ||
851 | ftape_timeout.reset = 670 * FT_SECOND; | ||
852 | ftape_timeout.rewind = 650 * FT_SECOND; | ||
853 | ftape_timeout.head_seek = 15 * FT_SECOND; | ||
854 | ftape_timeout.stop = 5 * FT_SECOND; | ||
855 | ftape_timeout.pause = 16 * FT_SECOND; | ||
856 | |||
857 | ft_qic_std = -1; | ||
858 | ftape_tape_len = 0; /* unknown */ | ||
859 | ftape_current_command = 0; | ||
860 | ftape_current_cylinder = -1; | ||
861 | |||
862 | ft_segments_per_track = 102; | ||
863 | ftape_segments_per_head = 1020; | ||
864 | ftape_segments_per_cylinder = 4; | ||
865 | ft_tracks_per_tape = 20; | ||
866 | |||
867 | ft_failure = 1; | ||
868 | |||
869 | ft_formatted = 0; | ||
870 | ft_no_tape = 1; | ||
871 | ft_write_protected = 1; | ||
872 | ft_new_tape = 1; | ||
873 | |||
874 | ft_driver_state = idle; | ||
875 | |||
876 | ft_data_rate = | ||
877 | ft_fdc_max_rate = 500; | ||
878 | ft_drive_max_rate = 0; /* triggers set_rate_test() */ | ||
879 | |||
880 | ftape_init_drive_needed = 1; | ||
881 | |||
882 | ft_header_segment_1 = -1; | ||
883 | ft_header_segment_2 = -1; | ||
884 | ft_used_header_segment = -1; | ||
885 | ft_first_data_segment = -1; | ||
886 | ft_last_data_segment = -1; | ||
887 | |||
888 | ft_location.track = -1; | ||
889 | ft_location.known = 0; | ||
890 | |||
891 | ftape_tape_running = 0; | ||
892 | ftape_might_be_off_track = 1; | ||
893 | |||
894 | ftape_new_cartridge(); /* init some tape related variables */ | ||
895 | ftape_init_bsm(); | ||
896 | TRACE_EXIT; | ||
897 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-ctl.h b/drivers/char/ftape/lowlevel/ftape-ctl.h new file mode 100644 index 000000000000..5f5e30bc3615 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ctl.h | |||
@@ -0,0 +1,162 @@ | |||
1 | #ifndef _FTAPE_CTL_H | ||
2 | #define _FTAPE_CTL_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ctl.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:09 $ | ||
26 | * | ||
27 | * This file contains the non-standard IOCTL related definitions | ||
28 | * for the QIC-40/80/3010/3020 floppy-tape driver "ftape" for | ||
29 | * Linux. | ||
30 | */ | ||
31 | |||
32 | #include <linux/ioctl.h> | ||
33 | #include <linux/mtio.h> | ||
34 | #include <linux/ftape-vendors.h> | ||
35 | |||
36 | #include "../lowlevel/ftape-rw.h" | ||
37 | #include <linux/ftape-header-segment.h> | ||
38 | |||
39 | typedef struct { | ||
40 | int used; /* any reading or writing done */ | ||
41 | /* isr statistics */ | ||
42 | unsigned int id_am_errors; /* id address mark not found */ | ||
43 | unsigned int id_crc_errors; /* crc error in id address mark */ | ||
44 | unsigned int data_am_errors; /* data address mark not found */ | ||
45 | unsigned int data_crc_errors; /* crc error in data field */ | ||
46 | unsigned int overrun_errors; /* fdc access timing problem */ | ||
47 | unsigned int no_data_errors; /* sector not found */ | ||
48 | unsigned int retries; /* number of tape retries */ | ||
49 | /* ecc statistics */ | ||
50 | unsigned int crc_errors; /* crc error in data */ | ||
51 | unsigned int crc_failures; /* bad data without crc error */ | ||
52 | unsigned int ecc_failures; /* failed to correct */ | ||
53 | unsigned int corrected; /* total sectors corrected */ | ||
54 | /* general statistics */ | ||
55 | unsigned int rewinds; /* number of tape rewinds */ | ||
56 | unsigned int defects; /* bad sectors due to media defects */ | ||
57 | } history_record; | ||
58 | |||
59 | /* this structure contains * ALL * information that we want | ||
60 | * our child modules to know about, but don't want them to | ||
61 | * modify. | ||
62 | */ | ||
63 | typedef struct { | ||
64 | /* vendor information */ | ||
65 | vendor_struct fti_drive_type; | ||
66 | /* data rates */ | ||
67 | unsigned int fti_used_data_rate; | ||
68 | unsigned int fti_drive_max_rate; | ||
69 | unsigned int fti_fdc_max_rate; | ||
70 | /* drive selection, either FTAPE_SEL_A/B/C/D */ | ||
71 | int fti_drive_sel; | ||
72 | /* flags set after decode the drive and tape status */ | ||
73 | unsigned int fti_formatted :1; | ||
74 | unsigned int fti_no_tape :1; | ||
75 | unsigned int fti_write_protected:1; | ||
76 | unsigned int fti_new_tape :1; | ||
77 | /* values of last queried drive/tape status and error */ | ||
78 | ft_drive_error fti_last_error; | ||
79 | ft_drive_status fti_last_status; | ||
80 | /* cartridge geometry */ | ||
81 | unsigned int fti_tracks_per_tape; | ||
82 | unsigned int fti_segments_per_track; | ||
83 | /* location of header segments, etc. */ | ||
84 | int fti_used_header_segment; | ||
85 | int fti_header_segment_1; | ||
86 | int fti_header_segment_2; | ||
87 | int fti_first_data_segment; | ||
88 | int fti_last_data_segment; | ||
89 | /* the format code as stored in the header segment */ | ||
90 | ft_format_type fti_format_code; | ||
91 | /* the following is the sole reason for the ftape_set_status() call */ | ||
92 | unsigned int fti_qic_std; | ||
93 | /* is tape running? */ | ||
94 | volatile enum runner_status_enum fti_runner_status; | ||
95 | /* is tape reading/writing/verifying/formatting/deleting */ | ||
96 | buffer_state_enum fti_state; | ||
97 | /* flags fatal hardware error */ | ||
98 | unsigned int fti_failure:1; | ||
99 | /* history record */ | ||
100 | history_record fti_history; | ||
101 | } ftape_info; | ||
102 | |||
103 | /* vendor information */ | ||
104 | #define ft_drive_type ftape_status.fti_drive_type | ||
105 | /* data rates */ | ||
106 | #define ft_data_rate ftape_status.fti_used_data_rate | ||
107 | #define ft_drive_max_rate ftape_status.fti_drive_max_rate | ||
108 | #define ft_fdc_max_rate ftape_status.fti_fdc_max_rate | ||
109 | /* drive selection, either FTAPE_SEL_A/B/C/D */ | ||
110 | #define ft_drive_sel ftape_status.fti_drive_sel | ||
111 | /* flags set after decode the drive and tape status */ | ||
112 | #define ft_formatted ftape_status.fti_formatted | ||
113 | #define ft_no_tape ftape_status.fti_no_tape | ||
114 | #define ft_write_protected ftape_status.fti_write_protected | ||
115 | #define ft_new_tape ftape_status.fti_new_tape | ||
116 | /* values of last queried drive/tape status and error */ | ||
117 | #define ft_last_error ftape_status.fti_last_error | ||
118 | #define ft_last_status ftape_status.fti_last_status | ||
119 | /* cartridge geometry */ | ||
120 | #define ft_tracks_per_tape ftape_status.fti_tracks_per_tape | ||
121 | #define ft_segments_per_track ftape_status.fti_segments_per_track | ||
122 | /* the format code as stored in the header segment */ | ||
123 | #define ft_format_code ftape_status.fti_format_code | ||
124 | /* the qic status as returned by report drive configuration */ | ||
125 | #define ft_qic_std ftape_status.fti_qic_std | ||
126 | #define ft_used_header_segment ftape_status.fti_used_header_segment | ||
127 | #define ft_header_segment_1 ftape_status.fti_header_segment_1 | ||
128 | #define ft_header_segment_2 ftape_status.fti_header_segment_2 | ||
129 | #define ft_first_data_segment ftape_status.fti_first_data_segment | ||
130 | #define ft_last_data_segment ftape_status.fti_last_data_segment | ||
131 | /* is tape running? */ | ||
132 | #define ft_runner_status ftape_status.fti_runner_status | ||
133 | /* is tape reading/writing/verifying/formatting/deleting */ | ||
134 | #define ft_driver_state ftape_status.fti_state | ||
135 | /* flags fatal hardware error */ | ||
136 | #define ft_failure ftape_status.fti_failure | ||
137 | /* history record */ | ||
138 | #define ft_history ftape_status.fti_history | ||
139 | |||
140 | /* | ||
141 | * ftape-ctl.c defined global vars. | ||
142 | */ | ||
143 | extern ftape_info ftape_status; | ||
144 | extern int ftape_segments_per_head; | ||
145 | extern int ftape_segments_per_cylinder; | ||
146 | extern int ftape_init_drive_needed; | ||
147 | |||
148 | /* | ||
149 | * ftape-ctl.c defined global functions. | ||
150 | */ | ||
151 | extern int ftape_mmap(struct vm_area_struct *vma); | ||
152 | extern int ftape_enable(int drive_selection); | ||
153 | extern void ftape_disable(void); | ||
154 | extern int ftape_seek_to_bot(void); | ||
155 | extern int ftape_seek_to_eot(void); | ||
156 | extern int ftape_abort_operation(void); | ||
157 | extern void ftape_calc_timeouts(unsigned int qic_std, | ||
158 | unsigned int data_rate, | ||
159 | unsigned int tape_len); | ||
160 | extern int ftape_calibrate_data_rate(unsigned int qic_std); | ||
161 | extern const ftape_info *ftape_get_status(void); | ||
162 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-ecc.c b/drivers/char/ftape/lowlevel/ftape-ecc.c new file mode 100644 index 000000000000..e5632f674bc8 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ecc.c | |||
@@ -0,0 +1,853 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (c) 1993 Ning and David Mosberger. | ||
4 | |||
5 | This is based on code originally written by Bas Laarhoven (bas@vimec.nl) | ||
6 | and David L. Brown, Jr., and incorporates improvements suggested by | ||
7 | Kai Harrekilde-Petersen. | ||
8 | |||
9 | This program is free software; you can redistribute it and/or | ||
10 | modify it under the terms of the GNU General Public License as | ||
11 | published by the Free Software Foundation; either version 2, or (at | ||
12 | your option) any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, but | ||
15 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; see the file COPYING. If not, write to | ||
21 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
22 | USA. | ||
23 | |||
24 | * | ||
25 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.c,v $ | ||
26 | * $Revision: 1.3 $ | ||
27 | * $Date: 1997/10/05 19:18:10 $ | ||
28 | * | ||
29 | * This file contains the Reed-Solomon error correction code | ||
30 | * for the QIC-40/80 floppy-tape driver for Linux. | ||
31 | */ | ||
32 | |||
33 | #include <linux/ftape.h> | ||
34 | |||
35 | #include "../lowlevel/ftape-tracing.h" | ||
36 | #include "../lowlevel/ftape-ecc.h" | ||
37 | |||
38 | /* Machines that are big-endian should define macro BIG_ENDIAN. | ||
39 | * Unfortunately, there doesn't appear to be a standard include file | ||
40 | * that works for all OSs. | ||
41 | */ | ||
42 | |||
43 | #if defined(__sparc__) || defined(__hppa) | ||
44 | #define BIG_ENDIAN | ||
45 | #endif /* __sparc__ || __hppa */ | ||
46 | |||
47 | #if defined(__mips__) | ||
48 | #error Find a smart way to determine the Endianness of the MIPS CPU | ||
49 | #endif | ||
50 | |||
51 | /* Notice: to minimize the potential for confusion, we use r to | ||
52 | * denote the independent variable of the polynomials in the | ||
53 | * Galois Field GF(2^8). We reserve x for polynomials that | ||
54 | * that have coefficients in GF(2^8). | ||
55 | * | ||
56 | * The Galois Field in which coefficient arithmetic is performed are | ||
57 | * the polynomials over Z_2 (i.e., 0 and 1) modulo the irreducible | ||
58 | * polynomial f(r), where f(r)=r^8 + r^7 + r^2 + r + 1. A polynomial | ||
59 | * is represented as a byte with the MSB as the coefficient of r^7 and | ||
60 | * the LSB as the coefficient of r^0. For example, the binary | ||
61 | * representation of f(x) is 0x187 (of course, this doesn't fit into 8 | ||
62 | * bits). In this field, the polynomial r is a primitive element. | ||
63 | * That is, r^i with i in 0,...,255 enumerates all elements in the | ||
64 | * field. | ||
65 | * | ||
66 | * The generator polynomial for the QIC-80 ECC is | ||
67 | * | ||
68 | * g(x) = x^3 + r^105*x^2 + r^105*x + 1 | ||
69 | * | ||
70 | * which can be factored into: | ||
71 | * | ||
72 | * g(x) = (x-r^-1)(x-r^0)(x-r^1) | ||
73 | * | ||
74 | * the byte representation of the coefficients are: | ||
75 | * | ||
76 | * r^105 = 0xc0 | ||
77 | * r^-1 = 0xc3 | ||
78 | * r^0 = 0x01 | ||
79 | * r^1 = 0x02 | ||
80 | * | ||
81 | * Notice that r^-1 = r^254 as exponent arithmetic is performed | ||
82 | * modulo 2^8-1 = 255. | ||
83 | * | ||
84 | * For more information on Galois Fields and Reed-Solomon codes, refer | ||
85 | * to any good book. I found _An Introduction to Error Correcting | ||
86 | * Codes with Applications_ by S. A. Vanstone and P. C. van Oorschot | ||
87 | * to be a good introduction into the former. _CODING THEORY: The | ||
88 | * Essentials_ I found very useful for its concise description of | ||
89 | * Reed-Solomon encoding/decoding. | ||
90 | * | ||
91 | */ | ||
92 | |||
93 | typedef __u8 Matrix[3][3]; | ||
94 | |||
95 | /* | ||
96 | * gfpow[] is defined such that gfpow[i] returns r^i if | ||
97 | * i is in the range [0..255]. | ||
98 | */ | ||
99 | static const __u8 gfpow[] = | ||
100 | { | ||
101 | 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, | ||
102 | 0x87, 0x89, 0x95, 0xad, 0xdd, 0x3d, 0x7a, 0xf4, | ||
103 | 0x6f, 0xde, 0x3b, 0x76, 0xec, 0x5f, 0xbe, 0xfb, | ||
104 | 0x71, 0xe2, 0x43, 0x86, 0x8b, 0x91, 0xa5, 0xcd, | ||
105 | 0x1d, 0x3a, 0x74, 0xe8, 0x57, 0xae, 0xdb, 0x31, | ||
106 | 0x62, 0xc4, 0x0f, 0x1e, 0x3c, 0x78, 0xf0, 0x67, | ||
107 | 0xce, 0x1b, 0x36, 0x6c, 0xd8, 0x37, 0x6e, 0xdc, | ||
108 | 0x3f, 0x7e, 0xfc, 0x7f, 0xfe, 0x7b, 0xf6, 0x6b, | ||
109 | 0xd6, 0x2b, 0x56, 0xac, 0xdf, 0x39, 0x72, 0xe4, | ||
110 | 0x4f, 0x9e, 0xbb, 0xf1, 0x65, 0xca, 0x13, 0x26, | ||
111 | 0x4c, 0x98, 0xb7, 0xe9, 0x55, 0xaa, 0xd3, 0x21, | ||
112 | 0x42, 0x84, 0x8f, 0x99, 0xb5, 0xed, 0x5d, 0xba, | ||
113 | 0xf3, 0x61, 0xc2, 0x03, 0x06, 0x0c, 0x18, 0x30, | ||
114 | 0x60, 0xc0, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, | ||
115 | 0x47, 0x8e, 0x9b, 0xb1, 0xe5, 0x4d, 0x9a, 0xb3, | ||
116 | 0xe1, 0x45, 0x8a, 0x93, 0xa1, 0xc5, 0x0d, 0x1a, | ||
117 | 0x34, 0x68, 0xd0, 0x27, 0x4e, 0x9c, 0xbf, 0xf9, | ||
118 | 0x75, 0xea, 0x53, 0xa6, 0xcb, 0x11, 0x22, 0x44, | ||
119 | 0x88, 0x97, 0xa9, 0xd5, 0x2d, 0x5a, 0xb4, 0xef, | ||
120 | 0x59, 0xb2, 0xe3, 0x41, 0x82, 0x83, 0x81, 0x85, | ||
121 | 0x8d, 0x9d, 0xbd, 0xfd, 0x7d, 0xfa, 0x73, 0xe6, | ||
122 | 0x4b, 0x96, 0xab, 0xd1, 0x25, 0x4a, 0x94, 0xaf, | ||
123 | 0xd9, 0x35, 0x6a, 0xd4, 0x2f, 0x5e, 0xbc, 0xff, | ||
124 | 0x79, 0xf2, 0x63, 0xc6, 0x0b, 0x16, 0x2c, 0x58, | ||
125 | 0xb0, 0xe7, 0x49, 0x92, 0xa3, 0xc1, 0x05, 0x0a, | ||
126 | 0x14, 0x28, 0x50, 0xa0, 0xc7, 0x09, 0x12, 0x24, | ||
127 | 0x48, 0x90, 0xa7, 0xc9, 0x15, 0x2a, 0x54, 0xa8, | ||
128 | 0xd7, 0x29, 0x52, 0xa4, 0xcf, 0x19, 0x32, 0x64, | ||
129 | 0xc8, 0x17, 0x2e, 0x5c, 0xb8, 0xf7, 0x69, 0xd2, | ||
130 | 0x23, 0x46, 0x8c, 0x9f, 0xb9, 0xf5, 0x6d, 0xda, | ||
131 | 0x33, 0x66, 0xcc, 0x1f, 0x3e, 0x7c, 0xf8, 0x77, | ||
132 | 0xee, 0x5b, 0xb6, 0xeb, 0x51, 0xa2, 0xc3, 0x01 | ||
133 | }; | ||
134 | |||
135 | /* | ||
136 | * This is a log table. That is, gflog[r^i] returns i (modulo f(r)). | ||
137 | * gflog[0] is undefined and the first element is therefore not valid. | ||
138 | */ | ||
139 | static const __u8 gflog[256] = | ||
140 | { | ||
141 | 0xff, 0x00, 0x01, 0x63, 0x02, 0xc6, 0x64, 0x6a, | ||
142 | 0x03, 0xcd, 0xc7, 0xbc, 0x65, 0x7e, 0x6b, 0x2a, | ||
143 | 0x04, 0x8d, 0xce, 0x4e, 0xc8, 0xd4, 0xbd, 0xe1, | ||
144 | 0x66, 0xdd, 0x7f, 0x31, 0x6c, 0x20, 0x2b, 0xf3, | ||
145 | 0x05, 0x57, 0x8e, 0xe8, 0xcf, 0xac, 0x4f, 0x83, | ||
146 | 0xc9, 0xd9, 0xd5, 0x41, 0xbe, 0x94, 0xe2, 0xb4, | ||
147 | 0x67, 0x27, 0xde, 0xf0, 0x80, 0xb1, 0x32, 0x35, | ||
148 | 0x6d, 0x45, 0x21, 0x12, 0x2c, 0x0d, 0xf4, 0x38, | ||
149 | 0x06, 0x9b, 0x58, 0x1a, 0x8f, 0x79, 0xe9, 0x70, | ||
150 | 0xd0, 0xc2, 0xad, 0xa8, 0x50, 0x75, 0x84, 0x48, | ||
151 | 0xca, 0xfc, 0xda, 0x8a, 0xd6, 0x54, 0x42, 0x24, | ||
152 | 0xbf, 0x98, 0x95, 0xf9, 0xe3, 0x5e, 0xb5, 0x15, | ||
153 | 0x68, 0x61, 0x28, 0xba, 0xdf, 0x4c, 0xf1, 0x2f, | ||
154 | 0x81, 0xe6, 0xb2, 0x3f, 0x33, 0xee, 0x36, 0x10, | ||
155 | 0x6e, 0x18, 0x46, 0xa6, 0x22, 0x88, 0x13, 0xf7, | ||
156 | 0x2d, 0xb8, 0x0e, 0x3d, 0xf5, 0xa4, 0x39, 0x3b, | ||
157 | 0x07, 0x9e, 0x9c, 0x9d, 0x59, 0x9f, 0x1b, 0x08, | ||
158 | 0x90, 0x09, 0x7a, 0x1c, 0xea, 0xa0, 0x71, 0x5a, | ||
159 | 0xd1, 0x1d, 0xc3, 0x7b, 0xae, 0x0a, 0xa9, 0x91, | ||
160 | 0x51, 0x5b, 0x76, 0x72, 0x85, 0xa1, 0x49, 0xeb, | ||
161 | 0xcb, 0x7c, 0xfd, 0xc4, 0xdb, 0x1e, 0x8b, 0xd2, | ||
162 | 0xd7, 0x92, 0x55, 0xaa, 0x43, 0x0b, 0x25, 0xaf, | ||
163 | 0xc0, 0x73, 0x99, 0x77, 0x96, 0x5c, 0xfa, 0x52, | ||
164 | 0xe4, 0xec, 0x5f, 0x4a, 0xb6, 0xa2, 0x16, 0x86, | ||
165 | 0x69, 0xc5, 0x62, 0xfe, 0x29, 0x7d, 0xbb, 0xcc, | ||
166 | 0xe0, 0xd3, 0x4d, 0x8c, 0xf2, 0x1f, 0x30, 0xdc, | ||
167 | 0x82, 0xab, 0xe7, 0x56, 0xb3, 0x93, 0x40, 0xd8, | ||
168 | 0x34, 0xb0, 0xef, 0x26, 0x37, 0x0c, 0x11, 0x44, | ||
169 | 0x6f, 0x78, 0x19, 0x9a, 0x47, 0x74, 0xa7, 0xc1, | ||
170 | 0x23, 0x53, 0x89, 0xfb, 0x14, 0x5d, 0xf8, 0x97, | ||
171 | 0x2e, 0x4b, 0xb9, 0x60, 0x0f, 0xed, 0x3e, 0xe5, | ||
172 | 0xf6, 0x87, 0xa5, 0x17, 0x3a, 0xa3, 0x3c, 0xb7 | ||
173 | }; | ||
174 | |||
175 | /* This is a multiplication table for the factor 0xc0 (i.e., r^105 (mod f(r)). | ||
176 | * gfmul_c0[f] returns r^105 * f(r) (modulo f(r)). | ||
177 | */ | ||
178 | static const __u8 gfmul_c0[256] = | ||
179 | { | ||
180 | 0x00, 0xc0, 0x07, 0xc7, 0x0e, 0xce, 0x09, 0xc9, | ||
181 | 0x1c, 0xdc, 0x1b, 0xdb, 0x12, 0xd2, 0x15, 0xd5, | ||
182 | 0x38, 0xf8, 0x3f, 0xff, 0x36, 0xf6, 0x31, 0xf1, | ||
183 | 0x24, 0xe4, 0x23, 0xe3, 0x2a, 0xea, 0x2d, 0xed, | ||
184 | 0x70, 0xb0, 0x77, 0xb7, 0x7e, 0xbe, 0x79, 0xb9, | ||
185 | 0x6c, 0xac, 0x6b, 0xab, 0x62, 0xa2, 0x65, 0xa5, | ||
186 | 0x48, 0x88, 0x4f, 0x8f, 0x46, 0x86, 0x41, 0x81, | ||
187 | 0x54, 0x94, 0x53, 0x93, 0x5a, 0x9a, 0x5d, 0x9d, | ||
188 | 0xe0, 0x20, 0xe7, 0x27, 0xee, 0x2e, 0xe9, 0x29, | ||
189 | 0xfc, 0x3c, 0xfb, 0x3b, 0xf2, 0x32, 0xf5, 0x35, | ||
190 | 0xd8, 0x18, 0xdf, 0x1f, 0xd6, 0x16, 0xd1, 0x11, | ||
191 | 0xc4, 0x04, 0xc3, 0x03, 0xca, 0x0a, 0xcd, 0x0d, | ||
192 | 0x90, 0x50, 0x97, 0x57, 0x9e, 0x5e, 0x99, 0x59, | ||
193 | 0x8c, 0x4c, 0x8b, 0x4b, 0x82, 0x42, 0x85, 0x45, | ||
194 | 0xa8, 0x68, 0xaf, 0x6f, 0xa6, 0x66, 0xa1, 0x61, | ||
195 | 0xb4, 0x74, 0xb3, 0x73, 0xba, 0x7a, 0xbd, 0x7d, | ||
196 | 0x47, 0x87, 0x40, 0x80, 0x49, 0x89, 0x4e, 0x8e, | ||
197 | 0x5b, 0x9b, 0x5c, 0x9c, 0x55, 0x95, 0x52, 0x92, | ||
198 | 0x7f, 0xbf, 0x78, 0xb8, 0x71, 0xb1, 0x76, 0xb6, | ||
199 | 0x63, 0xa3, 0x64, 0xa4, 0x6d, 0xad, 0x6a, 0xaa, | ||
200 | 0x37, 0xf7, 0x30, 0xf0, 0x39, 0xf9, 0x3e, 0xfe, | ||
201 | 0x2b, 0xeb, 0x2c, 0xec, 0x25, 0xe5, 0x22, 0xe2, | ||
202 | 0x0f, 0xcf, 0x08, 0xc8, 0x01, 0xc1, 0x06, 0xc6, | ||
203 | 0x13, 0xd3, 0x14, 0xd4, 0x1d, 0xdd, 0x1a, 0xda, | ||
204 | 0xa7, 0x67, 0xa0, 0x60, 0xa9, 0x69, 0xae, 0x6e, | ||
205 | 0xbb, 0x7b, 0xbc, 0x7c, 0xb5, 0x75, 0xb2, 0x72, | ||
206 | 0x9f, 0x5f, 0x98, 0x58, 0x91, 0x51, 0x96, 0x56, | ||
207 | 0x83, 0x43, 0x84, 0x44, 0x8d, 0x4d, 0x8a, 0x4a, | ||
208 | 0xd7, 0x17, 0xd0, 0x10, 0xd9, 0x19, 0xde, 0x1e, | ||
209 | 0xcb, 0x0b, 0xcc, 0x0c, 0xc5, 0x05, 0xc2, 0x02, | ||
210 | 0xef, 0x2f, 0xe8, 0x28, 0xe1, 0x21, 0xe6, 0x26, | ||
211 | 0xf3, 0x33, 0xf4, 0x34, 0xfd, 0x3d, 0xfa, 0x3a | ||
212 | }; | ||
213 | |||
214 | |||
215 | /* Returns V modulo 255 provided V is in the range -255,-254,...,509. | ||
216 | */ | ||
217 | static inline __u8 mod255(int v) | ||
218 | { | ||
219 | if (v > 0) { | ||
220 | if (v < 255) { | ||
221 | return v; | ||
222 | } else { | ||
223 | return v - 255; | ||
224 | } | ||
225 | } else { | ||
226 | return v + 255; | ||
227 | } | ||
228 | } | ||
229 | |||
230 | |||
231 | /* Add two numbers in the field. Addition in this field is equivalent | ||
232 | * to a bit-wise exclusive OR operation---subtraction is therefore | ||
233 | * identical to addition. | ||
234 | */ | ||
235 | static inline __u8 gfadd(__u8 a, __u8 b) | ||
236 | { | ||
237 | return a ^ b; | ||
238 | } | ||
239 | |||
240 | |||
241 | /* Add two vectors of numbers in the field. Each byte in A and B gets | ||
242 | * added individually. | ||
243 | */ | ||
244 | static inline unsigned long gfadd_long(unsigned long a, unsigned long b) | ||
245 | { | ||
246 | return a ^ b; | ||
247 | } | ||
248 | |||
249 | |||
250 | /* Multiply two numbers in the field: | ||
251 | */ | ||
252 | static inline __u8 gfmul(__u8 a, __u8 b) | ||
253 | { | ||
254 | if (a && b) { | ||
255 | return gfpow[mod255(gflog[a] + gflog[b])]; | ||
256 | } else { | ||
257 | return 0; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | |||
262 | /* Just like gfmul, except we have already looked up the log of the | ||
263 | * second number. | ||
264 | */ | ||
265 | static inline __u8 gfmul_exp(__u8 a, int b) | ||
266 | { | ||
267 | if (a) { | ||
268 | return gfpow[mod255(gflog[a] + b)]; | ||
269 | } else { | ||
270 | return 0; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | |||
275 | /* Just like gfmul_exp, except that A is a vector of numbers. That | ||
276 | * is, each byte in A gets multiplied by gfpow[mod255(B)]. | ||
277 | */ | ||
278 | static inline unsigned long gfmul_exp_long(unsigned long a, int b) | ||
279 | { | ||
280 | __u8 t; | ||
281 | |||
282 | if (sizeof(long) == 4) { | ||
283 | return ( | ||
284 | ((t = (__u32)a >> 24 & 0xff) ? | ||
285 | (((__u32) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | | ||
286 | ((t = (__u32)a >> 16 & 0xff) ? | ||
287 | (((__u32) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | | ||
288 | ((t = (__u32)a >> 8 & 0xff) ? | ||
289 | (((__u32) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | | ||
290 | ((t = (__u32)a >> 0 & 0xff) ? | ||
291 | (((__u32) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); | ||
292 | } else if (sizeof(long) == 8) { | ||
293 | return ( | ||
294 | ((t = (__u64)a >> 56 & 0xff) ? | ||
295 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 56) : 0) | | ||
296 | ((t = (__u64)a >> 48 & 0xff) ? | ||
297 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 48) : 0) | | ||
298 | ((t = (__u64)a >> 40 & 0xff) ? | ||
299 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 40) : 0) | | ||
300 | ((t = (__u64)a >> 32 & 0xff) ? | ||
301 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 32) : 0) | | ||
302 | ((t = (__u64)a >> 24 & 0xff) ? | ||
303 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 24) : 0) | | ||
304 | ((t = (__u64)a >> 16 & 0xff) ? | ||
305 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 16) : 0) | | ||
306 | ((t = (__u64)a >> 8 & 0xff) ? | ||
307 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 8) : 0) | | ||
308 | ((t = (__u64)a >> 0 & 0xff) ? | ||
309 | (((__u64) gfpow[mod255(gflog[t] + b)]) << 0) : 0)); | ||
310 | } else { | ||
311 | TRACE_FUN(ft_t_any); | ||
312 | TRACE_ABORT(-1, ft_t_err, "Error: size of long is %d bytes", | ||
313 | (int)sizeof(long)); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | |||
318 | /* Divide two numbers in the field. Returns a/b (modulo f(x)). | ||
319 | */ | ||
320 | static inline __u8 gfdiv(__u8 a, __u8 b) | ||
321 | { | ||
322 | if (!b) { | ||
323 | TRACE_FUN(ft_t_any); | ||
324 | TRACE_ABORT(0xff, ft_t_bug, "Error: division by zero"); | ||
325 | } else if (a == 0) { | ||
326 | return 0; | ||
327 | } else { | ||
328 | return gfpow[mod255(gflog[a] - gflog[b])]; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | |||
333 | /* The following functions return the inverse of the matrix of the | ||
334 | * linear system that needs to be solved to determine the error | ||
335 | * magnitudes. The first deals with matrices of rank 3, while the | ||
336 | * second deals with matrices of rank 2. The error indices are passed | ||
337 | * in arguments L0,..,L2 (0=first sector, 31=last sector). The error | ||
338 | * indices must be sorted in ascending order, i.e., L0<L1<L2. | ||
339 | * | ||
340 | * The linear system that needs to be solved for the error magnitudes | ||
341 | * is A * b = s, where s is the known vector of syndromes, b is the | ||
342 | * vector of error magnitudes and A in the ORDER=3 case: | ||
343 | * | ||
344 | * A_3 = {{1/r^L[0], 1/r^L[1], 1/r^L[2]}, | ||
345 | * { 1, 1, 1}, | ||
346 | * { r^L[0], r^L[1], r^L[2]}} | ||
347 | */ | ||
348 | static inline int gfinv3(__u8 l0, | ||
349 | __u8 l1, | ||
350 | __u8 l2, | ||
351 | Matrix Ainv) | ||
352 | { | ||
353 | __u8 det; | ||
354 | __u8 t20, t10, t21, t12, t01, t02; | ||
355 | int log_det; | ||
356 | |||
357 | /* compute some intermediate results: */ | ||
358 | t20 = gfpow[l2 - l0]; /* t20 = r^l2/r^l0 */ | ||
359 | t10 = gfpow[l1 - l0]; /* t10 = r^l1/r^l0 */ | ||
360 | t21 = gfpow[l2 - l1]; /* t21 = r^l2/r^l1 */ | ||
361 | t12 = gfpow[l1 - l2 + 255]; /* t12 = r^l1/r^l2 */ | ||
362 | t01 = gfpow[l0 - l1 + 255]; /* t01 = r^l0/r^l1 */ | ||
363 | t02 = gfpow[l0 - l2 + 255]; /* t02 = r^l0/r^l2 */ | ||
364 | /* Calculate the determinant of matrix A_3^-1 (sometimes | ||
365 | * called the Vandermonde determinant): | ||
366 | */ | ||
367 | det = gfadd(t20, gfadd(t10, gfadd(t21, gfadd(t12, gfadd(t01, t02))))); | ||
368 | if (!det) { | ||
369 | TRACE_FUN(ft_t_any); | ||
370 | TRACE_ABORT(0, ft_t_err, | ||
371 | "Inversion failed (3 CRC errors, >0 CRC failures)"); | ||
372 | } | ||
373 | log_det = 255 - gflog[det]; | ||
374 | |||
375 | /* Now, calculate all of the coefficients: | ||
376 | */ | ||
377 | Ainv[0][0]= gfmul_exp(gfadd(gfpow[l1], gfpow[l2]), log_det); | ||
378 | Ainv[0][1]= gfmul_exp(gfadd(t21, t12), log_det); | ||
379 | Ainv[0][2]= gfmul_exp(gfadd(gfpow[255 - l1], gfpow[255 - l2]),log_det); | ||
380 | |||
381 | Ainv[1][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l2]), log_det); | ||
382 | Ainv[1][1]= gfmul_exp(gfadd(t20, t02), log_det); | ||
383 | Ainv[1][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l2]),log_det); | ||
384 | |||
385 | Ainv[2][0]= gfmul_exp(gfadd(gfpow[l0], gfpow[l1]), log_det); | ||
386 | Ainv[2][1]= gfmul_exp(gfadd(t10, t01), log_det); | ||
387 | Ainv[2][2]= gfmul_exp(gfadd(gfpow[255 - l0], gfpow[255 - l1]),log_det); | ||
388 | |||
389 | return 1; | ||
390 | } | ||
391 | |||
392 | |||
393 | static inline int gfinv2(__u8 l0, __u8 l1, Matrix Ainv) | ||
394 | { | ||
395 | __u8 det; | ||
396 | __u8 t1, t2; | ||
397 | int log_det; | ||
398 | |||
399 | t1 = gfpow[255 - l0]; | ||
400 | t2 = gfpow[255 - l1]; | ||
401 | det = gfadd(t1, t2); | ||
402 | if (!det) { | ||
403 | TRACE_FUN(ft_t_any); | ||
404 | TRACE_ABORT(0, ft_t_err, | ||
405 | "Inversion failed (2 CRC errors, >0 CRC failures)"); | ||
406 | } | ||
407 | log_det = 255 - gflog[det]; | ||
408 | |||
409 | /* Now, calculate all of the coefficients: | ||
410 | */ | ||
411 | Ainv[0][0] = Ainv[1][0] = gfpow[log_det]; | ||
412 | |||
413 | Ainv[0][1] = gfmul_exp(t2, log_det); | ||
414 | Ainv[1][1] = gfmul_exp(t1, log_det); | ||
415 | |||
416 | return 1; | ||
417 | } | ||
418 | |||
419 | |||
420 | /* Multiply matrix A by vector S and return result in vector B. M is | ||
421 | * assumed to be of order NxN, S and B of order Nx1. | ||
422 | */ | ||
423 | static inline void gfmat_mul(int n, Matrix A, | ||
424 | __u8 *s, __u8 *b) | ||
425 | { | ||
426 | int i, j; | ||
427 | __u8 dot_prod; | ||
428 | |||
429 | for (i = 0; i < n; ++i) { | ||
430 | dot_prod = 0; | ||
431 | for (j = 0; j < n; ++j) { | ||
432 | dot_prod = gfadd(dot_prod, gfmul(A[i][j], s[j])); | ||
433 | } | ||
434 | b[i] = dot_prod; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | |||
439 | |||
440 | /* The Reed Solomon ECC codes are computed over the N-th byte of each | ||
441 | * block, where N=SECTOR_SIZE. There are up to 29 blocks of data, and | ||
442 | * 3 blocks of ECC. The blocks are stored contiguously in memory. A | ||
443 | * segment, consequently, is assumed to have at least 4 blocks: one or | ||
444 | * more data blocks plus three ECC blocks. | ||
445 | * | ||
446 | * Notice: In QIC-80 speak, a CRC error is a sector with an incorrect | ||
447 | * CRC. A CRC failure is a sector with incorrect data, but | ||
448 | * a valid CRC. In the error control literature, the former | ||
449 | * is usually called "erasure", the latter "error." | ||
450 | */ | ||
451 | /* Compute the parity bytes for C columns of data, where C is the | ||
452 | * number of bytes that fit into a long integer. We use a linear | ||
453 | * feed-back register to do this. The parity bytes P[0], P[STRIDE], | ||
454 | * P[2*STRIDE] are computed such that: | ||
455 | * | ||
456 | * x^k * p(x) + m(x) = 0 (modulo g(x)) | ||
457 | * | ||
458 | * where k = NBLOCKS, | ||
459 | * p(x) = P[0] + P[STRIDE]*x + P[2*STRIDE]*x^2, and | ||
460 | * m(x) = sum_{i=0}^k m_i*x^i. | ||
461 | * m_i = DATA[i*SECTOR_SIZE] | ||
462 | */ | ||
463 | static inline void set_parity(unsigned long *data, | ||
464 | int nblocks, | ||
465 | unsigned long *p, | ||
466 | int stride) | ||
467 | { | ||
468 | unsigned long p0, p1, p2, t1, t2, *end; | ||
469 | |||
470 | end = data + nblocks * (FT_SECTOR_SIZE / sizeof(long)); | ||
471 | p0 = p1 = p2 = 0; | ||
472 | while (data < end) { | ||
473 | /* The new parity bytes p0_i, p1_i, p2_i are computed | ||
474 | * from the old values p0_{i-1}, p1_{i-1}, p2_{i-1} | ||
475 | * recursively as: | ||
476 | * | ||
477 | * p0_i = p1_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) | ||
478 | * p1_i = p2_{i-1} + r^105 * (m_{i-1} - p0_{i-1}) | ||
479 | * p2_i = (m_{i-1} - p0_{i-1}) | ||
480 | * | ||
481 | * With the initial condition: p0_0 = p1_0 = p2_0 = 0. | ||
482 | */ | ||
483 | t1 = gfadd_long(*data, p0); | ||
484 | /* | ||
485 | * Multiply each byte in t1 by 0xc0: | ||
486 | */ | ||
487 | if (sizeof(long) == 4) { | ||
488 | t2= (((__u32) gfmul_c0[(__u32)t1 >> 24 & 0xff]) << 24 | | ||
489 | ((__u32) gfmul_c0[(__u32)t1 >> 16 & 0xff]) << 16 | | ||
490 | ((__u32) gfmul_c0[(__u32)t1 >> 8 & 0xff]) << 8 | | ||
491 | ((__u32) gfmul_c0[(__u32)t1 >> 0 & 0xff]) << 0); | ||
492 | } else if (sizeof(long) == 8) { | ||
493 | t2= (((__u64) gfmul_c0[(__u64)t1 >> 56 & 0xff]) << 56 | | ||
494 | ((__u64) gfmul_c0[(__u64)t1 >> 48 & 0xff]) << 48 | | ||
495 | ((__u64) gfmul_c0[(__u64)t1 >> 40 & 0xff]) << 40 | | ||
496 | ((__u64) gfmul_c0[(__u64)t1 >> 32 & 0xff]) << 32 | | ||
497 | ((__u64) gfmul_c0[(__u64)t1 >> 24 & 0xff]) << 24 | | ||
498 | ((__u64) gfmul_c0[(__u64)t1 >> 16 & 0xff]) << 16 | | ||
499 | ((__u64) gfmul_c0[(__u64)t1 >> 8 & 0xff]) << 8 | | ||
500 | ((__u64) gfmul_c0[(__u64)t1 >> 0 & 0xff]) << 0); | ||
501 | } else { | ||
502 | TRACE_FUN(ft_t_any); | ||
503 | TRACE(ft_t_err, "Error: long is of size %d", | ||
504 | (int) sizeof(long)); | ||
505 | TRACE_EXIT; | ||
506 | } | ||
507 | p0 = gfadd_long(t2, p1); | ||
508 | p1 = gfadd_long(t2, p2); | ||
509 | p2 = t1; | ||
510 | data += FT_SECTOR_SIZE / sizeof(long); | ||
511 | } | ||
512 | *p = p0; | ||
513 | p += stride; | ||
514 | *p = p1; | ||
515 | p += stride; | ||
516 | *p = p2; | ||
517 | return; | ||
518 | } | ||
519 | |||
520 | |||
521 | /* Compute the 3 syndrome values. DATA should point to the first byte | ||
522 | * of the column for which the syndromes are desired. The syndromes | ||
523 | * are computed over the first NBLOCKS of rows. The three bytes will | ||
524 | * be placed in S[0], S[1], and S[2]. | ||
525 | * | ||
526 | * S[i] is the value of the "message" polynomial m(x) evaluated at the | ||
527 | * i-th root of the generator polynomial g(x). | ||
528 | * | ||
529 | * As g(x)=(x-r^-1)(x-1)(x-r^1) we evaluate the message polynomial at | ||
530 | * x=r^-1 to get S[0], at x=r^0=1 to get S[1], and at x=r to get S[2]. | ||
531 | * This could be done directly and efficiently via the Horner scheme. | ||
532 | * However, it would require multiplication tables for the factors | ||
533 | * r^-1 (0xc3) and r (0x02). The following scheme does not require | ||
534 | * any multiplication tables beyond what's needed for set_parity() | ||
535 | * anyway and is slightly faster if there are no errors and slightly | ||
536 | * slower if there are errors. The latter is hopefully the infrequent | ||
537 | * case. | ||
538 | * | ||
539 | * To understand the alternative algorithm, notice that set_parity(m, | ||
540 | * k, p) computes parity bytes such that: | ||
541 | * | ||
542 | * x^k * p(x) = m(x) (modulo g(x)). | ||
543 | * | ||
544 | * That is, to evaluate m(r^m), where r^m is a root of g(x), we can | ||
545 | * simply evaluate (r^m)^k*p(r^m). Also, notice that p is 0 if and | ||
546 | * only if s is zero. That is, if all parity bytes are 0, we know | ||
547 | * there is no error in the data and consequently there is no need to | ||
548 | * compute s(x) at all! In all other cases, we compute s(x) from p(x) | ||
549 | * by evaluating (r^m)^k*p(r^m) for m=-1, m=0, and m=1. The p(x) | ||
550 | * polynomial is evaluated via the Horner scheme. | ||
551 | */ | ||
552 | static int compute_syndromes(unsigned long *data, int nblocks, unsigned long *s) | ||
553 | { | ||
554 | unsigned long p[3]; | ||
555 | |||
556 | set_parity(data, nblocks, p, 1); | ||
557 | if (p[0] | p[1] | p[2]) { | ||
558 | /* Some of the checked columns do not have a zero | ||
559 | * syndrome. For simplicity, we compute the syndromes | ||
560 | * for all columns that we have computed the | ||
561 | * remainders for. | ||
562 | */ | ||
563 | s[0] = gfmul_exp_long( | ||
564 | gfadd_long(p[0], | ||
565 | gfmul_exp_long( | ||
566 | gfadd_long(p[1], | ||
567 | gfmul_exp_long(p[2], -1)), | ||
568 | -1)), | ||
569 | -nblocks); | ||
570 | s[1] = gfadd_long(gfadd_long(p[2], p[1]), p[0]); | ||
571 | s[2] = gfmul_exp_long( | ||
572 | gfadd_long(p[0], | ||
573 | gfmul_exp_long( | ||
574 | gfadd_long(p[1], | ||
575 | gfmul_exp_long(p[2], 1)), | ||
576 | 1)), | ||
577 | nblocks); | ||
578 | return 0; | ||
579 | } else { | ||
580 | return 1; | ||
581 | } | ||
582 | } | ||
583 | |||
584 | |||
585 | /* Correct the block in the column pointed to by DATA. There are NBAD | ||
586 | * CRC errors and their indices are in BAD_LOC[0], up to | ||
587 | * BAD_LOC[NBAD-1]. If NBAD>1, Ainv holds the inverse of the matrix | ||
588 | * of the linear system that needs to be solved to determine the error | ||
589 | * magnitudes. S[0], S[1], and S[2] are the syndrome values. If row | ||
590 | * j gets corrected, then bit j will be set in CORRECTION_MAP. | ||
591 | */ | ||
592 | static inline int correct_block(__u8 *data, int nblocks, | ||
593 | int nbad, int *bad_loc, Matrix Ainv, | ||
594 | __u8 *s, | ||
595 | SectorMap * correction_map) | ||
596 | { | ||
597 | int ncorrected = 0; | ||
598 | int i; | ||
599 | __u8 t1, t2; | ||
600 | __u8 c0, c1, c2; /* check bytes */ | ||
601 | __u8 error_mag[3], log_error_mag; | ||
602 | __u8 *dp, l, e; | ||
603 | TRACE_FUN(ft_t_any); | ||
604 | |||
605 | switch (nbad) { | ||
606 | case 0: | ||
607 | /* might have a CRC failure: */ | ||
608 | if (s[0] == 0) { | ||
609 | /* more than one error */ | ||
610 | TRACE_ABORT(-1, ft_t_err, | ||
611 | "ECC failed (0 CRC errors, >1 CRC failures)"); | ||
612 | } | ||
613 | t1 = gfdiv(s[1], s[0]); | ||
614 | if ((bad_loc[nbad++] = gflog[t1]) >= nblocks) { | ||
615 | TRACE(ft_t_err, | ||
616 | "ECC failed (0 CRC errors, >1 CRC failures)"); | ||
617 | TRACE_ABORT(-1, ft_t_err, | ||
618 | "attempt to correct data at %d", bad_loc[0]); | ||
619 | } | ||
620 | error_mag[0] = s[1]; | ||
621 | break; | ||
622 | case 1: | ||
623 | t1 = gfadd(gfmul_exp(s[1], bad_loc[0]), s[2]); | ||
624 | t2 = gfadd(gfmul_exp(s[0], bad_loc[0]), s[1]); | ||
625 | if (t1 == 0 && t2 == 0) { | ||
626 | /* one erasure, no error: */ | ||
627 | Ainv[0][0] = gfpow[bad_loc[0]]; | ||
628 | } else if (t1 == 0 || t2 == 0) { | ||
629 | /* one erasure and more than one error: */ | ||
630 | TRACE_ABORT(-1, ft_t_err, | ||
631 | "ECC failed (1 erasure, >1 error)"); | ||
632 | } else { | ||
633 | /* one erasure, one error: */ | ||
634 | if ((bad_loc[nbad++] = gflog[gfdiv(t1, t2)]) | ||
635 | >= nblocks) { | ||
636 | TRACE(ft_t_err, "ECC failed " | ||
637 | "(1 CRC errors, >1 CRC failures)"); | ||
638 | TRACE_ABORT(-1, ft_t_err, | ||
639 | "attempt to correct data at %d", | ||
640 | bad_loc[1]); | ||
641 | } | ||
642 | if (!gfinv2(bad_loc[0], bad_loc[1], Ainv)) { | ||
643 | /* inversion failed---must have more | ||
644 | * than one error | ||
645 | */ | ||
646 | TRACE_EXIT -1; | ||
647 | } | ||
648 | } | ||
649 | /* FALL THROUGH TO ERROR MAGNITUDE COMPUTATION: | ||
650 | */ | ||
651 | case 2: | ||
652 | case 3: | ||
653 | /* compute error magnitudes: */ | ||
654 | gfmat_mul(nbad, Ainv, s, error_mag); | ||
655 | break; | ||
656 | |||
657 | default: | ||
658 | TRACE_ABORT(-1, ft_t_err, | ||
659 | "Internal Error: number of CRC errors > 3"); | ||
660 | } | ||
661 | |||
662 | /* Perform correction by adding ERROR_MAG[i] to the byte at | ||
663 | * offset BAD_LOC[i]. Also add the value of the computed | ||
664 | * error polynomial to the syndrome values. If the correction | ||
665 | * was successful, the resulting check bytes should be zero | ||
666 | * (i.e., the corrected data is a valid code word). | ||
667 | */ | ||
668 | c0 = s[0]; | ||
669 | c1 = s[1]; | ||
670 | c2 = s[2]; | ||
671 | for (i = 0; i < nbad; ++i) { | ||
672 | e = error_mag[i]; | ||
673 | if (e) { | ||
674 | /* correct the byte at offset L by magnitude E: */ | ||
675 | l = bad_loc[i]; | ||
676 | dp = &data[l * FT_SECTOR_SIZE]; | ||
677 | *dp = gfadd(*dp, e); | ||
678 | *correction_map |= 1 << l; | ||
679 | ++ncorrected; | ||
680 | |||
681 | log_error_mag = gflog[e]; | ||
682 | c0 = gfadd(c0, gfpow[mod255(log_error_mag - l)]); | ||
683 | c1 = gfadd(c1, e); | ||
684 | c2 = gfadd(c2, gfpow[mod255(log_error_mag + l)]); | ||
685 | } | ||
686 | } | ||
687 | if (c0 || c1 || c2) { | ||
688 | TRACE_ABORT(-1, ft_t_err, | ||
689 | "ECC self-check failed, too many errors"); | ||
690 | } | ||
691 | TRACE_EXIT ncorrected; | ||
692 | } | ||
693 | |||
694 | |||
695 | #if defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) | ||
696 | |||
697 | /* Perform a sanity check on the computed parity bytes: | ||
698 | */ | ||
699 | static int sanity_check(unsigned long *data, int nblocks) | ||
700 | { | ||
701 | TRACE_FUN(ft_t_any); | ||
702 | unsigned long s[3]; | ||
703 | |||
704 | if (!compute_syndromes(data, nblocks, s)) { | ||
705 | TRACE_ABORT(0, ft_bug, | ||
706 | "Internal Error: syndrome self-check failed"); | ||
707 | } | ||
708 | TRACE_EXIT 1; | ||
709 | } | ||
710 | |||
711 | #endif /* defined(ECC_SANITY_CHECK) || defined(ECC_PARANOID) */ | ||
712 | |||
713 | /* Compute the parity for an entire segment of data. | ||
714 | */ | ||
715 | int ftape_ecc_set_segment_parity(struct memory_segment *mseg) | ||
716 | { | ||
717 | int i; | ||
718 | __u8 *parity_bytes; | ||
719 | |||
720 | parity_bytes = &mseg->data[(mseg->blocks - 3) * FT_SECTOR_SIZE]; | ||
721 | for (i = 0; i < FT_SECTOR_SIZE; i += sizeof(long)) { | ||
722 | set_parity((unsigned long *) &mseg->data[i], mseg->blocks - 3, | ||
723 | (unsigned long *) &parity_bytes[i], | ||
724 | FT_SECTOR_SIZE / sizeof(long)); | ||
725 | #ifdef ECC_PARANOID | ||
726 | if (!sanity_check((unsigned long *) &mseg->data[i], | ||
727 | mseg->blocks)) { | ||
728 | return -1; | ||
729 | } | ||
730 | #endif /* ECC_PARANOID */ | ||
731 | } | ||
732 | return 0; | ||
733 | } | ||
734 | |||
735 | |||
736 | /* Checks and corrects (if possible) the segment MSEG. Returns one of | ||
737 | * ECC_OK, ECC_CORRECTED, and ECC_FAILED. | ||
738 | */ | ||
739 | int ftape_ecc_correct_data(struct memory_segment *mseg) | ||
740 | { | ||
741 | int col, i, result; | ||
742 | int ncorrected = 0; | ||
743 | int nerasures = 0; /* # of erasures (CRC errors) */ | ||
744 | int erasure_loc[3]; /* erasure locations */ | ||
745 | unsigned long ss[3]; | ||
746 | __u8 s[3]; | ||
747 | Matrix Ainv; | ||
748 | TRACE_FUN(ft_t_flow); | ||
749 | |||
750 | mseg->corrected = 0; | ||
751 | |||
752 | /* find first column that has non-zero syndromes: */ | ||
753 | for (col = 0; col < FT_SECTOR_SIZE; col += sizeof(long)) { | ||
754 | if (!compute_syndromes((unsigned long *) &mseg->data[col], | ||
755 | mseg->blocks, ss)) { | ||
756 | /* something is wrong---have to fix things */ | ||
757 | break; | ||
758 | } | ||
759 | } | ||
760 | if (col >= FT_SECTOR_SIZE) { | ||
761 | /* all syndromes are ok, therefore nothing to correct */ | ||
762 | TRACE_EXIT ECC_OK; | ||
763 | } | ||
764 | /* count the number of CRC errors if there were any: */ | ||
765 | if (mseg->read_bad) { | ||
766 | for (i = 0; i < mseg->blocks; i++) { | ||
767 | if (BAD_CHECK(mseg->read_bad, i)) { | ||
768 | if (nerasures >= 3) { | ||
769 | /* this is too much for ECC */ | ||
770 | TRACE_ABORT(ECC_FAILED, ft_t_err, | ||
771 | "ECC failed (>3 CRC errors)"); | ||
772 | } /* if */ | ||
773 | erasure_loc[nerasures++] = i; | ||
774 | } | ||
775 | } | ||
776 | } | ||
777 | /* | ||
778 | * If there are at least 2 CRC errors, determine inverse of matrix | ||
779 | * of linear system to be solved: | ||
780 | */ | ||
781 | switch (nerasures) { | ||
782 | case 2: | ||
783 | if (!gfinv2(erasure_loc[0], erasure_loc[1], Ainv)) { | ||
784 | TRACE_EXIT ECC_FAILED; | ||
785 | } | ||
786 | break; | ||
787 | case 3: | ||
788 | if (!gfinv3(erasure_loc[0], erasure_loc[1], | ||
789 | erasure_loc[2], Ainv)) { | ||
790 | TRACE_EXIT ECC_FAILED; | ||
791 | } | ||
792 | break; | ||
793 | default: | ||
794 | /* this is not an error condition... */ | ||
795 | break; | ||
796 | } | ||
797 | |||
798 | do { | ||
799 | for (i = 0; i < sizeof(long); ++i) { | ||
800 | s[0] = ss[0]; | ||
801 | s[1] = ss[1]; | ||
802 | s[2] = ss[2]; | ||
803 | if (s[0] | s[1] | s[2]) { | ||
804 | #ifdef BIG_ENDIAN | ||
805 | result = correct_block( | ||
806 | &mseg->data[col + sizeof(long) - 1 - i], | ||
807 | mseg->blocks, | ||
808 | nerasures, | ||
809 | erasure_loc, | ||
810 | Ainv, | ||
811 | s, | ||
812 | &mseg->corrected); | ||
813 | #else | ||
814 | result = correct_block(&mseg->data[col + i], | ||
815 | mseg->blocks, | ||
816 | nerasures, | ||
817 | erasure_loc, | ||
818 | Ainv, | ||
819 | s, | ||
820 | &mseg->corrected); | ||
821 | #endif | ||
822 | if (result < 0) { | ||
823 | TRACE_EXIT ECC_FAILED; | ||
824 | } | ||
825 | ncorrected += result; | ||
826 | } | ||
827 | ss[0] >>= 8; | ||
828 | ss[1] >>= 8; | ||
829 | ss[2] >>= 8; | ||
830 | } | ||
831 | |||
832 | #ifdef ECC_SANITY_CHECK | ||
833 | if (!sanity_check((unsigned long *) &mseg->data[col], | ||
834 | mseg->blocks)) { | ||
835 | TRACE_EXIT ECC_FAILED; | ||
836 | } | ||
837 | #endif /* ECC_SANITY_CHECK */ | ||
838 | |||
839 | /* find next column with non-zero syndromes: */ | ||
840 | while ((col += sizeof(long)) < FT_SECTOR_SIZE) { | ||
841 | if (!compute_syndromes((unsigned long *) | ||
842 | &mseg->data[col], mseg->blocks, ss)) { | ||
843 | /* something is wrong---have to fix things */ | ||
844 | break; | ||
845 | } | ||
846 | } | ||
847 | } while (col < FT_SECTOR_SIZE); | ||
848 | if (ncorrected && nerasures == 0) { | ||
849 | TRACE(ft_t_warn, "block contained error not caught by CRC"); | ||
850 | } | ||
851 | TRACE((ncorrected > 0) ? ft_t_noise : ft_t_any, "number of corrections: %d", ncorrected); | ||
852 | TRACE_EXIT ncorrected ? ECC_CORRECTED : ECC_OK; | ||
853 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-ecc.h b/drivers/char/ftape/lowlevel/ftape-ecc.h new file mode 100644 index 000000000000..4829146fe9a0 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-ecc.h | |||
@@ -0,0 +1,84 @@ | |||
1 | #ifndef _FTAPE_ECC_H_ | ||
2 | #define _FTAPE_ECC_H_ | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993 Ning and David Mosberger. | ||
6 | * Original: | ||
7 | * Copyright (C) 1993 Bas Laarhoven. | ||
8 | * Copyright (C) 1992 David L. Brown, Jr. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or | ||
11 | modify it under the terms of the GNU General Public License as | ||
12 | published by the Free Software Foundation; either version 2, or (at | ||
13 | your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, but | ||
16 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; see the file COPYING. If not, write to | ||
22 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
23 | USA. | ||
24 | |||
25 | * | ||
26 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-ecc.h,v $ | ||
27 | * $Revision: 1.2 $ | ||
28 | * $Date: 1997/10/05 19:18:11 $ | ||
29 | * | ||
30 | * This file contains the definitions for the | ||
31 | * Reed-Solomon error correction code | ||
32 | * for the QIC-40/80 tape streamer device driver. | ||
33 | */ | ||
34 | |||
35 | #include "../lowlevel/ftape-bsm.h" | ||
36 | |||
37 | #define BAD_CLEAR(entry) ((entry)=0) | ||
38 | #define BAD_SET(entry,sector) ((entry)|=(1<<(sector))) | ||
39 | #define BAD_CHECK(entry,sector) ((entry)&(1<<(sector))) | ||
40 | |||
41 | /* | ||
42 | * Return values for ecc_correct_data: | ||
43 | */ | ||
44 | enum { | ||
45 | ECC_OK, /* Data was correct. */ | ||
46 | ECC_CORRECTED, /* Correctable error in data. */ | ||
47 | ECC_FAILED, /* Could not correct data. */ | ||
48 | }; | ||
49 | |||
50 | /* | ||
51 | * Representation of an in memory segment. MARKED_BAD lists the | ||
52 | * sectors that were marked bad during formatting. If the N-th sector | ||
53 | * in a segment is marked bad, bit 1<<N will be set in MARKED_BAD. | ||
54 | * The sectors should be read in from the disk and packed, as if the | ||
55 | * bad sectors were not there, and the segment just contained fewer | ||
56 | * sectors. READ_SECTORS is a bitmap of errors encountered while | ||
57 | * reading the data. These offsets are relative to the packed data. | ||
58 | * BLOCKS is a count of the sectors not marked bad. This is just to | ||
59 | * prevent having to count the zero bits in MARKED_BAD each time this | ||
60 | * is needed. DATA is the actual sector packed data from (or to) the | ||
61 | * tape. | ||
62 | */ | ||
63 | struct memory_segment { | ||
64 | SectorMap marked_bad; | ||
65 | SectorMap read_bad; | ||
66 | int blocks; | ||
67 | __u8 *data; | ||
68 | SectorMap corrected; | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * ecc.c defined global variables: | ||
73 | */ | ||
74 | #ifdef TEST | ||
75 | extern int ftape_ecc_tracing; | ||
76 | #endif | ||
77 | |||
78 | /* | ||
79 | * ecc.c defined global functions: | ||
80 | */ | ||
81 | extern int ftape_ecc_correct_data(struct memory_segment *data); | ||
82 | extern int ftape_ecc_set_segment_parity(struct memory_segment *data); | ||
83 | |||
84 | #endif /* _FTAPE_ECC_H_ */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-format.c b/drivers/char/ftape/lowlevel/ftape-format.c new file mode 100644 index 000000000000..5dd4c59a3f34 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-format.c | |||
@@ -0,0 +1,344 @@ | |||
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 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-format.h b/drivers/char/ftape/lowlevel/ftape-format.h new file mode 100644 index 000000000000..f15161566643 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-format.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef _FTAPE_FORMAT_H | ||
2 | #define _FTAPE_FORMAT_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996-1997 Claus-Justus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-format.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:18:13 $ | ||
25 | * | ||
26 | * This file contains the low level definitions for the | ||
27 | * formatting support for the QIC-40/80/3010/3020 floppy-tape | ||
28 | * driver "ftape" for Linux. | ||
29 | */ | ||
30 | |||
31 | #ifdef __KERNEL__ | ||
32 | extern int ftape_format_track(const unsigned int track, const __u8 gap3); | ||
33 | extern int ftape_format_status(unsigned int *segment_id); | ||
34 | extern int ftape_verify_segment(const unsigned int segment_id, SectorMap *bsm); | ||
35 | #endif /* __KERNEL__ */ | ||
36 | |||
37 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-init.c b/drivers/char/ftape/lowlevel/ftape-init.c new file mode 100644 index 000000000000..b54260d457c2 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-init.c | |||
@@ -0,0 +1,161 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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 | * This file contains the code that interfaces the kernel | ||
21 | * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. | ||
22 | */ | ||
23 | |||
24 | #include <linux/config.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/signal.h> | ||
30 | #include <linux/major.h> | ||
31 | |||
32 | #include <linux/ftape.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/qic117.h> | ||
35 | #ifdef CONFIG_ZFTAPE | ||
36 | #include <linux/zftape.h> | ||
37 | #endif | ||
38 | |||
39 | #include "../lowlevel/ftape-init.h" | ||
40 | #include "../lowlevel/ftape-io.h" | ||
41 | #include "../lowlevel/ftape-read.h" | ||
42 | #include "../lowlevel/ftape-write.h" | ||
43 | #include "../lowlevel/ftape-ctl.h" | ||
44 | #include "../lowlevel/ftape-rw.h" | ||
45 | #include "../lowlevel/fdc-io.h" | ||
46 | #include "../lowlevel/ftape-buffer.h" | ||
47 | #include "../lowlevel/ftape-proc.h" | ||
48 | #include "../lowlevel/ftape-tracing.h" | ||
49 | |||
50 | |||
51 | #if defined(MODULE) && !defined(CONFIG_FT_NO_TRACE_AT_ALL) | ||
52 | static int ft_tracing = -1; | ||
53 | #endif | ||
54 | |||
55 | |||
56 | /* Called by modules package when installing the driver | ||
57 | * or by kernel during the initialization phase | ||
58 | */ | ||
59 | static int __init ftape_init(void) | ||
60 | { | ||
61 | TRACE_FUN(ft_t_flow); | ||
62 | |||
63 | #ifdef MODULE | ||
64 | #ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||
65 | if (ft_tracing != -1) { | ||
66 | ftape_tracing = ft_tracing; | ||
67 | } | ||
68 | #endif | ||
69 | printk(KERN_INFO FTAPE_VERSION "\n"); | ||
70 | if (TRACE_LEVEL >= ft_t_info) { | ||
71 | printk( | ||
72 | KERN_INFO "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl)\n" | ||
73 | KERN_INFO "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no)\n" | ||
74 | KERN_INFO "(c) 1996-1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" | ||
75 | KERN_INFO "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives\n"); | ||
76 | } | ||
77 | #else /* !MODULE */ | ||
78 | /* print a short no-nonsense boot message */ | ||
79 | printk(KERN_INFO FTAPE_VERSION "\n"); | ||
80 | #endif /* MODULE */ | ||
81 | TRACE(ft_t_info, "installing QIC-117 floppy tape hardware drive ... "); | ||
82 | TRACE(ft_t_info, "ftape_init @ 0x%p", ftape_init); | ||
83 | /* Allocate the DMA buffers. They are deallocated at cleanup() time. | ||
84 | */ | ||
85 | #ifdef TESTING | ||
86 | #ifdef MODULE | ||
87 | while (ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS) < 0) { | ||
88 | ftape_sleep(FT_SECOND/20); | ||
89 | if (signal_pending(current)) { | ||
90 | (void)ftape_set_nr_buffers(0); | ||
91 | TRACE(ft_t_bug, | ||
92 | "Killed by signal while allocating buffers."); | ||
93 | TRACE_ABORT(-EINTR, | ||
94 | ft_t_bug, "Free up memory and retry"); | ||
95 | } | ||
96 | } | ||
97 | #else | ||
98 | TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS), | ||
99 | (void)ftape_set_nr_buffers(0)); | ||
100 | #endif | ||
101 | #else | ||
102 | TRACE_CATCH(ftape_set_nr_buffers(CONFIG_FT_NR_BUFFERS), | ||
103 | (void)ftape_set_nr_buffers(0)); | ||
104 | #endif | ||
105 | ft_drive_sel = -1; | ||
106 | ft_failure = 1; /* inhibit any operation but open */ | ||
107 | ftape_udelay_calibrate(); /* must be before fdc_wait_calibrate ! */ | ||
108 | fdc_wait_calibrate(); | ||
109 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) | ||
110 | (void)ftape_proc_init(); | ||
111 | #endif | ||
112 | #ifdef CONFIG_ZFTAPE | ||
113 | (void)zft_init(); | ||
114 | #endif | ||
115 | TRACE_EXIT 0; | ||
116 | } | ||
117 | |||
118 | module_param(ft_fdc_base, uint, 0); | ||
119 | MODULE_PARM_DESC(ft_fdc_base, "Base address of FDC controller."); | ||
120 | module_param(ft_fdc_irq, uint, 0); | ||
121 | MODULE_PARM_DESC(ft_fdc_irq, "IRQ (interrupt channel) to use."); | ||
122 | module_param(ft_fdc_dma, uint, 0); | ||
123 | MODULE_PARM_DESC(ft_fdc_dma, "DMA channel to use."); | ||
124 | module_param(ft_fdc_threshold, uint, 0); | ||
125 | MODULE_PARM_DESC(ft_fdc_threshold, "Threshold of the FDC Fifo."); | ||
126 | module_param(ft_fdc_rate_limit, uint, 0); | ||
127 | MODULE_PARM_DESC(ft_fdc_rate_limit, "Maximal data rate for FDC."); | ||
128 | module_param(ft_probe_fc10, bool, 0); | ||
129 | MODULE_PARM_DESC(ft_probe_fc10, | ||
130 | "If non-zero, probe for a Colorado FC-10/FC-20 controller."); | ||
131 | module_param(ft_mach2, bool, 0); | ||
132 | MODULE_PARM_DESC(ft_mach2, | ||
133 | "If non-zero, probe for a Mountain MACH-2 controller."); | ||
134 | #if defined(MODULE) && !defined(CONFIG_FT_NO_TRACE_AT_ALL) | ||
135 | module_param(ft_tracing, int, 0644); | ||
136 | MODULE_PARM_DESC(ft_tracing, | ||
137 | "Amount of debugging output, 0 <= tracing <= 8, default 3."); | ||
138 | #endif | ||
139 | |||
140 | MODULE_AUTHOR( | ||
141 | "(c) 1993-1996 Bas Laarhoven (bas@vimec.nl), " | ||
142 | "(c) 1995-1996 Kai Harrekilde-Petersen (khp@dolphinics.no), " | ||
143 | "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)"); | ||
144 | MODULE_DESCRIPTION( | ||
145 | "QIC-117 driver for QIC-40/80/3010/3020 floppy tape drives."); | ||
146 | MODULE_LICENSE("GPL"); | ||
147 | |||
148 | static void __exit ftape_exit(void) | ||
149 | { | ||
150 | TRACE_FUN(ft_t_flow); | ||
151 | |||
152 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) | ||
153 | ftape_proc_destroy(); | ||
154 | #endif | ||
155 | (void)ftape_set_nr_buffers(0); | ||
156 | printk(KERN_INFO "ftape: unloaded.\n"); | ||
157 | TRACE_EXIT; | ||
158 | } | ||
159 | |||
160 | module_init(ftape_init); | ||
161 | module_exit(ftape_exit); | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-init.h b/drivers/char/ftape/lowlevel/ftape-init.h new file mode 100644 index 000000000000..99a7b8ab086f --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-init.h | |||
@@ -0,0 +1,43 @@ | |||
1 | #ifndef _FTAPE_INIT_H | ||
2 | #define _FTAPE_INIT_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-init.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:16 $ | ||
26 | * | ||
27 | * This file contains the definitions for the interface to | ||
28 | * the Linux kernel for floppy tape driver ftape. | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/linkage.h> | ||
33 | #include <linux/signal.h> | ||
34 | |||
35 | #define _NEVER_BLOCK (sigmask(SIGKILL) | sigmask(SIGSTOP)) | ||
36 | #define _DONT_BLOCK (_NEVER_BLOCK | sigmask(SIGINT)) | ||
37 | #define _DO_BLOCK (sigmask(SIGPIPE)) | ||
38 | |||
39 | #ifndef QIC117_TAPE_MAJOR | ||
40 | #define QIC117_TAPE_MAJOR 27 | ||
41 | #endif | ||
42 | |||
43 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-io.c b/drivers/char/ftape/lowlevel/ftape-io.c new file mode 100644 index 000000000000..259015aeff55 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-io.c | |||
@@ -0,0 +1,992 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996 Kai Harrekilde-Petersen, | ||
4 | * (C) 1997 Claus-Justus Heine. | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2, or (at your option) | ||
9 | any later version. | ||
10 | |||
11 | This program is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; see the file COPYING. If not, write to | ||
18 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | |||
20 | * | ||
21 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.c,v $ | ||
22 | * $Revision: 1.4 $ | ||
23 | * $Date: 1997/11/11 14:02:36 $ | ||
24 | * | ||
25 | * This file contains the general control functions for the | ||
26 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
27 | */ | ||
28 | |||
29 | #include <linux/errno.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <linux/ioctl.h> | ||
34 | #include <linux/mtio.h> | ||
35 | #include <linux/delay.h> | ||
36 | |||
37 | #include <linux/ftape.h> | ||
38 | #include <linux/qic117.h> | ||
39 | #include "../lowlevel/ftape-tracing.h" | ||
40 | #include "../lowlevel/fdc-io.h" | ||
41 | #include "../lowlevel/ftape-io.h" | ||
42 | #include "../lowlevel/ftape-ctl.h" | ||
43 | #include "../lowlevel/ftape-rw.h" | ||
44 | #include "../lowlevel/ftape-write.h" | ||
45 | #include "../lowlevel/ftape-read.h" | ||
46 | #include "../lowlevel/ftape-init.h" | ||
47 | #include "../lowlevel/ftape-calibr.h" | ||
48 | |||
49 | /* Global vars. | ||
50 | */ | ||
51 | /* NOTE: sectors start numbering at 1, all others at 0 ! */ | ||
52 | ft_timeout_table ftape_timeout; | ||
53 | unsigned int ftape_tape_len; | ||
54 | volatile qic117_cmd_t ftape_current_command; | ||
55 | const struct qic117_command_table qic117_cmds[] = QIC117_COMMANDS; | ||
56 | int ftape_might_be_off_track; | ||
57 | |||
58 | /* Local vars. | ||
59 | */ | ||
60 | static int diagnostic_mode; | ||
61 | static unsigned int ftape_udelay_count; | ||
62 | static unsigned int ftape_udelay_time; | ||
63 | |||
64 | void ftape_udelay(unsigned int usecs) | ||
65 | { | ||
66 | volatile int count = (ftape_udelay_count * usecs + | ||
67 | ftape_udelay_count - 1) / ftape_udelay_time; | ||
68 | volatile int i; | ||
69 | |||
70 | while (count-- > 0) { | ||
71 | for (i = 0; i < 20; ++i); | ||
72 | } | ||
73 | } | ||
74 | |||
75 | void ftape_udelay_calibrate(void) | ||
76 | { | ||
77 | ftape_calibrate("ftape_udelay", | ||
78 | ftape_udelay, &ftape_udelay_count, &ftape_udelay_time); | ||
79 | } | ||
80 | |||
81 | /* Delay (msec) routine. | ||
82 | */ | ||
83 | void ftape_sleep(unsigned int time) | ||
84 | { | ||
85 | TRACE_FUN(ft_t_any); | ||
86 | |||
87 | time *= 1000; /* msecs -> usecs */ | ||
88 | if (time < FT_USPT) { | ||
89 | /* Time too small for scheduler, do a busy wait ! */ | ||
90 | ftape_udelay(time); | ||
91 | } else { | ||
92 | long timeout; | ||
93 | unsigned long flags; | ||
94 | unsigned int ticks = (time + FT_USPT - 1) / FT_USPT; | ||
95 | |||
96 | TRACE(ft_t_any, "%d msec, %d ticks", time/1000, ticks); | ||
97 | timeout = ticks; | ||
98 | save_flags(flags); | ||
99 | sti(); | ||
100 | msleep_interruptible(jiffies_to_msecs(timeout)); | ||
101 | /* Mmm. Isn't current->blocked == 0xffffffff ? | ||
102 | */ | ||
103 | if (signal_pending(current)) { | ||
104 | TRACE(ft_t_err, "awoken by non-blocked signal :-("); | ||
105 | } | ||
106 | restore_flags(flags); | ||
107 | } | ||
108 | TRACE_EXIT; | ||
109 | } | ||
110 | |||
111 | /* send a command or parameter to the drive | ||
112 | * Generates # of step pulses. | ||
113 | */ | ||
114 | static inline int ft_send_to_drive(int arg) | ||
115 | { | ||
116 | /* Always wait for a command_timeout period to separate | ||
117 | * individuals commands and/or parameters. | ||
118 | */ | ||
119 | ftape_sleep(3 * FT_MILLISECOND); | ||
120 | /* Keep cylinder nr within range, step towards home if possible. | ||
121 | */ | ||
122 | if (ftape_current_cylinder >= arg) { | ||
123 | return fdc_seek(ftape_current_cylinder - arg); | ||
124 | } else { | ||
125 | return fdc_seek(ftape_current_cylinder + arg); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /* forward */ int ftape_report_raw_drive_status(int *status); | ||
130 | |||
131 | static int ft_check_cmd_restrictions(qic117_cmd_t command) | ||
132 | { | ||
133 | int status = -1; | ||
134 | TRACE_FUN(ft_t_any); | ||
135 | |||
136 | TRACE(ft_t_flow, "%s", qic117_cmds[command].name); | ||
137 | /* A new motion command during an uninterruptible (motion) | ||
138 | * command requires a ready status before the new command can | ||
139 | * be issued. Otherwise a new motion command needs to be | ||
140 | * checked against required status. | ||
141 | */ | ||
142 | if (qic117_cmds[command].cmd_type == motion && | ||
143 | qic117_cmds[ftape_current_command].non_intr) { | ||
144 | ftape_report_raw_drive_status(&status); | ||
145 | if ((status & QIC_STATUS_READY) == 0) { | ||
146 | TRACE(ft_t_noise, | ||
147 | "motion cmd (%d) during non-intr cmd (%d)", | ||
148 | command, ftape_current_command); | ||
149 | TRACE(ft_t_noise, "waiting until drive gets ready"); | ||
150 | ftape_ready_wait(ftape_timeout.seek, | ||
151 | &status); | ||
152 | } | ||
153 | } | ||
154 | if (qic117_cmds[command].mask != 0) { | ||
155 | __u8 difference; | ||
156 | /* Some commands do require a certain status: | ||
157 | */ | ||
158 | if (status == -1) { /* not yet set */ | ||
159 | ftape_report_raw_drive_status(&status); | ||
160 | } | ||
161 | difference = ((status ^ qic117_cmds[command].state) & | ||
162 | qic117_cmds[command].mask); | ||
163 | /* Wait until the drive gets | ||
164 | * ready. This may last forever if | ||
165 | * the drive never gets ready... | ||
166 | */ | ||
167 | while ((difference & QIC_STATUS_READY) != 0) { | ||
168 | TRACE(ft_t_noise, "command %d issued while not ready", | ||
169 | command); | ||
170 | TRACE(ft_t_noise, "waiting until drive gets ready"); | ||
171 | if (ftape_ready_wait(ftape_timeout.seek, | ||
172 | &status) == -EINTR) { | ||
173 | /* Bail out on signal ! | ||
174 | */ | ||
175 | TRACE_ABORT(-EINTR, ft_t_warn, | ||
176 | "interrupted by non-blockable signal"); | ||
177 | } | ||
178 | difference = ((status ^ qic117_cmds[command].state) & | ||
179 | qic117_cmds[command].mask); | ||
180 | } | ||
181 | while ((difference & QIC_STATUS_ERROR) != 0) { | ||
182 | int err; | ||
183 | qic117_cmd_t cmd; | ||
184 | |||
185 | TRACE(ft_t_noise, | ||
186 | "command %d issued while error pending", | ||
187 | command); | ||
188 | TRACE(ft_t_noise, "clearing error status"); | ||
189 | ftape_report_error(&err, &cmd, 1); | ||
190 | ftape_report_raw_drive_status(&status); | ||
191 | difference = ((status ^ qic117_cmds[command].state) & | ||
192 | qic117_cmds[command].mask); | ||
193 | if ((difference & QIC_STATUS_ERROR) != 0) { | ||
194 | /* Bail out on fatal signal ! | ||
195 | */ | ||
196 | FT_SIGNAL_EXIT(_NEVER_BLOCK); | ||
197 | } | ||
198 | } | ||
199 | if (difference) { | ||
200 | /* Any remaining difference can't be solved | ||
201 | * here. | ||
202 | */ | ||
203 | if (difference & (QIC_STATUS_CARTRIDGE_PRESENT | | ||
204 | QIC_STATUS_NEW_CARTRIDGE | | ||
205 | QIC_STATUS_REFERENCED)) { | ||
206 | TRACE(ft_t_warn, | ||
207 | "Fatal: tape removed or reinserted !"); | ||
208 | ft_failure = 1; | ||
209 | } else { | ||
210 | TRACE(ft_t_err, "wrong state: 0x%02x should be: 0x%02x", | ||
211 | status & qic117_cmds[command].mask, | ||
212 | qic117_cmds[command].state); | ||
213 | } | ||
214 | TRACE_EXIT -EIO; | ||
215 | } | ||
216 | if (~status & QIC_STATUS_READY & qic117_cmds[command].mask) { | ||
217 | TRACE_ABORT(-EBUSY, ft_t_err, "Bad: still busy!"); | ||
218 | } | ||
219 | } | ||
220 | TRACE_EXIT 0; | ||
221 | } | ||
222 | |||
223 | /* Issue a tape command: | ||
224 | */ | ||
225 | int ftape_command(qic117_cmd_t command) | ||
226 | { | ||
227 | int result = 0; | ||
228 | static int level; | ||
229 | TRACE_FUN(ft_t_any); | ||
230 | |||
231 | if ((unsigned int)command > NR_ITEMS(qic117_cmds)) { | ||
232 | /* This is a bug we'll want to know about too. | ||
233 | */ | ||
234 | TRACE_ABORT(-EIO, ft_t_bug, "bug - bad command: %d", command); | ||
235 | } | ||
236 | if (++level > 5) { /* This is a bug we'll want to know about. */ | ||
237 | --level; | ||
238 | TRACE_ABORT(-EIO, ft_t_bug, "bug - recursion for command: %d", | ||
239 | command); | ||
240 | } | ||
241 | /* disable logging and restriction check for some commands, | ||
242 | * check all other commands that have a prescribed starting | ||
243 | * status. | ||
244 | */ | ||
245 | if (diagnostic_mode) { | ||
246 | TRACE(ft_t_flow, "diagnostic command %d", command); | ||
247 | } else if (command == QIC_REPORT_DRIVE_STATUS || | ||
248 | command == QIC_REPORT_NEXT_BIT) { | ||
249 | TRACE(ft_t_any, "%s", qic117_cmds[command].name); | ||
250 | } else { | ||
251 | TRACE_CATCH(ft_check_cmd_restrictions(command), --level); | ||
252 | } | ||
253 | /* Now all conditions are met or result was < 0. | ||
254 | */ | ||
255 | result = ft_send_to_drive((unsigned int)command); | ||
256 | if (qic117_cmds[command].cmd_type == motion && | ||
257 | command != QIC_LOGICAL_FORWARD && command != QIC_STOP_TAPE) { | ||
258 | ft_location.known = 0; | ||
259 | } | ||
260 | ftape_current_command = command; | ||
261 | --level; | ||
262 | TRACE_EXIT result; | ||
263 | } | ||
264 | |||
265 | /* Send a tape command parameter: | ||
266 | * Generates command # of step pulses. | ||
267 | * Skips tape-status call ! | ||
268 | */ | ||
269 | int ftape_parameter(unsigned int parameter) | ||
270 | { | ||
271 | TRACE_FUN(ft_t_any); | ||
272 | |||
273 | TRACE(ft_t_flow, "called with parameter = %d", parameter); | ||
274 | TRACE_EXIT ft_send_to_drive(parameter + 2); | ||
275 | } | ||
276 | |||
277 | /* Wait for the drive to get ready. | ||
278 | * timeout time in milli-seconds | ||
279 | * Returned status is valid if result != -EIO | ||
280 | * | ||
281 | * Should we allow to be killed by SIGINT? (^C) | ||
282 | * Would be nice at least for large timeouts. | ||
283 | */ | ||
284 | int ftape_ready_wait(unsigned int timeout, int *status) | ||
285 | { | ||
286 | unsigned long t0; | ||
287 | unsigned int poll_delay; | ||
288 | int signal_retries; | ||
289 | TRACE_FUN(ft_t_any); | ||
290 | |||
291 | /* the following ** REALLY ** reduces the system load when | ||
292 | * e.g. one simply rewinds or retensions. The tape is slow | ||
293 | * anyway. It is really not necessary to detect error | ||
294 | * conditions with 1/10 seconds granularity | ||
295 | * | ||
296 | * On my AMD 133MHZ 486: 100 ms: 23% system load | ||
297 | * 1 sec: 5% | ||
298 | * 5 sec: 0.6%, yeah | ||
299 | */ | ||
300 | if (timeout <= FT_SECOND) { | ||
301 | poll_delay = 100 * FT_MILLISECOND; | ||
302 | signal_retries = 20; /* two seconds */ | ||
303 | } else if (timeout < 20 * FT_SECOND) { | ||
304 | TRACE(ft_t_flow, "setting poll delay to 1 second"); | ||
305 | poll_delay = FT_SECOND; | ||
306 | signal_retries = 2; /* two seconds */ | ||
307 | } else { | ||
308 | TRACE(ft_t_flow, "setting poll delay to 5 seconds"); | ||
309 | poll_delay = 5 * FT_SECOND; | ||
310 | signal_retries = 1; /* five seconds */ | ||
311 | } | ||
312 | for (;;) { | ||
313 | t0 = jiffies; | ||
314 | TRACE_CATCH(ftape_report_raw_drive_status(status),); | ||
315 | if (*status & QIC_STATUS_READY) { | ||
316 | TRACE_EXIT 0; | ||
317 | } | ||
318 | if (!signal_retries--) { | ||
319 | FT_SIGNAL_EXIT(_NEVER_BLOCK); | ||
320 | } | ||
321 | if ((int)timeout >= 0) { | ||
322 | /* this will fail when jiffies wraps around about | ||
323 | * once every year :-) | ||
324 | */ | ||
325 | timeout -= ((jiffies - t0) * FT_SECOND) / HZ; | ||
326 | if (timeout <= 0) { | ||
327 | TRACE_ABORT(-ETIME, ft_t_err, "timeout"); | ||
328 | } | ||
329 | ftape_sleep(poll_delay); | ||
330 | timeout -= poll_delay; | ||
331 | } else { | ||
332 | ftape_sleep(poll_delay); | ||
333 | } | ||
334 | } | ||
335 | TRACE_EXIT -ETIME; | ||
336 | } | ||
337 | |||
338 | /* Issue command and wait up to timeout milli seconds for drive ready | ||
339 | */ | ||
340 | int ftape_command_wait(qic117_cmd_t command, unsigned int timeout, int *status) | ||
341 | { | ||
342 | int result; | ||
343 | |||
344 | /* Drive should be ready, issue command | ||
345 | */ | ||
346 | result = ftape_command(command); | ||
347 | if (result >= 0) { | ||
348 | result = ftape_ready_wait(timeout, status); | ||
349 | } | ||
350 | return result; | ||
351 | } | ||
352 | |||
353 | static int ftape_parameter_wait(unsigned int parm, unsigned int timeout, int *status) | ||
354 | { | ||
355 | int result; | ||
356 | |||
357 | /* Drive should be ready, issue command | ||
358 | */ | ||
359 | result = ftape_parameter(parm); | ||
360 | if (result >= 0) { | ||
361 | result = ftape_ready_wait(timeout, status); | ||
362 | } | ||
363 | return result; | ||
364 | } | ||
365 | |||
366 | /*-------------------------------------------------------------------------- | ||
367 | * Report operations | ||
368 | */ | ||
369 | |||
370 | /* Query the drive about its status. The command is sent and | ||
371 | result_length bits of status are returned (2 extra bits are read | ||
372 | for start and stop). */ | ||
373 | |||
374 | int ftape_report_operation(int *status, | ||
375 | qic117_cmd_t command, | ||
376 | int result_length) | ||
377 | { | ||
378 | int i, st3; | ||
379 | unsigned int t0; | ||
380 | unsigned int dt; | ||
381 | TRACE_FUN(ft_t_any); | ||
382 | |||
383 | TRACE_CATCH(ftape_command(command),); | ||
384 | t0 = ftape_timestamp(); | ||
385 | i = 0; | ||
386 | do { | ||
387 | ++i; | ||
388 | ftape_sleep(3 * FT_MILLISECOND); /* see remark below */ | ||
389 | TRACE_CATCH(fdc_sense_drive_status(&st3),); | ||
390 | dt = ftape_timediff(t0, ftape_timestamp()); | ||
391 | /* Ack should be asserted within Ttimout + Tack = 6 msec. | ||
392 | * Looks like some drives fail to do this so extend this | ||
393 | * period to 300 msec. | ||
394 | */ | ||
395 | } while (!(st3 & ST3_TRACK_0) && dt < 300000); | ||
396 | if (!(st3 & ST3_TRACK_0)) { | ||
397 | TRACE(ft_t_err, | ||
398 | "No acknowledge after %u msec. (%i iter)", dt / 1000, i); | ||
399 | TRACE_ABORT(-EIO, ft_t_err, "timeout on Acknowledge"); | ||
400 | } | ||
401 | /* dt may be larger than expected because of other tasks | ||
402 | * scheduled while we were sleeping. | ||
403 | */ | ||
404 | if (i > 1 && dt > 6000) { | ||
405 | TRACE(ft_t_err, "Acknowledge after %u msec. (%i iter)", | ||
406 | dt / 1000, i); | ||
407 | } | ||
408 | *status = 0; | ||
409 | for (i = 0; i < result_length + 1; i++) { | ||
410 | TRACE_CATCH(ftape_command(QIC_REPORT_NEXT_BIT),); | ||
411 | TRACE_CATCH(fdc_sense_drive_status(&st3),); | ||
412 | if (i < result_length) { | ||
413 | *status |= ((st3 & ST3_TRACK_0) ? 1 : 0) << i; | ||
414 | } else if ((st3 & ST3_TRACK_0) == 0) { | ||
415 | TRACE_ABORT(-EIO, ft_t_err, "missing status stop bit"); | ||
416 | } | ||
417 | } | ||
418 | /* this command will put track zero and index back into normal state */ | ||
419 | (void)ftape_command(QIC_REPORT_NEXT_BIT); | ||
420 | TRACE_EXIT 0; | ||
421 | } | ||
422 | |||
423 | /* Report the current drive status. */ | ||
424 | |||
425 | int ftape_report_raw_drive_status(int *status) | ||
426 | { | ||
427 | int result; | ||
428 | int count = 0; | ||
429 | TRACE_FUN(ft_t_any); | ||
430 | |||
431 | do { | ||
432 | result = ftape_report_operation(status, | ||
433 | QIC_REPORT_DRIVE_STATUS, 8); | ||
434 | } while (result < 0 && ++count <= 3); | ||
435 | if (result < 0) { | ||
436 | TRACE_ABORT(-EIO, ft_t_err, | ||
437 | "report_operation failed after %d trials", count); | ||
438 | } | ||
439 | if ((*status & 0xff) == 0xff) { | ||
440 | TRACE_ABORT(-EIO, ft_t_err, | ||
441 | "impossible drive status 0xff"); | ||
442 | } | ||
443 | if (*status & QIC_STATUS_READY) { | ||
444 | ftape_current_command = QIC_NO_COMMAND; /* completed */ | ||
445 | } | ||
446 | ft_last_status.status.drive_status = (__u8)(*status & 0xff); | ||
447 | TRACE_EXIT 0; | ||
448 | } | ||
449 | |||
450 | int ftape_report_drive_status(int *status) | ||
451 | { | ||
452 | TRACE_FUN(ft_t_any); | ||
453 | |||
454 | TRACE_CATCH(ftape_report_raw_drive_status(status),); | ||
455 | if (*status & QIC_STATUS_NEW_CARTRIDGE || | ||
456 | !(*status & QIC_STATUS_CARTRIDGE_PRESENT)) { | ||
457 | ft_failure = 1; /* will inhibit further operations */ | ||
458 | TRACE_EXIT -EIO; | ||
459 | } | ||
460 | if (*status & QIC_STATUS_READY && *status & QIC_STATUS_ERROR) { | ||
461 | /* Let caller handle all errors */ | ||
462 | TRACE_ABORT(1, ft_t_warn, "warning: error status set!"); | ||
463 | } | ||
464 | TRACE_EXIT 0; | ||
465 | } | ||
466 | |||
467 | int ftape_report_error(unsigned int *error, | ||
468 | qic117_cmd_t *command, int report) | ||
469 | { | ||
470 | static const ftape_error ftape_errors[] = QIC117_ERRORS; | ||
471 | int code; | ||
472 | TRACE_FUN(ft_t_any); | ||
473 | |||
474 | TRACE_CATCH(ftape_report_operation(&code, QIC_REPORT_ERROR_CODE, 16),); | ||
475 | *error = (unsigned int)(code & 0xff); | ||
476 | *command = (qic117_cmd_t)((code>>8)&0xff); | ||
477 | /* remember hardware status, maybe useful for status ioctls | ||
478 | */ | ||
479 | ft_last_error.error.command = (__u8)*command; | ||
480 | ft_last_error.error.error = (__u8)*error; | ||
481 | if (!report) { | ||
482 | TRACE_EXIT 0; | ||
483 | } | ||
484 | if (*error == 0) { | ||
485 | TRACE_ABORT(0, ft_t_info, "No error"); | ||
486 | } | ||
487 | TRACE(ft_t_info, "errorcode: %d", *error); | ||
488 | if (*error < NR_ITEMS(ftape_errors)) { | ||
489 | TRACE(ft_t_noise, "%sFatal ERROR:", | ||
490 | (ftape_errors[*error].fatal ? "" : "Non-")); | ||
491 | TRACE(ft_t_noise, "%s ...", ftape_errors[*error].message); | ||
492 | } else { | ||
493 | TRACE(ft_t_noise, "Unknown ERROR !"); | ||
494 | } | ||
495 | if ((unsigned int)*command < NR_ITEMS(qic117_cmds) && | ||
496 | qic117_cmds[*command].name != NULL) { | ||
497 | TRACE(ft_t_noise, "... caused by command \'%s\'", | ||
498 | qic117_cmds[*command].name); | ||
499 | } else { | ||
500 | TRACE(ft_t_noise, "... caused by unknown command %d", | ||
501 | *command); | ||
502 | } | ||
503 | TRACE_EXIT 0; | ||
504 | } | ||
505 | |||
506 | int ftape_report_configuration(qic_model *model, | ||
507 | unsigned int *rate, | ||
508 | int *qic_std, | ||
509 | int *tape_len) | ||
510 | { | ||
511 | int result; | ||
512 | int config; | ||
513 | int status; | ||
514 | static const unsigned int qic_rates[ 4] = { 250, 2000, 500, 1000 }; | ||
515 | TRACE_FUN(ft_t_any); | ||
516 | |||
517 | result = ftape_report_operation(&config, | ||
518 | QIC_REPORT_DRIVE_CONFIGURATION, 8); | ||
519 | if (result < 0) { | ||
520 | ft_last_status.status.drive_config = (__u8)0x00; | ||
521 | *model = prehistoric; | ||
522 | *rate = 500; | ||
523 | *qic_std = QIC_TAPE_QIC40; | ||
524 | *tape_len = 205; | ||
525 | TRACE_EXIT 0; | ||
526 | } else { | ||
527 | ft_last_status.status.drive_config = (__u8)(config & 0xff); | ||
528 | } | ||
529 | *rate = qic_rates[(config & QIC_CONFIG_RATE_MASK) >> QIC_CONFIG_RATE_SHIFT]; | ||
530 | result = ftape_report_operation(&status, QIC_REPORT_TAPE_STATUS, 8); | ||
531 | if (result < 0) { | ||
532 | ft_last_status.status.tape_status = (__u8)0x00; | ||
533 | /* pre- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is valid. | ||
534 | */ | ||
535 | *qic_std = (config & QIC_CONFIG_80) ? | ||
536 | QIC_TAPE_QIC80 : QIC_TAPE_QIC40; | ||
537 | /* ?? how's about 425ft tapes? */ | ||
538 | *tape_len = (config & QIC_CONFIG_LONG) ? 307 : 0; | ||
539 | *model = pre_qic117c; | ||
540 | result = 0; | ||
541 | } else { | ||
542 | ft_last_status.status.tape_status = (__u8)(status & 0xff); | ||
543 | *model = post_qic117b; | ||
544 | TRACE(ft_t_any, "report tape status result = %02x", status); | ||
545 | /* post- QIC117 rev C spec. drive, QIC_CONFIG_80 bit is | ||
546 | * invalid. | ||
547 | */ | ||
548 | switch (status & QIC_TAPE_STD_MASK) { | ||
549 | case QIC_TAPE_QIC40: | ||
550 | case QIC_TAPE_QIC80: | ||
551 | case QIC_TAPE_QIC3020: | ||
552 | case QIC_TAPE_QIC3010: | ||
553 | *qic_std = status & QIC_TAPE_STD_MASK; | ||
554 | break; | ||
555 | default: | ||
556 | *qic_std = -1; | ||
557 | break; | ||
558 | } | ||
559 | switch (status & QIC_TAPE_LEN_MASK) { | ||
560 | case QIC_TAPE_205FT: | ||
561 | /* 205 or 425+ ft 550 Oe tape */ | ||
562 | *tape_len = 0; | ||
563 | break; | ||
564 | case QIC_TAPE_307FT: | ||
565 | /* 307.5 ft 550 Oe Extended Length (XL) tape */ | ||
566 | *tape_len = 307; | ||
567 | break; | ||
568 | case QIC_TAPE_VARIABLE: | ||
569 | /* Variable length 550 Oe tape */ | ||
570 | *tape_len = 0; | ||
571 | break; | ||
572 | case QIC_TAPE_1100FT: | ||
573 | /* 1100 ft 550 Oe tape */ | ||
574 | *tape_len = 1100; | ||
575 | break; | ||
576 | case QIC_TAPE_FLEX: | ||
577 | /* Variable length 900 Oe tape */ | ||
578 | *tape_len = 0; | ||
579 | break; | ||
580 | default: | ||
581 | *tape_len = -1; | ||
582 | break; | ||
583 | } | ||
584 | if (*qic_std == -1 || *tape_len == -1) { | ||
585 | TRACE(ft_t_any, | ||
586 | "post qic-117b spec drive with unknown tape"); | ||
587 | } | ||
588 | result = *tape_len == -1 ? -EIO : 0; | ||
589 | if (status & QIC_TAPE_WIDE) { | ||
590 | switch (*qic_std) { | ||
591 | case QIC_TAPE_QIC80: | ||
592 | TRACE(ft_t_info, "TR-1 tape detected"); | ||
593 | break; | ||
594 | case QIC_TAPE_QIC3010: | ||
595 | TRACE(ft_t_info, "TR-2 tape detected"); | ||
596 | break; | ||
597 | case QIC_TAPE_QIC3020: | ||
598 | TRACE(ft_t_info, "TR-3 tape detected"); | ||
599 | break; | ||
600 | default: | ||
601 | TRACE(ft_t_warn, | ||
602 | "Unknown Travan tape type detected"); | ||
603 | break; | ||
604 | } | ||
605 | } | ||
606 | } | ||
607 | TRACE_EXIT (result < 0) ? -EIO : 0; | ||
608 | } | ||
609 | |||
610 | static int ftape_report_rom_version(int *version) | ||
611 | { | ||
612 | |||
613 | if (ftape_report_operation(version, QIC_REPORT_ROM_VERSION, 8) < 0) { | ||
614 | return -EIO; | ||
615 | } else { | ||
616 | return 0; | ||
617 | } | ||
618 | } | ||
619 | |||
620 | void ftape_report_vendor_id(unsigned int *id) | ||
621 | { | ||
622 | int result; | ||
623 | TRACE_FUN(ft_t_any); | ||
624 | |||
625 | /* We'll try to get a vendor id from the drive. First | ||
626 | * according to the QIC-117 spec, a 16-bit id is requested. | ||
627 | * If that fails we'll try an 8-bit version, otherwise we'll | ||
628 | * try an undocumented query. | ||
629 | */ | ||
630 | result = ftape_report_operation((int *) id, QIC_REPORT_VENDOR_ID, 16); | ||
631 | if (result < 0) { | ||
632 | result = ftape_report_operation((int *) id, | ||
633 | QIC_REPORT_VENDOR_ID, 8); | ||
634 | if (result < 0) { | ||
635 | /* The following is an undocumented call found | ||
636 | * in the CMS code. | ||
637 | */ | ||
638 | result = ftape_report_operation((int *) id, 24, 8); | ||
639 | if (result < 0) { | ||
640 | *id = UNKNOWN_VENDOR; | ||
641 | } else { | ||
642 | TRACE(ft_t_noise, "got old 8 bit id: %04x", | ||
643 | *id); | ||
644 | *id |= 0x20000; | ||
645 | } | ||
646 | } else { | ||
647 | TRACE(ft_t_noise, "got 8 bit id: %04x", *id); | ||
648 | *id |= 0x10000; | ||
649 | } | ||
650 | } else { | ||
651 | TRACE(ft_t_noise, "got 16 bit id: %04x", *id); | ||
652 | } | ||
653 | if (*id == 0x0047) { | ||
654 | int version; | ||
655 | int sign; | ||
656 | |||
657 | if (ftape_report_rom_version(&version) < 0) { | ||
658 | TRACE(ft_t_bug, "report rom version failed"); | ||
659 | TRACE_EXIT; | ||
660 | } | ||
661 | TRACE(ft_t_noise, "CMS rom version: %d", version); | ||
662 | ftape_command(QIC_ENTER_DIAGNOSTIC_1); | ||
663 | ftape_command(QIC_ENTER_DIAGNOSTIC_1); | ||
664 | diagnostic_mode = 1; | ||
665 | if (ftape_report_operation(&sign, 9, 8) < 0) { | ||
666 | unsigned int error; | ||
667 | qic117_cmd_t command; | ||
668 | |||
669 | ftape_report_error(&error, &command, 1); | ||
670 | ftape_command(QIC_ENTER_PRIMARY_MODE); | ||
671 | diagnostic_mode = 0; | ||
672 | TRACE_EXIT; /* failure ! */ | ||
673 | } else { | ||
674 | TRACE(ft_t_noise, "CMS signature: %02x", sign); | ||
675 | } | ||
676 | if (sign == 0xa5) { | ||
677 | result = ftape_report_operation(&sign, 37, 8); | ||
678 | if (result < 0) { | ||
679 | if (version >= 63) { | ||
680 | *id = 0x8880; | ||
681 | TRACE(ft_t_noise, | ||
682 | "This is an Iomega drive !"); | ||
683 | } else { | ||
684 | *id = 0x0047; | ||
685 | TRACE(ft_t_noise, | ||
686 | "This is a real CMS drive !"); | ||
687 | } | ||
688 | } else { | ||
689 | *id = 0x0047; | ||
690 | TRACE(ft_t_noise, "CMS status: %d", sign); | ||
691 | } | ||
692 | } else { | ||
693 | *id = UNKNOWN_VENDOR; | ||
694 | } | ||
695 | ftape_command(QIC_ENTER_PRIMARY_MODE); | ||
696 | diagnostic_mode = 0; | ||
697 | } | ||
698 | TRACE_EXIT; | ||
699 | } | ||
700 | |||
701 | static int qic_rate_code(unsigned int rate) | ||
702 | { | ||
703 | switch (rate) { | ||
704 | case 250: | ||
705 | return QIC_CONFIG_RATE_250; | ||
706 | case 500: | ||
707 | return QIC_CONFIG_RATE_500; | ||
708 | case 1000: | ||
709 | return QIC_CONFIG_RATE_1000; | ||
710 | case 2000: | ||
711 | return QIC_CONFIG_RATE_2000; | ||
712 | default: | ||
713 | return QIC_CONFIG_RATE_500; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | static int ftape_set_rate_test(unsigned int *max_rate) | ||
718 | { | ||
719 | unsigned int error; | ||
720 | qic117_cmd_t command; | ||
721 | int status; | ||
722 | int supported = 0; | ||
723 | TRACE_FUN(ft_t_any); | ||
724 | |||
725 | /* Check if the drive does support the select rate command | ||
726 | * by testing all different settings. If any one is accepted | ||
727 | * we assume the command is supported, else not. | ||
728 | */ | ||
729 | for (*max_rate = 2000; *max_rate >= 250; *max_rate /= 2) { | ||
730 | if (ftape_command(QIC_SELECT_RATE) < 0) { | ||
731 | continue; | ||
732 | } | ||
733 | if (ftape_parameter_wait(qic_rate_code(*max_rate), | ||
734 | 1 * FT_SECOND, &status) < 0) { | ||
735 | continue; | ||
736 | } | ||
737 | if (status & QIC_STATUS_ERROR) { | ||
738 | ftape_report_error(&error, &command, 0); | ||
739 | continue; | ||
740 | } | ||
741 | supported = 1; /* did accept a request */ | ||
742 | break; | ||
743 | } | ||
744 | TRACE(ft_t_noise, "Select Rate command is%s supported", | ||
745 | supported ? "" : " not"); | ||
746 | TRACE_EXIT supported; | ||
747 | } | ||
748 | |||
749 | int ftape_set_data_rate(unsigned int new_rate /* Kbps */, unsigned int qic_std) | ||
750 | { | ||
751 | int status; | ||
752 | int result = 0; | ||
753 | unsigned int data_rate = new_rate; | ||
754 | static int supported; | ||
755 | int rate_changed = 0; | ||
756 | qic_model dummy_model; | ||
757 | unsigned int dummy_qic_std, dummy_tape_len; | ||
758 | TRACE_FUN(ft_t_any); | ||
759 | |||
760 | if (ft_drive_max_rate == 0) { /* first time */ | ||
761 | supported = ftape_set_rate_test(&ft_drive_max_rate); | ||
762 | } | ||
763 | if (supported) { | ||
764 | ftape_command(QIC_SELECT_RATE); | ||
765 | result = ftape_parameter_wait(qic_rate_code(new_rate), | ||
766 | 1 * FT_SECOND, &status); | ||
767 | if (result >= 0 && !(status & QIC_STATUS_ERROR)) { | ||
768 | rate_changed = 1; | ||
769 | } | ||
770 | } | ||
771 | TRACE_CATCH(result = ftape_report_configuration(&dummy_model, | ||
772 | &data_rate, | ||
773 | &dummy_qic_std, | ||
774 | &dummy_tape_len),); | ||
775 | if (data_rate != new_rate) { | ||
776 | if (!supported) { | ||
777 | TRACE(ft_t_warn, "Rate change not supported!"); | ||
778 | } else if (rate_changed) { | ||
779 | TRACE(ft_t_warn, "Requested: %d, got %d", | ||
780 | new_rate, data_rate); | ||
781 | } else { | ||
782 | TRACE(ft_t_warn, "Rate change failed!"); | ||
783 | } | ||
784 | result = -EINVAL; | ||
785 | } | ||
786 | /* | ||
787 | * Set data rate and write precompensation as specified: | ||
788 | * | ||
789 | * | QIC-40/80 | QIC-3010/3020 | ||
790 | * rate | precomp | precomp | ||
791 | * ----------+-------------+-------------- | ||
792 | * 250 Kbps. | 250 ns. | 0 ns. | ||
793 | * 500 Kbps. | 125 ns. | 0 ns. | ||
794 | * 1 Mbps. | 42 ns. | 0 ns. | ||
795 | * 2 Mbps | N/A | 0 ns. | ||
796 | */ | ||
797 | if ((qic_std == QIC_TAPE_QIC40 && data_rate > 500) || | ||
798 | (qic_std == QIC_TAPE_QIC80 && data_rate > 1000)) { | ||
799 | TRACE_ABORT(-EINVAL, | ||
800 | ft_t_warn, "Datarate too high for QIC-mode"); | ||
801 | } | ||
802 | TRACE_CATCH(fdc_set_data_rate(data_rate),_res = -EINVAL); | ||
803 | ft_data_rate = data_rate; | ||
804 | if (qic_std == QIC_TAPE_QIC40 || qic_std == QIC_TAPE_QIC80) { | ||
805 | switch (data_rate) { | ||
806 | case 250: | ||
807 | fdc_set_write_precomp(250); | ||
808 | break; | ||
809 | default: | ||
810 | case 500: | ||
811 | fdc_set_write_precomp(125); | ||
812 | break; | ||
813 | case 1000: | ||
814 | fdc_set_write_precomp(42); | ||
815 | break; | ||
816 | } | ||
817 | } else { | ||
818 | fdc_set_write_precomp(0); | ||
819 | } | ||
820 | TRACE_EXIT result; | ||
821 | } | ||
822 | |||
823 | /* The next two functions are used to cope with excessive overrun errors | ||
824 | */ | ||
825 | int ftape_increase_threshold(void) | ||
826 | { | ||
827 | TRACE_FUN(ft_t_flow); | ||
828 | |||
829 | if (fdc.type < i82077 || ft_fdc_threshold >= 12) { | ||
830 | TRACE_ABORT(-EIO, ft_t_err, "cannot increase fifo threshold"); | ||
831 | } | ||
832 | if (fdc_fifo_threshold(++ft_fdc_threshold, NULL, NULL, NULL) < 0) { | ||
833 | TRACE(ft_t_err, "cannot increase fifo threshold"); | ||
834 | ft_fdc_threshold --; | ||
835 | fdc_reset(); | ||
836 | } | ||
837 | TRACE(ft_t_info, "New FIFO threshold: %d", ft_fdc_threshold); | ||
838 | TRACE_EXIT 0; | ||
839 | } | ||
840 | |||
841 | int ftape_half_data_rate(void) | ||
842 | { | ||
843 | if (ft_data_rate < 500) { | ||
844 | return -1; | ||
845 | } | ||
846 | if (ftape_set_data_rate(ft_data_rate / 2, ft_qic_std) < 0) { | ||
847 | return -EIO; | ||
848 | } | ||
849 | ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | /* Seek the head to the specified track. | ||
854 | */ | ||
855 | int ftape_seek_head_to_track(unsigned int track) | ||
856 | { | ||
857 | int status; | ||
858 | TRACE_FUN(ft_t_any); | ||
859 | |||
860 | ft_location.track = -1; /* remains set in case of error */ | ||
861 | if (track >= ft_tracks_per_tape) { | ||
862 | TRACE_ABORT(-EINVAL, ft_t_bug, "track out of bounds"); | ||
863 | } | ||
864 | TRACE(ft_t_flow, "seeking track %d", track); | ||
865 | TRACE_CATCH(ftape_command(QIC_SEEK_HEAD_TO_TRACK),); | ||
866 | TRACE_CATCH(ftape_parameter_wait(track, ftape_timeout.head_seek, | ||
867 | &status),); | ||
868 | ft_location.track = track; | ||
869 | ftape_might_be_off_track = 0; | ||
870 | TRACE_EXIT 0; | ||
871 | } | ||
872 | |||
873 | int ftape_wakeup_drive(wake_up_types method) | ||
874 | { | ||
875 | int status; | ||
876 | int motor_on = 0; | ||
877 | TRACE_FUN(ft_t_any); | ||
878 | |||
879 | switch (method) { | ||
880 | case wake_up_colorado: | ||
881 | TRACE_CATCH(ftape_command(QIC_PHANTOM_SELECT),); | ||
882 | TRACE_CATCH(ftape_parameter(0 /* ft_drive_sel ?? */),); | ||
883 | break; | ||
884 | case wake_up_mountain: | ||
885 | TRACE_CATCH(ftape_command(QIC_SOFT_SELECT),); | ||
886 | ftape_sleep(FT_MILLISECOND); /* NEEDED */ | ||
887 | TRACE_CATCH(ftape_parameter(18),); | ||
888 | break; | ||
889 | case wake_up_insight: | ||
890 | ftape_sleep(100 * FT_MILLISECOND); | ||
891 | motor_on = 1; | ||
892 | fdc_motor(motor_on); /* enable is done by motor-on */ | ||
893 | case no_wake_up: | ||
894 | break; | ||
895 | default: | ||
896 | TRACE_EXIT -ENODEV; /* unknown wakeup method */ | ||
897 | break; | ||
898 | } | ||
899 | /* If wakeup succeeded we shouldn't get an error here.. | ||
900 | */ | ||
901 | TRACE_CATCH(ftape_report_raw_drive_status(&status), | ||
902 | if (motor_on) { | ||
903 | fdc_motor(0); | ||
904 | }); | ||
905 | TRACE_EXIT 0; | ||
906 | } | ||
907 | |||
908 | int ftape_put_drive_to_sleep(wake_up_types method) | ||
909 | { | ||
910 | TRACE_FUN(ft_t_any); | ||
911 | |||
912 | switch (method) { | ||
913 | case wake_up_colorado: | ||
914 | TRACE_CATCH(ftape_command(QIC_PHANTOM_DESELECT),); | ||
915 | break; | ||
916 | case wake_up_mountain: | ||
917 | TRACE_CATCH(ftape_command(QIC_SOFT_DESELECT),); | ||
918 | break; | ||
919 | case wake_up_insight: | ||
920 | fdc_motor(0); /* enable is done by motor-on */ | ||
921 | case no_wake_up: /* no wakeup / no sleep ! */ | ||
922 | break; | ||
923 | default: | ||
924 | TRACE_EXIT -ENODEV; /* unknown wakeup method */ | ||
925 | } | ||
926 | TRACE_EXIT 0; | ||
927 | } | ||
928 | |||
929 | int ftape_reset_drive(void) | ||
930 | { | ||
931 | int result = 0; | ||
932 | int status; | ||
933 | unsigned int err_code; | ||
934 | qic117_cmd_t err_command; | ||
935 | int i; | ||
936 | TRACE_FUN(ft_t_any); | ||
937 | |||
938 | /* We want to re-establish contact with our drive. Fire a | ||
939 | * number of reset commands (single step pulses) and pray for | ||
940 | * success. | ||
941 | */ | ||
942 | for (i = 0; i < 2; ++i) { | ||
943 | TRACE(ft_t_flow, "Resetting fdc"); | ||
944 | fdc_reset(); | ||
945 | ftape_sleep(10 * FT_MILLISECOND); | ||
946 | TRACE(ft_t_flow, "Reset command to drive"); | ||
947 | result = ftape_command(QIC_RESET); | ||
948 | if (result == 0) { | ||
949 | ftape_sleep(1 * FT_SECOND); /* drive not | ||
950 | * accessible | ||
951 | * during 1 second | ||
952 | */ | ||
953 | TRACE(ft_t_flow, "Re-selecting drive"); | ||
954 | |||
955 | /* Strange, the QIC-117 specs don't mention | ||
956 | * this but the drive gets deselected after a | ||
957 | * soft reset ! So we need to enable it | ||
958 | * again. | ||
959 | */ | ||
960 | if (ftape_wakeup_drive(ft_drive_type.wake_up) < 0) { | ||
961 | TRACE(ft_t_err, "Wakeup failed !"); | ||
962 | } | ||
963 | TRACE(ft_t_flow, "Waiting until drive gets ready"); | ||
964 | result= ftape_ready_wait(ftape_timeout.reset, &status); | ||
965 | if (result == 0 && (status & QIC_STATUS_ERROR)) { | ||
966 | result = ftape_report_error(&err_code, | ||
967 | &err_command, 1); | ||
968 | if (result == 0 && err_code == 27) { | ||
969 | /* Okay, drive saw reset | ||
970 | * command and responded as it | ||
971 | * should | ||
972 | */ | ||
973 | break; | ||
974 | } else { | ||
975 | result = -EIO; | ||
976 | } | ||
977 | } else { | ||
978 | result = -EIO; | ||
979 | } | ||
980 | } | ||
981 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
982 | } | ||
983 | if (result != 0) { | ||
984 | TRACE(ft_t_err, "General failure to reset tape drive"); | ||
985 | } else { | ||
986 | /* Restore correct settings: keep original rate | ||
987 | */ | ||
988 | ftape_set_data_rate(ft_data_rate, ft_qic_std); | ||
989 | } | ||
990 | ftape_init_drive_needed = 1; | ||
991 | TRACE_EXIT result; | ||
992 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-io.h b/drivers/char/ftape/lowlevel/ftape-io.h new file mode 100644 index 000000000000..26a7baad8717 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-io.h | |||
@@ -0,0 +1,90 @@ | |||
1 | #ifndef _FTAPE_IO_H | ||
2 | #define _FTAPE_IO_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-io.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:18 $ | ||
26 | * | ||
27 | * This file contains definitions for the glue part of the | ||
28 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
29 | */ | ||
30 | |||
31 | #include <linux/qic117.h> | ||
32 | #include <linux/ftape-vendors.h> | ||
33 | |||
34 | typedef struct { | ||
35 | unsigned int seek; | ||
36 | unsigned int reset; | ||
37 | unsigned int rewind; | ||
38 | unsigned int head_seek; | ||
39 | unsigned int stop; | ||
40 | unsigned int pause; | ||
41 | } ft_timeout_table; | ||
42 | |||
43 | typedef enum { | ||
44 | prehistoric, pre_qic117c, post_qic117b, post_qic117d | ||
45 | } qic_model; | ||
46 | |||
47 | /* | ||
48 | * ftape-io.c defined global vars. | ||
49 | */ | ||
50 | extern ft_timeout_table ftape_timeout; | ||
51 | extern unsigned int ftape_tape_len; | ||
52 | extern volatile qic117_cmd_t ftape_current_command; | ||
53 | extern const struct qic117_command_table qic117_cmds[]; | ||
54 | extern int ftape_might_be_off_track; | ||
55 | |||
56 | /* | ||
57 | * ftape-io.c defined global functions. | ||
58 | */ | ||
59 | extern void ftape_udelay(unsigned int usecs); | ||
60 | extern void ftape_udelay_calibrate(void); | ||
61 | extern void ftape_sleep(unsigned int time); | ||
62 | extern void ftape_report_vendor_id(unsigned int *id); | ||
63 | extern int ftape_command(qic117_cmd_t command); | ||
64 | extern int ftape_command_wait(qic117_cmd_t command, | ||
65 | unsigned int timeout, | ||
66 | int *status); | ||
67 | extern int ftape_parameter(unsigned int parameter); | ||
68 | extern int ftape_report_operation(int *status, | ||
69 | qic117_cmd_t command, | ||
70 | int result_length); | ||
71 | extern int ftape_report_configuration(qic_model *model, | ||
72 | unsigned int *rate, | ||
73 | int *qic_std, | ||
74 | int *tape_len); | ||
75 | extern int ftape_report_drive_status(int *status); | ||
76 | extern int ftape_report_raw_drive_status(int *status); | ||
77 | extern int ftape_report_status(int *status); | ||
78 | extern int ftape_ready_wait(unsigned int timeout, int *status); | ||
79 | extern int ftape_seek_head_to_track(unsigned int track); | ||
80 | extern int ftape_set_data_rate(unsigned int new_rate, unsigned int qic_std); | ||
81 | extern int ftape_report_error(unsigned int *error, | ||
82 | qic117_cmd_t *command, | ||
83 | int report); | ||
84 | extern int ftape_reset_drive(void); | ||
85 | extern int ftape_put_drive_to_sleep(wake_up_types method); | ||
86 | extern int ftape_wakeup_drive(wake_up_types method); | ||
87 | extern int ftape_increase_threshold(void); | ||
88 | extern int ftape_half_data_rate(void); | ||
89 | |||
90 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.c b/drivers/char/ftape/lowlevel/ftape-proc.c new file mode 100644 index 000000000000..c66251e997ed --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-proc.c | |||
@@ -0,0 +1,215 @@ | |||
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-proc.c,v $ | ||
20 | * $Revision: 1.11 $ | ||
21 | * $Date: 1997/10/24 14:47:37 $ | ||
22 | * | ||
23 | * This file contains the procfs interface for the | ||
24 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
25 | |||
26 | * Old code removed, switched to dynamic proc entry. | ||
27 | */ | ||
28 | |||
29 | #include <linux/config.h> | ||
30 | |||
31 | #if defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) | ||
32 | |||
33 | #include <linux/proc_fs.h> | ||
34 | |||
35 | #include <linux/ftape.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/qic117.h> | ||
38 | |||
39 | #include "../lowlevel/ftape-io.h" | ||
40 | #include "../lowlevel/ftape-ctl.h" | ||
41 | #include "../lowlevel/ftape-proc.h" | ||
42 | #include "../lowlevel/ftape-tracing.h" | ||
43 | |||
44 | static size_t get_driver_info(char *buf) | ||
45 | { | ||
46 | const char *debug_level[] = { "bugs" , | ||
47 | "errors", | ||
48 | "warnings", | ||
49 | "informational", | ||
50 | "noisy", | ||
51 | "program flow", | ||
52 | "fdc and dma", | ||
53 | "data flow", | ||
54 | "anything" }; | ||
55 | |||
56 | return sprintf(buf, | ||
57 | "version : %s\n" | ||
58 | "used data rate: %d kbit/sec\n" | ||
59 | "dma memory : %d kb\n" | ||
60 | "debug messages: %s\n", | ||
61 | FTAPE_VERSION, | ||
62 | ft_data_rate, | ||
63 | FT_BUFF_SIZE * ft_nr_buffers >> 10, | ||
64 | debug_level[TRACE_LEVEL]); | ||
65 | } | ||
66 | |||
67 | static size_t get_tapedrive_info(char *buf) | ||
68 | { | ||
69 | return sprintf(buf, | ||
70 | "vendor id : 0x%04x\n" | ||
71 | "drive name: %s\n" | ||
72 | "wind speed: %d ips\n" | ||
73 | "wakeup : %s\n" | ||
74 | "max. rate : %d kbit/sec\n", | ||
75 | ft_drive_type.vendor_id, | ||
76 | ft_drive_type.name, | ||
77 | ft_drive_type.speed, | ||
78 | ((ft_drive_type.wake_up == no_wake_up) | ||
79 | ? "No wakeup needed" : | ||
80 | ((ft_drive_type.wake_up == wake_up_colorado) | ||
81 | ? "Colorado" : | ||
82 | ((ft_drive_type.wake_up == wake_up_mountain) | ||
83 | ? "Mountain" : | ||
84 | ((ft_drive_type.wake_up == wake_up_insight) | ||
85 | ? "Motor on" : | ||
86 | "Unknown")))), | ||
87 | ft_drive_max_rate); | ||
88 | } | ||
89 | |||
90 | static size_t get_cartridge_info(char *buf) | ||
91 | { | ||
92 | if (ftape_init_drive_needed) { | ||
93 | return sprintf(buf, "uninitialized\n"); | ||
94 | } | ||
95 | if (ft_no_tape) { | ||
96 | return sprintf(buf, "no cartridge inserted\n"); | ||
97 | } | ||
98 | return sprintf(buf, | ||
99 | "segments : %5d\n" | ||
100 | "tracks : %5d\n" | ||
101 | "length : %5dft\n" | ||
102 | "formatted : %3s\n" | ||
103 | "writable : %3s\n" | ||
104 | "QIC spec. : QIC-%s\n" | ||
105 | "fmt-code : %1d\n", | ||
106 | ft_segments_per_track, | ||
107 | ft_tracks_per_tape, | ||
108 | ftape_tape_len, | ||
109 | (ft_formatted == 1) ? "yes" : "no", | ||
110 | (ft_write_protected == 1) ? "no" : "yes", | ||
111 | ((ft_qic_std == QIC_TAPE_QIC40) ? "40" : | ||
112 | ((ft_qic_std == QIC_TAPE_QIC80) ? "80" : | ||
113 | ((ft_qic_std == QIC_TAPE_QIC3010) ? "3010" : | ||
114 | ((ft_qic_std == QIC_TAPE_QIC3020) ? "3020" : | ||
115 | "???")))), | ||
116 | ft_format_code); | ||
117 | } | ||
118 | |||
119 | static size_t get_controller_info(char *buf) | ||
120 | { | ||
121 | const char *fdc_name[] = { "no fdc", | ||
122 | "i8272", | ||
123 | "i82077", | ||
124 | "i82077AA", | ||
125 | "Colorado FC-10 or FC-20", | ||
126 | "i82078", | ||
127 | "i82078_1" }; | ||
128 | |||
129 | return sprintf(buf, | ||
130 | "FDC type : %s\n" | ||
131 | "FDC base : 0x%03x\n" | ||
132 | "FDC irq : %d\n" | ||
133 | "FDC dma : %d\n" | ||
134 | "FDC thr. : %d\n" | ||
135 | "max. rate : %d kbit/sec\n", | ||
136 | ft_mach2 ? "Mountain MACH-2" : fdc_name[fdc.type], | ||
137 | fdc.sra, fdc.irq, fdc.dma, | ||
138 | ft_fdc_threshold, ft_fdc_max_rate); | ||
139 | } | ||
140 | |||
141 | static size_t get_history_info(char *buf) | ||
142 | { | ||
143 | size_t len; | ||
144 | |||
145 | len = sprintf(buf, | ||
146 | "\nFDC isr statistics\n" | ||
147 | " id_am_errors : %3d\n" | ||
148 | " id_crc_errors : %3d\n" | ||
149 | " data_am_errors : %3d\n" | ||
150 | " data_crc_errors : %3d\n" | ||
151 | " overrun_errors : %3d\n" | ||
152 | " no_data_errors : %3d\n" | ||
153 | " retries : %3d\n", | ||
154 | ft_history.id_am_errors, ft_history.id_crc_errors, | ||
155 | ft_history.data_am_errors, ft_history.data_crc_errors, | ||
156 | ft_history.overrun_errors, ft_history.no_data_errors, | ||
157 | ft_history.retries); | ||
158 | len += sprintf(buf + len, | ||
159 | "\nECC statistics\n" | ||
160 | " crc_errors : %3d\n" | ||
161 | " crc_failures : %3d\n" | ||
162 | " ecc_failures : %3d\n" | ||
163 | " sectors corrected: %3d\n", | ||
164 | ft_history.crc_errors, ft_history.crc_failures, | ||
165 | ft_history.ecc_failures, ft_history.corrected); | ||
166 | len += sprintf(buf + len, | ||
167 | "\ntape quality statistics\n" | ||
168 | " media defects : %3d\n", | ||
169 | ft_history.defects); | ||
170 | len += sprintf(buf + len, | ||
171 | "\ntape motion statistics\n" | ||
172 | " repositions : %3d\n", | ||
173 | ft_history.rewinds); | ||
174 | return len; | ||
175 | } | ||
176 | |||
177 | static int ftape_read_proc(char *page, char **start, off_t off, | ||
178 | int count, int *eof, void *data) | ||
179 | { | ||
180 | char *ptr = page; | ||
181 | size_t len; | ||
182 | |||
183 | ptr += sprintf(ptr, "Kernel Driver\n\n"); | ||
184 | ptr += get_driver_info(ptr); | ||
185 | ptr += sprintf(ptr, "\nTape Drive\n\n"); | ||
186 | ptr += get_tapedrive_info(ptr); | ||
187 | ptr += sprintf(ptr, "\nFDC Controller\n\n"); | ||
188 | ptr += get_controller_info(ptr); | ||
189 | ptr += sprintf(ptr, "\nTape Cartridge\n\n"); | ||
190 | ptr += get_cartridge_info(ptr); | ||
191 | ptr += sprintf(ptr, "\nHistory Record\n\n"); | ||
192 | ptr += get_history_info(ptr); | ||
193 | |||
194 | len = strlen(page); | ||
195 | *start = NULL; | ||
196 | if (off+count >= len) { | ||
197 | *eof = 1; | ||
198 | } else { | ||
199 | *eof = 0; | ||
200 | } | ||
201 | return len; | ||
202 | } | ||
203 | |||
204 | int __init ftape_proc_init(void) | ||
205 | { | ||
206 | return create_proc_read_entry("ftape", 0, &proc_root, | ||
207 | ftape_read_proc, NULL) != NULL; | ||
208 | } | ||
209 | |||
210 | void ftape_proc_destroy(void) | ||
211 | { | ||
212 | remove_proc_entry("ftape", &proc_root); | ||
213 | } | ||
214 | |||
215 | #endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_FT_PROC_FS) */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-proc.h b/drivers/char/ftape/lowlevel/ftape-proc.h new file mode 100644 index 000000000000..264dfcc1d22d --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-proc.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef _FTAPE_PROC_H | ||
2 | #define _FTAPE_PROC_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1997 Claus-Justus Heine | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-proc.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:18:20 $ | ||
25 | * | ||
26 | * This file contains definitions for the procfs interface of the | ||
27 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
28 | */ | ||
29 | |||
30 | #include <linux/proc_fs.h> | ||
31 | |||
32 | extern int ftape_proc_init(void); | ||
33 | extern void ftape_proc_destroy(void); | ||
34 | |||
35 | #endif | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-read.c b/drivers/char/ftape/lowlevel/ftape-read.c new file mode 100644 index 000000000000..d967d8cd86dc --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-read.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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-read.c,v $ | ||
21 | * $Revision: 1.6 $ | ||
22 | * $Date: 1997/10/21 14:39:22 $ | ||
23 | * | ||
24 | * This file contains the reading code | ||
25 | * for the QIC-117 floppy-tape driver for Linux. | ||
26 | * | ||
27 | */ | ||
28 | |||
29 | #include <linux/string.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/mm.h> | ||
32 | |||
33 | #include <linux/ftape.h> | ||
34 | #include <linux/qic117.h> | ||
35 | #include "../lowlevel/ftape-tracing.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-write.h" | ||
41 | #include "../lowlevel/ftape-ecc.h" | ||
42 | #include "../lowlevel/ftape-bsm.h" | ||
43 | |||
44 | /* Global vars. | ||
45 | */ | ||
46 | |||
47 | /* Local vars. | ||
48 | */ | ||
49 | |||
50 | void ftape_zap_read_buffers(void) | ||
51 | { | ||
52 | int i; | ||
53 | |||
54 | for (i = 0; i < ft_nr_buffers; ++i) { | ||
55 | /* changed to "fit" with dynamic allocation of tape_buffer. --khp */ | ||
56 | ft_buffer[i]->status = waiting; | ||
57 | ft_buffer[i]->bytes = 0; | ||
58 | ft_buffer[i]->skip = 0; | ||
59 | ft_buffer[i]->retry = 0; | ||
60 | } | ||
61 | /* ftape_reset_buffer(); */ | ||
62 | } | ||
63 | |||
64 | static SectorMap convert_sector_map(buffer_struct * buff) | ||
65 | { | ||
66 | int i = 0; | ||
67 | SectorMap bad_map = ftape_get_bad_sector_entry(buff->segment_id); | ||
68 | SectorMap src_map = buff->soft_error_map | buff->hard_error_map; | ||
69 | SectorMap dst_map = 0; | ||
70 | TRACE_FUN(ft_t_any); | ||
71 | |||
72 | if (bad_map || src_map) { | ||
73 | TRACE(ft_t_flow, "bad_map = 0x%08lx", (long) bad_map); | ||
74 | TRACE(ft_t_flow, "src_map = 0x%08lx", (long) src_map); | ||
75 | } | ||
76 | while (bad_map) { | ||
77 | while ((bad_map & 1) == 0) { | ||
78 | if (src_map & 1) { | ||
79 | dst_map |= (1 << i); | ||
80 | } | ||
81 | src_map >>= 1; | ||
82 | bad_map >>= 1; | ||
83 | ++i; | ||
84 | } | ||
85 | /* (bad_map & 1) == 1 */ | ||
86 | src_map >>= 1; | ||
87 | bad_map >>= 1; | ||
88 | } | ||
89 | if (src_map) { | ||
90 | dst_map |= (src_map << i); | ||
91 | } | ||
92 | if (dst_map) { | ||
93 | TRACE(ft_t_flow, "dst_map = 0x%08lx", (long) dst_map); | ||
94 | } | ||
95 | TRACE_EXIT dst_map; | ||
96 | } | ||
97 | |||
98 | static int correct_and_copy_fraction(buffer_struct *buff, __u8 * destination, | ||
99 | int start, int size) | ||
100 | { | ||
101 | struct memory_segment mseg; | ||
102 | int result; | ||
103 | SectorMap read_bad; | ||
104 | TRACE_FUN(ft_t_any); | ||
105 | |||
106 | mseg.read_bad = convert_sector_map(buff); | ||
107 | mseg.marked_bad = 0; /* not used... */ | ||
108 | mseg.blocks = buff->bytes / FT_SECTOR_SIZE; | ||
109 | mseg.data = buff->address; | ||
110 | /* If there are no data sectors we can skip this segment. | ||
111 | */ | ||
112 | if (mseg.blocks <= 3) { | ||
113 | TRACE_ABORT(0, ft_t_noise, "empty segment"); | ||
114 | } | ||
115 | read_bad = mseg.read_bad; | ||
116 | ft_history.crc_errors += count_ones(read_bad); | ||
117 | result = ftape_ecc_correct_data(&mseg); | ||
118 | if (read_bad != 0 || mseg.corrected != 0) { | ||
119 | TRACE(ft_t_noise, "crc error map: 0x%08lx", (unsigned long)read_bad); | ||
120 | TRACE(ft_t_noise, "corrected map: 0x%08lx", (unsigned long)mseg.corrected); | ||
121 | ft_history.corrected += count_ones(mseg.corrected); | ||
122 | } | ||
123 | if (result == ECC_CORRECTED || result == ECC_OK) { | ||
124 | if (result == ECC_CORRECTED) { | ||
125 | TRACE(ft_t_info, "ecc corrected segment: %d", buff->segment_id); | ||
126 | } | ||
127 | if(start < 0) { | ||
128 | start= 0; | ||
129 | } | ||
130 | if((start+size) > ((mseg.blocks - 3) * FT_SECTOR_SIZE)) { | ||
131 | size = (mseg.blocks - 3) * FT_SECTOR_SIZE - start; | ||
132 | } | ||
133 | if (size < 0) { | ||
134 | size= 0; | ||
135 | } | ||
136 | if(size > 0) { | ||
137 | memcpy(destination + start, mseg.data + start, size); | ||
138 | } | ||
139 | if ((read_bad ^ mseg.corrected) & mseg.corrected) { | ||
140 | /* sectors corrected without crc errors set */ | ||
141 | ft_history.crc_failures++; | ||
142 | } | ||
143 | TRACE_EXIT size; /* (mseg.blocks - 3) * FT_SECTOR_SIZE; */ | ||
144 | } else { | ||
145 | ft_history.ecc_failures++; | ||
146 | TRACE_ABORT(-EAGAIN, | ||
147 | ft_t_err, "ecc failure on segment %d", | ||
148 | buff->segment_id); | ||
149 | } | ||
150 | TRACE_EXIT 0; | ||
151 | } | ||
152 | |||
153 | /* Read given segment into buffer at address. | ||
154 | */ | ||
155 | int ftape_read_segment_fraction(const int segment_id, | ||
156 | void *address, | ||
157 | const ft_read_mode_t read_mode, | ||
158 | const int start, | ||
159 | const int size) | ||
160 | { | ||
161 | int result = 0; | ||
162 | int retry = 0; | ||
163 | int bytes_read = 0; | ||
164 | int read_done = 0; | ||
165 | TRACE_FUN(ft_t_flow); | ||
166 | |||
167 | ft_history.used |= 1; | ||
168 | TRACE(ft_t_data_flow, "segment_id = %d", segment_id); | ||
169 | if (ft_driver_state != reading) { | ||
170 | TRACE(ft_t_noise, "calling ftape_abort_operation"); | ||
171 | TRACE_CATCH(ftape_abort_operation(),); | ||
172 | ftape_set_state(reading); | ||
173 | } | ||
174 | for(;;) { | ||
175 | buffer_struct *tail; | ||
176 | /* Allow escape from this loop on signal ! | ||
177 | */ | ||
178 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
179 | /* Search all full buffers for the first matching the | ||
180 | * wanted segment. Clear other buffers on the fly. | ||
181 | */ | ||
182 | tail = ftape_get_buffer(ft_queue_tail); | ||
183 | while (!read_done && tail->status == done) { | ||
184 | /* Allow escape from this loop on signal ! | ||
185 | */ | ||
186 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
187 | if (tail->segment_id == segment_id) { | ||
188 | /* If out buffer is already full, | ||
189 | * return its contents. | ||
190 | */ | ||
191 | TRACE(ft_t_flow, "found segment in cache: %d", | ||
192 | segment_id); | ||
193 | if (tail->deleted) { | ||
194 | /* Return a value that | ||
195 | * read_header_segment | ||
196 | * understands. As this | ||
197 | * should only occur when | ||
198 | * searching for the header | ||
199 | * segments it shouldn't be | ||
200 | * misinterpreted elsewhere. | ||
201 | */ | ||
202 | TRACE_EXIT 0; | ||
203 | } | ||
204 | result = correct_and_copy_fraction( | ||
205 | tail, | ||
206 | address, | ||
207 | start, | ||
208 | size); | ||
209 | TRACE(ft_t_flow, "segment contains (bytes): %d", | ||
210 | result); | ||
211 | if (result < 0) { | ||
212 | if (result != -EAGAIN) { | ||
213 | TRACE_EXIT result; | ||
214 | } | ||
215 | /* keep read_done == 0, will | ||
216 | * trigger | ||
217 | * ftape_abort_operation | ||
218 | * because reading wrong | ||
219 | * segment. | ||
220 | */ | ||
221 | TRACE(ft_t_err, "ecc failed, retry"); | ||
222 | ++retry; | ||
223 | } else { | ||
224 | read_done = 1; | ||
225 | bytes_read = result; | ||
226 | } | ||
227 | } else { | ||
228 | TRACE(ft_t_flow,"zapping segment in cache: %d", | ||
229 | tail->segment_id); | ||
230 | } | ||
231 | tail->status = waiting; | ||
232 | tail = ftape_next_buffer(ft_queue_tail); | ||
233 | } | ||
234 | if (!read_done && tail->status == reading) { | ||
235 | if (tail->segment_id == segment_id) { | ||
236 | switch(ftape_wait_segment(reading)) { | ||
237 | case 0: | ||
238 | break; | ||
239 | case -EINTR: | ||
240 | TRACE_ABORT(-EINTR, ft_t_warn, | ||
241 | "interrupted by " | ||
242 | "non-blockable signal"); | ||
243 | break; | ||
244 | default: | ||
245 | TRACE(ft_t_noise, | ||
246 | "wait_segment failed"); | ||
247 | ftape_abort_operation(); | ||
248 | ftape_set_state(reading); | ||
249 | break; | ||
250 | } | ||
251 | } else { | ||
252 | /* We're reading the wrong segment, | ||
253 | * stop runner. | ||
254 | */ | ||
255 | TRACE(ft_t_noise, "reading wrong segment"); | ||
256 | ftape_abort_operation(); | ||
257 | ftape_set_state(reading); | ||
258 | } | ||
259 | } | ||
260 | /* should runner stop ? | ||
261 | */ | ||
262 | if (ft_runner_status == aborting) { | ||
263 | buffer_struct *head = ftape_get_buffer(ft_queue_head); | ||
264 | switch(head->status) { | ||
265 | case error: | ||
266 | ft_history.defects += | ||
267 | count_ones(head->hard_error_map); | ||
268 | case reading: | ||
269 | head->status = waiting; | ||
270 | break; | ||
271 | default: | ||
272 | break; | ||
273 | } | ||
274 | TRACE_CATCH(ftape_dumb_stop(),); | ||
275 | } else { | ||
276 | /* If just passed last segment on tape: wait | ||
277 | * for BOT or EOT mark. Sets ft_runner_status to | ||
278 | * idle if at lEOT and successful | ||
279 | */ | ||
280 | TRACE_CATCH(ftape_handle_logical_eot(),); | ||
281 | } | ||
282 | /* If we got a segment: quit, or else retry up to limit. | ||
283 | * | ||
284 | * If segment to read is empty, do not start runner for it, | ||
285 | * but wait for next read call. | ||
286 | */ | ||
287 | if (read_done || | ||
288 | ftape_get_bad_sector_entry(segment_id) == EMPTY_SEGMENT ) { | ||
289 | /* bytes_read = 0; should still be zero */ | ||
290 | TRACE_EXIT bytes_read; | ||
291 | |||
292 | } | ||
293 | if (retry > FT_RETRIES_ON_ECC_ERROR) { | ||
294 | ft_history.defects++; | ||
295 | TRACE_ABORT(-ENODATA, ft_t_err, | ||
296 | "too many retries on ecc failure"); | ||
297 | } | ||
298 | /* Now at least one buffer is empty ! | ||
299 | * Restart runner & tape if needed. | ||
300 | */ | ||
301 | TRACE(ft_t_any, "head: %d, tail: %d, ft_runner_status: %d", | ||
302 | ftape_buffer_id(ft_queue_head), | ||
303 | ftape_buffer_id(ft_queue_tail), | ||
304 | ft_runner_status); | ||
305 | TRACE(ft_t_any, "buffer[].status, [head]: %d, [tail]: %d", | ||
306 | ftape_get_buffer(ft_queue_head)->status, | ||
307 | ftape_get_buffer(ft_queue_tail)->status); | ||
308 | tail = ftape_get_buffer(ft_queue_tail); | ||
309 | if (tail->status == waiting) { | ||
310 | buffer_struct *head = ftape_get_buffer(ft_queue_head); | ||
311 | |||
312 | ftape_setup_new_segment(head, segment_id, -1); | ||
313 | if (read_mode == FT_RD_SINGLE) { | ||
314 | /* disable read-ahead */ | ||
315 | head->next_segment = 0; | ||
316 | } | ||
317 | ftape_calc_next_cluster(head); | ||
318 | if (ft_runner_status == idle) { | ||
319 | result = ftape_start_tape(segment_id, | ||
320 | head->sector_offset); | ||
321 | if (result < 0) { | ||
322 | TRACE_ABORT(result, ft_t_err, "Error: " | ||
323 | "segment %d unreachable", | ||
324 | segment_id); | ||
325 | } | ||
326 | } | ||
327 | head->status = reading; | ||
328 | fdc_setup_read_write(head, FDC_READ); | ||
329 | } | ||
330 | } | ||
331 | /* not reached */ | ||
332 | TRACE_EXIT -EIO; | ||
333 | } | ||
334 | |||
335 | int ftape_read_header_segment(__u8 *address) | ||
336 | { | ||
337 | int result; | ||
338 | int header_segment; | ||
339 | int first_failed = 0; | ||
340 | int status; | ||
341 | TRACE_FUN(ft_t_flow); | ||
342 | |||
343 | ft_used_header_segment = -1; | ||
344 | TRACE_CATCH(ftape_report_drive_status(&status),); | ||
345 | TRACE(ft_t_flow, "reading..."); | ||
346 | /* We're looking for the first header segment. | ||
347 | * A header segment cannot contain bad sectors, therefor at the | ||
348 | * tape start, segments with bad sectors are (according to QIC-40/80) | ||
349 | * written with deleted data marks and must be skipped. | ||
350 | */ | ||
351 | memset(address, '\0', (FT_SECTORS_PER_SEGMENT - 3) * FT_SECTOR_SIZE); | ||
352 | result = 0; | ||
353 | #define HEADER_SEGMENT_BOUNDARY 68 /* why not 42? */ | ||
354 | for (header_segment = 0; | ||
355 | header_segment < HEADER_SEGMENT_BOUNDARY && result == 0; | ||
356 | ++header_segment) { | ||
357 | /* Set no read-ahead, the isr will force read-ahead whenever | ||
358 | * it encounters deleted data ! | ||
359 | */ | ||
360 | result = ftape_read_segment(header_segment, | ||
361 | address, | ||
362 | FT_RD_SINGLE); | ||
363 | if (result < 0 && !first_failed) { | ||
364 | TRACE(ft_t_err, "header segment damaged, trying backup"); | ||
365 | first_failed = 1; | ||
366 | result = 0; /* force read of next (backup) segment */ | ||
367 | } | ||
368 | } | ||
369 | if (result < 0 || header_segment >= HEADER_SEGMENT_BOUNDARY) { | ||
370 | TRACE_ABORT(-EIO, ft_t_err, | ||
371 | "no readable header segment found"); | ||
372 | } | ||
373 | TRACE_CATCH(ftape_abort_operation(),); | ||
374 | ft_used_header_segment = header_segment; | ||
375 | result = ftape_decode_header_segment(address); | ||
376 | TRACE_EXIT result; | ||
377 | } | ||
378 | |||
379 | int ftape_decode_header_segment(__u8 *address) | ||
380 | { | ||
381 | unsigned int max_floppy_side; | ||
382 | unsigned int max_floppy_track; | ||
383 | unsigned int max_floppy_sector; | ||
384 | unsigned int new_tape_len; | ||
385 | TRACE_FUN(ft_t_flow); | ||
386 | |||
387 | if (GET4(address, FT_SIGNATURE) == FT_D2G_MAGIC) { | ||
388 | /* Ditto 2GB header segment. They encrypt the bad sector map. | ||
389 | * We decrypt it and store them in normal format. | ||
390 | * I hope this is correct. | ||
391 | */ | ||
392 | int i; | ||
393 | TRACE(ft_t_warn, | ||
394 | "Found Ditto 2GB tape, " | ||
395 | "trying to decrypt bad sector map"); | ||
396 | for (i=256; i < 29 * FT_SECTOR_SIZE; i++) { | ||
397 | address[i] = ~(address[i] - (i&0xff)); | ||
398 | } | ||
399 | PUT4(address, 0,FT_HSEG_MAGIC); | ||
400 | } else if (GET4(address, FT_SIGNATURE) != FT_HSEG_MAGIC) { | ||
401 | TRACE_ABORT(-EIO, ft_t_err, | ||
402 | "wrong signature in header segment"); | ||
403 | } | ||
404 | ft_format_code = (ft_format_type) address[FT_FMT_CODE]; | ||
405 | if (ft_format_code != fmt_big) { | ||
406 | ft_header_segment_1 = GET2(address, FT_HSEG_1); | ||
407 | ft_header_segment_2 = GET2(address, FT_HSEG_2); | ||
408 | ft_first_data_segment = GET2(address, FT_FRST_SEG); | ||
409 | ft_last_data_segment = GET2(address, FT_LAST_SEG); | ||
410 | } else { | ||
411 | ft_header_segment_1 = GET4(address, FT_6_HSEG_1); | ||
412 | ft_header_segment_2 = GET4(address, FT_6_HSEG_2); | ||
413 | ft_first_data_segment = GET4(address, FT_6_FRST_SEG); | ||
414 | ft_last_data_segment = GET4(address, FT_6_LAST_SEG); | ||
415 | } | ||
416 | TRACE(ft_t_noise, "first data segment: %d", ft_first_data_segment); | ||
417 | TRACE(ft_t_noise, "last data segment: %d", ft_last_data_segment); | ||
418 | TRACE(ft_t_noise, "header segments are %d and %d", | ||
419 | ft_header_segment_1, ft_header_segment_2); | ||
420 | |||
421 | /* Verify tape parameters... | ||
422 | * QIC-40/80 spec: tape_parameters: | ||
423 | * | ||
424 | * segments-per-track segments_per_track | ||
425 | * tracks-per-cartridge tracks_per_tape | ||
426 | * max-floppy-side (segments_per_track * | ||
427 | * tracks_per_tape - 1) / | ||
428 | * ftape_segments_per_head | ||
429 | * max-floppy-track ftape_segments_per_head / | ||
430 | * ftape_segments_per_cylinder - 1 | ||
431 | * max-floppy-sector ftape_segments_per_cylinder * | ||
432 | * FT_SECTORS_PER_SEGMENT | ||
433 | */ | ||
434 | ft_segments_per_track = GET2(address, FT_SPT); | ||
435 | ft_tracks_per_tape = address[FT_TPC]; | ||
436 | max_floppy_side = address[FT_FHM]; | ||
437 | max_floppy_track = address[FT_FTM]; | ||
438 | max_floppy_sector = address[FT_FSM]; | ||
439 | TRACE(ft_t_noise, "(fmt/spt/tpc/fhm/ftm/fsm) = %d/%d/%d/%d/%d/%d", | ||
440 | ft_format_code, ft_segments_per_track, ft_tracks_per_tape, | ||
441 | max_floppy_side, max_floppy_track, max_floppy_sector); | ||
442 | new_tape_len = ftape_tape_len; | ||
443 | switch (ft_format_code) { | ||
444 | case fmt_425ft: | ||
445 | new_tape_len = 425; | ||
446 | break; | ||
447 | case fmt_normal: | ||
448 | if (ftape_tape_len == 0) { /* otherwise 307 ft */ | ||
449 | new_tape_len = 205; | ||
450 | } | ||
451 | break; | ||
452 | case fmt_1100ft: | ||
453 | new_tape_len = 1100; | ||
454 | break; | ||
455 | case fmt_var:{ | ||
456 | int segments_per_1000_inch = 1; /* non-zero default for switch */ | ||
457 | switch (ft_qic_std) { | ||
458 | case QIC_TAPE_QIC40: | ||
459 | segments_per_1000_inch = 332; | ||
460 | break; | ||
461 | case QIC_TAPE_QIC80: | ||
462 | segments_per_1000_inch = 488; | ||
463 | break; | ||
464 | case QIC_TAPE_QIC3010: | ||
465 | segments_per_1000_inch = 730; | ||
466 | break; | ||
467 | case QIC_TAPE_QIC3020: | ||
468 | segments_per_1000_inch = 1430; | ||
469 | break; | ||
470 | } | ||
471 | new_tape_len = (1000 * ft_segments_per_track + | ||
472 | (segments_per_1000_inch - 1)) / segments_per_1000_inch; | ||
473 | break; | ||
474 | } | ||
475 | case fmt_big:{ | ||
476 | int segments_per_1000_inch = 1; /* non-zero default for switch */ | ||
477 | switch (ft_qic_std) { | ||
478 | case QIC_TAPE_QIC40: | ||
479 | segments_per_1000_inch = 332; | ||
480 | break; | ||
481 | case QIC_TAPE_QIC80: | ||
482 | segments_per_1000_inch = 488; | ||
483 | break; | ||
484 | case QIC_TAPE_QIC3010: | ||
485 | segments_per_1000_inch = 730; | ||
486 | break; | ||
487 | case QIC_TAPE_QIC3020: | ||
488 | segments_per_1000_inch = 1430; | ||
489 | break; | ||
490 | default: | ||
491 | TRACE_ABORT(-EIO, ft_t_bug, | ||
492 | "%x QIC-standard with fmt-code %d, please report", | ||
493 | ft_qic_std, ft_format_code); | ||
494 | } | ||
495 | new_tape_len = ((1000 * ft_segments_per_track + | ||
496 | (segments_per_1000_inch - 1)) / | ||
497 | segments_per_1000_inch); | ||
498 | break; | ||
499 | } | ||
500 | default: | ||
501 | TRACE_ABORT(-EIO, ft_t_err, | ||
502 | "unknown tape format, please report !"); | ||
503 | } | ||
504 | if (new_tape_len != ftape_tape_len) { | ||
505 | ftape_tape_len = new_tape_len; | ||
506 | TRACE(ft_t_info, "calculated tape length is %d ft", | ||
507 | ftape_tape_len); | ||
508 | ftape_calc_timeouts(ft_qic_std, ft_data_rate, ftape_tape_len); | ||
509 | } | ||
510 | if (ft_segments_per_track == 0 && ft_tracks_per_tape == 0 && | ||
511 | max_floppy_side == 0 && max_floppy_track == 0 && | ||
512 | max_floppy_sector == 0) { | ||
513 | /* QIC-40 Rev E and earlier has no values in the header. | ||
514 | */ | ||
515 | ft_segments_per_track = 68; | ||
516 | ft_tracks_per_tape = 20; | ||
517 | max_floppy_side = 1; | ||
518 | max_floppy_track = 169; | ||
519 | max_floppy_sector = 128; | ||
520 | } | ||
521 | /* This test will compensate for the wrong parameter on tapes | ||
522 | * formatted by Conner software. | ||
523 | */ | ||
524 | if (ft_segments_per_track == 150 && | ||
525 | ft_tracks_per_tape == 28 && | ||
526 | max_floppy_side == 7 && | ||
527 | max_floppy_track == 149 && | ||
528 | max_floppy_sector == 128) { | ||
529 | TRACE(ft_t_info, "the famous CONNER bug: max_floppy_side off by one !"); | ||
530 | max_floppy_side = 6; | ||
531 | } | ||
532 | /* These tests will compensate for the wrong parameter on tapes | ||
533 | * formatted by ComByte Windows software. | ||
534 | * | ||
535 | * First, for 205 foot tapes | ||
536 | */ | ||
537 | if (ft_segments_per_track == 100 && | ||
538 | ft_tracks_per_tape == 28 && | ||
539 | max_floppy_side == 9 && | ||
540 | max_floppy_track == 149 && | ||
541 | max_floppy_sector == 128) { | ||
542 | TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); | ||
543 | max_floppy_side = 4; | ||
544 | } | ||
545 | /* Next, for 307 foot tapes. */ | ||
546 | if (ft_segments_per_track == 150 && | ||
547 | ft_tracks_per_tape == 28 && | ||
548 | max_floppy_side == 9 && | ||
549 | max_floppy_track == 149 && | ||
550 | max_floppy_sector == 128) { | ||
551 | TRACE(ft_t_info, "the ComByte bug: max_floppy_side incorrect!"); | ||
552 | max_floppy_side = 6; | ||
553 | } | ||
554 | /* This test will compensate for the wrong parameter on tapes | ||
555 | * formatted by Colorado Windows software. | ||
556 | */ | ||
557 | if (ft_segments_per_track == 150 && | ||
558 | ft_tracks_per_tape == 28 && | ||
559 | max_floppy_side == 6 && | ||
560 | max_floppy_track == 150 && | ||
561 | max_floppy_sector == 128) { | ||
562 | TRACE(ft_t_info, "the famous Colorado bug: max_floppy_track off by one !"); | ||
563 | max_floppy_track = 149; | ||
564 | } | ||
565 | ftape_segments_per_head = ((max_floppy_sector/FT_SECTORS_PER_SEGMENT) * | ||
566 | (max_floppy_track + 1)); | ||
567 | /* This test will compensate for some bug reported by Dima | ||
568 | * Brodsky. Seems to be a Colorado bug, either. (freebee | ||
569 | * Imation tape shipped together with Colorado T3000 | ||
570 | */ | ||
571 | if ((ft_format_code == fmt_var || ft_format_code == fmt_big) && | ||
572 | ft_tracks_per_tape == 50 && | ||
573 | max_floppy_side == 54 && | ||
574 | max_floppy_track == 255 && | ||
575 | max_floppy_sector == 128) { | ||
576 | TRACE(ft_t_info, "the famous ??? bug: max_floppy_track off by one !"); | ||
577 | max_floppy_track = 254; | ||
578 | } | ||
579 | /* | ||
580 | * Verify drive_configuration with tape parameters | ||
581 | */ | ||
582 | if (ftape_segments_per_head == 0 || ftape_segments_per_cylinder == 0 || | ||
583 | ((ft_segments_per_track * ft_tracks_per_tape - 1) / ftape_segments_per_head | ||
584 | != max_floppy_side) || | ||
585 | (ftape_segments_per_head / ftape_segments_per_cylinder - 1 != max_floppy_track) || | ||
586 | (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT != max_floppy_sector) | ||
587 | #ifdef TESTING | ||
588 | || ((ft_format_code == fmt_var || ft_format_code == fmt_big) && | ||
589 | (max_floppy_track != 254 || max_floppy_sector != 128)) | ||
590 | #endif | ||
591 | ) { | ||
592 | char segperheadz = ftape_segments_per_head ? ' ' : '?'; | ||
593 | char segpercylz = ftape_segments_per_cylinder ? ' ' : '?'; | ||
594 | TRACE(ft_t_err,"Tape parameters inconsistency, please report"); | ||
595 | TRACE(ft_t_err, "reported = %d/%d/%d/%d/%d/%d", | ||
596 | ft_format_code, | ||
597 | ft_segments_per_track, | ||
598 | ft_tracks_per_tape, | ||
599 | max_floppy_side, | ||
600 | max_floppy_track, | ||
601 | max_floppy_sector); | ||
602 | TRACE(ft_t_err, "required = %d/%d/%d/%d%c/%d%c/%d", | ||
603 | ft_format_code, | ||
604 | ft_segments_per_track, | ||
605 | ft_tracks_per_tape, | ||
606 | ftape_segments_per_head ? | ||
607 | ((ft_segments_per_track * ft_tracks_per_tape -1) / | ||
608 | ftape_segments_per_head ) : | ||
609 | (ft_segments_per_track * ft_tracks_per_tape -1), | ||
610 | segperheadz, | ||
611 | ftape_segments_per_cylinder ? | ||
612 | (ftape_segments_per_head / | ||
613 | ftape_segments_per_cylinder - 1 ) : | ||
614 | ftape_segments_per_head - 1, | ||
615 | segpercylz, | ||
616 | (ftape_segments_per_cylinder * FT_SECTORS_PER_SEGMENT)); | ||
617 | TRACE_EXIT -EIO; | ||
618 | } | ||
619 | ftape_extract_bad_sector_map(address); | ||
620 | TRACE_EXIT 0; | ||
621 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-read.h b/drivers/char/ftape/lowlevel/ftape-read.h new file mode 100644 index 000000000000..069f99f2a984 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-read.h | |||
@@ -0,0 +1,51 @@ | |||
1 | #ifndef _FTAPE_READ_H | ||
2 | #define _FTAPE_READ_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-read.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:22 $ | ||
26 | * | ||
27 | * This file contains the definitions for the read functions | ||
28 | * for the QIC-117 floppy-tape driver for Linux. | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | /* ftape-read.c defined global functions. | ||
33 | */ | ||
34 | typedef enum { | ||
35 | FT_RD_SINGLE = 0, | ||
36 | FT_RD_AHEAD = 1, | ||
37 | } ft_read_mode_t; | ||
38 | |||
39 | extern int ftape_read_header_segment(__u8 *address); | ||
40 | extern int ftape_decode_header_segment(__u8 *address); | ||
41 | extern int ftape_read_segment_fraction(const int segment, | ||
42 | void *address, | ||
43 | const ft_read_mode_t read_mode, | ||
44 | const int start, | ||
45 | const int size); | ||
46 | #define ftape_read_segment(segment, address, read_mode) \ | ||
47 | ftape_read_segment_fraction(segment, address, read_mode, \ | ||
48 | 0, FT_SEGMENT_SIZE) | ||
49 | extern void ftape_zap_read_buffers(void); | ||
50 | |||
51 | #endif /* _FTAPE_READ_H */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.c b/drivers/char/ftape/lowlevel/ftape-rw.c new file mode 100644 index 000000000000..c0d6dc2cbfd3 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-rw.c | |||
@@ -0,0 +1,1092 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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-rw.c,v $ | ||
21 | * $Revision: 1.7 $ | ||
22 | * $Date: 1997/10/28 14:26:49 $ | ||
23 | * | ||
24 | * This file contains some common code for the segment read and | ||
25 | * segment write routines for the QIC-117 floppy-tape driver for | ||
26 | * Linux. | ||
27 | */ | ||
28 | |||
29 | #include <linux/string.h> | ||
30 | #include <linux/errno.h> | ||
31 | |||
32 | #include <linux/ftape.h> | ||
33 | #include <linux/qic117.h> | ||
34 | #include "../lowlevel/ftape-tracing.h" | ||
35 | #include "../lowlevel/ftape-rw.h" | ||
36 | #include "../lowlevel/fdc-io.h" | ||
37 | #include "../lowlevel/ftape-init.h" | ||
38 | #include "../lowlevel/ftape-io.h" | ||
39 | #include "../lowlevel/ftape-ctl.h" | ||
40 | #include "../lowlevel/ftape-read.h" | ||
41 | #include "../lowlevel/ftape-ecc.h" | ||
42 | #include "../lowlevel/ftape-bsm.h" | ||
43 | |||
44 | /* Global vars. | ||
45 | */ | ||
46 | int ft_nr_buffers; | ||
47 | buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS]; | ||
48 | static volatile int ft_head; | ||
49 | static volatile int ft_tail; /* not volatile but need same type as head */ | ||
50 | int fdc_setup_error; | ||
51 | location_record ft_location = {-1, 0}; | ||
52 | volatile int ftape_tape_running; | ||
53 | |||
54 | /* Local vars. | ||
55 | */ | ||
56 | static int overrun_count_offset; | ||
57 | static int inhibit_correction; | ||
58 | |||
59 | /* maxmimal allowed overshoot when fast seeking | ||
60 | */ | ||
61 | #define OVERSHOOT_LIMIT 10 | ||
62 | |||
63 | /* Increment cyclic buffer nr. | ||
64 | */ | ||
65 | buffer_struct *ftape_next_buffer(ft_buffer_queue_t pos) | ||
66 | { | ||
67 | switch (pos) { | ||
68 | case ft_queue_head: | ||
69 | if (++ft_head >= ft_nr_buffers) { | ||
70 | ft_head = 0; | ||
71 | } | ||
72 | return ft_buffer[ft_head]; | ||
73 | case ft_queue_tail: | ||
74 | if (++ft_tail >= ft_nr_buffers) { | ||
75 | ft_tail = 0; | ||
76 | } | ||
77 | return ft_buffer[ft_tail]; | ||
78 | default: | ||
79 | return NULL; | ||
80 | } | ||
81 | } | ||
82 | int ftape_buffer_id(ft_buffer_queue_t pos) | ||
83 | { | ||
84 | switch(pos) { | ||
85 | case ft_queue_head: return ft_head; | ||
86 | case ft_queue_tail: return ft_tail; | ||
87 | default: return -1; | ||
88 | } | ||
89 | } | ||
90 | buffer_struct *ftape_get_buffer(ft_buffer_queue_t pos) | ||
91 | { | ||
92 | switch(pos) { | ||
93 | case ft_queue_head: return ft_buffer[ft_head]; | ||
94 | case ft_queue_tail: return ft_buffer[ft_tail]; | ||
95 | default: return NULL; | ||
96 | } | ||
97 | } | ||
98 | void ftape_reset_buffer(void) | ||
99 | { | ||
100 | ft_head = ft_tail = 0; | ||
101 | } | ||
102 | |||
103 | buffer_state_enum ftape_set_state(buffer_state_enum new_state) | ||
104 | { | ||
105 | buffer_state_enum old_state = ft_driver_state; | ||
106 | |||
107 | ft_driver_state = new_state; | ||
108 | return old_state; | ||
109 | } | ||
110 | /* Calculate Floppy Disk Controller and DMA parameters for a segment. | ||
111 | * head: selects buffer struct in array. | ||
112 | * offset: number of physical sectors to skip (including bad ones). | ||
113 | * count: number of physical sectors to handle (including bad ones). | ||
114 | */ | ||
115 | static int setup_segment(buffer_struct * buff, | ||
116 | int segment_id, | ||
117 | unsigned int sector_offset, | ||
118 | unsigned int sector_count, | ||
119 | int retry) | ||
120 | { | ||
121 | SectorMap offset_mask; | ||
122 | SectorMap mask; | ||
123 | TRACE_FUN(ft_t_any); | ||
124 | |||
125 | buff->segment_id = segment_id; | ||
126 | buff->sector_offset = sector_offset; | ||
127 | buff->remaining = sector_count; | ||
128 | buff->head = segment_id / ftape_segments_per_head; | ||
129 | buff->cyl = (segment_id % ftape_segments_per_head) / ftape_segments_per_cylinder; | ||
130 | buff->sect = (segment_id % ftape_segments_per_cylinder) * FT_SECTORS_PER_SEGMENT + 1; | ||
131 | buff->deleted = 0; | ||
132 | offset_mask = (1 << buff->sector_offset) - 1; | ||
133 | mask = ftape_get_bad_sector_entry(segment_id) & offset_mask; | ||
134 | while (mask) { | ||
135 | if (mask & 1) { | ||
136 | offset_mask >>= 1; /* don't count bad sector */ | ||
137 | } | ||
138 | mask >>= 1; | ||
139 | } | ||
140 | buff->data_offset = count_ones(offset_mask); /* good sectors to skip */ | ||
141 | buff->ptr = buff->address + buff->data_offset * FT_SECTOR_SIZE; | ||
142 | TRACE(ft_t_flow, "data offset = %d sectors", buff->data_offset); | ||
143 | if (retry) { | ||
144 | buff->soft_error_map &= offset_mask; /* keep skipped part */ | ||
145 | } else { | ||
146 | buff->hard_error_map = buff->soft_error_map = 0; | ||
147 | } | ||
148 | buff->bad_sector_map = ftape_get_bad_sector_entry(buff->segment_id); | ||
149 | if (buff->bad_sector_map != 0) { | ||
150 | TRACE(ft_t_noise, "segment: %d, bad sector map: %08lx", | ||
151 | buff->segment_id, (long)buff->bad_sector_map); | ||
152 | } else { | ||
153 | TRACE(ft_t_flow, "segment: %d", buff->segment_id); | ||
154 | } | ||
155 | if (buff->sector_offset > 0) { | ||
156 | buff->bad_sector_map >>= buff->sector_offset; | ||
157 | } | ||
158 | if (buff->sector_offset != 0 || buff->remaining != FT_SECTORS_PER_SEGMENT) { | ||
159 | TRACE(ft_t_flow, "sector offset = %d, count = %d", | ||
160 | buff->sector_offset, buff->remaining); | ||
161 | } | ||
162 | /* Segments with 3 or less sectors are not written with valid | ||
163 | * data because there is no space left for the ecc. The | ||
164 | * data written is whatever happens to be in the buffer. | ||
165 | * Reading such a segment will return a zero byte-count. | ||
166 | * To allow us to read/write segments with all bad sectors | ||
167 | * we fake one readable sector in the segment. This | ||
168 | * prevents having to handle these segments in a very | ||
169 | * special way. It is not important if the reading of this | ||
170 | * bad sector fails or not (the data is ignored). It is | ||
171 | * only read to keep the driver running. | ||
172 | * | ||
173 | * The QIC-40/80 spec. has no information on how to handle | ||
174 | * this case, so this is my interpretation. | ||
175 | */ | ||
176 | if (buff->bad_sector_map == EMPTY_SEGMENT) { | ||
177 | TRACE(ft_t_flow, "empty segment %d, fake first sector good", | ||
178 | buff->segment_id); | ||
179 | if (buff->ptr != buff->address) { | ||
180 | TRACE(ft_t_bug, "This is a bug: %p/%p", | ||
181 | buff->ptr, buff->address); | ||
182 | } | ||
183 | buff->bad_sector_map = FAKE_SEGMENT; | ||
184 | } | ||
185 | fdc_setup_error = 0; | ||
186 | buff->next_segment = segment_id + 1; | ||
187 | TRACE_EXIT 0; | ||
188 | } | ||
189 | |||
190 | /* Calculate Floppy Disk Controller and DMA parameters for a new segment. | ||
191 | */ | ||
192 | int ftape_setup_new_segment(buffer_struct * buff, int segment_id, int skip) | ||
193 | { | ||
194 | int result = 0; | ||
195 | static int old_segment_id = -1; | ||
196 | static buffer_state_enum old_ft_driver_state = idle; | ||
197 | int retry = 0; | ||
198 | unsigned offset = 0; | ||
199 | int count = FT_SECTORS_PER_SEGMENT; | ||
200 | TRACE_FUN(ft_t_flow); | ||
201 | |||
202 | TRACE(ft_t_flow, "%s segment %d (old = %d)", | ||
203 | (ft_driver_state == reading || ft_driver_state == verifying) | ||
204 | ? "reading" : "writing", | ||
205 | segment_id, old_segment_id); | ||
206 | if (ft_driver_state != old_ft_driver_state) { /* when verifying */ | ||
207 | old_segment_id = -1; | ||
208 | old_ft_driver_state = ft_driver_state; | ||
209 | } | ||
210 | if (segment_id == old_segment_id) { | ||
211 | ++buff->retry; | ||
212 | ++ft_history.retries; | ||
213 | TRACE(ft_t_flow, "setting up for retry nr %d", buff->retry); | ||
214 | retry = 1; | ||
215 | if (skip && buff->skip > 0) { /* allow skip on retry */ | ||
216 | offset = buff->skip; | ||
217 | count -= offset; | ||
218 | TRACE(ft_t_flow, "skipping %d sectors", offset); | ||
219 | } | ||
220 | } else { | ||
221 | buff->retry = 0; | ||
222 | buff->skip = 0; | ||
223 | old_segment_id = segment_id; | ||
224 | } | ||
225 | result = setup_segment(buff, segment_id, offset, count, retry); | ||
226 | TRACE_EXIT result; | ||
227 | } | ||
228 | |||
229 | /* Determine size of next cluster of good sectors. | ||
230 | */ | ||
231 | int ftape_calc_next_cluster(buffer_struct * buff) | ||
232 | { | ||
233 | /* Skip bad sectors. | ||
234 | */ | ||
235 | while (buff->remaining > 0 && (buff->bad_sector_map & 1) != 0) { | ||
236 | buff->bad_sector_map >>= 1; | ||
237 | ++buff->sector_offset; | ||
238 | --buff->remaining; | ||
239 | } | ||
240 | /* Find next cluster of good sectors | ||
241 | */ | ||
242 | if (buff->bad_sector_map == 0) { /* speed up */ | ||
243 | buff->sector_count = buff->remaining; | ||
244 | } else { | ||
245 | SectorMap map = buff->bad_sector_map; | ||
246 | |||
247 | buff->sector_count = 0; | ||
248 | while (buff->sector_count < buff->remaining && (map & 1) == 0) { | ||
249 | ++buff->sector_count; | ||
250 | map >>= 1; | ||
251 | } | ||
252 | } | ||
253 | return buff->sector_count; | ||
254 | } | ||
255 | |||
256 | /* if just passed the last segment on a track, wait for BOT | ||
257 | * or EOT mark. | ||
258 | */ | ||
259 | int ftape_handle_logical_eot(void) | ||
260 | { | ||
261 | TRACE_FUN(ft_t_flow); | ||
262 | |||
263 | if (ft_runner_status == logical_eot) { | ||
264 | int status; | ||
265 | |||
266 | TRACE(ft_t_noise, "tape at logical EOT"); | ||
267 | TRACE_CATCH(ftape_ready_wait(ftape_timeout.seek, &status),); | ||
268 | if ((status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) == 0) { | ||
269 | TRACE_ABORT(-EIO, ft_t_err, "eot/bot not reached"); | ||
270 | } | ||
271 | ft_runner_status = end_of_tape; | ||
272 | } | ||
273 | if (ft_runner_status == end_of_tape) { | ||
274 | TRACE(ft_t_noise, "runner stopped because of logical EOT"); | ||
275 | ft_runner_status = idle; | ||
276 | } | ||
277 | TRACE_EXIT 0; | ||
278 | } | ||
279 | |||
280 | static int check_bot_eot(int status) | ||
281 | { | ||
282 | TRACE_FUN(ft_t_flow); | ||
283 | |||
284 | if (status & (QIC_STATUS_AT_BOT | QIC_STATUS_AT_EOT)) { | ||
285 | ft_location.bot = ((ft_location.track & 1) == 0 ? | ||
286 | (status & QIC_STATUS_AT_BOT) != 0: | ||
287 | (status & QIC_STATUS_AT_EOT) != 0); | ||
288 | ft_location.eot = !ft_location.bot; | ||
289 | ft_location.segment = (ft_location.track + | ||
290 | (ft_location.bot ? 0 : 1)) * ft_segments_per_track - 1; | ||
291 | ft_location.sector = -1; | ||
292 | ft_location.known = 1; | ||
293 | TRACE(ft_t_flow, "tape at logical %s", | ||
294 | ft_location.bot ? "bot" : "eot"); | ||
295 | TRACE(ft_t_flow, "segment = %d", ft_location.segment); | ||
296 | } else { | ||
297 | ft_location.known = 0; | ||
298 | } | ||
299 | TRACE_EXIT ft_location.known; | ||
300 | } | ||
301 | |||
302 | /* Read Id of first sector passing tape head. | ||
303 | */ | ||
304 | static int ftape_read_id(void) | ||
305 | { | ||
306 | int status; | ||
307 | __u8 out[2]; | ||
308 | TRACE_FUN(ft_t_any); | ||
309 | |||
310 | /* Assume tape is running on entry, be able to handle | ||
311 | * situation where it stopped or is stopping. | ||
312 | */ | ||
313 | ft_location.known = 0; /* default is location not known */ | ||
314 | out[0] = FDC_READID; | ||
315 | out[1] = ft_drive_sel; | ||
316 | TRACE_CATCH(fdc_command(out, 2),); | ||
317 | switch (fdc_interrupt_wait(20 * FT_SECOND)) { | ||
318 | case 0: | ||
319 | if (fdc_sect == 0) { | ||
320 | if (ftape_report_drive_status(&status) >= 0 && | ||
321 | (status & QIC_STATUS_READY)) { | ||
322 | ftape_tape_running = 0; | ||
323 | TRACE(ft_t_flow, "tape has stopped"); | ||
324 | check_bot_eot(status); | ||
325 | } | ||
326 | } else { | ||
327 | ft_location.known = 1; | ||
328 | ft_location.segment = (ftape_segments_per_head | ||
329 | * fdc_head | ||
330 | + ftape_segments_per_cylinder | ||
331 | * fdc_cyl | ||
332 | + (fdc_sect - 1) | ||
333 | / FT_SECTORS_PER_SEGMENT); | ||
334 | ft_location.sector = ((fdc_sect - 1) | ||
335 | % FT_SECTORS_PER_SEGMENT); | ||
336 | ft_location.eot = ft_location.bot = 0; | ||
337 | } | ||
338 | break; | ||
339 | case -ETIME: | ||
340 | /* Didn't find id on tape, must be near end: Wait | ||
341 | * until stopped. | ||
342 | */ | ||
343 | if (ftape_ready_wait(FT_FOREVER, &status) >= 0) { | ||
344 | ftape_tape_running = 0; | ||
345 | TRACE(ft_t_flow, "tape has stopped"); | ||
346 | check_bot_eot(status); | ||
347 | } | ||
348 | break; | ||
349 | default: | ||
350 | /* Interrupted or otherwise failing | ||
351 | * fdc_interrupt_wait() | ||
352 | */ | ||
353 | TRACE(ft_t_err, "fdc_interrupt_wait failed"); | ||
354 | break; | ||
355 | } | ||
356 | if (!ft_location.known) { | ||
357 | TRACE_ABORT(-EIO, ft_t_flow, "no id found"); | ||
358 | } | ||
359 | if (ft_location.sector == 0) { | ||
360 | TRACE(ft_t_flow, "passing segment %d/%d", | ||
361 | ft_location.segment, ft_location.sector); | ||
362 | } else { | ||
363 | TRACE(ft_t_fdc_dma, "passing segment %d/%d", | ||
364 | ft_location.segment, ft_location.sector); | ||
365 | } | ||
366 | TRACE_EXIT 0; | ||
367 | } | ||
368 | |||
369 | static int logical_forward(void) | ||
370 | { | ||
371 | ftape_tape_running = 1; | ||
372 | return ftape_command(QIC_LOGICAL_FORWARD); | ||
373 | } | ||
374 | |||
375 | int ftape_stop_tape(int *pstatus) | ||
376 | { | ||
377 | int retry = 0; | ||
378 | int result; | ||
379 | TRACE_FUN(ft_t_flow); | ||
380 | |||
381 | do { | ||
382 | result = ftape_command_wait(QIC_STOP_TAPE, | ||
383 | ftape_timeout.stop, pstatus); | ||
384 | if (result == 0) { | ||
385 | if ((*pstatus & QIC_STATUS_READY) == 0) { | ||
386 | result = -EIO; | ||
387 | } else { | ||
388 | ftape_tape_running = 0; | ||
389 | } | ||
390 | } | ||
391 | } while (result < 0 && ++retry <= 3); | ||
392 | if (result < 0) { | ||
393 | TRACE(ft_t_err, "failed ! (fatal)"); | ||
394 | } | ||
395 | TRACE_EXIT result; | ||
396 | } | ||
397 | |||
398 | int ftape_dumb_stop(void) | ||
399 | { | ||
400 | int result; | ||
401 | int status; | ||
402 | TRACE_FUN(ft_t_flow); | ||
403 | |||
404 | /* Abort current fdc operation if it's busy (probably read | ||
405 | * or write operation pending) with a reset. | ||
406 | */ | ||
407 | if (fdc_ready_wait(100 /* usec */) < 0) { | ||
408 | TRACE(ft_t_noise, "aborting fdc operation"); | ||
409 | fdc_reset(); | ||
410 | } | ||
411 | /* Reading id's after the last segment on a track may fail | ||
412 | * but eventually the drive will become ready (logical eot). | ||
413 | */ | ||
414 | result = ftape_report_drive_status(&status); | ||
415 | ft_location.known = 0; | ||
416 | do { | ||
417 | if (result == 0 && status & QIC_STATUS_READY) { | ||
418 | /* Tape is not running any more. | ||
419 | */ | ||
420 | TRACE(ft_t_noise, "tape already halted"); | ||
421 | check_bot_eot(status); | ||
422 | ftape_tape_running = 0; | ||
423 | } else if (ftape_tape_running) { | ||
424 | /* Tape is (was) still moving. | ||
425 | */ | ||
426 | #ifdef TESTING | ||
427 | ftape_read_id(); | ||
428 | #endif | ||
429 | result = ftape_stop_tape(&status); | ||
430 | } else { | ||
431 | /* Tape not yet ready but stopped. | ||
432 | */ | ||
433 | result = ftape_ready_wait(ftape_timeout.pause,&status); | ||
434 | } | ||
435 | } while (ftape_tape_running | ||
436 | && !(sigtestsetmask(¤t->pending.signal, _NEVER_BLOCK))); | ||
437 | #ifndef TESTING | ||
438 | ft_location.known = 0; | ||
439 | #endif | ||
440 | if (ft_runner_status == aborting || ft_runner_status == do_abort) { | ||
441 | ft_runner_status = idle; | ||
442 | } | ||
443 | TRACE_EXIT result; | ||
444 | } | ||
445 | |||
446 | /* Wait until runner has finished tail buffer. | ||
447 | * | ||
448 | */ | ||
449 | int ftape_wait_segment(buffer_state_enum state) | ||
450 | { | ||
451 | int status; | ||
452 | int result = 0; | ||
453 | TRACE_FUN(ft_t_flow); | ||
454 | |||
455 | while (ft_buffer[ft_tail]->status == state) { | ||
456 | TRACE(ft_t_flow, "state: %d", ft_buffer[ft_tail]->status); | ||
457 | /* First buffer still being worked on, wait up to timeout. | ||
458 | * | ||
459 | * Note: we check two times for being killed. 50 | ||
460 | * seconds are quite long. Note that | ||
461 | * fdc_interrupt_wait() is not killable by any | ||
462 | * means. ftape_read_segment() wants us to return | ||
463 | * -EINTR in case of a signal. | ||
464 | */ | ||
465 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
466 | result = fdc_interrupt_wait(50 * FT_SECOND); | ||
467 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
468 | if (result < 0) { | ||
469 | TRACE_ABORT(result, | ||
470 | ft_t_err, "fdc_interrupt_wait failed"); | ||
471 | } | ||
472 | if (fdc_setup_error) { | ||
473 | /* recover... FIXME */ | ||
474 | TRACE_ABORT(-EIO, ft_t_err, "setup error"); | ||
475 | } | ||
476 | } | ||
477 | if (ft_buffer[ft_tail]->status != error) { | ||
478 | TRACE_EXIT 0; | ||
479 | } | ||
480 | TRACE_CATCH(ftape_report_drive_status(&status),); | ||
481 | TRACE(ft_t_noise, "ftape_report_drive_status: 0x%02x", status); | ||
482 | if ((status & QIC_STATUS_READY) && | ||
483 | (status & QIC_STATUS_ERROR)) { | ||
484 | unsigned int error; | ||
485 | qic117_cmd_t command; | ||
486 | |||
487 | /* Report and clear error state. | ||
488 | * In case the drive can't operate at the selected | ||
489 | * rate, select the next lower data rate. | ||
490 | */ | ||
491 | ftape_report_error(&error, &command, 1); | ||
492 | if (error == 31 && command == QIC_LOGICAL_FORWARD) { | ||
493 | /* drive does not accept this data rate */ | ||
494 | if (ft_data_rate > 250) { | ||
495 | TRACE(ft_t_info, | ||
496 | "Probable data rate conflict"); | ||
497 | TRACE(ft_t_info, | ||
498 | "Lowering data rate to %d Kbps", | ||
499 | ft_data_rate / 2); | ||
500 | ftape_half_data_rate(); | ||
501 | if (ft_buffer[ft_tail]->retry > 0) { | ||
502 | /* give it a chance */ | ||
503 | --ft_buffer[ft_tail]->retry; | ||
504 | } | ||
505 | } else { | ||
506 | /* no rate is accepted... */ | ||
507 | TRACE(ft_t_err, "We're dead :("); | ||
508 | } | ||
509 | } else { | ||
510 | TRACE(ft_t_err, "Unknown error"); | ||
511 | } | ||
512 | TRACE_EXIT -EIO; /* g.p. error */ | ||
513 | } | ||
514 | TRACE_EXIT 0; | ||
515 | } | ||
516 | |||
517 | /* forward */ static int seek_forward(int segment_id, int fast); | ||
518 | |||
519 | static int fast_seek(int count, int reverse) | ||
520 | { | ||
521 | int result = 0; | ||
522 | int status; | ||
523 | TRACE_FUN(ft_t_flow); | ||
524 | |||
525 | if (count > 0) { | ||
526 | /* If positioned at begin or end of tape, fast seeking needs | ||
527 | * special treatment. | ||
528 | * Starting from logical bot needs a (slow) seek to the first | ||
529 | * segment before the high speed seek. Most drives do this | ||
530 | * automatically but some older don't, so we treat them | ||
531 | * all the same. | ||
532 | * Starting from logical eot is even more difficult because | ||
533 | * we cannot (slow) reverse seek to the last segment. | ||
534 | * TO BE IMPLEMENTED. | ||
535 | */ | ||
536 | inhibit_correction = 0; | ||
537 | if (ft_location.known && | ||
538 | ((ft_location.bot && !reverse) || | ||
539 | (ft_location.eot && reverse))) { | ||
540 | if (!reverse) { | ||
541 | /* (slow) skip to first segment on a track | ||
542 | */ | ||
543 | seek_forward(ft_location.track * ft_segments_per_track, 0); | ||
544 | --count; | ||
545 | } else { | ||
546 | /* When seeking backwards from | ||
547 | * end-of-tape the number of erased | ||
548 | * gaps found seems to be higher than | ||
549 | * expected. Therefor the drive must | ||
550 | * skip some more segments than | ||
551 | * calculated, but we don't know how | ||
552 | * many. Thus we will prevent the | ||
553 | * re-calculation of offset and | ||
554 | * overshoot when seeking backwards. | ||
555 | */ | ||
556 | inhibit_correction = 1; | ||
557 | count += 3; /* best guess */ | ||
558 | } | ||
559 | } | ||
560 | } else { | ||
561 | TRACE(ft_t_flow, "warning: zero or negative count: %d", count); | ||
562 | } | ||
563 | if (count > 0) { | ||
564 | int i; | ||
565 | int nibbles = count > 255 ? 3 : 2; | ||
566 | |||
567 | if (count > 4095) { | ||
568 | TRACE(ft_t_noise, "skipping clipped at 4095 segment"); | ||
569 | count = 4095; | ||
570 | } | ||
571 | /* Issue this tape command first. */ | ||
572 | if (!reverse) { | ||
573 | TRACE(ft_t_noise, "skipping %d segment(s)", count); | ||
574 | result = ftape_command(nibbles == 3 ? | ||
575 | QIC_SKIP_EXTENDED_FORWARD : QIC_SKIP_FORWARD); | ||
576 | } else { | ||
577 | TRACE(ft_t_noise, "backing up %d segment(s)", count); | ||
578 | result = ftape_command(nibbles == 3 ? | ||
579 | QIC_SKIP_EXTENDED_REVERSE : QIC_SKIP_REVERSE); | ||
580 | } | ||
581 | if (result < 0) { | ||
582 | TRACE(ft_t_noise, "Skip command failed"); | ||
583 | } else { | ||
584 | --count; /* 0 means one gap etc. */ | ||
585 | for (i = 0; i < nibbles; ++i) { | ||
586 | if (result >= 0) { | ||
587 | result = ftape_parameter(count & 15); | ||
588 | count /= 16; | ||
589 | } | ||
590 | } | ||
591 | result = ftape_ready_wait(ftape_timeout.rewind, &status); | ||
592 | if (result >= 0) { | ||
593 | ftape_tape_running = 0; | ||
594 | } | ||
595 | } | ||
596 | } | ||
597 | TRACE_EXIT result; | ||
598 | } | ||
599 | |||
600 | static int validate(int id) | ||
601 | { | ||
602 | /* Check to see if position found is off-track as reported | ||
603 | * once. Because all tracks in one direction lie next to | ||
604 | * each other, if off-track the error will be approximately | ||
605 | * 2 * ft_segments_per_track. | ||
606 | */ | ||
607 | if (ft_location.track == -1) { | ||
608 | return 1; /* unforseen situation, don't generate error */ | ||
609 | } else { | ||
610 | /* Use margin of ft_segments_per_track on both sides | ||
611 | * because ftape needs some margin and the error we're | ||
612 | * looking for is much larger ! | ||
613 | */ | ||
614 | int lo = (ft_location.track - 1) * ft_segments_per_track; | ||
615 | int hi = (ft_location.track + 2) * ft_segments_per_track; | ||
616 | |||
617 | return (id >= lo && id < hi); | ||
618 | } | ||
619 | } | ||
620 | |||
621 | static int seek_forward(int segment_id, int fast) | ||
622 | { | ||
623 | int failures = 0; | ||
624 | int count; | ||
625 | static int margin = 1; /* fixed: stop this before target */ | ||
626 | static int overshoot = 1; | ||
627 | static int min_count = 8; | ||
628 | int expected = -1; | ||
629 | int target = segment_id - margin; | ||
630 | int fast_seeking; | ||
631 | int prev_segment = ft_location.segment; | ||
632 | TRACE_FUN(ft_t_flow); | ||
633 | |||
634 | if (!ft_location.known) { | ||
635 | TRACE_ABORT(-EIO, ft_t_err, | ||
636 | "fatal: cannot seek from unknown location"); | ||
637 | } | ||
638 | if (!validate(segment_id)) { | ||
639 | ftape_sleep(1 * FT_SECOND); | ||
640 | ft_failure = 1; | ||
641 | TRACE_ABORT(-EIO, ft_t_err, | ||
642 | "fatal: head off track (bad hardware?)"); | ||
643 | } | ||
644 | TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", | ||
645 | ft_location.segment, ft_location.sector,segment_id,margin); | ||
646 | count = target - ft_location.segment - overshoot; | ||
647 | fast_seeking = (fast && | ||
648 | count > (min_count + (ft_location.bot ? 1 : 0))); | ||
649 | if (fast_seeking) { | ||
650 | TRACE(ft_t_noise, "fast skipping %d segments", count); | ||
651 | expected = segment_id - margin; | ||
652 | fast_seek(count, 0); | ||
653 | } | ||
654 | if (!ftape_tape_running) { | ||
655 | logical_forward(); | ||
656 | } | ||
657 | while (ft_location.segment < segment_id) { | ||
658 | /* This requires at least one sector in a (bad) segment to | ||
659 | * have a valid and readable sector id ! | ||
660 | * It looks like this is not guaranteed, so we must try | ||
661 | * to find a way to skip an EMPTY_SEGMENT. !!! FIXME !!! | ||
662 | */ | ||
663 | if (ftape_read_id() < 0 || !ft_location.known || | ||
664 | sigtestsetmask(¤t->pending.signal, _DONT_BLOCK)) { | ||
665 | ft_location.known = 0; | ||
666 | if (!ftape_tape_running || | ||
667 | ++failures > FT_SECTORS_PER_SEGMENT) { | ||
668 | TRACE_ABORT(-EIO, ft_t_err, | ||
669 | "read_id failed completely"); | ||
670 | } | ||
671 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
672 | TRACE(ft_t_flow, "read_id failed, retry (%d)", | ||
673 | failures); | ||
674 | continue; | ||
675 | } | ||
676 | if (fast_seeking) { | ||
677 | TRACE(ft_t_noise, "ended at %d/%d (%d,%d)", | ||
678 | ft_location.segment, ft_location.sector, | ||
679 | overshoot, inhibit_correction); | ||
680 | if (!inhibit_correction && | ||
681 | (ft_location.segment < expected || | ||
682 | ft_location.segment > expected + margin)) { | ||
683 | int error = ft_location.segment - expected; | ||
684 | TRACE(ft_t_noise, | ||
685 | "adjusting overshoot from %d to %d", | ||
686 | overshoot, overshoot + error); | ||
687 | overshoot += error; | ||
688 | /* All overshoots have the same | ||
689 | * direction, so it should never | ||
690 | * become negative, but who knows. | ||
691 | */ | ||
692 | if (overshoot < -5 || | ||
693 | overshoot > OVERSHOOT_LIMIT) { | ||
694 | if (overshoot < 0) { | ||
695 | /* keep sane value */ | ||
696 | overshoot = -5; | ||
697 | } else { | ||
698 | /* keep sane value */ | ||
699 | overshoot = OVERSHOOT_LIMIT; | ||
700 | } | ||
701 | TRACE(ft_t_noise, | ||
702 | "clipped overshoot to %d", | ||
703 | overshoot); | ||
704 | } | ||
705 | } | ||
706 | fast_seeking = 0; | ||
707 | } | ||
708 | if (ft_location.known) { | ||
709 | if (ft_location.segment > prev_segment + 1) { | ||
710 | TRACE(ft_t_noise, | ||
711 | "missed segment %d while skipping", | ||
712 | prev_segment + 1); | ||
713 | } | ||
714 | prev_segment = ft_location.segment; | ||
715 | } | ||
716 | } | ||
717 | if (ft_location.segment > segment_id) { | ||
718 | TRACE_ABORT(-EIO, | ||
719 | ft_t_noise, "failed: skip ended at segment %d/%d", | ||
720 | ft_location.segment, ft_location.sector); | ||
721 | } | ||
722 | TRACE_EXIT 0; | ||
723 | } | ||
724 | |||
725 | static int skip_reverse(int segment_id, int *pstatus) | ||
726 | { | ||
727 | int failures = 0; | ||
728 | static int overshoot = 1; | ||
729 | static int min_rewind = 2; /* 1 + overshoot */ | ||
730 | static const int margin = 1; /* stop this before target */ | ||
731 | int expected = 0; | ||
732 | int count = 1; | ||
733 | int short_seek; | ||
734 | int target = segment_id - margin; | ||
735 | TRACE_FUN(ft_t_flow); | ||
736 | |||
737 | if (ft_location.known && !validate(segment_id)) { | ||
738 | ftape_sleep(1 * FT_SECOND); | ||
739 | ft_failure = 1; | ||
740 | TRACE_ABORT(-EIO, ft_t_err, | ||
741 | "fatal: head off track (bad hardware?)"); | ||
742 | } | ||
743 | do { | ||
744 | if (!ft_location.known) { | ||
745 | TRACE(ft_t_warn, "warning: location not known"); | ||
746 | } | ||
747 | TRACE(ft_t_noise, "from %d/%d to %d/0 - %d", | ||
748 | ft_location.segment, ft_location.sector, | ||
749 | segment_id, margin); | ||
750 | /* min_rewind == 1 + overshoot_when_doing_minimum_rewind | ||
751 | * overshoot == overshoot_when_doing_larger_rewind | ||
752 | * Initially min_rewind == 1 + overshoot, optimization | ||
753 | * of both values will be done separately. | ||
754 | * overshoot and min_rewind can be negative as both are | ||
755 | * sums of three components: | ||
756 | * any_overshoot == rewind_overshoot - | ||
757 | * stop_overshoot - | ||
758 | * start_overshoot | ||
759 | */ | ||
760 | if (ft_location.segment - target - (min_rewind - 1) < 1) { | ||
761 | short_seek = 1; | ||
762 | } else { | ||
763 | count = ft_location.segment - target - overshoot; | ||
764 | short_seek = (count < 1); | ||
765 | } | ||
766 | if (short_seek) { | ||
767 | count = 1; /* do shortest rewind */ | ||
768 | expected = ft_location.segment - min_rewind; | ||
769 | if (expected/ft_segments_per_track != ft_location.track) { | ||
770 | expected = (ft_location.track * | ||
771 | ft_segments_per_track); | ||
772 | } | ||
773 | } else { | ||
774 | expected = target; | ||
775 | } | ||
776 | fast_seek(count, 1); | ||
777 | logical_forward(); | ||
778 | if (ftape_read_id() < 0 || !ft_location.known || | ||
779 | (sigtestsetmask(¤t->pending.signal, _DONT_BLOCK))) { | ||
780 | if ((!ftape_tape_running && !ft_location.known) || | ||
781 | ++failures > FT_SECTORS_PER_SEGMENT) { | ||
782 | TRACE_ABORT(-EIO, ft_t_err, | ||
783 | "read_id failed completely"); | ||
784 | } | ||
785 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
786 | TRACE_CATCH(ftape_report_drive_status(pstatus),); | ||
787 | TRACE(ft_t_noise, "ftape_read_id failed, retry (%d)", | ||
788 | failures); | ||
789 | continue; | ||
790 | } | ||
791 | TRACE(ft_t_noise, "ended at %d/%d (%d,%d,%d)", | ||
792 | ft_location.segment, ft_location.sector, | ||
793 | min_rewind, overshoot, inhibit_correction); | ||
794 | if (!inhibit_correction && | ||
795 | (ft_location.segment < expected || | ||
796 | ft_location.segment > expected + margin)) { | ||
797 | int error = expected - ft_location.segment; | ||
798 | if (short_seek) { | ||
799 | TRACE(ft_t_noise, | ||
800 | "adjusting min_rewind from %d to %d", | ||
801 | min_rewind, min_rewind + error); | ||
802 | min_rewind += error; | ||
803 | if (min_rewind < -5) { | ||
804 | /* is this right ? FIXME ! */ | ||
805 | /* keep sane value */ | ||
806 | min_rewind = -5; | ||
807 | TRACE(ft_t_noise, | ||
808 | "clipped min_rewind to %d", | ||
809 | min_rewind); | ||
810 | } | ||
811 | } else { | ||
812 | TRACE(ft_t_noise, | ||
813 | "adjusting overshoot from %d to %d", | ||
814 | overshoot, overshoot + error); | ||
815 | overshoot += error; | ||
816 | if (overshoot < -5 || | ||
817 | overshoot > OVERSHOOT_LIMIT) { | ||
818 | if (overshoot < 0) { | ||
819 | /* keep sane value */ | ||
820 | overshoot = -5; | ||
821 | } else { | ||
822 | /* keep sane value */ | ||
823 | overshoot = OVERSHOOT_LIMIT; | ||
824 | } | ||
825 | TRACE(ft_t_noise, | ||
826 | "clipped overshoot to %d", | ||
827 | overshoot); | ||
828 | } | ||
829 | } | ||
830 | } | ||
831 | } while (ft_location.segment > segment_id); | ||
832 | if (ft_location.known) { | ||
833 | TRACE(ft_t_noise, "current location: %d/%d", | ||
834 | ft_location.segment, ft_location.sector); | ||
835 | } | ||
836 | TRACE_EXIT 0; | ||
837 | } | ||
838 | |||
839 | static int determine_position(void) | ||
840 | { | ||
841 | int retry = 0; | ||
842 | int status; | ||
843 | int result; | ||
844 | TRACE_FUN(ft_t_flow); | ||
845 | |||
846 | if (!ftape_tape_running) { | ||
847 | /* This should only happen if tape is stopped by isr. | ||
848 | */ | ||
849 | TRACE(ft_t_flow, "waiting for tape stop"); | ||
850 | if (ftape_ready_wait(ftape_timeout.pause, &status) < 0) { | ||
851 | TRACE(ft_t_flow, "drive still running (fatal)"); | ||
852 | ftape_tape_running = 1; /* ? */ | ||
853 | } | ||
854 | } else { | ||
855 | ftape_report_drive_status(&status); | ||
856 | } | ||
857 | if (status & QIC_STATUS_READY) { | ||
858 | /* Drive must be ready to check error state ! | ||
859 | */ | ||
860 | TRACE(ft_t_flow, "drive is ready"); | ||
861 | if (status & QIC_STATUS_ERROR) { | ||
862 | unsigned int error; | ||
863 | qic117_cmd_t command; | ||
864 | |||
865 | /* Report and clear error state, try to continue. | ||
866 | */ | ||
867 | TRACE(ft_t_flow, "error status set"); | ||
868 | ftape_report_error(&error, &command, 1); | ||
869 | ftape_ready_wait(ftape_timeout.reset, &status); | ||
870 | ftape_tape_running = 0; /* ? */ | ||
871 | } | ||
872 | if (check_bot_eot(status)) { | ||
873 | if (ft_location.bot) { | ||
874 | if ((status & QIC_STATUS_READY) == 0) { | ||
875 | /* tape moving away from | ||
876 | * bot/eot, let's see if we | ||
877 | * can catch up with the first | ||
878 | * segment on this track. | ||
879 | */ | ||
880 | } else { | ||
881 | TRACE(ft_t_flow, | ||
882 | "start tape from logical bot"); | ||
883 | logical_forward(); /* start moving */ | ||
884 | } | ||
885 | } else { | ||
886 | if ((status & QIC_STATUS_READY) == 0) { | ||
887 | TRACE(ft_t_noise, "waiting for logical end of track"); | ||
888 | result = ftape_ready_wait(ftape_timeout.reset, &status); | ||
889 | /* error handling needed ? */ | ||
890 | } else { | ||
891 | TRACE(ft_t_noise, | ||
892 | "tape at logical end of track"); | ||
893 | } | ||
894 | } | ||
895 | } else { | ||
896 | TRACE(ft_t_flow, "start tape"); | ||
897 | logical_forward(); /* start moving */ | ||
898 | ft_location.known = 0; /* not cleared by logical forward ! */ | ||
899 | } | ||
900 | } | ||
901 | /* tape should be moving now, start reading id's | ||
902 | */ | ||
903 | while (!ft_location.known && | ||
904 | retry++ < FT_SECTORS_PER_SEGMENT && | ||
905 | (result = ftape_read_id()) < 0) { | ||
906 | |||
907 | TRACE(ft_t_flow, "location unknown"); | ||
908 | |||
909 | /* exit on signal | ||
910 | */ | ||
911 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
912 | |||
913 | /* read-id somehow failed, tape may | ||
914 | * have reached end or some other | ||
915 | * error happened. | ||
916 | */ | ||
917 | TRACE(ft_t_flow, "read-id failed"); | ||
918 | TRACE_CATCH(ftape_report_drive_status(&status),); | ||
919 | TRACE(ft_t_err, "ftape_report_drive_status: 0x%02x", status); | ||
920 | if (status & QIC_STATUS_READY) { | ||
921 | ftape_tape_running = 0; | ||
922 | TRACE(ft_t_noise, "tape stopped for unknown reason! " | ||
923 | "status = 0x%02x", status); | ||
924 | if (status & QIC_STATUS_ERROR || | ||
925 | !check_bot_eot(status)) { | ||
926 | /* oops, tape stopped but not at end! | ||
927 | */ | ||
928 | TRACE_EXIT -EIO; | ||
929 | } | ||
930 | } | ||
931 | } | ||
932 | TRACE(ft_t_flow, | ||
933 | "tape is positioned at segment %d", ft_location.segment); | ||
934 | TRACE_EXIT ft_location.known ? 0 : -EIO; | ||
935 | } | ||
936 | |||
937 | /* Get the tape running and position it just before the | ||
938 | * requested segment. | ||
939 | * Seek tape-track and reposition as needed. | ||
940 | */ | ||
941 | int ftape_start_tape(int segment_id, int sector_offset) | ||
942 | { | ||
943 | int track = segment_id / ft_segments_per_track; | ||
944 | int result = -EIO; | ||
945 | int status; | ||
946 | static int last_segment = -1; | ||
947 | static int bad_bus_timing = 0; | ||
948 | /* number of segments passing the head between starting the tape | ||
949 | * and being able to access the first sector. | ||
950 | */ | ||
951 | static int start_offset = 1; | ||
952 | int retry; | ||
953 | TRACE_FUN(ft_t_flow); | ||
954 | |||
955 | /* If sector_offset > 0, seek into wanted segment instead of | ||
956 | * into previous. | ||
957 | * This allows error recovery if a part of the segment is bad | ||
958 | * (erased) causing the tape drive to generate an index pulse | ||
959 | * thus causing a no-data error before the requested sector | ||
960 | * is reached. | ||
961 | */ | ||
962 | ftape_tape_running = 0; | ||
963 | TRACE(ft_t_noise, "target segment: %d/%d%s", segment_id, sector_offset, | ||
964 | ft_buffer[ft_head]->retry > 0 ? " retry" : ""); | ||
965 | if (ft_buffer[ft_head]->retry > 0) { /* this is a retry */ | ||
966 | int dist = segment_id - last_segment; | ||
967 | |||
968 | if ((int)ft_history.overrun_errors < overrun_count_offset) { | ||
969 | overrun_count_offset = ft_history.overrun_errors; | ||
970 | } else if (dist < 0 || dist > 50) { | ||
971 | overrun_count_offset = ft_history.overrun_errors; | ||
972 | } else if ((ft_history.overrun_errors - | ||
973 | overrun_count_offset) >= 8) { | ||
974 | if (ftape_increase_threshold() >= 0) { | ||
975 | --ft_buffer[ft_head]->retry; | ||
976 | overrun_count_offset = | ||
977 | ft_history.overrun_errors; | ||
978 | TRACE(ft_t_warn, "increased threshold because " | ||
979 | "of excessive overrun errors"); | ||
980 | } else if (!bad_bus_timing && ft_data_rate >= 1000) { | ||
981 | ftape_half_data_rate(); | ||
982 | --ft_buffer[ft_head]->retry; | ||
983 | bad_bus_timing = 1; | ||
984 | overrun_count_offset = | ||
985 | ft_history.overrun_errors; | ||
986 | TRACE(ft_t_warn, "reduced datarate because " | ||
987 | "of excessive overrun errors"); | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | last_segment = segment_id; | ||
992 | if (ft_location.track != track || | ||
993 | (ftape_might_be_off_track && ft_buffer[ft_head]->retry== 0)) { | ||
994 | /* current track unknown or not equal to destination | ||
995 | */ | ||
996 | ftape_ready_wait(ftape_timeout.seek, &status); | ||
997 | ftape_seek_head_to_track(track); | ||
998 | /* overrun_count_offset = ft_history.overrun_errors; */ | ||
999 | } | ||
1000 | result = -EIO; | ||
1001 | retry = 0; | ||
1002 | while (result < 0 && | ||
1003 | retry++ <= 5 && | ||
1004 | !ft_failure && | ||
1005 | !(sigtestsetmask(¤t->pending.signal, _DONT_BLOCK))) { | ||
1006 | |||
1007 | if (retry && start_offset < 5) { | ||
1008 | start_offset ++; | ||
1009 | } | ||
1010 | /* Check if we are able to catch the requested | ||
1011 | * segment in time. | ||
1012 | */ | ||
1013 | if ((ft_location.known || (determine_position() == 0)) && | ||
1014 | ft_location.segment >= | ||
1015 | (segment_id - | ||
1016 | ((ftape_tape_running || ft_location.bot) | ||
1017 | ? 0 : start_offset))) { | ||
1018 | /* Too far ahead (in or past target segment). | ||
1019 | */ | ||
1020 | if (ftape_tape_running) { | ||
1021 | if ((result = ftape_stop_tape(&status)) < 0) { | ||
1022 | TRACE(ft_t_err, | ||
1023 | "stop tape failed with code %d", | ||
1024 | result); | ||
1025 | break; | ||
1026 | } | ||
1027 | TRACE(ft_t_noise, "tape stopped"); | ||
1028 | ftape_tape_running = 0; | ||
1029 | } | ||
1030 | TRACE(ft_t_noise, "repositioning"); | ||
1031 | ++ft_history.rewinds; | ||
1032 | if (segment_id % ft_segments_per_track < start_offset){ | ||
1033 | TRACE(ft_t_noise, "end of track condition\n" | ||
1034 | KERN_INFO "segment_id : %d\n" | ||
1035 | KERN_INFO "ft_segments_per_track: %d\n" | ||
1036 | KERN_INFO "start_offset : %d", | ||
1037 | segment_id, ft_segments_per_track, | ||
1038 | start_offset); | ||
1039 | |||
1040 | /* If seeking to first segments on | ||
1041 | * track better do a complete rewind | ||
1042 | * to logical begin of track to get a | ||
1043 | * more steady tape motion. | ||
1044 | */ | ||
1045 | result = ftape_command_wait( | ||
1046 | (ft_location.track & 1) | ||
1047 | ? QIC_PHYSICAL_FORWARD | ||
1048 | : QIC_PHYSICAL_REVERSE, | ||
1049 | ftape_timeout.rewind, &status); | ||
1050 | check_bot_eot(status); /* update location */ | ||
1051 | } else { | ||
1052 | result= skip_reverse(segment_id - start_offset, | ||
1053 | &status); | ||
1054 | } | ||
1055 | } | ||
1056 | if (!ft_location.known) { | ||
1057 | TRACE(ft_t_bug, "panic: location not known"); | ||
1058 | result = -EIO; | ||
1059 | continue; /* while() will check for failure */ | ||
1060 | } | ||
1061 | TRACE(ft_t_noise, "current segment: %d/%d", | ||
1062 | ft_location.segment, ft_location.sector); | ||
1063 | /* We're on the right track somewhere before the | ||
1064 | * wanted segment. Start tape movement if needed and | ||
1065 | * skip to just before or inside the requested | ||
1066 | * segment. Keep tape running. | ||
1067 | */ | ||
1068 | result = 0; | ||
1069 | if (ft_location.segment < | ||
1070 | (segment_id - ((ftape_tape_running || ft_location.bot) | ||
1071 | ? 0 : start_offset))) { | ||
1072 | if (sector_offset > 0) { | ||
1073 | result = seek_forward(segment_id, | ||
1074 | retry <= 3); | ||
1075 | } else { | ||
1076 | result = seek_forward(segment_id - 1, | ||
1077 | retry <= 3); | ||
1078 | } | ||
1079 | } | ||
1080 | if (result == 0 && | ||
1081 | ft_location.segment != | ||
1082 | (segment_id - (sector_offset > 0 ? 0 : 1))) { | ||
1083 | result = -EIO; | ||
1084 | } | ||
1085 | } | ||
1086 | if (result < 0) { | ||
1087 | TRACE(ft_t_err, "failed to reposition"); | ||
1088 | } else { | ||
1089 | ft_runner_status = running; | ||
1090 | } | ||
1091 | TRACE_EXIT result; | ||
1092 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-rw.h b/drivers/char/ftape/lowlevel/ftape-rw.h new file mode 100644 index 000000000000..32f4feeb887c --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-rw.h | |||
@@ -0,0 +1,111 @@ | |||
1 | #ifndef _FTAPE_RW_H | ||
2 | #define _FTAPE_RW_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-rw.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:25 $ | ||
26 | * | ||
27 | * This file contains the definitions for the read and write | ||
28 | * functions for the QIC-117 floppy-tape driver for Linux. | ||
29 | * | ||
30 | * Claus-Justus Heine (1996/09/20): Add definition of format code 6 | ||
31 | * Claus-Justus Heine (1996/10/04): Changed GET/PUT macros to cast to (__u8 *) | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | #include "../lowlevel/fdc-io.h" | ||
36 | #include "../lowlevel/ftape-init.h" | ||
37 | #include "../lowlevel/ftape-bsm.h" | ||
38 | |||
39 | #include <asm/unaligned.h> | ||
40 | |||
41 | #define GET2(address, offset) get_unaligned((__u16*)((__u8 *)address + offset)) | ||
42 | #define GET4(address, offset) get_unaligned((__u32*)((__u8 *)address + offset)) | ||
43 | #define GET8(address, offset) get_unaligned((__u64*)((__u8 *)address + offset)) | ||
44 | #define PUT2(address, offset , value) put_unaligned((value), (__u16*)((__u8 *)address + offset)) | ||
45 | #define PUT4(address, offset , value) put_unaligned((value), (__u32*)((__u8 *)address + offset)) | ||
46 | #define PUT8(address, offset , value) put_unaligned((value), (__u64*)((__u8 *)address + offset)) | ||
47 | |||
48 | enum runner_status_enum { | ||
49 | idle = 0, | ||
50 | running, | ||
51 | do_abort, | ||
52 | aborting, | ||
53 | logical_eot, | ||
54 | end_of_tape, | ||
55 | }; | ||
56 | |||
57 | typedef enum ft_buffer_queue { | ||
58 | ft_queue_head = 0, | ||
59 | ft_queue_tail = 1 | ||
60 | } ft_buffer_queue_t; | ||
61 | |||
62 | |||
63 | typedef struct { | ||
64 | int track; /* tape head position */ | ||
65 | volatile int segment; /* current segment */ | ||
66 | volatile int sector; /* sector offset within current segment */ | ||
67 | volatile unsigned int bot; /* logical begin of track */ | ||
68 | volatile unsigned int eot; /* logical end of track */ | ||
69 | volatile unsigned int known; /* validates bot, segment, sector */ | ||
70 | } location_record; | ||
71 | |||
72 | /* Count nr of 1's in pattern. | ||
73 | */ | ||
74 | static inline int count_ones(unsigned long mask) | ||
75 | { | ||
76 | int bits; | ||
77 | |||
78 | for (bits = 0; mask != 0; mask >>= 1) { | ||
79 | if (mask & 1) { | ||
80 | ++bits; | ||
81 | } | ||
82 | } | ||
83 | return bits; | ||
84 | } | ||
85 | |||
86 | #define FT_MAX_NR_BUFFERS 16 /* arbitrary value */ | ||
87 | /* ftape-rw.c defined global vars. | ||
88 | */ | ||
89 | extern buffer_struct *ft_buffer[FT_MAX_NR_BUFFERS]; | ||
90 | extern int ft_nr_buffers; | ||
91 | extern location_record ft_location; | ||
92 | extern volatile int ftape_tape_running; | ||
93 | |||
94 | /* ftape-rw.c defined global functions. | ||
95 | */ | ||
96 | extern int ftape_setup_new_segment(buffer_struct * buff, | ||
97 | int segment_id, | ||
98 | int offset); | ||
99 | extern int ftape_calc_next_cluster(buffer_struct * buff); | ||
100 | extern buffer_struct *ftape_next_buffer (ft_buffer_queue_t pos); | ||
101 | extern buffer_struct *ftape_get_buffer (ft_buffer_queue_t pos); | ||
102 | extern int ftape_buffer_id (ft_buffer_queue_t pos); | ||
103 | extern void ftape_reset_buffer(void); | ||
104 | extern void ftape_tape_parameters(__u8 drive_configuration); | ||
105 | extern int ftape_wait_segment(buffer_state_enum state); | ||
106 | extern int ftape_dumb_stop(void); | ||
107 | extern int ftape_start_tape(int segment_id, int offset); | ||
108 | extern int ftape_stop_tape(int *pstatus); | ||
109 | extern int ftape_handle_logical_eot(void); | ||
110 | extern buffer_state_enum ftape_set_state(buffer_state_enum new_state); | ||
111 | #endif /* _FTAPE_RW_H */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-setup.c b/drivers/char/ftape/lowlevel/ftape-setup.c new file mode 100644 index 000000000000..280a1a55d87e --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-setup.c | |||
@@ -0,0 +1,105 @@ | |||
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/lowlevel/ftape-setup.c,v $ | ||
20 | * $Revision: 1.7 $ | ||
21 | * $Date: 1997/10/10 09:57:06 $ | ||
22 | * | ||
23 | * This file contains the code for processing the kernel command | ||
24 | * line options for the QIC-40/80/3010/3020 floppy-tape driver | ||
25 | * "ftape" for Linux. | ||
26 | */ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/string.h> | ||
30 | #include <linux/errno.h> | ||
31 | #include <linux/mm.h> | ||
32 | |||
33 | #include <linux/ftape.h> | ||
34 | #include <linux/init.h> | ||
35 | #include "../lowlevel/ftape-tracing.h" | ||
36 | #include "../lowlevel/fdc-io.h" | ||
37 | |||
38 | static struct param_table { | ||
39 | const char *name; | ||
40 | int *var; | ||
41 | int def_param; | ||
42 | int min; | ||
43 | int max; | ||
44 | } config_params[] __initdata = { | ||
45 | #ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||
46 | { "tracing", &ftape_tracing, 3, ft_t_bug, ft_t_any}, | ||
47 | #endif | ||
48 | { "ioport", &ft_fdc_base, CONFIG_FT_FDC_BASE, 0x0, 0xfff}, | ||
49 | { "irq", &ft_fdc_irq, CONFIG_FT_FDC_IRQ, 2, 15}, | ||
50 | { "dma", &ft_fdc_dma, CONFIG_FT_FDC_DMA, 0, 3}, | ||
51 | { "threshold", &ft_fdc_threshold, CONFIG_FT_FDC_THR, 1, 16}, | ||
52 | { "datarate", &ft_fdc_rate_limit, CONFIG_FT_FDC_MAX_RATE, 500, 2000}, | ||
53 | { "fc10", &ft_probe_fc10, CONFIG_FT_PROBE_FC10, 0, 1}, | ||
54 | { "mach2", &ft_mach2, CONFIG_FT_MACH2, 0, 1} | ||
55 | }; | ||
56 | |||
57 | static int __init ftape_setup(char *str) | ||
58 | { | ||
59 | int i; | ||
60 | int param; | ||
61 | int ints[2]; | ||
62 | |||
63 | TRACE_FUN(ft_t_flow); | ||
64 | |||
65 | str = get_options(str, ARRAY_SIZE(ints), ints); | ||
66 | if (str) { | ||
67 | for (i=0; i < NR_ITEMS(config_params); i++) { | ||
68 | if (strcmp(str,config_params[i].name) == 0){ | ||
69 | if (ints[0]) { | ||
70 | param = ints[1]; | ||
71 | } else { | ||
72 | param = config_params[i].def_param; | ||
73 | } | ||
74 | if (param < config_params[i].min || | ||
75 | param > config_params[i].max) { | ||
76 | TRACE(ft_t_err, | ||
77 | "parameter %s out of range %d ... %d", | ||
78 | config_params[i].name, | ||
79 | config_params[i].min, | ||
80 | config_params[i].max); | ||
81 | goto out; | ||
82 | } | ||
83 | if(config_params[i].var) { | ||
84 | TRACE(ft_t_info, "%s=%d", str, param); | ||
85 | *config_params[i].var = param; | ||
86 | } | ||
87 | goto out; | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | if (str) { | ||
92 | TRACE(ft_t_err, "unknown ftape option [%s]", str); | ||
93 | |||
94 | TRACE(ft_t_err, "allowed options are:"); | ||
95 | for (i=0; i < NR_ITEMS(config_params); i++) { | ||
96 | TRACE(ft_t_err, " %s",config_params[i].name); | ||
97 | } | ||
98 | } else { | ||
99 | TRACE(ft_t_err, "botched ftape option"); | ||
100 | } | ||
101 | out: | ||
102 | TRACE_EXIT 1; | ||
103 | } | ||
104 | |||
105 | __setup("ftape=", ftape_setup); | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.c b/drivers/char/ftape/lowlevel/ftape-tracing.c new file mode 100644 index 000000000000..7fdc6567440b --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-tracing.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1996 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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-tracing.c,v $ | ||
21 | * $Revision: 1.2 $ | ||
22 | * $Date: 1997/10/05 19:18:27 $ | ||
23 | * | ||
24 | * This file contains the reading code | ||
25 | * for the QIC-117 floppy-tape driver for Linux. | ||
26 | */ | ||
27 | |||
28 | #include <linux/ftape.h> | ||
29 | #include "../lowlevel/ftape-tracing.h" | ||
30 | |||
31 | /* Global vars. | ||
32 | */ | ||
33 | /* tracing | ||
34 | * set it to: to log : | ||
35 | * 0 bugs | ||
36 | * 1 + errors | ||
37 | * 2 + warnings | ||
38 | * 3 + information | ||
39 | * 4 + more information | ||
40 | * 5 + program flow | ||
41 | * 6 + fdc/dma info | ||
42 | * 7 + data flow | ||
43 | * 8 + everything else | ||
44 | */ | ||
45 | ft_trace_t ftape_tracing = ft_t_info; /* Default level: information and up */ | ||
46 | int ftape_function_nest_level; | ||
47 | |||
48 | /* Local vars. | ||
49 | */ | ||
50 | static __u8 trace_id; | ||
51 | static char spacing[] = "* "; | ||
52 | |||
53 | void ftape_trace_call(const char *file, const char *name) | ||
54 | { | ||
55 | char *indent; | ||
56 | |||
57 | /* Since printk seems not to work with "%*s" format | ||
58 | * we'll use this work-around. | ||
59 | */ | ||
60 | if (ftape_function_nest_level < 0) { | ||
61 | printk(KERN_INFO "function nest level (%d) < 0\n", | ||
62 | ftape_function_nest_level); | ||
63 | ftape_function_nest_level = 0; | ||
64 | } | ||
65 | if (ftape_function_nest_level < sizeof(spacing)) { | ||
66 | indent = (spacing + | ||
67 | sizeof(spacing) - 1 - | ||
68 | ftape_function_nest_level); | ||
69 | } else { | ||
70 | indent = spacing; | ||
71 | } | ||
72 | printk(KERN_INFO "[%03d]%s+%s (%s)\n", | ||
73 | (int) trace_id++, indent, file, name); | ||
74 | } | ||
75 | |||
76 | void ftape_trace_exit(const char *file, const char *name) | ||
77 | { | ||
78 | char *indent; | ||
79 | |||
80 | /* Since printk seems not to work with "%*s" format | ||
81 | * we'll use this work-around. | ||
82 | */ | ||
83 | if (ftape_function_nest_level < 0) { | ||
84 | printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level); | ||
85 | ftape_function_nest_level = 0; | ||
86 | } | ||
87 | if (ftape_function_nest_level < sizeof(spacing)) { | ||
88 | indent = (spacing + | ||
89 | sizeof(spacing) - 1 - | ||
90 | ftape_function_nest_level); | ||
91 | } else { | ||
92 | indent = spacing; | ||
93 | } | ||
94 | printk(KERN_INFO "[%03d]%s-%s (%s)\n", | ||
95 | (int) trace_id++, indent, file, name); | ||
96 | } | ||
97 | |||
98 | void ftape_trace_log(const char *file, const char *function) | ||
99 | { | ||
100 | char *indent; | ||
101 | |||
102 | /* Since printk seems not to work with "%*s" format | ||
103 | * we'll use this work-around. | ||
104 | */ | ||
105 | if (ftape_function_nest_level < 0) { | ||
106 | printk(KERN_INFO "function nest level (%d) < 0\n", ftape_function_nest_level); | ||
107 | ftape_function_nest_level = 0; | ||
108 | } | ||
109 | if (ftape_function_nest_level < sizeof(spacing)) { | ||
110 | indent = (spacing + | ||
111 | sizeof(spacing) - 1 - | ||
112 | ftape_function_nest_level); | ||
113 | } else { | ||
114 | indent = spacing; | ||
115 | } | ||
116 | printk(KERN_INFO "[%03d]%s%s (%s) - ", | ||
117 | (int) trace_id++, indent, file, function); | ||
118 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-tracing.h b/drivers/char/ftape/lowlevel/ftape-tracing.h new file mode 100644 index 000000000000..fa7cd20ee66c --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-tracing.h | |||
@@ -0,0 +1,180 @@ | |||
1 | #ifndef _FTAPE_TRACING_H | ||
2 | #define _FTAPE_TRACING_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1996 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-tracing.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:18:28 $ | ||
26 | * | ||
27 | * This file contains definitions that eases the debugging of the | ||
28 | * QIC-40/80/3010/3020 floppy-tape driver "ftape" for Linux. | ||
29 | */ | ||
30 | |||
31 | #include <linux/config.h> | ||
32 | #include <linux/kernel.h> | ||
33 | |||
34 | /* | ||
35 | * Be very careful with TRACE_EXIT and TRACE_ABORT. | ||
36 | * | ||
37 | * if (something) TRACE_EXIT error; | ||
38 | * | ||
39 | * will NOT work. Use | ||
40 | * | ||
41 | * if (something) { | ||
42 | * TRACE_EXIT error; | ||
43 | * } | ||
44 | * | ||
45 | * instead. Maybe a bit dangerous, but save lots of lines of code. | ||
46 | */ | ||
47 | |||
48 | #define LL_X "%d/%d KB" | ||
49 | #define LL(x) (unsigned int)((__u64)(x)>>10), (unsigned int)((x)&1023) | ||
50 | |||
51 | typedef enum { | ||
52 | ft_t_nil = -1, | ||
53 | ft_t_bug, | ||
54 | ft_t_err, | ||
55 | ft_t_warn, | ||
56 | ft_t_info, | ||
57 | ft_t_noise, | ||
58 | ft_t_flow, | ||
59 | ft_t_fdc_dma, | ||
60 | ft_t_data_flow, | ||
61 | ft_t_any | ||
62 | } ft_trace_t; | ||
63 | |||
64 | #ifdef CONFIG_FT_NO_TRACE_AT_ALL | ||
65 | /* the compiler will optimize away most TRACE() macros | ||
66 | */ | ||
67 | #define FT_TRACE_TOP_LEVEL ft_t_bug | ||
68 | #define TRACE_FUN(level) do {} while(0) | ||
69 | #define TRACE_EXIT return | ||
70 | #define TRACE(l, m, i...) \ | ||
71 | { \ | ||
72 | if ((ft_trace_t)(l) == FT_TRACE_TOP_LEVEL) { \ | ||
73 | printk(KERN_INFO"ftape%s(%s):\n" \ | ||
74 | KERN_INFO m".\n" ,__FILE__, __FUNCTION__ , ##i); \ | ||
75 | } \ | ||
76 | } | ||
77 | #define SET_TRACE_LEVEL(l) if ((l) == (l)) do {} while(0) | ||
78 | #define TRACE_LEVEL FT_TRACE_TOP_LEVEL | ||
79 | |||
80 | #else | ||
81 | |||
82 | #ifdef CONFIG_FT_NO_TRACE | ||
83 | /* the compiler will optimize away many TRACE() macros | ||
84 | * the ftape_simple_trace_call() function simply increments | ||
85 | * the function nest level. | ||
86 | */ | ||
87 | #define FT_TRACE_TOP_LEVEL ft_t_warn | ||
88 | #define TRACE_FUN(level) ftape_function_nest_level++ | ||
89 | #define TRACE_EXIT ftape_function_nest_level--; return | ||
90 | |||
91 | #else | ||
92 | #ifdef CONFIG_FT_FULL_DEBUG | ||
93 | #define FT_TRACE_TOP_LEVEL ft_t_any | ||
94 | #else | ||
95 | #define FT_TRACE_TOP_LEVEL ft_t_flow | ||
96 | #endif | ||
97 | #define TRACE_FUN(level) \ | ||
98 | const ft_trace_t _tracing = level; \ | ||
99 | if (ftape_tracing >= (ft_trace_t)(level) && \ | ||
100 | (ft_trace_t)(level) <= FT_TRACE_TOP_LEVEL) \ | ||
101 | ftape_trace_call(__FILE__, __FUNCTION__); \ | ||
102 | ftape_function_nest_level ++; | ||
103 | |||
104 | #define TRACE_EXIT \ | ||
105 | --ftape_function_nest_level; \ | ||
106 | if (ftape_tracing >= (ft_trace_t)(_tracing) && \ | ||
107 | (ft_trace_t)(_tracing) <= FT_TRACE_TOP_LEVEL) \ | ||
108 | ftape_trace_exit(__FILE__, __FUNCTION__); \ | ||
109 | return | ||
110 | |||
111 | #endif | ||
112 | |||
113 | #define TRACE(l, m, i...) \ | ||
114 | { \ | ||
115 | if (ftape_tracing >= (ft_trace_t)(l) && \ | ||
116 | (ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \ | ||
117 | ftape_trace_log(__FILE__, __FUNCTION__); \ | ||
118 | printk(m".\n" ,##i); \ | ||
119 | } \ | ||
120 | } | ||
121 | |||
122 | #define SET_TRACE_LEVEL(l) \ | ||
123 | { \ | ||
124 | if ((ft_trace_t)(l) <= FT_TRACE_TOP_LEVEL) { \ | ||
125 | ftape_tracing = (ft_trace_t)(l); \ | ||
126 | } else { \ | ||
127 | ftape_tracing = FT_TRACE_TOP_LEVEL; \ | ||
128 | } \ | ||
129 | } | ||
130 | #define TRACE_LEVEL \ | ||
131 | ((ftape_tracing <= FT_TRACE_TOP_LEVEL) ? ftape_tracing : FT_TRACE_TOP_LEVEL) | ||
132 | |||
133 | |||
134 | /* Global variables declared in tracing.c | ||
135 | */ | ||
136 | extern ft_trace_t ftape_tracing; /* sets default level */ | ||
137 | extern int ftape_function_nest_level; | ||
138 | |||
139 | /* Global functions declared in tracing.c | ||
140 | */ | ||
141 | extern void ftape_trace_call(const char *file, const char *name); | ||
142 | extern void ftape_trace_exit(const char *file, const char *name); | ||
143 | extern void ftape_trace_log (const char *file, const char *name); | ||
144 | |||
145 | #endif /* !defined(CONFIG_FT_NO_TRACE_AT_ALL) */ | ||
146 | |||
147 | /* | ||
148 | * Abort with a message. | ||
149 | */ | ||
150 | #define TRACE_ABORT(res, i...) \ | ||
151 | { \ | ||
152 | TRACE(i); \ | ||
153 | TRACE_EXIT res; \ | ||
154 | } | ||
155 | |||
156 | /* The following transforms the common "if(result < 0) ... " into a | ||
157 | * one-liner. | ||
158 | */ | ||
159 | #define _TRACE_CATCH(level, fun, action) \ | ||
160 | { \ | ||
161 | int _res = (fun); \ | ||
162 | if (_res < 0) { \ | ||
163 | do { action /* */ ; } while(0); \ | ||
164 | TRACE_ABORT(_res, level, "%s failed: %d", #fun, _res); \ | ||
165 | } \ | ||
166 | } | ||
167 | |||
168 | #define TRACE_CATCH(fun, fail) _TRACE_CATCH(ft_t_err, fun, fail) | ||
169 | |||
170 | /* Abort the current function when signalled. This doesn't belong here, | ||
171 | * but rather into ftape-rw.h (maybe) | ||
172 | */ | ||
173 | #define FT_SIGNAL_EXIT(sig_mask) \ | ||
174 | if (sigtestsetmask(¤t->pending.signal, sig_mask)) { \ | ||
175 | TRACE_ABORT(-EINTR, \ | ||
176 | ft_t_warn, \ | ||
177 | "interrupted by non-blockable signal"); \ | ||
178 | } | ||
179 | |||
180 | #endif /* _FTAPE_TRACING_H */ | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-write.c b/drivers/char/ftape/lowlevel/ftape-write.c new file mode 100644 index 000000000000..45601ec801ee --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-write.c | |||
@@ -0,0 +1,336 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1993-1995 Bas Laarhoven, | ||
3 | * (C) 1996-1997 Claus-Justus 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-write.c,v $ | ||
21 | * $Revision: 1.3.4.1 $ | ||
22 | * $Date: 1997/11/14 18:07:04 $ | ||
23 | * | ||
24 | * This file contains the writing code | ||
25 | * for the QIC-117 floppy-tape driver for Linux. | ||
26 | */ | ||
27 | |||
28 | #include <linux/string.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <linux/mm.h> | ||
31 | |||
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" | ||
43 | |||
44 | /* Global vars. | ||
45 | */ | ||
46 | |||
47 | /* Local vars. | ||
48 | */ | ||
49 | static int last_write_failed; | ||
50 | |||
51 | void ftape_zap_write_buffers(void) | ||
52 | { | ||
53 | int i; | ||
54 | |||
55 | for (i = 0; i < ft_nr_buffers; ++i) { | ||
56 | ft_buffer[i]->status = done; | ||
57 | } | ||
58 | ftape_reset_buffer(); | ||
59 | } | ||
60 | |||
61 | static int copy_and_gen_ecc(void *destination, | ||
62 | const void *source, | ||
63 | const SectorMap bad_sector_map) | ||
64 | { | ||
65 | int result; | ||
66 | struct memory_segment mseg; | ||
67 | int bads = count_ones(bad_sector_map); | ||
68 | TRACE_FUN(ft_t_any); | ||
69 | |||
70 | if (bads > 0) { | ||
71 | TRACE(ft_t_noise, "bad sectors in map: %d", bads); | ||
72 | } | ||
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 */ | ||
77 | } else { | ||
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); | ||
82 | if (result < 0) { | ||
83 | TRACE(ft_t_err, "ecc_set_segment_parity failed"); | ||
84 | } else { | ||
85 | result = (mseg.blocks - 3) * FT_SECTOR_SIZE; | ||
86 | } | ||
87 | } | ||
88 | TRACE_EXIT result; | ||
89 | } | ||
90 | |||
91 | |||
92 | int ftape_start_writing(const ft_write_mode_t mode) | ||
93 | { | ||
94 | buffer_struct *head = ftape_get_buffer(ft_queue_head); | ||
95 | int segment_id = head->segment_id; | ||
96 | int result; | ||
97 | buffer_state_enum wanted_state = (mode == FT_WR_DELETE | ||
98 | ? deleting | ||
99 | : writing); | ||
100 | TRACE_FUN(ft_t_flow); | ||
101 | |||
102 | if ((ft_driver_state != wanted_state) || head->status != waiting) { | ||
103 | TRACE_EXIT 0; | ||
104 | } | ||
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; | ||
109 | } | ||
110 | ftape_calc_next_cluster(head); /* prepare */ | ||
111 | head->status = ft_driver_state; /* either writing or deleting */ | ||
112 | if (ft_runner_status == idle) { | ||
113 | TRACE(ft_t_noise, | ||
114 | "starting runner for segment %d", segment_id); | ||
115 | TRACE_CATCH(ftape_start_tape(segment_id,head->sector_offset),); | ||
116 | } else { | ||
117 | TRACE(ft_t_noise, "runner not idle, not starting tape"); | ||
118 | } | ||
119 | /* go */ | ||
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 */ | ||
123 | TRACE_EXIT result; | ||
124 | } | ||
125 | |||
126 | /* Wait until all data is actually written to tape. | ||
127 | * | ||
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. | ||
130 | */ | ||
131 | int ftape_loop_until_writes_done(void) | ||
132 | { | ||
133 | buffer_struct *head; | ||
134 | TRACE_FUN(ft_t_flow); | ||
135 | |||
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); | ||
146 | } else { | ||
147 | TRACE_CATCH(ftape_start_writing(FT_WR_MULTI), | ||
148 | last_write_failed = 1); | ||
149 | } | ||
150 | } | ||
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 ! | ||
159 | */ | ||
160 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
161 | if (head->hard_error_map != 0) { | ||
162 | /* Implement hard write error recovery here | ||
163 | */ | ||
164 | } | ||
165 | /* retry this one */ | ||
166 | head->status = waiting; | ||
167 | if (ft_runner_status == aborting) { | ||
168 | ftape_dumb_stop(); | ||
169 | } | ||
170 | if (ft_runner_status != idle) { | ||
171 | TRACE_ABORT(-EIO, ft_t_err, | ||
172 | "unexpected state: " | ||
173 | "ft_runner_status != idle"); | ||
174 | } | ||
175 | ftape_start_writing(ft_driver_state == deleting | ||
176 | ? FT_WR_MULTI : FT_WR_DELETE); | ||
177 | } | ||
178 | TRACE(ft_t_noise, "looping until writes done"); | ||
179 | } | ||
180 | ftape_set_state(idle); | ||
181 | TRACE_EXIT 0; | ||
182 | } | ||
183 | |||
184 | /* Write given segment from buffer at address to tape. | ||
185 | */ | ||
186 | static int write_segment(const int segment_id, | ||
187 | const void *address, | ||
188 | const ft_write_mode_t write_mode) | ||
189 | { | ||
190 | int bytes_written = 0; | ||
191 | buffer_struct *tail; | ||
192 | buffer_state_enum wanted_state = (write_mode == FT_WR_DELETE | ||
193 | ? deleting : writing); | ||
194 | TRACE_FUN(ft_t_flow); | ||
195 | |||
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(),); | ||
201 | } | ||
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); | ||
206 | } | ||
207 | /* if all buffers full we'll have to wait... | ||
208 | */ | ||
209 | ftape_wait_segment(wanted_state); | ||
210 | tail = ftape_get_buffer(ft_queue_tail); | ||
211 | switch(tail->status) { | ||
212 | case done: | ||
213 | ft_history.defects += count_ones(tail->hard_error_map); | ||
214 | break; | ||
215 | case waiting: | ||
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. | ||
219 | */ | ||
220 | bytes_written = -EAGAIN; | ||
221 | break; | ||
222 | case error: | ||
223 | /* setup for a retry | ||
224 | */ | ||
225 | tail->status = waiting; | ||
226 | bytes_written = -EAGAIN; /* force retry */ | ||
227 | if (tail->hard_error_map != 0) { | ||
228 | TRACE(ft_t_warn, | ||
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 | ||
234 | */ | ||
235 | } | ||
236 | break; | ||
237 | default: | ||
238 | TRACE_ABORT(-EIO, ft_t_err, | ||
239 | "wait for empty segment failed, tail status: %d", | ||
240 | tail->status); | ||
241 | } | ||
242 | /* should runner stop ? | ||
243 | */ | ||
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; /* ???? */ | ||
248 | } | ||
249 | /* don't call abort_operation(), we don't want to zap | ||
250 | * the dma buffers | ||
251 | */ | ||
252 | TRACE_CATCH(ftape_dumb_stop(),); | ||
253 | } else { | ||
254 | /* If just passed last segment on tape: wait for BOT | ||
255 | * or EOT mark. Sets ft_runner_status to idle if at lEOT | ||
256 | * and successful | ||
257 | */ | ||
258 | TRACE_CATCH(ftape_handle_logical_eot(),); | ||
259 | } | ||
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! | ||
265 | * | ||
266 | * Empty segments are handled inside coyp_and_gen_ecc() | ||
267 | */ | ||
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)),); | ||
272 | } | ||
273 | tail->segment_id = segment_id; | ||
274 | tail->status = waiting; | ||
275 | tail = ftape_next_buffer(ft_queue_tail); | ||
276 | } | ||
277 | /* Start tape only if all buffers full or flush mode. | ||
278 | * This will give higher probability of streaming. | ||
279 | */ | ||
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),); | ||
285 | } | ||
286 | TRACE_EXIT bytes_written; | ||
287 | } | ||
288 | |||
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: | ||
292 | * -EIO write failed | ||
293 | * -EINTR interrupted by signal | ||
294 | * -ENOSPC device full | ||
295 | */ | ||
296 | int ftape_write_segment(const int segment_id, | ||
297 | const void *buffer, | ||
298 | const ft_write_mode_t flush) | ||
299 | { | ||
300 | int retry = 0; | ||
301 | int result; | ||
302 | TRACE_FUN(ft_t_flow); | ||
303 | |||
304 | ft_history.used |= 2; | ||
305 | if (segment_id >= ft_tracks_per_tape*ft_segments_per_track) { | ||
306 | /* tape full */ | ||
307 | TRACE_ABORT(-ENOSPC, ft_t_err, | ||
308 | "invalid segment id: %d (max %d)", | ||
309 | segment_id, | ||
310 | ft_tracks_per_tape * ft_segments_per_track -1); | ||
311 | } | ||
312 | for (;;) { | ||
313 | if ((result = write_segment(segment_id, buffer, flush)) >= 0) { | ||
314 | if (result == 0) { /* empty segment */ | ||
315 | TRACE(ft_t_noise, | ||
316 | "empty segment, nothing written"); | ||
317 | } | ||
318 | TRACE_EXIT result; | ||
319 | } | ||
320 | if (result == -EAGAIN) { | ||
321 | if (++retry > 100) { /* give up */ | ||
322 | TRACE_ABORT(-EIO, ft_t_err, | ||
323 | "write failed, >100 retries in segment"); | ||
324 | } | ||
325 | TRACE(ft_t_warn, "write error, retry %d (%d)", | ||
326 | retry, | ||
327 | ftape_get_buffer(ft_queue_tail)->segment_id); | ||
328 | } else { | ||
329 | TRACE_ABORT(result, ft_t_err, | ||
330 | "write_segment failed, error: %d", result); | ||
331 | } | ||
332 | /* Allow escape from loop when signaled ! | ||
333 | */ | ||
334 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
335 | } | ||
336 | } | ||
diff --git a/drivers/char/ftape/lowlevel/ftape-write.h b/drivers/char/ftape/lowlevel/ftape-write.h new file mode 100644 index 000000000000..0e7f898b7af9 --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape-write.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #ifndef _FTAPE_WRITE_H | ||
2 | #define _FTAPE_WRITE_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1995 Bas Laarhoven, | ||
6 | * (C) 1996-1997 Claus-Justus Heine. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | $Source: /homes/cvs/ftape-stacked/ftape/lowlevel/ftape-write.h,v $ | ||
24 | $Author: claus $ | ||
25 | * | ||
26 | $Revision: 1.2 $ | ||
27 | $Date: 1997/10/05 19:18:30 $ | ||
28 | $State: Exp $ | ||
29 | * | ||
30 | * This file contains the definitions for the write functions | ||
31 | * for the QIC-117 floppy-tape driver for Linux. | ||
32 | * | ||
33 | */ | ||
34 | |||
35 | |||
36 | /* ftape-write.c defined global functions. | ||
37 | */ | ||
38 | typedef enum { | ||
39 | FT_WR_ASYNC = 0, /* start tape only when all buffers are full */ | ||
40 | FT_WR_MULTI = 1, /* start tape, but don't necessarily stop */ | ||
41 | FT_WR_SINGLE = 2, /* write a single segment and stop afterwards */ | ||
42 | FT_WR_DELETE = 3 /* write deleted data marks */ | ||
43 | } ft_write_mode_t; | ||
44 | |||
45 | extern int ftape_start_writing(const ft_write_mode_t mode); | ||
46 | extern int ftape_write_segment(const int segment, | ||
47 | const void *address, | ||
48 | const ft_write_mode_t flushing); | ||
49 | extern void ftape_zap_write_buffers(void); | ||
50 | extern int ftape_loop_until_writes_done(void); | ||
51 | |||
52 | #endif /* _FTAPE_WRITE_H */ | ||
53 | |||
diff --git a/drivers/char/ftape/lowlevel/ftape_syms.c b/drivers/char/ftape/lowlevel/ftape_syms.c new file mode 100644 index 000000000000..5dc3a380c9bf --- /dev/null +++ b/drivers/char/ftape/lowlevel/ftape_syms.c | |||
@@ -0,0 +1,88 @@ | |||
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/lowlevel/ftape_syms.c,v $ | ||
20 | * $Revision: 1.4 $ | ||
21 | * $Date: 1997/10/17 00:03:51 $ | ||
22 | * | ||
23 | * This file contains the symbols that the ftape low level | ||
24 | * part of the QIC-40/80/3010/3020 floppy-tape driver "ftape" | ||
25 | * exports to its high level clients | ||
26 | */ | ||
27 | |||
28 | #include <linux/config.h> | ||
29 | #include <linux/module.h> | ||
30 | |||
31 | #include <linux/ftape.h> | ||
32 | #include "../lowlevel/ftape-tracing.h" | ||
33 | #include "../lowlevel/ftape-init.h" | ||
34 | #include "../lowlevel/fdc-io.h" | ||
35 | #include "../lowlevel/ftape-read.h" | ||
36 | #include "../lowlevel/ftape-write.h" | ||
37 | #include "../lowlevel/ftape-io.h" | ||
38 | #include "../lowlevel/ftape-ctl.h" | ||
39 | #include "../lowlevel/ftape-rw.h" | ||
40 | #include "../lowlevel/ftape-bsm.h" | ||
41 | #include "../lowlevel/ftape-buffer.h" | ||
42 | #include "../lowlevel/ftape-format.h" | ||
43 | |||
44 | /* bad sector handling from ftape-bsm.c */ | ||
45 | EXPORT_SYMBOL(ftape_get_bad_sector_entry); | ||
46 | EXPORT_SYMBOL(ftape_find_end_of_bsm_list); | ||
47 | /* from ftape-rw.c */ | ||
48 | EXPORT_SYMBOL(ftape_set_state); | ||
49 | /* from ftape-ctl.c */ | ||
50 | EXPORT_SYMBOL(ftape_seek_to_bot); | ||
51 | EXPORT_SYMBOL(ftape_seek_to_eot); | ||
52 | EXPORT_SYMBOL(ftape_abort_operation); | ||
53 | EXPORT_SYMBOL(ftape_get_status); | ||
54 | EXPORT_SYMBOL(ftape_enable); | ||
55 | EXPORT_SYMBOL(ftape_disable); | ||
56 | EXPORT_SYMBOL(ftape_mmap); | ||
57 | EXPORT_SYMBOL(ftape_calibrate_data_rate); | ||
58 | /* from ftape-io.c */ | ||
59 | EXPORT_SYMBOL(ftape_reset_drive); | ||
60 | EXPORT_SYMBOL(ftape_command); | ||
61 | EXPORT_SYMBOL(ftape_parameter); | ||
62 | EXPORT_SYMBOL(ftape_ready_wait); | ||
63 | EXPORT_SYMBOL(ftape_report_operation); | ||
64 | EXPORT_SYMBOL(ftape_report_error); | ||
65 | /* from ftape-read.c */ | ||
66 | EXPORT_SYMBOL(ftape_read_segment_fraction); | ||
67 | EXPORT_SYMBOL(ftape_zap_read_buffers); | ||
68 | EXPORT_SYMBOL(ftape_read_header_segment); | ||
69 | EXPORT_SYMBOL(ftape_decode_header_segment); | ||
70 | /* from ftape-write.c */ | ||
71 | EXPORT_SYMBOL(ftape_write_segment); | ||
72 | EXPORT_SYMBOL(ftape_start_writing); | ||
73 | EXPORT_SYMBOL(ftape_loop_until_writes_done); | ||
74 | /* from ftape-buffer.h */ | ||
75 | EXPORT_SYMBOL(ftape_set_nr_buffers); | ||
76 | /* from ftape-format.h */ | ||
77 | EXPORT_SYMBOL(ftape_format_track); | ||
78 | EXPORT_SYMBOL(ftape_format_status); | ||
79 | EXPORT_SYMBOL(ftape_verify_segment); | ||
80 | /* from tracing.c */ | ||
81 | #ifndef CONFIG_FT_NO_TRACE_AT_ALL | ||
82 | EXPORT_SYMBOL(ftape_tracing); | ||
83 | EXPORT_SYMBOL(ftape_function_nest_level); | ||
84 | EXPORT_SYMBOL(ftape_trace_call); | ||
85 | EXPORT_SYMBOL(ftape_trace_exit); | ||
86 | EXPORT_SYMBOL(ftape_trace_log); | ||
87 | #endif | ||
88 | |||
diff --git a/drivers/char/ftape/zftape/Makefile b/drivers/char/ftape/zftape/Makefile new file mode 100644 index 000000000000..6d91c1f77c05 --- /dev/null +++ b/drivers/char/ftape/zftape/Makefile | |||
@@ -0,0 +1,36 @@ | |||
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 | # $Source: /homes/cvs/ftape-stacked/ftape/zftape/Makefile,v $ | ||
19 | # $Revision: 1.4 $ | ||
20 | # $Date: 1997/10/05 19:18:58 $ | ||
21 | # | ||
22 | # Makefile for the QIC-40/80/3010/3020 zftape interface VFS to | ||
23 | # ftape | ||
24 | # | ||
25 | |||
26 | |||
27 | # ZFT_OBSOLETE - enable the MTIOC_ZFTAPE_GETBLKSZ ioctl. You should | ||
28 | # leave this enabled for compatibility with taper. | ||
29 | |||
30 | obj-$(CONFIG_ZFTAPE) += zftape.o | ||
31 | |||
32 | zftape-objs := zftape-rw.o zftape-ctl.o zftape-read.o \ | ||
33 | zftape-write.o zftape-vtbl.o zftape-eof.o \ | ||
34 | zftape-init.o zftape-buffers.o zftape_syms.o | ||
35 | |||
36 | EXTRA_CFLAGS := -DZFT_OBSOLETE | ||
diff --git a/drivers/char/ftape/zftape/zftape-buffers.c b/drivers/char/ftape/zftape/zftape-buffers.c new file mode 100644 index 000000000000..da06f138334e --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-buffers.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1995-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-buffers.c,v $ | ||
20 | * $Revision: 1.2 $ | ||
21 | * $Date: 1997/10/05 19:18:59 $ | ||
22 | * | ||
23 | * This file contains the dynamic buffer allocation routines | ||
24 | * of zftape | ||
25 | */ | ||
26 | |||
27 | #include <linux/errno.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/delay.h> | ||
31 | |||
32 | #include <linux/zftape.h> | ||
33 | |||
34 | #include <linux/vmalloc.h> | ||
35 | |||
36 | #include "../zftape/zftape-init.h" | ||
37 | #include "../zftape/zftape-eof.h" | ||
38 | #include "../zftape/zftape-ctl.h" | ||
39 | #include "../zftape/zftape-write.h" | ||
40 | #include "../zftape/zftape-read.h" | ||
41 | #include "../zftape/zftape-rw.h" | ||
42 | #include "../zftape/zftape-vtbl.h" | ||
43 | |||
44 | /* global variables | ||
45 | */ | ||
46 | |||
47 | /* local varibales | ||
48 | */ | ||
49 | static unsigned int used_memory; | ||
50 | static unsigned int peak_memory; | ||
51 | |||
52 | void zft_memory_stats(void) | ||
53 | { | ||
54 | TRACE_FUN(ft_t_flow); | ||
55 | |||
56 | TRACE(ft_t_noise, "Memory usage (vmalloc allocations):\n" | ||
57 | KERN_INFO "total allocated: %d\n" | ||
58 | KERN_INFO "peak allocation: %d", | ||
59 | used_memory, peak_memory); | ||
60 | peak_memory = used_memory; | ||
61 | TRACE_EXIT; | ||
62 | } | ||
63 | |||
64 | int zft_vcalloc_once(void *new, size_t size) | ||
65 | { | ||
66 | TRACE_FUN(ft_t_flow); | ||
67 | if (zft_vmalloc_once(new, size) < 0) { | ||
68 | TRACE_EXIT -ENOMEM; | ||
69 | } | ||
70 | memset(*(void **)new, '\0', size); | ||
71 | TRACE_EXIT 0; | ||
72 | } | ||
73 | int zft_vmalloc_once(void *new, size_t size) | ||
74 | { | ||
75 | TRACE_FUN(ft_t_flow); | ||
76 | |||
77 | if (*(void **)new != NULL || size == 0) { | ||
78 | TRACE_EXIT 0; | ||
79 | } | ||
80 | if ((*(void **)new = vmalloc(size)) == NULL) { | ||
81 | TRACE_EXIT -ENOMEM; | ||
82 | } | ||
83 | used_memory += size; | ||
84 | if (peak_memory < used_memory) { | ||
85 | peak_memory = used_memory; | ||
86 | } | ||
87 | TRACE_ABORT(0, ft_t_noise, | ||
88 | "allocated buffer @ %p, %d bytes", *(void **)new, size); | ||
89 | } | ||
90 | int zft_vmalloc_always(void *new, size_t size) | ||
91 | { | ||
92 | TRACE_FUN(ft_t_flow); | ||
93 | |||
94 | zft_vfree(new, size); | ||
95 | TRACE_EXIT zft_vmalloc_once(new, size); | ||
96 | } | ||
97 | void zft_vfree(void *old, size_t size) | ||
98 | { | ||
99 | TRACE_FUN(ft_t_flow); | ||
100 | |||
101 | if (*(void **)old) { | ||
102 | vfree(*(void **)old); | ||
103 | used_memory -= size; | ||
104 | TRACE(ft_t_noise, "released buffer @ %p, %d bytes", | ||
105 | *(void **)old, size); | ||
106 | *(void **)old = NULL; | ||
107 | } | ||
108 | TRACE_EXIT; | ||
109 | } | ||
110 | |||
111 | void *zft_kmalloc(size_t size) | ||
112 | { | ||
113 | void *new; | ||
114 | |||
115 | while ((new = kmalloc(size, GFP_KERNEL)) == NULL) { | ||
116 | msleep_interruptible(100); | ||
117 | } | ||
118 | memset(new, 0, size); | ||
119 | used_memory += size; | ||
120 | if (peak_memory < used_memory) { | ||
121 | peak_memory = used_memory; | ||
122 | } | ||
123 | return new; | ||
124 | } | ||
125 | |||
126 | void zft_kfree(void *old, size_t size) | ||
127 | { | ||
128 | kfree(old); | ||
129 | used_memory -= size; | ||
130 | } | ||
131 | |||
132 | /* there are some more buffers that are allocated on demand. | ||
133 | * cleanup_module() calles this function to be sure to have released | ||
134 | * them | ||
135 | */ | ||
136 | void zft_uninit_mem(void) | ||
137 | { | ||
138 | TRACE_FUN(ft_t_flow); | ||
139 | |||
140 | zft_vfree(&zft_hseg_buf, FT_SEGMENT_SIZE); | ||
141 | zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); zft_deblock_segment = -1; | ||
142 | zft_free_vtbl(); | ||
143 | if (zft_cmpr_lock(0 /* don't load */) == 0) { | ||
144 | (*zft_cmpr_ops->cleanup)(); | ||
145 | (*zft_cmpr_ops->reset)(); /* unlock it again */ | ||
146 | } | ||
147 | zft_memory_stats(); | ||
148 | TRACE_EXIT; | ||
149 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-buffers.h b/drivers/char/ftape/zftape/zftape-buffers.h new file mode 100644 index 000000000000..798e3128c682 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-buffers.h | |||
@@ -0,0 +1,55 @@ | |||
1 | #ifndef _FTAPE_DYNMEM_H | ||
2 | #define _FTAPE_DYNMEM_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1995-1997 Claus-Justus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-buffers.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:18:59 $ | ||
25 | * | ||
26 | * memory allocation routines. | ||
27 | * | ||
28 | */ | ||
29 | |||
30 | /* we do not allocate all of the really large buffer memory before | ||
31 | * someone tries to open the drive. ftape_open() may fail with | ||
32 | * -ENOMEM, but that's better having 200k of vmalloced memory which | ||
33 | * cannot be swapped out. | ||
34 | */ | ||
35 | |||
36 | extern void zft_memory_stats(void); | ||
37 | extern int zft_vmalloc_once(void *new, size_t size); | ||
38 | extern int zft_vcalloc_once(void *new, size_t size); | ||
39 | extern int zft_vmalloc_always(void *new, size_t size); | ||
40 | extern void zft_vfree(void *old, size_t size); | ||
41 | extern void *zft_kmalloc(size_t size); | ||
42 | extern void zft_kfree(void *old, size_t size); | ||
43 | |||
44 | /* called by cleanup_module() | ||
45 | */ | ||
46 | extern void zft_uninit_mem(void); | ||
47 | |||
48 | #endif | ||
49 | |||
50 | |||
51 | |||
52 | |||
53 | |||
54 | |||
55 | |||
diff --git a/drivers/char/ftape/zftape/zftape-ctl.c b/drivers/char/ftape/zftape/zftape-ctl.c new file mode 100644 index 000000000000..6c7874e5c199 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-ctl.c | |||
@@ -0,0 +1,1418 @@ | |||
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-ctl.c,v $ | ||
20 | * $Revision: 1.2.6.2 $ | ||
21 | * $Date: 1997/11/14 18:07:33 $ | ||
22 | * | ||
23 | * This file contains the non-read/write zftape functions | ||
24 | * for the QIC-40/80/3010/3020 floppy-tape driver for Linux. | ||
25 | */ | ||
26 | |||
27 | #include <linux/config.h> | ||
28 | #include <linux/errno.h> | ||
29 | #include <linux/mm.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/fcntl.h> | ||
32 | |||
33 | #include <linux/zftape.h> | ||
34 | |||
35 | #include <asm/uaccess.h> | ||
36 | |||
37 | #include "../zftape/zftape-init.h" | ||
38 | #include "../zftape/zftape-eof.h" | ||
39 | #include "../zftape/zftape-ctl.h" | ||
40 | #include "../zftape/zftape-write.h" | ||
41 | #include "../zftape/zftape-read.h" | ||
42 | #include "../zftape/zftape-rw.h" | ||
43 | #include "../zftape/zftape-vtbl.h" | ||
44 | |||
45 | /* Global vars. | ||
46 | */ | ||
47 | int zft_write_protected; /* this is when cartridge rdonly or O_RDONLY */ | ||
48 | int zft_header_read; | ||
49 | int zft_offline; | ||
50 | unsigned int zft_unit; | ||
51 | int zft_resid; | ||
52 | int zft_mt_compression; | ||
53 | |||
54 | /* Local vars. | ||
55 | */ | ||
56 | static int going_offline; | ||
57 | |||
58 | typedef int (mt_fun)(int *argptr); | ||
59 | typedef int (*mt_funp)(int *argptr); | ||
60 | typedef struct | ||
61 | { | ||
62 | mt_funp function; | ||
63 | unsigned offline : 1; /* op permitted if offline or no_tape */ | ||
64 | unsigned write_protected : 1; /* op permitted if write-protected */ | ||
65 | unsigned not_formatted : 1; /* op permitted if tape not formatted */ | ||
66 | unsigned raw_mode : 1; /* op permitted if zft_mode == 0 */ | ||
67 | unsigned need_idle_state : 1; /* need to call def_idle_state */ | ||
68 | char *name; | ||
69 | } fun_entry; | ||
70 | |||
71 | static mt_fun mt_dummy, mt_reset, mt_fsr, mt_bsr, mt_rew, mt_offl, mt_nop, | ||
72 | mt_weof, mt_erase, mt_ras2, mt_setblk, mt_setdensity, | ||
73 | mt_seek, mt_tell, mt_reten, mt_eom, mt_fsf, mt_bsf, | ||
74 | mt_fsfm, mt_bsfm, mt_setdrvbuffer, mt_compression; | ||
75 | |||
76 | static fun_entry mt_funs[]= | ||
77 | { | ||
78 | {mt_reset , 1, 1, 1, 1, 0, "MT_RESET" }, /* 0 */ | ||
79 | {mt_fsf , 0, 1, 0, 0, 1, "MT_FSF" }, | ||
80 | {mt_bsf , 0, 1, 0, 0, 1, "MT_BSF" }, | ||
81 | {mt_fsr , 0, 1, 0, 1, 1, "MT_FSR" }, | ||
82 | {mt_bsr , 0, 1, 0, 1, 1, "MT_BSR" }, | ||
83 | {mt_weof , 0, 0, 0, 0, 0, "MT_WEOF" }, /* 5 */ | ||
84 | {mt_rew , 0, 1, 1, 1, 0, "MT_REW" }, | ||
85 | {mt_offl , 0, 1, 1, 1, 0, "MT_OFFL" }, | ||
86 | {mt_nop , 1, 1, 1, 1, 0, "MT_NOP" }, | ||
87 | {mt_reten , 0, 1, 1, 1, 0, "MT_RETEN" }, | ||
88 | {mt_bsfm , 0, 1, 0, 0, 1, "MT_BSFM" }, /* 10 */ | ||
89 | {mt_fsfm , 0, 1, 0, 0, 1, "MT_FSFM" }, | ||
90 | {mt_eom , 0, 1, 0, 0, 1, "MT_EOM" }, | ||
91 | {mt_erase , 0, 0, 0, 1, 0, "MT_ERASE" }, | ||
92 | {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS1" }, | ||
93 | {mt_ras2 , 0, 0, 0, 1, 0, "MT_RAS2" }, | ||
94 | {mt_dummy , 1, 1, 1, 1, 0, "MT_RAS3" }, | ||
95 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
96 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
97 | {mt_dummy , 1, 1, 1, 1, 0, "UNKNOWN" }, | ||
98 | {mt_setblk , 1, 1, 1, 1, 1, "MT_SETBLK"}, /* 20 */ | ||
99 | {mt_setdensity , 1, 1, 1, 1, 0, "MT_SETDENSITY"}, | ||
100 | {mt_seek , 0, 1, 0, 1, 1, "MT_SEEK" }, | ||
101 | {mt_dummy , 0, 1, 0, 1, 1, "MT_TELL" }, /* wr-only ?! */ | ||
102 | {mt_setdrvbuffer, 1, 1, 1, 1, 0, "MT_SETDRVBUFFER" }, | ||
103 | {mt_dummy , 1, 1, 1, 1, 0, "MT_FSS" }, /* 25 */ | ||
104 | {mt_dummy , 1, 1, 1, 1, 0, "MT_BSS" }, | ||
105 | {mt_dummy , 1, 1, 1, 1, 0, "MT_WSM" }, | ||
106 | {mt_dummy , 1, 1, 1, 1, 0, "MT_LOCK" }, | ||
107 | {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOCK"}, | ||
108 | {mt_dummy , 1, 1, 1, 1, 0, "MT_LOAD" }, /* 30 */ | ||
109 | {mt_dummy , 1, 1, 1, 1, 0, "MT_UNLOAD"}, | ||
110 | {mt_compression , 1, 1, 1, 0, 1, "MT_COMPRESSION"}, | ||
111 | {mt_dummy , 1, 1, 1, 1, 0, "MT_SETPART"}, | ||
112 | {mt_dummy , 1, 1, 1, 1, 0, "MT_MKPART"} | ||
113 | }; | ||
114 | |||
115 | #define NR_MT_CMDS NR_ITEMS(mt_funs) | ||
116 | |||
117 | void zft_reset_position(zft_position *pos) | ||
118 | { | ||
119 | TRACE_FUN(ft_t_flow); | ||
120 | |||
121 | pos->seg_byte_pos = | ||
122 | pos->volume_pos = 0; | ||
123 | if (zft_header_read) { | ||
124 | /* need to keep track of the volume table and | ||
125 | * compression map. We therefor simply | ||
126 | * position at the beginning of the first | ||
127 | * volume. This covers old ftape archives as | ||
128 | * well has various flavours of the | ||
129 | * compression map segments. The worst case is | ||
130 | * that the compression map shows up as a | ||
131 | * additional volume in front of all others. | ||
132 | */ | ||
133 | pos->seg_pos = zft_find_volume(0)->start_seg; | ||
134 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
135 | } else { | ||
136 | pos->tape_pos = 0; | ||
137 | pos->seg_pos = -1; | ||
138 | } | ||
139 | zft_just_before_eof = 0; | ||
140 | zft_deblock_segment = -1; | ||
141 | zft_io_state = zft_idle; | ||
142 | zft_zap_read_buffers(); | ||
143 | zft_prevent_flush(); | ||
144 | /* unlock the compresison module if it is loaded. | ||
145 | * The zero arg means not to try to load the module. | ||
146 | */ | ||
147 | if (zft_cmpr_lock(0) == 0) { | ||
148 | (*zft_cmpr_ops->reset)(); /* unlock */ | ||
149 | } | ||
150 | TRACE_EXIT; | ||
151 | } | ||
152 | |||
153 | static void zft_init_driver(void) | ||
154 | { | ||
155 | TRACE_FUN(ft_t_flow); | ||
156 | |||
157 | zft_resid = | ||
158 | zft_header_read = | ||
159 | zft_old_ftape = | ||
160 | zft_offline = | ||
161 | zft_write_protected = | ||
162 | going_offline = | ||
163 | zft_mt_compression = | ||
164 | zft_header_changed = | ||
165 | zft_volume_table_changed = | ||
166 | zft_written_segments = 0; | ||
167 | zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; | ||
168 | zft_reset_position(&zft_pos); /* does most of the stuff */ | ||
169 | ftape_zap_read_buffers(); | ||
170 | ftape_set_state(idle); | ||
171 | TRACE_EXIT; | ||
172 | } | ||
173 | |||
174 | int zft_def_idle_state(void) | ||
175 | { | ||
176 | int result = 0; | ||
177 | TRACE_FUN(ft_t_flow); | ||
178 | |||
179 | if (!zft_header_read) { | ||
180 | result = zft_read_header_segments(); | ||
181 | } else if ((result = zft_flush_buffers()) >= 0 && zft_qic_mode) { | ||
182 | /* don't move past eof | ||
183 | */ | ||
184 | (void)zft_close_volume(&zft_pos); | ||
185 | } | ||
186 | if (ftape_abort_operation() < 0) { | ||
187 | TRACE(ft_t_warn, "ftape_abort_operation() failed"); | ||
188 | result = -EIO; | ||
189 | } | ||
190 | /* clear remaining read buffers */ | ||
191 | zft_zap_read_buffers(); | ||
192 | zft_io_state = zft_idle; | ||
193 | TRACE_EXIT result; | ||
194 | } | ||
195 | |||
196 | /***************************************************************************** | ||
197 | * * | ||
198 | * functions for the MTIOCTOP commands * | ||
199 | * * | ||
200 | *****************************************************************************/ | ||
201 | |||
202 | static int mt_dummy(int *dummy) | ||
203 | { | ||
204 | TRACE_FUN(ft_t_flow); | ||
205 | |||
206 | TRACE_EXIT -ENOSYS; | ||
207 | } | ||
208 | |||
209 | static int mt_reset(int *dummy) | ||
210 | { | ||
211 | TRACE_FUN(ft_t_flow); | ||
212 | |||
213 | (void)ftape_seek_to_bot(); | ||
214 | TRACE_CATCH(ftape_reset_drive(), | ||
215 | zft_init_driver(); zft_uninit_mem(); zft_offline = 1); | ||
216 | /* fake a re-open of the device. This will set all flage and | ||
217 | * allocate buffers as appropriate. The new tape condition will | ||
218 | * force the open routine to do anything we need. | ||
219 | */ | ||
220 | TRACE_CATCH(_zft_open(-1 /* fake reopen */, 0 /* dummy */),); | ||
221 | TRACE_EXIT 0; | ||
222 | } | ||
223 | |||
224 | static int mt_fsf(int *arg) | ||
225 | { | ||
226 | int result; | ||
227 | TRACE_FUN(ft_t_flow); | ||
228 | |||
229 | result = zft_skip_volumes(*arg, &zft_pos); | ||
230 | zft_just_before_eof = 0; | ||
231 | TRACE_EXIT result; | ||
232 | } | ||
233 | |||
234 | static int mt_bsf(int *arg) | ||
235 | { | ||
236 | int result = 0; | ||
237 | TRACE_FUN(ft_t_flow); | ||
238 | |||
239 | if (*arg != 0) { | ||
240 | result = zft_skip_volumes(-*arg + 1, &zft_pos); | ||
241 | } | ||
242 | TRACE_EXIT result; | ||
243 | } | ||
244 | |||
245 | static int seek_block(__s64 data_offset, | ||
246 | __s64 block_increment, | ||
247 | zft_position *pos) | ||
248 | { | ||
249 | int result = 0; | ||
250 | __s64 new_block_pos; | ||
251 | __s64 vol_block_count; | ||
252 | const zft_volinfo *volume; | ||
253 | int exceed; | ||
254 | TRACE_FUN(ft_t_flow); | ||
255 | |||
256 | volume = zft_find_volume(pos->seg_pos); | ||
257 | if (volume->start_seg == 0 || volume->end_seg == 0) { | ||
258 | TRACE_EXIT -EIO; | ||
259 | } | ||
260 | new_block_pos = (zft_div_blksz(data_offset, volume->blk_sz) | ||
261 | + block_increment); | ||
262 | vol_block_count = zft_div_blksz(volume->size, volume->blk_sz); | ||
263 | if (new_block_pos < 0) { | ||
264 | TRACE(ft_t_noise, | ||
265 | "new_block_pos " LL_X " < 0", LL(new_block_pos)); | ||
266 | zft_resid = (int)new_block_pos; | ||
267 | new_block_pos = 0; | ||
268 | exceed = 1; | ||
269 | } else if (new_block_pos > vol_block_count) { | ||
270 | TRACE(ft_t_noise, | ||
271 | "new_block_pos " LL_X " exceeds size of volume " LL_X, | ||
272 | LL(new_block_pos), LL(vol_block_count)); | ||
273 | zft_resid = (int)(vol_block_count - new_block_pos); | ||
274 | new_block_pos = vol_block_count; | ||
275 | exceed = 1; | ||
276 | } else { | ||
277 | exceed = 0; | ||
278 | } | ||
279 | if (zft_use_compression && volume->use_compression) { | ||
280 | TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); | ||
281 | result = (*zft_cmpr_ops->seek)(new_block_pos, pos, volume, | ||
282 | zft_deblock_buf); | ||
283 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
284 | pos->tape_pos += pos->seg_byte_pos; | ||
285 | } else { | ||
286 | pos->volume_pos = zft_mul_blksz(new_block_pos, volume->blk_sz); | ||
287 | pos->tape_pos = zft_calc_tape_pos(volume->start_seg); | ||
288 | pos->tape_pos += pos->volume_pos; | ||
289 | pos->seg_pos = zft_calc_seg_byte_coord(&pos->seg_byte_pos, | ||
290 | pos->tape_pos); | ||
291 | } | ||
292 | zft_just_before_eof = volume->size == pos->volume_pos; | ||
293 | if (zft_just_before_eof) { | ||
294 | /* why this? because zft_file_no checks agains start | ||
295 | * and end segment of a volume. We do not want to | ||
296 | * advance to the next volume with this function. | ||
297 | */ | ||
298 | TRACE(ft_t_noise, "set zft_just_before_eof"); | ||
299 | zft_position_before_eof(pos, volume); | ||
300 | } | ||
301 | TRACE(ft_t_noise, "\n" | ||
302 | KERN_INFO "new_seg_pos : %d\n" | ||
303 | KERN_INFO "new_tape_pos: " LL_X "\n" | ||
304 | KERN_INFO "vol_size : " LL_X "\n" | ||
305 | KERN_INFO "seg_byte_pos: %d\n" | ||
306 | KERN_INFO "blk_sz : %d", | ||
307 | pos->seg_pos, LL(pos->tape_pos), | ||
308 | LL(volume->size), pos->seg_byte_pos, | ||
309 | volume->blk_sz); | ||
310 | if (!exceed) { | ||
311 | zft_resid = new_block_pos - zft_div_blksz(pos->volume_pos, | ||
312 | volume->blk_sz); | ||
313 | } | ||
314 | if (zft_resid < 0) { | ||
315 | zft_resid = -zft_resid; | ||
316 | } | ||
317 | TRACE_EXIT ((exceed || zft_resid != 0) && result >= 0) ? -EINVAL : result; | ||
318 | } | ||
319 | |||
320 | static int mt_fsr(int *arg) | ||
321 | { | ||
322 | int result; | ||
323 | TRACE_FUN(ft_t_flow); | ||
324 | |||
325 | result = seek_block(zft_pos.volume_pos, *arg, &zft_pos); | ||
326 | TRACE_EXIT result; | ||
327 | } | ||
328 | |||
329 | static int mt_bsr(int *arg) | ||
330 | { | ||
331 | int result; | ||
332 | TRACE_FUN(ft_t_flow); | ||
333 | |||
334 | result = seek_block(zft_pos.volume_pos, -*arg, &zft_pos); | ||
335 | TRACE_EXIT result; | ||
336 | } | ||
337 | |||
338 | static int mt_weof(int *arg) | ||
339 | { | ||
340 | int result; | ||
341 | TRACE_FUN(ft_t_flow); | ||
342 | |||
343 | TRACE_CATCH(zft_flush_buffers(),); | ||
344 | result = zft_weof(*arg, &zft_pos); | ||
345 | TRACE_EXIT result; | ||
346 | } | ||
347 | |||
348 | static int mt_rew(int *dummy) | ||
349 | { | ||
350 | int result; | ||
351 | TRACE_FUN(ft_t_flow); | ||
352 | |||
353 | if(zft_header_read) { | ||
354 | (void)zft_def_idle_state(); | ||
355 | } | ||
356 | result = ftape_seek_to_bot(); | ||
357 | zft_reset_position(&zft_pos); | ||
358 | TRACE_EXIT result; | ||
359 | } | ||
360 | |||
361 | static int mt_offl(int *dummy) | ||
362 | { | ||
363 | int result; | ||
364 | TRACE_FUN(ft_t_flow); | ||
365 | |||
366 | going_offline= 1; | ||
367 | result = mt_rew(NULL); | ||
368 | TRACE_EXIT result; | ||
369 | } | ||
370 | |||
371 | static int mt_nop(int *dummy) | ||
372 | { | ||
373 | TRACE_FUN(ft_t_flow); | ||
374 | /* should we set tape status? | ||
375 | */ | ||
376 | if (!zft_offline) { /* offline includes no_tape */ | ||
377 | (void)zft_def_idle_state(); | ||
378 | } | ||
379 | TRACE_EXIT 0; | ||
380 | } | ||
381 | |||
382 | static int mt_reten(int *dummy) | ||
383 | { | ||
384 | int result; | ||
385 | TRACE_FUN(ft_t_flow); | ||
386 | |||
387 | if(zft_header_read) { | ||
388 | (void)zft_def_idle_state(); | ||
389 | } | ||
390 | result = ftape_seek_to_eot(); | ||
391 | if (result >= 0) { | ||
392 | result = ftape_seek_to_bot(); | ||
393 | } | ||
394 | TRACE_EXIT(result); | ||
395 | } | ||
396 | |||
397 | static int fsfbsfm(int arg, zft_position *pos) | ||
398 | { | ||
399 | const zft_volinfo *vtbl; | ||
400 | __s64 block_pos; | ||
401 | TRACE_FUN(ft_t_flow); | ||
402 | |||
403 | /* What to do? This should seek to the next file-mark and | ||
404 | * position BEFORE. That is, a next write would just extend | ||
405 | * the current file. Well. Let's just seek to the end of the | ||
406 | * current file, if count == 1. If count > 1, then do a | ||
407 | * "mt_fsf(count - 1)", and then seek to the end of that file. | ||
408 | * If count == 0, do nothing | ||
409 | */ | ||
410 | if (arg == 0) { | ||
411 | TRACE_EXIT 0; | ||
412 | } | ||
413 | zft_just_before_eof = 0; | ||
414 | TRACE_CATCH(zft_skip_volumes(arg < 0 ? arg : arg-1, pos), | ||
415 | if (arg > 0) { | ||
416 | zft_resid ++; | ||
417 | }); | ||
418 | vtbl = zft_find_volume(pos->seg_pos); | ||
419 | block_pos = zft_div_blksz(vtbl->size, vtbl->blk_sz); | ||
420 | (void)seek_block(0, block_pos, pos); | ||
421 | if (pos->volume_pos != vtbl->size) { | ||
422 | zft_just_before_eof = 0; | ||
423 | zft_resid = 1; | ||
424 | /* we didn't managed to go there */ | ||
425 | TRACE_ABORT(-EIO, ft_t_err, | ||
426 | "wanted file position " LL_X ", arrived at " LL_X, | ||
427 | LL(vtbl->size), LL(pos->volume_pos)); | ||
428 | } | ||
429 | zft_just_before_eof = 1; | ||
430 | TRACE_EXIT 0; | ||
431 | } | ||
432 | |||
433 | static int mt_bsfm(int *arg) | ||
434 | { | ||
435 | int result; | ||
436 | TRACE_FUN(ft_t_flow); | ||
437 | |||
438 | result = fsfbsfm(-*arg, &zft_pos); | ||
439 | TRACE_EXIT result; | ||
440 | } | ||
441 | |||
442 | static int mt_fsfm(int *arg) | ||
443 | { | ||
444 | int result; | ||
445 | TRACE_FUN(ft_t_flow); | ||
446 | |||
447 | result = fsfbsfm(*arg, &zft_pos); | ||
448 | TRACE_EXIT result; | ||
449 | } | ||
450 | |||
451 | static int mt_eom(int *dummy) | ||
452 | { | ||
453 | TRACE_FUN(ft_t_flow); | ||
454 | |||
455 | zft_skip_to_eom(&zft_pos); | ||
456 | TRACE_EXIT 0; | ||
457 | } | ||
458 | |||
459 | static int mt_erase(int *dummy) | ||
460 | { | ||
461 | int result; | ||
462 | TRACE_FUN(ft_t_flow); | ||
463 | |||
464 | result = zft_erase(); | ||
465 | TRACE_EXIT result; | ||
466 | } | ||
467 | |||
468 | static int mt_ras2(int *dummy) | ||
469 | { | ||
470 | int result; | ||
471 | TRACE_FUN(ft_t_flow); | ||
472 | |||
473 | result = -ENOSYS; | ||
474 | TRACE_EXIT result; | ||
475 | } | ||
476 | |||
477 | /* Sets the new blocksize in BYTES | ||
478 | * | ||
479 | */ | ||
480 | static int mt_setblk(int *new_size) | ||
481 | { | ||
482 | TRACE_FUN(ft_t_flow); | ||
483 | |||
484 | if((unsigned int)(*new_size) > ZFT_MAX_BLK_SZ) { | ||
485 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
486 | "desired blk_sz (%d) should be <= %d bytes", | ||
487 | *new_size, ZFT_MAX_BLK_SZ); | ||
488 | } | ||
489 | if ((*new_size & (FT_SECTOR_SIZE-1)) != 0) { | ||
490 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
491 | "desired blk_sz (%d) must be a multiple of %d bytes", | ||
492 | *new_size, FT_SECTOR_SIZE); | ||
493 | } | ||
494 | if (*new_size == 0) { | ||
495 | if (zft_use_compression) { | ||
496 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
497 | "Variable block size not yet " | ||
498 | "supported with compression"); | ||
499 | } | ||
500 | *new_size = 1; | ||
501 | } | ||
502 | zft_blk_sz = *new_size; | ||
503 | TRACE_EXIT 0; | ||
504 | } | ||
505 | |||
506 | static int mt_setdensity(int *arg) | ||
507 | { | ||
508 | TRACE_FUN(ft_t_flow); | ||
509 | |||
510 | SET_TRACE_LEVEL(*arg); | ||
511 | TRACE(TRACE_LEVEL, "tracing set to %d", TRACE_LEVEL); | ||
512 | if ((int)TRACE_LEVEL != *arg) { | ||
513 | TRACE_EXIT -EINVAL; | ||
514 | } | ||
515 | TRACE_EXIT 0; | ||
516 | } | ||
517 | |||
518 | static int mt_seek(int *new_block_pos) | ||
519 | { | ||
520 | int result= 0; | ||
521 | TRACE_FUN(ft_t_any); | ||
522 | |||
523 | result = seek_block(0, (__s64)*new_block_pos, &zft_pos); | ||
524 | TRACE_EXIT result; | ||
525 | } | ||
526 | |||
527 | /* OK, this is totally different from SCSI, but the worst thing that can | ||
528 | * happen is that there is not enough defragmentated memory that can be | ||
529 | * allocated. Also, there is a hardwired limit of 16 dma buffers in the | ||
530 | * stock ftape module. This shouldn't bring the system down. | ||
531 | * | ||
532 | * NOTE: the argument specifies the total number of dma buffers to use. | ||
533 | * The driver needs at least 3 buffers to function at all. | ||
534 | * | ||
535 | */ | ||
536 | static int mt_setdrvbuffer(int *cnt) | ||
537 | { | ||
538 | TRACE_FUN(ft_t_flow); | ||
539 | |||
540 | if (*cnt < 3) { | ||
541 | TRACE_EXIT -EINVAL; | ||
542 | } | ||
543 | TRACE_CATCH(ftape_set_nr_buffers(*cnt),); | ||
544 | TRACE_EXIT 0; | ||
545 | } | ||
546 | /* return the block position from start of volume | ||
547 | */ | ||
548 | static int mt_tell(int *arg) | ||
549 | { | ||
550 | TRACE_FUN(ft_t_flow); | ||
551 | |||
552 | *arg = zft_div_blksz(zft_pos.volume_pos, | ||
553 | zft_find_volume(zft_pos.seg_pos)->blk_sz); | ||
554 | TRACE_EXIT 0; | ||
555 | } | ||
556 | |||
557 | static int mt_compression(int *arg) | ||
558 | { | ||
559 | TRACE_FUN(ft_t_flow); | ||
560 | |||
561 | /* Ok. We could also check whether compression is available at | ||
562 | * all by trying to load the compression module. We could | ||
563 | * also check for a block size of 1 byte which is illegal | ||
564 | * with compression. Instead of doing it here we rely on | ||
565 | * zftape_write() to do the proper checks. | ||
566 | */ | ||
567 | if ((unsigned int)*arg > 1) { | ||
568 | TRACE_EXIT -EINVAL; | ||
569 | } | ||
570 | if (*arg != 0 && zft_blk_sz == 1) { /* variable block size */ | ||
571 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
572 | "Compression not yet supported " | ||
573 | "with variable block size"); | ||
574 | } | ||
575 | zft_mt_compression = *arg; | ||
576 | if ((zft_unit & ZFT_ZIP_MODE) == 0) { | ||
577 | zft_use_compression = zft_mt_compression; | ||
578 | } | ||
579 | TRACE_EXIT 0; | ||
580 | } | ||
581 | |||
582 | /* check whether write access is allowed. Write access is denied when | ||
583 | * + zft_write_protected == 1 -- this accounts for either hard write | ||
584 | * protection of the cartridge or for | ||
585 | * O_RDONLY access mode of the tape device | ||
586 | * + zft_offline == 1 -- this meany that there is either no tape | ||
587 | * or that the MTOFFLINE ioctl has been | ||
588 | * previously issued (`soft eject') | ||
589 | * + ft_formatted == 0 -- this means that the cartridge is not | ||
590 | * formatted | ||
591 | * Then we distinuguish two cases. When zft_qic_mode is TRUE, then we try | ||
592 | * to emulate a `traditional' (aka SCSI like) UN*X tape device. Therefore we | ||
593 | * deny writes when | ||
594 | * + zft_qic_mode ==1 && | ||
595 | * (!zft_tape_at_lbot() && -- tape no at logical BOT | ||
596 | * !(zft_tape_at_eom() || -- tape not at logical EOM (or EOD) | ||
597 | * (zft_tape_at_eom() && | ||
598 | * zft_old_ftape()))) -- we can't add new volume to tapes | ||
599 | * written by old ftape because ftape | ||
600 | * don't use the volume table | ||
601 | * | ||
602 | * when the drive is in true raw mode (aka /dev/rawft0) then we don't | ||
603 | * care about LBOT and EOM conditions. This device is intended for a | ||
604 | * user level program that wants to truly implement the QIC-80 compliance | ||
605 | * at the logical data layout level of the cartridge, i.e. implement all | ||
606 | * that volume table and volume directory stuff etc.< | ||
607 | */ | ||
608 | int zft_check_write_access(zft_position *pos) | ||
609 | { | ||
610 | TRACE_FUN(ft_t_flow); | ||
611 | |||
612 | if (zft_offline) { /* offline includes no_tape */ | ||
613 | TRACE_ABORT(-ENXIO, | ||
614 | ft_t_info, "tape is offline or no cartridge"); | ||
615 | } | ||
616 | if (!ft_formatted) { | ||
617 | TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); | ||
618 | } | ||
619 | if (zft_write_protected) { | ||
620 | TRACE_ABORT(-EACCES, ft_t_info, "cartridge write protected"); | ||
621 | } | ||
622 | if (zft_qic_mode) { | ||
623 | /* check BOT condition */ | ||
624 | if (!zft_tape_at_lbot(pos)) { | ||
625 | /* protect cartridges written by old ftape if | ||
626 | * not at BOT because they use the vtbl | ||
627 | * segment for storing data | ||
628 | */ | ||
629 | if (zft_old_ftape) { | ||
630 | TRACE_ABORT(-EACCES, ft_t_warn, | ||
631 | "Cannot write to cartridges written by old ftape when not at BOT"); | ||
632 | } | ||
633 | /* not at BOT, but allow writes at EOD, of course | ||
634 | */ | ||
635 | if (!zft_tape_at_eod(pos)) { | ||
636 | TRACE_ABORT(-EACCES, ft_t_info, | ||
637 | "tape not at BOT and not at EOD"); | ||
638 | } | ||
639 | } | ||
640 | /* fine. Now the tape is either at BOT or at EOD. */ | ||
641 | } | ||
642 | /* or in raw mode in which case we don't care about BOT and EOD */ | ||
643 | TRACE_EXIT 0; | ||
644 | } | ||
645 | |||
646 | /* OPEN routine called by kernel-interface code | ||
647 | * | ||
648 | * NOTE: this is also called by mt_reset() with dev_minor == -1 | ||
649 | * to fake a reopen after a reset. | ||
650 | */ | ||
651 | int _zft_open(unsigned int dev_minor, unsigned int access_mode) | ||
652 | { | ||
653 | static unsigned int tape_unit; | ||
654 | static unsigned int file_access_mode; | ||
655 | int result; | ||
656 | TRACE_FUN(ft_t_flow); | ||
657 | |||
658 | if ((int)dev_minor == -1) { | ||
659 | /* fake reopen */ | ||
660 | zft_unit = tape_unit; | ||
661 | access_mode = file_access_mode; | ||
662 | zft_init_driver(); /* reset all static data to defaults */ | ||
663 | } else { | ||
664 | tape_unit = dev_minor; | ||
665 | file_access_mode = access_mode; | ||
666 | if ((result = ftape_enable(FTAPE_SEL(dev_minor))) < 0) { | ||
667 | TRACE_ABORT(-ENXIO, ft_t_err, | ||
668 | "ftape_enable failed: %d", result); | ||
669 | } | ||
670 | if (ft_new_tape || ft_no_tape || !ft_formatted || | ||
671 | (FTAPE_SEL(zft_unit) != FTAPE_SEL(dev_minor)) || | ||
672 | (zft_unit & ZFT_RAW_MODE) != (dev_minor & ZFT_RAW_MODE)) { | ||
673 | /* reset all static data to defaults, | ||
674 | */ | ||
675 | zft_init_driver(); | ||
676 | } | ||
677 | zft_unit = dev_minor; | ||
678 | } | ||
679 | zft_set_flags(zft_unit); /* decode the minor bits */ | ||
680 | if (zft_blk_sz == 1 && zft_use_compression) { | ||
681 | ftape_disable(); /* resets ft_no_tape */ | ||
682 | TRACE_ABORT(-ENODEV, ft_t_warn, "Variable block size not yet " | ||
683 | "supported with compression"); | ||
684 | } | ||
685 | /* no need for most of the buffers when no tape or not | ||
686 | * formatted. for the read/write operations, it is the | ||
687 | * regardless whether there is no tape, a not-formatted tape | ||
688 | * or the whether the driver is soft offline. | ||
689 | * Nevertheless we allow some ioctls with non-formatted tapes, | ||
690 | * like rewind and reset. | ||
691 | */ | ||
692 | if (ft_no_tape || !ft_formatted) { | ||
693 | zft_uninit_mem(); | ||
694 | } | ||
695 | if (ft_no_tape) { | ||
696 | zft_offline = 1; /* so we need not test two variables */ | ||
697 | } | ||
698 | if ((access_mode == O_WRONLY || access_mode == O_RDWR) && | ||
699 | (ft_write_protected || ft_no_tape)) { | ||
700 | ftape_disable(); /* resets ft_no_tape */ | ||
701 | TRACE_ABORT(ft_no_tape ? -ENXIO : -EROFS, | ||
702 | ft_t_warn, "wrong access mode %s cartridge", | ||
703 | ft_no_tape ? "without a" : "with write protected"); | ||
704 | } | ||
705 | zft_write_protected = (access_mode == O_RDONLY || | ||
706 | ft_write_protected != 0); | ||
707 | if (zft_write_protected) { | ||
708 | TRACE(ft_t_noise, | ||
709 | "read only access mode: %d, " | ||
710 | "drive write protected: %d", | ||
711 | access_mode == O_RDONLY, | ||
712 | ft_write_protected != 0); | ||
713 | } | ||
714 | if (!zft_offline) { | ||
715 | TRACE_CATCH(zft_vmalloc_once(&zft_deblock_buf,FT_SEGMENT_SIZE), | ||
716 | ftape_disable()); | ||
717 | } | ||
718 | /* zft_seg_pos should be greater than the vtbl segpos but not | ||
719 | * if in compatibility mode and only after we read in the | ||
720 | * header segments | ||
721 | * | ||
722 | * might also be a problem if the user makes a backup with a | ||
723 | * *qft* device and rewinds it with a raw device. | ||
724 | */ | ||
725 | if (zft_qic_mode && | ||
726 | !zft_old_ftape && | ||
727 | zft_pos.seg_pos >= 0 && | ||
728 | zft_header_read && | ||
729 | zft_pos.seg_pos <= ft_first_data_segment) { | ||
730 | TRACE(ft_t_noise, "you probably mixed up the zftape devices!"); | ||
731 | zft_reset_position(&zft_pos); | ||
732 | } | ||
733 | TRACE_EXIT 0; | ||
734 | } | ||
735 | |||
736 | /* RELEASE routine called by kernel-interface code | ||
737 | */ | ||
738 | int _zft_close(void) | ||
739 | { | ||
740 | int result = 0; | ||
741 | TRACE_FUN(ft_t_flow); | ||
742 | |||
743 | if (zft_offline) { | ||
744 | /* call the hardware release routine. Puts the drive offline */ | ||
745 | ftape_disable(); | ||
746 | TRACE_EXIT 0; | ||
747 | } | ||
748 | if (!(ft_write_protected || zft_old_ftape)) { | ||
749 | result = zft_flush_buffers(); | ||
750 | TRACE(ft_t_noise, "writing file mark at current position"); | ||
751 | if (zft_qic_mode && zft_close_volume(&zft_pos) == 0) { | ||
752 | zft_move_past_eof(&zft_pos); | ||
753 | } | ||
754 | if ((zft_tape_at_lbot(&zft_pos) || | ||
755 | !(zft_unit & FTAPE_NO_REWIND))) { | ||
756 | if (result >= 0) { | ||
757 | result = zft_update_header_segments(); | ||
758 | } else { | ||
759 | TRACE(ft_t_err, | ||
760 | "Error: unable to update header segments"); | ||
761 | } | ||
762 | } | ||
763 | } | ||
764 | ftape_abort_operation(); | ||
765 | if (!(zft_unit & FTAPE_NO_REWIND)) { | ||
766 | TRACE(ft_t_noise, "rewinding tape"); | ||
767 | if (ftape_seek_to_bot() < 0 && result >= 0) { | ||
768 | result = -EIO; /* keep old value */ | ||
769 | } | ||
770 | zft_reset_position(&zft_pos); | ||
771 | } | ||
772 | zft_zap_read_buffers(); | ||
773 | /* now free up memory as much as possible. We don't destroy | ||
774 | * the deblock buffer if it containes a valid segment. | ||
775 | */ | ||
776 | if (zft_deblock_segment == -1) { | ||
777 | zft_vfree(&zft_deblock_buf, FT_SEGMENT_SIZE); | ||
778 | } | ||
779 | /* high level driver status, forces creation of a new volume | ||
780 | * when calling ftape_write again and not zft_just_before_eof | ||
781 | */ | ||
782 | zft_io_state = zft_idle; | ||
783 | if (going_offline) { | ||
784 | zft_init_driver(); | ||
785 | zft_uninit_mem(); | ||
786 | going_offline = 0; | ||
787 | zft_offline = 1; | ||
788 | } else if (zft_cmpr_lock(0 /* don't load */) == 0) { | ||
789 | (*zft_cmpr_ops->reset)(); /* unlock it again */ | ||
790 | } | ||
791 | zft_memory_stats(); | ||
792 | /* call the hardware release routine. Puts the drive offline */ | ||
793 | ftape_disable(); | ||
794 | TRACE_EXIT result; | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | * the wrapper function around the wrapper MTIOCTOP ioctl | ||
799 | */ | ||
800 | static int mtioctop(struct mtop *mtop, int arg_size) | ||
801 | { | ||
802 | int result = 0; | ||
803 | fun_entry *mt_fun_entry; | ||
804 | TRACE_FUN(ft_t_flow); | ||
805 | |||
806 | if (arg_size != sizeof(struct mtop) || mtop->mt_op >= NR_MT_CMDS) { | ||
807 | TRACE_EXIT -EINVAL; | ||
808 | } | ||
809 | TRACE(ft_t_noise, "calling MTIOCTOP command: %s", | ||
810 | mt_funs[mtop->mt_op].name); | ||
811 | mt_fun_entry= &mt_funs[mtop->mt_op]; | ||
812 | zft_resid = mtop->mt_count; | ||
813 | if (!mt_fun_entry->offline && zft_offline) { | ||
814 | if (ft_no_tape) { | ||
815 | TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); | ||
816 | } else { | ||
817 | TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); | ||
818 | } | ||
819 | } | ||
820 | if (!mt_fun_entry->not_formatted && !ft_formatted) { | ||
821 | TRACE_ABORT(-EACCES, ft_t_info, "tape is not formatted"); | ||
822 | } | ||
823 | if (!mt_fun_entry->write_protected) { | ||
824 | TRACE_CATCH(zft_check_write_access(&zft_pos),); | ||
825 | } | ||
826 | if (mt_fun_entry->need_idle_state && !(zft_offline || !ft_formatted)) { | ||
827 | TRACE_CATCH(zft_def_idle_state(),); | ||
828 | } | ||
829 | if (!zft_qic_mode && !mt_fun_entry->raw_mode) { | ||
830 | TRACE_ABORT(-EACCES, ft_t_info, | ||
831 | "Drive needs to be in QIC-80 compatibility mode for this command"); | ||
832 | } | ||
833 | result = (mt_fun_entry->function)(&mtop->mt_count); | ||
834 | if (zft_tape_at_lbot(&zft_pos)) { | ||
835 | TRACE_CATCH(zft_update_header_segments(),); | ||
836 | } | ||
837 | if (result >= 0) { | ||
838 | zft_resid = 0; | ||
839 | } | ||
840 | TRACE_EXIT result; | ||
841 | } | ||
842 | |||
843 | /* | ||
844 | * standard MTIOCGET ioctl | ||
845 | */ | ||
846 | static int mtiocget(struct mtget *mtget, int arg_size) | ||
847 | { | ||
848 | const zft_volinfo *volume; | ||
849 | __s64 max_tape_pos; | ||
850 | TRACE_FUN(ft_t_flow); | ||
851 | |||
852 | if (arg_size != sizeof(struct mtget)) { | ||
853 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
854 | arg_size); | ||
855 | } | ||
856 | mtget->mt_type = ft_drive_type.vendor_id + 0x800000; | ||
857 | mtget->mt_dsreg = ft_last_status.space; | ||
858 | mtget->mt_erreg = ft_last_error.space; /* error register */ | ||
859 | mtget->mt_resid = zft_resid; /* residuum of writes, reads and | ||
860 | * MTIOCTOP commands | ||
861 | */ | ||
862 | if (!zft_offline) { /* neither no_tape nor soft offline */ | ||
863 | mtget->mt_gstat = GMT_ONLINE(~0UL); | ||
864 | /* should rather return the status of the cartridge | ||
865 | * than the access mode of the file, therefor use | ||
866 | * ft_write_protected, not zft_write_protected | ||
867 | */ | ||
868 | if (ft_write_protected) { | ||
869 | mtget->mt_gstat |= GMT_WR_PROT(~0UL); | ||
870 | } | ||
871 | if(zft_header_read) { /* this catches non-formatted */ | ||
872 | volume = zft_find_volume(zft_pos.seg_pos); | ||
873 | mtget->mt_fileno = volume->count; | ||
874 | max_tape_pos = zft_capacity - zft_blk_sz; | ||
875 | if (zft_use_compression) { | ||
876 | max_tape_pos -= ZFT_CMPR_OVERHEAD; | ||
877 | } | ||
878 | if (zft_tape_at_eod(&zft_pos)) { | ||
879 | mtget->mt_gstat |= GMT_EOD(~0UL); | ||
880 | } | ||
881 | if (zft_pos.tape_pos > max_tape_pos) { | ||
882 | mtget->mt_gstat |= GMT_EOT(~0UL); | ||
883 | } | ||
884 | mtget->mt_blkno = zft_div_blksz(zft_pos.volume_pos, | ||
885 | volume->blk_sz); | ||
886 | if (zft_just_before_eof) { | ||
887 | mtget->mt_gstat |= GMT_EOF(~0UL); | ||
888 | } | ||
889 | if (zft_tape_at_lbot(&zft_pos)) { | ||
890 | mtget->mt_gstat |= GMT_BOT(~0UL); | ||
891 | } | ||
892 | } else { | ||
893 | mtget->mt_fileno = mtget->mt_blkno = -1; | ||
894 | if (mtget->mt_dsreg & QIC_STATUS_AT_BOT) { | ||
895 | mtget->mt_gstat |= GMT_BOT(~0UL); | ||
896 | } | ||
897 | } | ||
898 | } else { | ||
899 | if (ft_no_tape) { | ||
900 | mtget->mt_gstat = GMT_DR_OPEN(~0UL); | ||
901 | } else { | ||
902 | mtget->mt_gstat = 0UL; | ||
903 | } | ||
904 | mtget->mt_fileno = mtget->mt_blkno = -1; | ||
905 | } | ||
906 | TRACE_EXIT 0; | ||
907 | } | ||
908 | |||
909 | #ifdef MTIOCRDFTSEG | ||
910 | /* | ||
911 | * Read a floppy tape segment. This is useful for manipulating the | ||
912 | * volume table, and read the old header segment before re-formatting | ||
913 | * the cartridge. | ||
914 | */ | ||
915 | static int mtiocrdftseg(struct mtftseg * mtftseg, int arg_size) | ||
916 | { | ||
917 | TRACE_FUN(ft_t_flow); | ||
918 | |||
919 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCRDFTSEG"); | ||
920 | if (zft_qic_mode) { | ||
921 | TRACE_ABORT(-EACCES, ft_t_info, | ||
922 | "driver needs to be in raw mode for this ioctl"); | ||
923 | } | ||
924 | if (arg_size != sizeof(struct mtftseg)) { | ||
925 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
926 | arg_size); | ||
927 | } | ||
928 | if (zft_offline) { | ||
929 | TRACE_EXIT -ENXIO; | ||
930 | } | ||
931 | if (mtftseg->mt_mode != FT_RD_SINGLE && | ||
932 | mtftseg->mt_mode != FT_RD_AHEAD) { | ||
933 | TRACE_ABORT(-EINVAL, ft_t_info, "invalid read mode"); | ||
934 | } | ||
935 | if (!ft_formatted) { | ||
936 | TRACE_EXIT -EACCES; /* -ENXIO ? */ | ||
937 | |||
938 | } | ||
939 | if (!zft_header_read) { | ||
940 | TRACE_CATCH(zft_def_idle_state(),); | ||
941 | } | ||
942 | if (mtftseg->mt_segno > ft_last_data_segment) { | ||
943 | TRACE_ABORT(-EINVAL, ft_t_info, "segment number is too large"); | ||
944 | } | ||
945 | mtftseg->mt_result = ftape_read_segment(mtftseg->mt_segno, | ||
946 | zft_deblock_buf, | ||
947 | mtftseg->mt_mode); | ||
948 | if (mtftseg->mt_result < 0) { | ||
949 | /* a negativ result is not an ioctl error. if | ||
950 | * the user wants to read damaged tapes, | ||
951 | * it's up to her/him | ||
952 | */ | ||
953 | TRACE_EXIT 0; | ||
954 | } | ||
955 | if (copy_to_user(mtftseg->mt_data, | ||
956 | zft_deblock_buf, | ||
957 | mtftseg->mt_result) != 0) { | ||
958 | TRACE_EXIT -EFAULT; | ||
959 | } | ||
960 | TRACE_EXIT 0; | ||
961 | } | ||
962 | #endif | ||
963 | |||
964 | #ifdef MTIOCWRFTSEG | ||
965 | /* | ||
966 | * write a floppy tape segment. This version features writing of | ||
967 | * deleted address marks, and gracefully ignores the (software) | ||
968 | * ft_formatted flag to support writing of header segments after | ||
969 | * formatting. | ||
970 | */ | ||
971 | static int mtiocwrftseg(struct mtftseg * mtftseg, int arg_size) | ||
972 | { | ||
973 | int result; | ||
974 | TRACE_FUN(ft_t_flow); | ||
975 | |||
976 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCWRFTSEG"); | ||
977 | if (zft_write_protected || zft_qic_mode) { | ||
978 | TRACE_EXIT -EACCES; | ||
979 | } | ||
980 | if (arg_size != sizeof(struct mtftseg)) { | ||
981 | TRACE_ABORT(-EINVAL, ft_t_info, "bad argument size: %d", | ||
982 | arg_size); | ||
983 | } | ||
984 | if (zft_offline) { | ||
985 | TRACE_EXIT -ENXIO; | ||
986 | } | ||
987 | if (mtftseg->mt_mode != FT_WR_ASYNC && | ||
988 | mtftseg->mt_mode != FT_WR_MULTI && | ||
989 | mtftseg->mt_mode != FT_WR_SINGLE && | ||
990 | mtftseg->mt_mode != FT_WR_DELETE) { | ||
991 | TRACE_ABORT(-EINVAL, ft_t_info, "invalid write mode"); | ||
992 | } | ||
993 | /* | ||
994 | * We don't check for ft_formatted, because this gives | ||
995 | * only the software status of the driver. | ||
996 | * | ||
997 | * We assume that the user knows what it is | ||
998 | * doing. And rely on the low level stuff to fail | ||
999 | * when the tape isn't formatted. We only make sure | ||
1000 | * that The header segment buffer is allocated, | ||
1001 | * because it holds the bad sector map. | ||
1002 | */ | ||
1003 | if (zft_hseg_buf == NULL) { | ||
1004 | TRACE_EXIT -ENXIO; | ||
1005 | } | ||
1006 | if (mtftseg->mt_mode != FT_WR_DELETE) { | ||
1007 | if (copy_from_user(zft_deblock_buf, | ||
1008 | mtftseg->mt_data, | ||
1009 | FT_SEGMENT_SIZE) != 0) { | ||
1010 | TRACE_EXIT -EFAULT; | ||
1011 | } | ||
1012 | } | ||
1013 | mtftseg->mt_result = ftape_write_segment(mtftseg->mt_segno, | ||
1014 | zft_deblock_buf, | ||
1015 | mtftseg->mt_mode); | ||
1016 | if (mtftseg->mt_result >= 0 && mtftseg->mt_mode == FT_WR_SINGLE) { | ||
1017 | /* | ||
1018 | * a negativ result is not an ioctl error. if | ||
1019 | * the user wants to write damaged tapes, | ||
1020 | * it's up to her/him | ||
1021 | */ | ||
1022 | if ((result = ftape_loop_until_writes_done()) < 0) { | ||
1023 | mtftseg->mt_result = result; | ||
1024 | } | ||
1025 | } | ||
1026 | TRACE_EXIT 0; | ||
1027 | } | ||
1028 | #endif | ||
1029 | |||
1030 | #ifdef MTIOCVOLINFO | ||
1031 | /* | ||
1032 | * get information about volume positioned at. | ||
1033 | */ | ||
1034 | static int mtiocvolinfo(struct mtvolinfo *volinfo, int arg_size) | ||
1035 | { | ||
1036 | const zft_volinfo *volume; | ||
1037 | TRACE_FUN(ft_t_flow); | ||
1038 | |||
1039 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCVOLINFO"); | ||
1040 | if (arg_size != sizeof(struct mtvolinfo)) { | ||
1041 | TRACE_ABORT(-EINVAL, | ||
1042 | ft_t_info, "bad argument size: %d", arg_size); | ||
1043 | } | ||
1044 | if (zft_offline) { | ||
1045 | TRACE_EXIT -ENXIO; | ||
1046 | } | ||
1047 | if (!ft_formatted) { | ||
1048 | TRACE_EXIT -EACCES; | ||
1049 | } | ||
1050 | TRACE_CATCH(zft_def_idle_state(),); | ||
1051 | volume = zft_find_volume(zft_pos.seg_pos); | ||
1052 | volinfo->mt_volno = volume->count; | ||
1053 | volinfo->mt_blksz = volume->blk_sz == 1 ? 0 : volume->blk_sz; | ||
1054 | volinfo->mt_size = volume->size >> 10; | ||
1055 | volinfo->mt_rawsize = ((zft_calc_tape_pos(volume->end_seg + 1) >> 10) - | ||
1056 | (zft_calc_tape_pos(volume->start_seg) >> 10)); | ||
1057 | volinfo->mt_cmpr = volume->use_compression; | ||
1058 | TRACE_EXIT 0; | ||
1059 | } | ||
1060 | #endif | ||
1061 | |||
1062 | #ifdef ZFT_OBSOLETE | ||
1063 | static int mtioc_zftape_getblksz(struct mtblksz *blksz, int arg_size) | ||
1064 | { | ||
1065 | TRACE_FUN(ft_t_flow); | ||
1066 | |||
1067 | TRACE(ft_t_noise, "\n" | ||
1068 | KERN_INFO "Mag tape ioctl command: MTIOC_ZTAPE_GETBLKSZ\n" | ||
1069 | KERN_INFO "This ioctl is here merely for compatibility.\n" | ||
1070 | KERN_INFO "Please use MTIOCVOLINFO instead"); | ||
1071 | if (arg_size != sizeof(struct mtblksz)) { | ||
1072 | TRACE_ABORT(-EINVAL, | ||
1073 | ft_t_info, "bad argument size: %d", arg_size); | ||
1074 | } | ||
1075 | if (zft_offline) { | ||
1076 | TRACE_EXIT -ENXIO; | ||
1077 | } | ||
1078 | if (!ft_formatted) { | ||
1079 | TRACE_EXIT -EACCES; | ||
1080 | } | ||
1081 | TRACE_CATCH(zft_def_idle_state(),); | ||
1082 | blksz->mt_blksz = zft_find_volume(zft_pos.seg_pos)->blk_sz; | ||
1083 | TRACE_EXIT 0; | ||
1084 | } | ||
1085 | #endif | ||
1086 | |||
1087 | #ifdef MTIOCGETSIZE | ||
1088 | /* | ||
1089 | * get the capacity of the tape cartridge. | ||
1090 | */ | ||
1091 | static int mtiocgetsize(struct mttapesize *size, int arg_size) | ||
1092 | { | ||
1093 | TRACE_FUN(ft_t_flow); | ||
1094 | |||
1095 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOC_ZFTAPE_GETSIZE"); | ||
1096 | if (arg_size != sizeof(struct mttapesize)) { | ||
1097 | TRACE_ABORT(-EINVAL, | ||
1098 | ft_t_info, "bad argument size: %d", arg_size); | ||
1099 | } | ||
1100 | if (zft_offline) { | ||
1101 | TRACE_EXIT -ENXIO; | ||
1102 | } | ||
1103 | if (!ft_formatted) { | ||
1104 | TRACE_EXIT -EACCES; | ||
1105 | } | ||
1106 | TRACE_CATCH(zft_def_idle_state(),); | ||
1107 | size->mt_capacity = (unsigned int)(zft_capacity>>10); | ||
1108 | size->mt_used = (unsigned int)(zft_get_eom_pos()>>10); | ||
1109 | TRACE_EXIT 0; | ||
1110 | } | ||
1111 | #endif | ||
1112 | |||
1113 | static int mtiocpos(struct mtpos *mtpos, int arg_size) | ||
1114 | { | ||
1115 | int result; | ||
1116 | TRACE_FUN(ft_t_flow); | ||
1117 | |||
1118 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCPOS"); | ||
1119 | if (arg_size != sizeof(struct mtpos)) { | ||
1120 | TRACE_ABORT(-EINVAL, | ||
1121 | ft_t_info, "bad argument size: %d", arg_size); | ||
1122 | } | ||
1123 | result = mt_tell((int *)&mtpos->mt_blkno); | ||
1124 | TRACE_EXIT result; | ||
1125 | } | ||
1126 | |||
1127 | #ifdef MTIOCFTFORMAT | ||
1128 | /* | ||
1129 | * formatting of floppy tape cartridges. This is intended to be used | ||
1130 | * together with the MTIOCFTCMD ioctl and the new mmap feature | ||
1131 | */ | ||
1132 | |||
1133 | /* | ||
1134 | * This function uses ftape_decode_header_segment() to inform the low | ||
1135 | * level ftape module about the new parameters. | ||
1136 | * | ||
1137 | * It erases the hseg_buf. The calling process must specify all | ||
1138 | * parameters to assure proper operation. | ||
1139 | * | ||
1140 | * return values: -EINVAL - wrong argument size | ||
1141 | * -EINVAL - if ftape_decode_header_segment() failed. | ||
1142 | */ | ||
1143 | static int set_format_parms(struct ftfmtparms *p, __u8 *hseg_buf) | ||
1144 | { | ||
1145 | ft_trace_t old_level = TRACE_LEVEL; | ||
1146 | TRACE_FUN(ft_t_flow); | ||
1147 | |||
1148 | TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_SETPARMS"); | ||
1149 | memset(hseg_buf, 0, FT_SEGMENT_SIZE); | ||
1150 | PUT4(hseg_buf, FT_SIGNATURE, FT_HSEG_MAGIC); | ||
1151 | |||
1152 | /* fill in user specified parameters | ||
1153 | */ | ||
1154 | hseg_buf[FT_FMT_CODE] = (__u8)p->ft_fmtcode; | ||
1155 | PUT2(hseg_buf, FT_SPT, p->ft_spt); | ||
1156 | hseg_buf[FT_TPC] = (__u8)p->ft_tpc; | ||
1157 | hseg_buf[FT_FHM] = (__u8)p->ft_fhm; | ||
1158 | hseg_buf[FT_FTM] = (__u8)p->ft_ftm; | ||
1159 | |||
1160 | /* fill in sane defaults to make ftape happy. | ||
1161 | */ | ||
1162 | hseg_buf[FT_FSM] = (__u8)128; /* 128 is hard wired all over ftape */ | ||
1163 | if (p->ft_fmtcode == fmt_big) { | ||
1164 | PUT4(hseg_buf, FT_6_HSEG_1, 0); | ||
1165 | PUT4(hseg_buf, FT_6_HSEG_2, 1); | ||
1166 | PUT4(hseg_buf, FT_6_FRST_SEG, 2); | ||
1167 | PUT4(hseg_buf, FT_6_LAST_SEG, p->ft_spt * p->ft_tpc - 1); | ||
1168 | } else { | ||
1169 | PUT2(hseg_buf, FT_HSEG_1, 0); | ||
1170 | PUT2(hseg_buf, FT_HSEG_2, 1); | ||
1171 | PUT2(hseg_buf, FT_FRST_SEG, 2); | ||
1172 | PUT2(hseg_buf, FT_LAST_SEG, p->ft_spt * p->ft_tpc - 1); | ||
1173 | } | ||
1174 | |||
1175 | /* Synchronize with the low level module. This is particularly | ||
1176 | * needed for unformatted cartridges as the QIC std was previously | ||
1177 | * unknown BUT is needed to set data rate and to calculate timeouts. | ||
1178 | */ | ||
1179 | TRACE_CATCH(ftape_calibrate_data_rate(p->ft_qicstd&QIC_TAPE_STD_MASK), | ||
1180 | _res = -EINVAL); | ||
1181 | |||
1182 | /* The following will also recalcualte the timeouts for the tape | ||
1183 | * length and QIC std we want to format to. | ||
1184 | * abort with -EINVAL rather than -EIO | ||
1185 | */ | ||
1186 | SET_TRACE_LEVEL(ft_t_warn); | ||
1187 | TRACE_CATCH(ftape_decode_header_segment(hseg_buf), | ||
1188 | SET_TRACE_LEVEL(old_level); _res = -EINVAL); | ||
1189 | SET_TRACE_LEVEL(old_level); | ||
1190 | TRACE_EXIT 0; | ||
1191 | } | ||
1192 | |||
1193 | /* | ||
1194 | * Return the internal SOFTWARE status of the kernel driver. This does | ||
1195 | * NOT query the tape drive about its status. | ||
1196 | */ | ||
1197 | static int get_format_parms(struct ftfmtparms *p, __u8 *hseg_buffer) | ||
1198 | { | ||
1199 | TRACE_FUN(ft_t_flow); | ||
1200 | |||
1201 | TRACE(ft_t_noise, "MTIOCFTFORMAT operation FTFMT_GETPARMS"); | ||
1202 | p->ft_qicstd = ft_qic_std; | ||
1203 | p->ft_fmtcode = ft_format_code; | ||
1204 | p->ft_fhm = hseg_buffer[FT_FHM]; | ||
1205 | p->ft_ftm = hseg_buffer[FT_FTM]; | ||
1206 | p->ft_spt = ft_segments_per_track; | ||
1207 | p->ft_tpc = ft_tracks_per_tape; | ||
1208 | TRACE_EXIT 0; | ||
1209 | } | ||
1210 | |||
1211 | static int mtiocftformat(struct mtftformat *mtftformat, int arg_size) | ||
1212 | { | ||
1213 | int result; | ||
1214 | union fmt_arg *arg = &mtftformat->fmt_arg; | ||
1215 | TRACE_FUN(ft_t_flow); | ||
1216 | |||
1217 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTFORMAT"); | ||
1218 | if (zft_offline) { | ||
1219 | if (ft_no_tape) { | ||
1220 | TRACE_ABORT(-ENXIO, ft_t_info, "no tape present"); | ||
1221 | } else { | ||
1222 | TRACE_ABORT(-ENXIO, ft_t_info, "drive is offline"); | ||
1223 | } | ||
1224 | } | ||
1225 | if (zft_qic_mode) { | ||
1226 | TRACE_ABORT(-EACCES, ft_t_info, | ||
1227 | "driver needs to be in raw mode for this ioctl"); | ||
1228 | } | ||
1229 | if (zft_hseg_buf == NULL) { | ||
1230 | TRACE_CATCH(zft_vcalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); | ||
1231 | } | ||
1232 | zft_header_read = 0; | ||
1233 | switch(mtftformat->fmt_op) { | ||
1234 | case FTFMT_SET_PARMS: | ||
1235 | TRACE_CATCH(set_format_parms(&arg->fmt_parms, zft_hseg_buf),); | ||
1236 | TRACE_EXIT 0; | ||
1237 | case FTFMT_GET_PARMS: | ||
1238 | TRACE_CATCH(get_format_parms(&arg->fmt_parms, zft_hseg_buf),); | ||
1239 | TRACE_EXIT 0; | ||
1240 | case FTFMT_FORMAT_TRACK: | ||
1241 | if ((ft_formatted && zft_check_write_access(&zft_pos) < 0) || | ||
1242 | (!ft_formatted && zft_write_protected)) { | ||
1243 | TRACE_ABORT(-EACCES, ft_t_info, "Write access denied"); | ||
1244 | } | ||
1245 | TRACE_CATCH(ftape_format_track(arg->fmt_track.ft_track, | ||
1246 | arg->fmt_track.ft_gap3),); | ||
1247 | TRACE_EXIT 0; | ||
1248 | case FTFMT_STATUS: | ||
1249 | TRACE_CATCH(ftape_format_status(&arg->fmt_status.ft_segment),); | ||
1250 | TRACE_EXIT 0; | ||
1251 | case FTFMT_VERIFY: | ||
1252 | TRACE_CATCH(ftape_verify_segment(arg->fmt_verify.ft_segment, | ||
1253 | (SectorMap *)&arg->fmt_verify.ft_bsm),); | ||
1254 | TRACE_EXIT 0; | ||
1255 | default: | ||
1256 | TRACE_ABORT(-EINVAL, ft_t_err, "Invalid format operation"); | ||
1257 | } | ||
1258 | TRACE_EXIT result; | ||
1259 | } | ||
1260 | #endif | ||
1261 | |||
1262 | #ifdef MTIOCFTCMD | ||
1263 | /* | ||
1264 | * send a QIC-117 command to the drive, with optional timeouts, | ||
1265 | * parameter and result bits. This is intended to be used together | ||
1266 | * with the formatting ioctl. | ||
1267 | */ | ||
1268 | static int mtiocftcmd(struct mtftcmd *ftcmd, int arg_size) | ||
1269 | { | ||
1270 | int i; | ||
1271 | TRACE_FUN(ft_t_flow); | ||
1272 | |||
1273 | TRACE(ft_t_noise, "Mag tape ioctl command: MTIOCFTCMD"); | ||
1274 | if (!capable(CAP_SYS_ADMIN)) { | ||
1275 | TRACE_ABORT(-EPERM, ft_t_info, | ||
1276 | "need CAP_SYS_ADMIN capability to send raw qic-117 commands"); | ||
1277 | } | ||
1278 | if (zft_qic_mode) { | ||
1279 | TRACE_ABORT(-EACCES, ft_t_info, | ||
1280 | "driver needs to be in raw mode for this ioctl"); | ||
1281 | } | ||
1282 | if (arg_size != sizeof(struct mtftcmd)) { | ||
1283 | TRACE_ABORT(-EINVAL, | ||
1284 | ft_t_info, "bad argument size: %d", arg_size); | ||
1285 | } | ||
1286 | if (ftcmd->ft_wait_before) { | ||
1287 | TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_before, | ||
1288 | &ftcmd->ft_status),); | ||
1289 | } | ||
1290 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1291 | goto ftmtcmd_error; | ||
1292 | if (ftcmd->ft_result_bits != 0) { | ||
1293 | TRACE_CATCH(ftape_report_operation(&ftcmd->ft_result, | ||
1294 | ftcmd->ft_cmd, | ||
1295 | ftcmd->ft_result_bits),); | ||
1296 | } else { | ||
1297 | TRACE_CATCH(ftape_command(ftcmd->ft_cmd),); | ||
1298 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1299 | goto ftmtcmd_error; | ||
1300 | for (i = 0; i < ftcmd->ft_parm_cnt; i++) { | ||
1301 | TRACE_CATCH(ftape_parameter(ftcmd->ft_parms[i]&0x0f),); | ||
1302 | if (ftcmd->ft_status & QIC_STATUS_ERROR) | ||
1303 | goto ftmtcmd_error; | ||
1304 | } | ||
1305 | } | ||
1306 | if (ftcmd->ft_wait_after != 0) { | ||
1307 | TRACE_CATCH(ftape_ready_wait(ftcmd->ft_wait_after, | ||
1308 | &ftcmd->ft_status),); | ||
1309 | } | ||
1310 | ftmtcmd_error: | ||
1311 | if (ftcmd->ft_status & QIC_STATUS_ERROR) { | ||
1312 | TRACE(ft_t_noise, "error status set"); | ||
1313 | TRACE_CATCH(ftape_report_error(&ftcmd->ft_error, | ||
1314 | &ftcmd->ft_cmd, 1),); | ||
1315 | } | ||
1316 | TRACE_EXIT 0; /* this is not an i/o error */ | ||
1317 | } | ||
1318 | #endif | ||
1319 | |||
1320 | /* IOCTL routine called by kernel-interface code | ||
1321 | */ | ||
1322 | int _zft_ioctl(unsigned int command, void __user * arg) | ||
1323 | { | ||
1324 | int result; | ||
1325 | union { struct mtop mtop; | ||
1326 | struct mtget mtget; | ||
1327 | struct mtpos mtpos; | ||
1328 | #ifdef MTIOCRDFTSEG | ||
1329 | struct mtftseg mtftseg; | ||
1330 | #endif | ||
1331 | #ifdef MTIOCVOLINFO | ||
1332 | struct mtvolinfo mtvolinfo; | ||
1333 | #endif | ||
1334 | #ifdef MTIOCGETSIZE | ||
1335 | struct mttapesize mttapesize; | ||
1336 | #endif | ||
1337 | #ifdef MTIOCFTFORMAT | ||
1338 | struct mtftformat mtftformat; | ||
1339 | #endif | ||
1340 | #ifdef ZFT_OBSOLETE | ||
1341 | struct mtblksz mtblksz; | ||
1342 | #endif | ||
1343 | #ifdef MTIOCFTCMD | ||
1344 | struct mtftcmd mtftcmd; | ||
1345 | #endif | ||
1346 | } krnl_arg; | ||
1347 | int arg_size = _IOC_SIZE(command); | ||
1348 | int dir = _IOC_DIR(command); | ||
1349 | TRACE_FUN(ft_t_flow); | ||
1350 | |||
1351 | /* This check will only catch arguments that are too large ! | ||
1352 | */ | ||
1353 | if (dir & (_IOC_READ | _IOC_WRITE) && arg_size > sizeof(krnl_arg)) { | ||
1354 | TRACE_ABORT(-EINVAL, | ||
1355 | ft_t_info, "bad argument size: %d", arg_size); | ||
1356 | } | ||
1357 | if (dir & _IOC_WRITE) { | ||
1358 | if (copy_from_user(&krnl_arg, arg, arg_size) != 0) { | ||
1359 | TRACE_EXIT -EFAULT; | ||
1360 | } | ||
1361 | } | ||
1362 | TRACE(ft_t_flow, "called with ioctl command: 0x%08x", command); | ||
1363 | switch (command) { | ||
1364 | case MTIOCTOP: | ||
1365 | result = mtioctop(&krnl_arg.mtop, arg_size); | ||
1366 | break; | ||
1367 | case MTIOCGET: | ||
1368 | result = mtiocget(&krnl_arg.mtget, arg_size); | ||
1369 | break; | ||
1370 | case MTIOCPOS: | ||
1371 | result = mtiocpos(&krnl_arg.mtpos, arg_size); | ||
1372 | break; | ||
1373 | #ifdef MTIOCVOLINFO | ||
1374 | case MTIOCVOLINFO: | ||
1375 | result = mtiocvolinfo(&krnl_arg.mtvolinfo, arg_size); | ||
1376 | break; | ||
1377 | #endif | ||
1378 | #ifdef ZFT_OBSOLETE | ||
1379 | case MTIOC_ZFTAPE_GETBLKSZ: | ||
1380 | result = mtioc_zftape_getblksz(&krnl_arg.mtblksz, arg_size); | ||
1381 | break; | ||
1382 | #endif | ||
1383 | #ifdef MTIOCRDFTSEG | ||
1384 | case MTIOCRDFTSEG: /* read a segment via ioctl */ | ||
1385 | result = mtiocrdftseg(&krnl_arg.mtftseg, arg_size); | ||
1386 | break; | ||
1387 | #endif | ||
1388 | #ifdef MTIOCWRFTSEG | ||
1389 | case MTIOCWRFTSEG: /* write a segment via ioctl */ | ||
1390 | result = mtiocwrftseg(&krnl_arg.mtftseg, arg_size); | ||
1391 | break; | ||
1392 | #endif | ||
1393 | #ifdef MTIOCGETSIZE | ||
1394 | case MTIOCGETSIZE: | ||
1395 | result = mtiocgetsize(&krnl_arg.mttapesize, arg_size); | ||
1396 | break; | ||
1397 | #endif | ||
1398 | #ifdef MTIOCFTFORMAT | ||
1399 | case MTIOCFTFORMAT: | ||
1400 | result = mtiocftformat(&krnl_arg.mtftformat, arg_size); | ||
1401 | break; | ||
1402 | #endif | ||
1403 | #ifdef MTIOCFTCMD | ||
1404 | case MTIOCFTCMD: | ||
1405 | result = mtiocftcmd(&krnl_arg.mtftcmd, arg_size); | ||
1406 | break; | ||
1407 | #endif | ||
1408 | default: | ||
1409 | result = -EINVAL; | ||
1410 | break; | ||
1411 | } | ||
1412 | if ((result >= 0) && (dir & _IOC_READ)) { | ||
1413 | if (copy_to_user(arg, &krnl_arg, arg_size) != 0) { | ||
1414 | TRACE_EXIT -EFAULT; | ||
1415 | } | ||
1416 | } | ||
1417 | TRACE_EXIT result; | ||
1418 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-ctl.h b/drivers/char/ftape/zftape/zftape-ctl.h new file mode 100644 index 000000000000..414159891990 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-ctl.h | |||
@@ -0,0 +1,59 @@ | |||
1 | #ifndef _ZFTAPE_CTL_H | ||
2 | #define _ZFTAPE_CTL_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996, 1997 Claus-Justus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-ctl.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:19:02 $ | ||
25 | * | ||
26 | * This file contains the non-standard IOCTL related definitions | ||
27 | * for the QIC-40/80 floppy-tape driver for Linux. | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | #include <linux/ioctl.h> | ||
32 | #include <linux/mtio.h> | ||
33 | |||
34 | #include "../zftape/zftape-rw.h" | ||
35 | |||
36 | #ifdef CONFIG_ZFTAPE_MODULE | ||
37 | #define ftape_status (*zft_status) | ||
38 | #endif | ||
39 | |||
40 | extern int zft_offline; | ||
41 | extern int zft_mt_compression; | ||
42 | extern int zft_write_protected; | ||
43 | extern int zft_header_read; | ||
44 | extern unsigned int zft_unit; | ||
45 | extern int zft_resid; | ||
46 | |||
47 | extern void zft_reset_position(zft_position *pos); | ||
48 | extern int zft_check_write_access(zft_position *pos); | ||
49 | extern int zft_def_idle_state(void); | ||
50 | |||
51 | /* hooks for the VFS interface | ||
52 | */ | ||
53 | extern int _zft_open(unsigned int dev_minor, unsigned int access_mode); | ||
54 | extern int _zft_close(void); | ||
55 | extern int _zft_ioctl(unsigned int command, void __user *arg); | ||
56 | #endif | ||
57 | |||
58 | |||
59 | |||
diff --git a/drivers/char/ftape/zftape/zftape-eof.c b/drivers/char/ftape/zftape/zftape-eof.c new file mode 100644 index 000000000000..dcadcaee9ac1 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-eof.c | |||
@@ -0,0 +1,199 @@ | |||
1 | /* | ||
2 | * I use these routines just to decide when I have to fake a | ||
3 | * volume-table to preserve compatibility to original ftape. | ||
4 | */ | ||
5 | /* | ||
6 | * Copyright (C) 1994-1995 Bas Laarhoven. | ||
7 | * | ||
8 | * Modified for zftape 1996, 1997 Claus Heine. | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2, or (at your option) | ||
13 | any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; see the file COPYING. If not, write to | ||
22 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | |||
24 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.c,v $ | ||
25 | * $Revision: 1.2 $ | ||
26 | * $Date: 1997/10/05 19:19:02 $ | ||
27 | * | ||
28 | * This file contains the eof mark handling code | ||
29 | * for the QIC-40/80 floppy-tape driver for Linux. | ||
30 | */ | ||
31 | |||
32 | #include <linux/string.h> | ||
33 | #include <linux/errno.h> | ||
34 | |||
35 | #include <linux/zftape.h> | ||
36 | |||
37 | #include "../zftape/zftape-init.h" | ||
38 | #include "../zftape/zftape-rw.h" | ||
39 | #include "../zftape/zftape-eof.h" | ||
40 | |||
41 | /* Global vars. | ||
42 | */ | ||
43 | |||
44 | /* a copy of the failed sector log from the header segment. | ||
45 | */ | ||
46 | eof_mark_union *zft_eof_map; | ||
47 | |||
48 | /* number of eof marks (entries in bad sector log) on tape. | ||
49 | */ | ||
50 | int zft_nr_eof_marks = -1; | ||
51 | |||
52 | |||
53 | /* Local vars. | ||
54 | */ | ||
55 | |||
56 | static char linux_tape_label[] = "Linux raw format V"; | ||
57 | enum { | ||
58 | min_fmt_version = 1, max_fmt_version = 2 | ||
59 | }; | ||
60 | static unsigned ftape_fmt_version = 0; | ||
61 | |||
62 | |||
63 | /* Ftape (mis)uses the bad sector log to record end-of-file marks. | ||
64 | * Initially (when the tape is erased) all entries in the bad sector | ||
65 | * log are added to the tape's bad sector map. The bad sector log then | ||
66 | * is cleared. | ||
67 | * | ||
68 | * The bad sector log normally contains entries of the form: | ||
69 | * even 16-bit word: segment number of bad sector | ||
70 | * odd 16-bit word: encoded date | ||
71 | * There can be a total of 448 entries (1792 bytes). | ||
72 | * | ||
73 | * My guess is that no program is using this bad sector log (the * | ||
74 | * format seems useless as there is no indication of the bad sector | ||
75 | * itself, only the segment) However, if any program does use the bad | ||
76 | * sector log, the format used by ftape will let the program think | ||
77 | * there are some bad sectors and no harm is done. | ||
78 | * | ||
79 | * The eof mark entries that ftape stores in the bad sector log: even | ||
80 | * 16-bit word: segment number of eof mark odd 16-bit word: sector | ||
81 | * number of eof mark [1..32] | ||
82 | * | ||
83 | * The zft_eof_map as maintained is a sorted list of eof mark entries. | ||
84 | * | ||
85 | * | ||
86 | * The tape name field in the header segments is used to store a linux | ||
87 | * tape identification string and a version number. This way the tape | ||
88 | * can be recognized as a Linux raw format tape when using tools under | ||
89 | * other OS's. | ||
90 | * | ||
91 | * 'Wide' QIC tapes (format code 4) don't have a failed sector list | ||
92 | * anymore. That space is used for the (longer) bad sector map that | ||
93 | * now is a variable length list too. We now store our end-of-file | ||
94 | * marker list after the bad-sector-map on tape. The list is delimited | ||
95 | * by a (__u32) 0 entry. | ||
96 | */ | ||
97 | |||
98 | int zft_ftape_validate_label(char *label) | ||
99 | { | ||
100 | static char tmp_label[45]; | ||
101 | int result = 0; | ||
102 | TRACE_FUN(ft_t_any); | ||
103 | |||
104 | memcpy(tmp_label, label, FT_LABEL_SZ); | ||
105 | tmp_label[FT_LABEL_SZ] = '\0'; | ||
106 | TRACE(ft_t_noise, "tape label = `%s'", tmp_label); | ||
107 | ftape_fmt_version = 0; | ||
108 | if (memcmp(label, linux_tape_label, strlen(linux_tape_label)) == 0) { | ||
109 | int pos = strlen(linux_tape_label); | ||
110 | while (label[pos] >= '0' && label[pos] <= '9') { | ||
111 | ftape_fmt_version *= 10; | ||
112 | ftape_fmt_version = label[ pos++] - '0'; | ||
113 | } | ||
114 | result = (ftape_fmt_version >= min_fmt_version && | ||
115 | ftape_fmt_version <= max_fmt_version); | ||
116 | } | ||
117 | TRACE(ft_t_noise, "format version = %d", ftape_fmt_version); | ||
118 | TRACE_EXIT result; | ||
119 | } | ||
120 | |||
121 | static __u8 * find_end_of_eof_list(__u8 * ptr, __u8 * limit) | ||
122 | { | ||
123 | while (ptr + 3 < limit) { | ||
124 | |||
125 | if (get_unaligned((__u32*)ptr)) { | ||
126 | ptr += sizeof(__u32); | ||
127 | } else { | ||
128 | return ptr; | ||
129 | } | ||
130 | } | ||
131 | return NULL; | ||
132 | } | ||
133 | |||
134 | void zft_ftape_extract_file_marks(__u8* address) | ||
135 | { | ||
136 | int i; | ||
137 | TRACE_FUN(ft_t_any); | ||
138 | |||
139 | zft_eof_map = NULL; | ||
140 | if (ft_format_code == fmt_var || ft_format_code == fmt_big) { | ||
141 | __u8* end; | ||
142 | __u8* start = ftape_find_end_of_bsm_list(address); | ||
143 | |||
144 | zft_nr_eof_marks = 0; | ||
145 | if (start) { | ||
146 | start += 3; /* skip end of list mark */ | ||
147 | end = find_end_of_eof_list(start, | ||
148 | address + FT_SEGMENT_SIZE); | ||
149 | if (end && end - start <= FT_FSL_SIZE) { | ||
150 | zft_nr_eof_marks = ((end - start) / | ||
151 | sizeof(eof_mark_union)); | ||
152 | zft_eof_map = (eof_mark_union *)start; | ||
153 | } else { | ||
154 | TRACE(ft_t_err, | ||
155 | "EOF Mark List is too long or damaged!"); | ||
156 | } | ||
157 | } else { | ||
158 | TRACE(ft_t_err, | ||
159 | "Bad Sector List is too long or damaged !"); | ||
160 | } | ||
161 | } else { | ||
162 | zft_eof_map = (eof_mark_union *)&address[FT_FSL]; | ||
163 | zft_nr_eof_marks = GET2(address, FT_FSL_CNT); | ||
164 | } | ||
165 | TRACE(ft_t_noise, "number of file marks: %d", zft_nr_eof_marks); | ||
166 | if (ftape_fmt_version == 1) { | ||
167 | TRACE(ft_t_info, "swapping version 1 fields"); | ||
168 | /* version 1 format uses swapped sector and segment | ||
169 | * fields, correct that ! | ||
170 | */ | ||
171 | for (i = 0; i < zft_nr_eof_marks; ++i) { | ||
172 | __u16 tmp = GET2(&zft_eof_map[i].mark.segment,0); | ||
173 | PUT2(&zft_eof_map[i].mark.segment, 0, | ||
174 | GET2(&zft_eof_map[i].mark.date,0)); | ||
175 | PUT2(&zft_eof_map[i].mark.date, 0, tmp); | ||
176 | } | ||
177 | } | ||
178 | for (i = 0; i < zft_nr_eof_marks; ++i) { | ||
179 | TRACE(ft_t_noise, "eof mark: %5d/%2d", | ||
180 | GET2(&zft_eof_map[i].mark.segment, 0), | ||
181 | GET2(&zft_eof_map[i].mark.date,0)); | ||
182 | } | ||
183 | TRACE_EXIT; | ||
184 | } | ||
185 | |||
186 | void zft_clear_ftape_file_marks(void) | ||
187 | { | ||
188 | TRACE_FUN(ft_t_flow); | ||
189 | /* Clear failed sector log: remove all tape marks. We | ||
190 | * don't use old ftape-style EOF-marks. | ||
191 | */ | ||
192 | TRACE(ft_t_info, "Clearing old ftape's eof map"); | ||
193 | memset(zft_eof_map, 0, zft_nr_eof_marks * sizeof(__u32)); | ||
194 | zft_nr_eof_marks = 0; | ||
195 | PUT2(zft_hseg_buf, FT_FSL_CNT, 0); /* nr of eof-marks */ | ||
196 | zft_header_changed = 1; | ||
197 | zft_update_label(zft_hseg_buf); | ||
198 | TRACE_EXIT; | ||
199 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-eof.h b/drivers/char/ftape/zftape/zftape-eof.h new file mode 100644 index 000000000000..26568c26c518 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-eof.h | |||
@@ -0,0 +1,52 @@ | |||
1 | #ifndef _ZFTAPE_EOF_H | ||
2 | #define _ZFTAPE_EOF_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1994-1995 Bas Laarhoven. | ||
6 | * adaptaed for zftape 1996, 1997 by Claus Heine | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License | ||
19 | along with this program; see the file COPYING. If not, write to | ||
20 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-eof.h,v $ | ||
24 | * $Revision: 1.2 $ | ||
25 | * $Date: 1997/10/05 19:19:03 $ | ||
26 | * | ||
27 | * Definitions and declarations for the end of file markers | ||
28 | * for the QIC-40/80 floppy-tape driver for Linux. | ||
29 | */ | ||
30 | |||
31 | #include <linux/ftape-header-segment.h> | ||
32 | #include "../zftape/zftape-buffers.h" | ||
33 | /* failed sector log size (only used if format code != 4). | ||
34 | */ | ||
35 | |||
36 | typedef union { | ||
37 | ft_fsl_entry mark; | ||
38 | __u32 entry; | ||
39 | } eof_mark_union; | ||
40 | |||
41 | /* ftape-eof.c defined global vars. | ||
42 | */ | ||
43 | extern int zft_nr_eof_marks; | ||
44 | extern eof_mark_union *zft_eof_map; | ||
45 | |||
46 | /* ftape-eof.c defined global functions. | ||
47 | */ | ||
48 | extern void zft_ftape_extract_file_marks(__u8* address); | ||
49 | extern int zft_ftape_validate_label(char* label); | ||
50 | extern void zft_clear_ftape_file_marks(void); | ||
51 | |||
52 | #endif | ||
diff --git a/drivers/char/ftape/zftape/zftape-init.c b/drivers/char/ftape/zftape/zftape-init.c new file mode 100644 index 000000000000..dbac7e54e8e0 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-init.c | |||
@@ -0,0 +1,403 @@ | |||
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 | * This file contains the code that registers the zftape frontend | ||
20 | * to the ftape floppy tape driver for Linux | ||
21 | */ | ||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/fs.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/signal.h> | ||
29 | #include <linux/major.h> | ||
30 | #include <linux/slab.h> | ||
31 | #ifdef CONFIG_KMOD | ||
32 | #include <linux/kmod.h> | ||
33 | #endif | ||
34 | #include <linux/fcntl.h> | ||
35 | #include <linux/smp_lock.h> | ||
36 | #include <linux/devfs_fs_kernel.h> | ||
37 | |||
38 | #include <linux/zftape.h> | ||
39 | #include <linux/init.h> | ||
40 | #include <linux/device.h> | ||
41 | |||
42 | #include "../zftape/zftape-init.h" | ||
43 | #include "../zftape/zftape-read.h" | ||
44 | #include "../zftape/zftape-write.h" | ||
45 | #include "../zftape/zftape-ctl.h" | ||
46 | #include "../zftape/zftape-buffers.h" | ||
47 | |||
48 | MODULE_AUTHOR("(c) 1996, 1997 Claus-Justus Heine " | ||
49 | "(claus@momo.math.rwth-aachen.de)"); | ||
50 | MODULE_DESCRIPTION(ZFTAPE_VERSION " - " | ||
51 | "VFS interface for the Linux floppy tape driver. " | ||
52 | "Support for QIC-113 compatible volume table " | ||
53 | "and builtin compression (lzrw3 algorithm)"); | ||
54 | MODULE_SUPPORTED_DEVICE("char-major-27"); | ||
55 | MODULE_LICENSE("GPL"); | ||
56 | |||
57 | /* Global vars. | ||
58 | */ | ||
59 | struct zft_cmpr_ops *zft_cmpr_ops = NULL; | ||
60 | const ftape_info *zft_status; | ||
61 | |||
62 | /* Local vars. | ||
63 | */ | ||
64 | static unsigned long busy_flag; | ||
65 | |||
66 | static sigset_t orig_sigmask; | ||
67 | |||
68 | /* the interface to the kernel vfs layer | ||
69 | */ | ||
70 | |||
71 | /* Note about llseek(): | ||
72 | * | ||
73 | * st.c and tpqic.c update fp->f_pos but don't implment llseek() and | ||
74 | * initialize the llseek component of the file_ops struct with NULL. | ||
75 | * This means that the user will get the default seek, but the tape | ||
76 | * device will not respect the new position, but happily read from the | ||
77 | * old position. Think a zftape specific llseek() function would be | ||
78 | * better, returning -ESPIPE. TODO. | ||
79 | */ | ||
80 | |||
81 | static int zft_open (struct inode *ino, struct file *filep); | ||
82 | static int zft_close(struct inode *ino, struct file *filep); | ||
83 | static int zft_ioctl(struct inode *ino, struct file *filep, | ||
84 | unsigned int command, unsigned long arg); | ||
85 | static int zft_mmap(struct file *filep, struct vm_area_struct *vma); | ||
86 | static ssize_t zft_read (struct file *fp, char __user *buff, | ||
87 | size_t req_len, loff_t *ppos); | ||
88 | static ssize_t zft_write(struct file *fp, const char __user *buff, | ||
89 | size_t req_len, loff_t *ppos); | ||
90 | |||
91 | static struct file_operations zft_cdev = | ||
92 | { | ||
93 | .owner = THIS_MODULE, | ||
94 | .read = zft_read, | ||
95 | .write = zft_write, | ||
96 | .ioctl = zft_ioctl, | ||
97 | .mmap = zft_mmap, | ||
98 | .open = zft_open, | ||
99 | .release = zft_close, | ||
100 | }; | ||
101 | |||
102 | static struct class_simple *zft_class; | ||
103 | |||
104 | /* Open floppy tape device | ||
105 | */ | ||
106 | static int zft_open(struct inode *ino, struct file *filep) | ||
107 | { | ||
108 | int result; | ||
109 | TRACE_FUN(ft_t_flow); | ||
110 | |||
111 | nonseekable_open(ino, filep); | ||
112 | TRACE(ft_t_flow, "called for minor %d", iminor(ino)); | ||
113 | if ( test_and_set_bit(0,&busy_flag) ) { | ||
114 | TRACE_ABORT(-EBUSY, ft_t_warn, "failed: already busy"); | ||
115 | } | ||
116 | if ((iminor(ino) & ~(ZFT_MINOR_OP_MASK | FTAPE_NO_REWIND)) | ||
117 | > | ||
118 | FTAPE_SEL_D) { | ||
119 | clear_bit(0,&busy_flag); | ||
120 | TRACE_ABORT(-ENXIO, ft_t_err, "failed: invalid unit nr"); | ||
121 | } | ||
122 | orig_sigmask = current->blocked; | ||
123 | sigfillset(¤t->blocked); | ||
124 | result = _zft_open(iminor(ino), filep->f_flags & O_ACCMODE); | ||
125 | if (result < 0) { | ||
126 | current->blocked = orig_sigmask; /* restore mask */ | ||
127 | clear_bit(0,&busy_flag); | ||
128 | TRACE_ABORT(result, ft_t_err, "_ftape_open failed"); | ||
129 | } else { | ||
130 | /* Mask signals that will disturb proper operation of the | ||
131 | * program that is calling. | ||
132 | */ | ||
133 | current->blocked = orig_sigmask; | ||
134 | sigaddsetmask (¤t->blocked, _DO_BLOCK); | ||
135 | TRACE_EXIT 0; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | /* Close floppy tape device | ||
140 | */ | ||
141 | static int zft_close(struct inode *ino, struct file *filep) | ||
142 | { | ||
143 | int result; | ||
144 | TRACE_FUN(ft_t_flow); | ||
145 | |||
146 | if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit) { | ||
147 | TRACE(ft_t_err, "failed: not busy or wrong unit"); | ||
148 | TRACE_EXIT 0; | ||
149 | } | ||
150 | sigfillset(¤t->blocked); | ||
151 | result = _zft_close(); | ||
152 | if (result < 0) { | ||
153 | TRACE(ft_t_err, "_zft_close failed"); | ||
154 | } | ||
155 | current->blocked = orig_sigmask; /* restore before open state */ | ||
156 | clear_bit(0,&busy_flag); | ||
157 | TRACE_EXIT 0; | ||
158 | } | ||
159 | |||
160 | /* Ioctl for floppy tape device | ||
161 | */ | ||
162 | static int zft_ioctl(struct inode *ino, struct file *filep, | ||
163 | unsigned int command, unsigned long arg) | ||
164 | { | ||
165 | int result = -EIO; | ||
166 | sigset_t old_sigmask; | ||
167 | TRACE_FUN(ft_t_flow); | ||
168 | |||
169 | if ( !test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { | ||
170 | TRACE_ABORT(-EIO, ft_t_err, | ||
171 | "failed: not busy, failure or wrong unit"); | ||
172 | } | ||
173 | old_sigmask = current->blocked; /* save mask */ | ||
174 | sigfillset(¤t->blocked); | ||
175 | /* This will work as long as sizeof(void *) == sizeof(long) */ | ||
176 | result = _zft_ioctl(command, (void __user *) arg); | ||
177 | current->blocked = old_sigmask; /* restore mask */ | ||
178 | TRACE_EXIT result; | ||
179 | } | ||
180 | |||
181 | /* Ioctl for floppy tape device | ||
182 | */ | ||
183 | static int zft_mmap(struct file *filep, struct vm_area_struct *vma) | ||
184 | { | ||
185 | int result = -EIO; | ||
186 | sigset_t old_sigmask; | ||
187 | TRACE_FUN(ft_t_flow); | ||
188 | |||
189 | if ( !test_bit(0,&busy_flag) || | ||
190 | iminor(filep->f_dentry->d_inode) != zft_unit || | ||
191 | ft_failure) | ||
192 | { | ||
193 | TRACE_ABORT(-EIO, ft_t_err, | ||
194 | "failed: not busy, failure or wrong unit"); | ||
195 | } | ||
196 | old_sigmask = current->blocked; /* save mask */ | ||
197 | sigfillset(¤t->blocked); | ||
198 | if ((result = ftape_mmap(vma)) >= 0) { | ||
199 | #ifndef MSYNC_BUG_WAS_FIXED | ||
200 | static struct vm_operations_struct dummy = { NULL, }; | ||
201 | vma->vm_ops = &dummy; | ||
202 | #endif | ||
203 | } | ||
204 | current->blocked = old_sigmask; /* restore mask */ | ||
205 | TRACE_EXIT result; | ||
206 | } | ||
207 | |||
208 | /* Read from floppy tape device | ||
209 | */ | ||
210 | static ssize_t zft_read(struct file *fp, char __user *buff, | ||
211 | size_t req_len, loff_t *ppos) | ||
212 | { | ||
213 | int result = -EIO; | ||
214 | sigset_t old_sigmask; | ||
215 | struct inode *ino = fp->f_dentry->d_inode; | ||
216 | TRACE_FUN(ft_t_flow); | ||
217 | |||
218 | TRACE(ft_t_data_flow, "called with count: %ld", (unsigned long)req_len); | ||
219 | if (!test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { | ||
220 | TRACE_ABORT(-EIO, ft_t_err, | ||
221 | "failed: not busy, failure or wrong unit"); | ||
222 | } | ||
223 | old_sigmask = current->blocked; /* save mask */ | ||
224 | sigfillset(¤t->blocked); | ||
225 | result = _zft_read(buff, req_len); | ||
226 | current->blocked = old_sigmask; /* restore mask */ | ||
227 | TRACE(ft_t_data_flow, "return with count: %d", result); | ||
228 | TRACE_EXIT result; | ||
229 | } | ||
230 | |||
231 | /* Write to tape device | ||
232 | */ | ||
233 | static ssize_t zft_write(struct file *fp, const char __user *buff, | ||
234 | size_t req_len, loff_t *ppos) | ||
235 | { | ||
236 | int result = -EIO; | ||
237 | sigset_t old_sigmask; | ||
238 | struct inode *ino = fp->f_dentry->d_inode; | ||
239 | TRACE_FUN(ft_t_flow); | ||
240 | |||
241 | TRACE(ft_t_flow, "called with count: %ld", (unsigned long)req_len); | ||
242 | if (!test_bit(0,&busy_flag) || iminor(ino) != zft_unit || ft_failure) { | ||
243 | TRACE_ABORT(-EIO, ft_t_err, | ||
244 | "failed: not busy, failure or wrong unit"); | ||
245 | } | ||
246 | old_sigmask = current->blocked; /* save mask */ | ||
247 | sigfillset(¤t->blocked); | ||
248 | result = _zft_write(buff, req_len); | ||
249 | current->blocked = old_sigmask; /* restore mask */ | ||
250 | TRACE(ft_t_data_flow, "return with count: %d", result); | ||
251 | TRACE_EXIT result; | ||
252 | } | ||
253 | |||
254 | /* END OF VFS INTERFACE | ||
255 | * | ||
256 | *****************************************************************************/ | ||
257 | |||
258 | /* driver/module initialization | ||
259 | */ | ||
260 | |||
261 | /* the compression module has to call this function to hook into the zftape | ||
262 | * code | ||
263 | */ | ||
264 | int zft_cmpr_register(struct zft_cmpr_ops *new_ops) | ||
265 | { | ||
266 | TRACE_FUN(ft_t_flow); | ||
267 | |||
268 | if (zft_cmpr_ops != NULL) { | ||
269 | TRACE_EXIT -EBUSY; | ||
270 | } else { | ||
271 | zft_cmpr_ops = new_ops; | ||
272 | TRACE_EXIT 0; | ||
273 | } | ||
274 | } | ||
275 | |||
276 | /* lock the zft-compressor() module. | ||
277 | */ | ||
278 | int zft_cmpr_lock(int try_to_load) | ||
279 | { | ||
280 | if (zft_cmpr_ops == NULL) { | ||
281 | #ifdef CONFIG_KMOD | ||
282 | if (try_to_load) { | ||
283 | request_module("zft-compressor"); | ||
284 | if (zft_cmpr_ops == NULL) { | ||
285 | return -ENOSYS; | ||
286 | } | ||
287 | } else { | ||
288 | return -ENOSYS; | ||
289 | } | ||
290 | #else | ||
291 | return -ENOSYS; | ||
292 | #endif | ||
293 | } | ||
294 | (*zft_cmpr_ops->lock)(); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | #ifdef CONFIG_ZFT_COMPRESSOR | ||
299 | extern int zft_compressor_init(void); | ||
300 | #endif | ||
301 | |||
302 | /* Called by modules package when installing the driver or by kernel | ||
303 | * during the initialization phase | ||
304 | */ | ||
305 | int __init zft_init(void) | ||
306 | { | ||
307 | int i; | ||
308 | TRACE_FUN(ft_t_flow); | ||
309 | |||
310 | #ifdef MODULE | ||
311 | printk(KERN_INFO ZFTAPE_VERSION "\n"); | ||
312 | if (TRACE_LEVEL >= ft_t_info) { | ||
313 | printk( | ||
314 | KERN_INFO | ||
315 | "(c) 1996, 1997 Claus-Justus Heine (claus@momo.math.rwth-aachen.de)\n" | ||
316 | KERN_INFO | ||
317 | "vfs interface for ftape floppy tape driver.\n" | ||
318 | KERN_INFO | ||
319 | "Support for QIC-113 compatible volume table, dynamic memory allocation\n" | ||
320 | KERN_INFO | ||
321 | "and builtin compression (lzrw3 algorithm).\n"); | ||
322 | } | ||
323 | #else /* !MODULE */ | ||
324 | /* print a short no-nonsense boot message */ | ||
325 | printk(KERN_INFO ZFTAPE_VERSION "\n"); | ||
326 | #endif /* MODULE */ | ||
327 | TRACE(ft_t_info, "zft_init @ 0x%p", zft_init); | ||
328 | TRACE(ft_t_info, | ||
329 | "installing zftape VFS interface for ftape driver ..."); | ||
330 | TRACE_CATCH(register_chrdev(QIC117_TAPE_MAJOR, "zft", &zft_cdev),); | ||
331 | |||
332 | zft_class = class_simple_create(THIS_MODULE, "zft"); | ||
333 | for (i = 0; i < 4; i++) { | ||
334 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i), NULL, "qft%i", i); | ||
335 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i), | ||
336 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
337 | "qft%i", i); | ||
338 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 4), NULL, "nqft%i", i); | ||
339 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 4), | ||
340 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
341 | "nqft%i", i); | ||
342 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 16), NULL, "zqft%i", i); | ||
343 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 16), | ||
344 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
345 | "zqft%i", i); | ||
346 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 20), NULL, "nzqft%i", i); | ||
347 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 20), | ||
348 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
349 | "nzqft%i", i); | ||
350 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 32), NULL, "rawqft%i", i); | ||
351 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 32), | ||
352 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
353 | "rawqft%i", i); | ||
354 | class_simple_device_add(zft_class, MKDEV(QIC117_TAPE_MAJOR, i + 36), NULL, "nrawrawqft%i", i); | ||
355 | devfs_mk_cdev(MKDEV(QIC117_TAPE_MAJOR, i + 36), | ||
356 | S_IFCHR | S_IRUSR | S_IWUSR, | ||
357 | "nrawqft%i", i); | ||
358 | } | ||
359 | |||
360 | #ifdef CONFIG_ZFT_COMPRESSOR | ||
361 | (void)zft_compressor_init(); | ||
362 | #endif | ||
363 | zft_status = ftape_get_status(); /* fetch global data of ftape | ||
364 | * hardware driver | ||
365 | */ | ||
366 | TRACE_EXIT 0; | ||
367 | } | ||
368 | |||
369 | |||
370 | /* Called by modules package when removing the driver | ||
371 | */ | ||
372 | static void zft_exit(void) | ||
373 | { | ||
374 | int i; | ||
375 | TRACE_FUN(ft_t_flow); | ||
376 | |||
377 | if (unregister_chrdev(QIC117_TAPE_MAJOR, "zft") != 0) { | ||
378 | TRACE(ft_t_warn, "failed"); | ||
379 | } else { | ||
380 | TRACE(ft_t_info, "successful"); | ||
381 | } | ||
382 | for (i = 0; i < 4; i++) { | ||
383 | devfs_remove("qft%i", i); | ||
384 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i)); | ||
385 | devfs_remove("nqft%i", i); | ||
386 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 4)); | ||
387 | devfs_remove("zqft%i", i); | ||
388 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 16)); | ||
389 | devfs_remove("nzqft%i", i); | ||
390 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 20)); | ||
391 | devfs_remove("rawqft%i", i); | ||
392 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 32)); | ||
393 | devfs_remove("nrawqft%i", i); | ||
394 | class_simple_device_remove(MKDEV(QIC117_TAPE_MAJOR, i + 36)); | ||
395 | } | ||
396 | class_simple_destroy(zft_class); | ||
397 | zft_uninit_mem(); /* release remaining memory, if any */ | ||
398 | printk(KERN_INFO "zftape successfully unloaded.\n"); | ||
399 | TRACE_EXIT; | ||
400 | } | ||
401 | |||
402 | module_init(zft_init); | ||
403 | module_exit(zft_exit); | ||
diff --git a/drivers/char/ftape/zftape/zftape-init.h b/drivers/char/ftape/zftape/zftape-init.h new file mode 100644 index 000000000000..937e5d48c20e --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-init.h | |||
@@ -0,0 +1,77 @@ | |||
1 | #ifndef _ZFTAPE_INIT_H | ||
2 | #define _ZFTAPE_INIT_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996, 1997 Claus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-init.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:19:05 $ | ||
25 | * | ||
26 | * This file contains definitions and macro for the vfs | ||
27 | * interface defined by zftape | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include <linux/ftape-header-segment.h> | ||
32 | |||
33 | #include "../lowlevel/ftape-tracing.h" | ||
34 | #include "../lowlevel/ftape-ctl.h" | ||
35 | #include "../lowlevel/ftape-read.h" | ||
36 | #include "../lowlevel/ftape-write.h" | ||
37 | #include "../lowlevel/ftape-bsm.h" | ||
38 | #include "../lowlevel/ftape-io.h" | ||
39 | #include "../lowlevel/ftape-buffer.h" | ||
40 | #include "../lowlevel/ftape-format.h" | ||
41 | |||
42 | #include "../zftape/zftape-rw.h" | ||
43 | |||
44 | #ifdef MODULE | ||
45 | #define ftape_status (*zft_status) | ||
46 | #endif | ||
47 | |||
48 | extern const ftape_info *zft_status; /* needed for zftape-vtbl.h */ | ||
49 | |||
50 | #include "../zftape/zftape-vtbl.h" | ||
51 | |||
52 | struct zft_cmpr_ops { | ||
53 | int (*write)(int *write_cnt, | ||
54 | __u8 *dst_buf, const int seg_sz, | ||
55 | const __u8 __user *src_buf, const int req_len, | ||
56 | const zft_position *pos, const zft_volinfo *volume); | ||
57 | int (*read)(int *read_cnt, | ||
58 | __u8 __user *dst_buf, const int req_len, | ||
59 | const __u8 *src_buf, const int seg_sz, | ||
60 | const zft_position *pos, const zft_volinfo *volume); | ||
61 | int (*seek)(unsigned int new_block_pos, | ||
62 | zft_position *pos, const zft_volinfo *volume, | ||
63 | __u8 *buffer); | ||
64 | void (*lock) (void); | ||
65 | void (*reset) (void); | ||
66 | void (*cleanup)(void); | ||
67 | }; | ||
68 | |||
69 | extern struct zft_cmpr_ops *zft_cmpr_ops; | ||
70 | /* zftape-init.c defined global functions. | ||
71 | */ | ||
72 | extern int zft_cmpr_register(struct zft_cmpr_ops *new_ops); | ||
73 | extern int zft_cmpr_lock(int try_to_load); | ||
74 | |||
75 | #endif | ||
76 | |||
77 | |||
diff --git a/drivers/char/ftape/zftape/zftape-read.c b/drivers/char/ftape/zftape/zftape-read.c new file mode 100644 index 000000000000..214bf03dce68 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-read.c | |||
@@ -0,0 +1,377 @@ | |||
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-read.c,v $ | ||
20 | * $Revision: 1.2 $ | ||
21 | * $Date: 1997/10/05 19:19:06 $ | ||
22 | * | ||
23 | * This file contains the high level reading code | ||
24 | * for the QIC-117 floppy-tape driver for Linux. | ||
25 | */ | ||
26 | |||
27 | #include <linux/errno.h> | ||
28 | #include <linux/mm.h> | ||
29 | |||
30 | #include <linux/zftape.h> | ||
31 | |||
32 | #include <asm/uaccess.h> | ||
33 | |||
34 | #include "../zftape/zftape-init.h" | ||
35 | #include "../zftape/zftape-eof.h" | ||
36 | #include "../zftape/zftape-ctl.h" | ||
37 | #include "../zftape/zftape-write.h" | ||
38 | #include "../zftape/zftape-read.h" | ||
39 | #include "../zftape/zftape-rw.h" | ||
40 | #include "../zftape/zftape-vtbl.h" | ||
41 | |||
42 | /* Global vars. | ||
43 | */ | ||
44 | int zft_just_before_eof; | ||
45 | |||
46 | /* Local vars. | ||
47 | */ | ||
48 | static int buf_len_rd; | ||
49 | |||
50 | void zft_zap_read_buffers(void) | ||
51 | { | ||
52 | buf_len_rd = 0; | ||
53 | } | ||
54 | |||
55 | int zft_read_header_segments(void) | ||
56 | { | ||
57 | TRACE_FUN(ft_t_flow); | ||
58 | |||
59 | zft_header_read = 0; | ||
60 | TRACE_CATCH(zft_vmalloc_once(&zft_hseg_buf, FT_SEGMENT_SIZE),); | ||
61 | TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); | ||
62 | TRACE(ft_t_info, "Segments written since first format: %d", | ||
63 | (int)GET4(zft_hseg_buf, FT_SEG_CNT)); | ||
64 | zft_qic113 = (ft_format_code != fmt_normal && | ||
65 | ft_format_code != fmt_1100ft && | ||
66 | ft_format_code != fmt_425ft); | ||
67 | TRACE(ft_t_info, "ft_first_data_segment: %d, ft_last_data_segment: %d", | ||
68 | ft_first_data_segment, ft_last_data_segment); | ||
69 | zft_capacity = zft_get_capacity(); | ||
70 | zft_old_ftape = zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]); | ||
71 | if (zft_old_ftape) { | ||
72 | TRACE(ft_t_info, | ||
73 | "Found old ftaped tape, emulating eof marks, entering read-only mode"); | ||
74 | zft_ftape_extract_file_marks(zft_hseg_buf); | ||
75 | TRACE_CATCH(zft_fake_volume_headers(zft_eof_map, | ||
76 | zft_nr_eof_marks),); | ||
77 | } else { | ||
78 | /* the specs say that the volume table must be | ||
79 | * initialized with zeroes during formatting, so it | ||
80 | * MUST be readable, i.e. contain vaid ECC | ||
81 | * information. | ||
82 | */ | ||
83 | TRACE_CATCH(ftape_read_segment(ft_first_data_segment, | ||
84 | zft_deblock_buf, | ||
85 | FT_RD_SINGLE),); | ||
86 | TRACE_CATCH(zft_extract_volume_headers(zft_deblock_buf),); | ||
87 | } | ||
88 | zft_header_read = 1; | ||
89 | zft_set_flags(zft_unit); | ||
90 | zft_reset_position(&zft_pos); | ||
91 | TRACE_EXIT 0; | ||
92 | } | ||
93 | |||
94 | int zft_fetch_segment_fraction(const unsigned int segment, void *buffer, | ||
95 | const ft_read_mode_t read_mode, | ||
96 | const unsigned int start, | ||
97 | const unsigned int size) | ||
98 | { | ||
99 | int seg_sz; | ||
100 | TRACE_FUN(ft_t_flow); | ||
101 | |||
102 | if (segment == zft_deblock_segment) { | ||
103 | TRACE(ft_t_data_flow, | ||
104 | "re-using segment %d already in deblock buffer", | ||
105 | segment); | ||
106 | seg_sz = zft_get_seg_sz(segment); | ||
107 | if (start > seg_sz) { | ||
108 | TRACE_ABORT(-EINVAL, ft_t_bug, | ||
109 | "trying to read beyond end of segment:\n" | ||
110 | KERN_INFO "seg_sz : %d\n" | ||
111 | KERN_INFO "start : %d\n" | ||
112 | KERN_INFO "segment: %d", | ||
113 | seg_sz, start, segment); | ||
114 | } | ||
115 | if ((start + size) > seg_sz) { | ||
116 | TRACE_EXIT seg_sz - start; | ||
117 | } | ||
118 | TRACE_EXIT size; | ||
119 | } | ||
120 | seg_sz = ftape_read_segment_fraction(segment, buffer, read_mode, | ||
121 | start, size); | ||
122 | TRACE(ft_t_data_flow, "segment %d, result %d", segment, seg_sz); | ||
123 | if ((int)seg_sz >= 0 && start == 0 && size == FT_SEGMENT_SIZE) { | ||
124 | /* this implicitly assumes that we are always called with | ||
125 | * buffer == zft_deblock_buf | ||
126 | */ | ||
127 | zft_deblock_segment = segment; | ||
128 | } else { | ||
129 | zft_deblock_segment = -1; | ||
130 | } | ||
131 | TRACE_EXIT seg_sz; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * out: | ||
136 | * | ||
137 | * int *read_cnt: the number of bytes we removed from the | ||
138 | * zft_deblock_buf (result) | ||
139 | * | ||
140 | * int *to_do : the remaining size of the read-request. Is changed. | ||
141 | * | ||
142 | * in: | ||
143 | * | ||
144 | * char *buff : buff is the address of the upper part of the user | ||
145 | * buffer, that hasn't been filled with data yet. | ||
146 | * int buf_pos_read: copy of buf_pos_rd | ||
147 | * int buf_len_read: copy of buf_len_rd | ||
148 | * char *zft_deblock_buf: ftape_zft_deblock_buf | ||
149 | * | ||
150 | * returns the amount of data actually copied to the user-buffer | ||
151 | * | ||
152 | * to_do MUST NOT SHRINK except to indicate an EOT. In this case to_do | ||
153 | * has to be set to 0. We cannot return -ENOSPC, because we return the | ||
154 | * amount of data actually * copied to the user-buffer | ||
155 | */ | ||
156 | static int zft_simple_read (int *read_cnt, | ||
157 | __u8 __user *dst_buf, | ||
158 | const int to_do, | ||
159 | const __u8 *src_buf, | ||
160 | const int seg_sz, | ||
161 | const zft_position *pos, | ||
162 | const zft_volinfo *volume) | ||
163 | { | ||
164 | TRACE_FUN(ft_t_flow); | ||
165 | |||
166 | if (seg_sz - pos->seg_byte_pos < to_do) { | ||
167 | *read_cnt = seg_sz - pos->seg_byte_pos; | ||
168 | } else { | ||
169 | *read_cnt = to_do; | ||
170 | } | ||
171 | if (copy_to_user(dst_buf, | ||
172 | src_buf + pos->seg_byte_pos, *read_cnt) != 0) { | ||
173 | TRACE_EXIT -EFAULT; | ||
174 | } | ||
175 | TRACE(ft_t_noise, "nr bytes just read: %d", *read_cnt); | ||
176 | TRACE_EXIT *read_cnt; | ||
177 | } | ||
178 | |||
179 | /* req_len: gets clipped due to EOT of EOF. | ||
180 | * req_clipped: is a flag indicating whether req_len was clipped or not | ||
181 | * volume: contains information on current volume (blk_sz etc.) | ||
182 | */ | ||
183 | static int check_read_access(int *req_len, | ||
184 | const zft_volinfo **volume, | ||
185 | int *req_clipped, | ||
186 | const zft_position *pos) | ||
187 | { | ||
188 | static __s64 remaining; | ||
189 | static int eod; | ||
190 | TRACE_FUN(ft_t_flow); | ||
191 | |||
192 | if (zft_io_state != zft_reading) { | ||
193 | if (zft_offline) { /* offline includes no_tape */ | ||
194 | TRACE_ABORT(-ENXIO, ft_t_warn, | ||
195 | "tape is offline or no cartridge"); | ||
196 | } | ||
197 | if (!ft_formatted) { | ||
198 | TRACE_ABORT(-EACCES, | ||
199 | ft_t_warn, "tape is not formatted"); | ||
200 | } | ||
201 | /* now enter defined state, read header segment if not | ||
202 | * already done and flush write buffers | ||
203 | */ | ||
204 | TRACE_CATCH(zft_def_idle_state(),); | ||
205 | zft_io_state = zft_reading; | ||
206 | if (zft_tape_at_eod(pos)) { | ||
207 | eod = 1; | ||
208 | TRACE_EXIT 1; | ||
209 | } | ||
210 | eod = 0; | ||
211 | *volume = zft_find_volume(pos->seg_pos); | ||
212 | /* get the space left until EOF */ | ||
213 | remaining = zft_check_for_eof(*volume, pos); | ||
214 | buf_len_rd = 0; | ||
215 | TRACE(ft_t_noise, "remaining: " LL_X ", vol_no: %d", | ||
216 | LL(remaining), (*volume)->count); | ||
217 | } else if (zft_tape_at_eod(pos)) { | ||
218 | if (++eod > 2) { | ||
219 | TRACE_EXIT -EIO; /* st.c also returns -EIO */ | ||
220 | } else { | ||
221 | TRACE_EXIT 1; | ||
222 | } | ||
223 | } | ||
224 | if ((*req_len % (*volume)->blk_sz) != 0) { | ||
225 | /* this message is informational only. The user gets the | ||
226 | * proper return value | ||
227 | */ | ||
228 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
229 | "req_len %d not a multiple of block size %d", | ||
230 | *req_len, (*volume)->blk_sz); | ||
231 | } | ||
232 | /* As GNU tar doesn't accept partial read counts when the | ||
233 | * multiple volume flag is set, we make sure to return the | ||
234 | * requested amount of data. Except, of course, at the end of | ||
235 | * the tape or file mark. | ||
236 | */ | ||
237 | remaining -= *req_len; | ||
238 | if (remaining <= 0) { | ||
239 | TRACE(ft_t_noise, | ||
240 | "clipped request from %d to %d.", | ||
241 | *req_len, (int)(*req_len + remaining)); | ||
242 | *req_len += remaining; | ||
243 | *req_clipped = 1; | ||
244 | } else { | ||
245 | *req_clipped = 0; | ||
246 | } | ||
247 | TRACE_EXIT 0; | ||
248 | } | ||
249 | |||
250 | /* this_segs_size: the current segment's size. | ||
251 | * buff: the USER-SPACE buffer provided by the calling function. | ||
252 | * req_len: how much data should be read at most. | ||
253 | * volume: contains information on current volume (blk_sz etc.) | ||
254 | */ | ||
255 | static int empty_deblock_buf(__u8 __user *usr_buf, const int req_len, | ||
256 | const __u8 *src_buf, const int seg_sz, | ||
257 | zft_position *pos, | ||
258 | const zft_volinfo *volume) | ||
259 | { | ||
260 | int cnt; | ||
261 | int result = 0; | ||
262 | TRACE_FUN(ft_t_flow); | ||
263 | |||
264 | TRACE(ft_t_data_flow, "this_segs_size: %d", seg_sz); | ||
265 | if (zft_use_compression && volume->use_compression) { | ||
266 | TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); | ||
267 | TRACE_CATCH(result= (*zft_cmpr_ops->read)(&cnt, | ||
268 | usr_buf, req_len, | ||
269 | src_buf, seg_sz, | ||
270 | pos, volume),); | ||
271 | } else { | ||
272 | TRACE_CATCH(result= zft_simple_read (&cnt, | ||
273 | usr_buf, req_len, | ||
274 | src_buf, seg_sz, | ||
275 | pos, volume),); | ||
276 | } | ||
277 | pos->volume_pos += result; | ||
278 | pos->tape_pos += cnt; | ||
279 | pos->seg_byte_pos += cnt; | ||
280 | buf_len_rd -= cnt; /* remaining bytes in buffer */ | ||
281 | TRACE(ft_t_data_flow, "buf_len_rd: %d, cnt: %d", buf_len_rd, cnt); | ||
282 | if(pos->seg_byte_pos >= seg_sz) { | ||
283 | pos->seg_pos++; | ||
284 | pos->seg_byte_pos = 0; | ||
285 | } | ||
286 | TRACE(ft_t_data_flow, "bytes moved out of deblock-buffer: %d", cnt); | ||
287 | TRACE_EXIT result; | ||
288 | } | ||
289 | |||
290 | |||
291 | /* note: we store the segment id of the segment that is inside the | ||
292 | * deblock buffer. This spares a lot of ftape_read_segment()s when we | ||
293 | * use small block-sizes. The block-size may be 1kb (SECTOR_SIZE). In | ||
294 | * this case a MTFSR 28 maybe still inside the same segment. | ||
295 | */ | ||
296 | int _zft_read(char __user *buff, int req_len) | ||
297 | { | ||
298 | int req_clipped; | ||
299 | int result = 0; | ||
300 | int bytes_read = 0; | ||
301 | static unsigned int seg_sz = 0; | ||
302 | static const zft_volinfo *volume = NULL; | ||
303 | TRACE_FUN(ft_t_flow); | ||
304 | |||
305 | zft_resid = req_len; | ||
306 | result = check_read_access(&req_len, &volume, | ||
307 | &req_clipped, &zft_pos); | ||
308 | switch(result) { | ||
309 | case 0: | ||
310 | break; /* nothing special */ | ||
311 | case 1: | ||
312 | TRACE(ft_t_noise, "EOD reached"); | ||
313 | TRACE_EXIT 0; /* EOD */ | ||
314 | default: | ||
315 | TRACE_ABORT(result, ft_t_noise, | ||
316 | "check_read_access() failed with result %d", | ||
317 | result); | ||
318 | TRACE_EXIT result; | ||
319 | } | ||
320 | while (req_len > 0) { | ||
321 | /* Allow escape from this loop on signal ! | ||
322 | */ | ||
323 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
324 | /* buf_len_rd == 0 means that we need to read a new | ||
325 | * segment. | ||
326 | */ | ||
327 | if (buf_len_rd == 0) { | ||
328 | while((result = zft_fetch_segment(zft_pos.seg_pos, | ||
329 | zft_deblock_buf, | ||
330 | FT_RD_AHEAD)) == 0) { | ||
331 | zft_pos.seg_pos ++; | ||
332 | zft_pos.seg_byte_pos = 0; | ||
333 | } | ||
334 | if (result < 0) { | ||
335 | zft_resid -= bytes_read; | ||
336 | TRACE_ABORT(result, ft_t_noise, | ||
337 | "zft_fetch_segment(): %d", | ||
338 | result); | ||
339 | } | ||
340 | seg_sz = result; | ||
341 | buf_len_rd = seg_sz - zft_pos.seg_byte_pos; | ||
342 | } | ||
343 | TRACE_CATCH(result = empty_deblock_buf(buff, | ||
344 | req_len, | ||
345 | zft_deblock_buf, | ||
346 | seg_sz, | ||
347 | &zft_pos, | ||
348 | volume), | ||
349 | zft_resid -= bytes_read); | ||
350 | TRACE(ft_t_data_flow, "bytes just read: %d", result); | ||
351 | bytes_read += result; /* what we got so far */ | ||
352 | buff += result; /* index in user-buffer */ | ||
353 | req_len -= result; /* what's left from req_len */ | ||
354 | } /* while (req_len > 0) */ | ||
355 | if (req_clipped) { | ||
356 | TRACE(ft_t_data_flow, | ||
357 | "maybe partial count because of eof mark"); | ||
358 | if (zft_just_before_eof && bytes_read == 0) { | ||
359 | /* req_len was > 0, but user didn't get | ||
360 | * anything the user has read in the eof-mark | ||
361 | */ | ||
362 | zft_move_past_eof(&zft_pos); | ||
363 | ftape_abort_operation(); | ||
364 | } else { | ||
365 | /* don't skip to the next file before the user | ||
366 | * tried to read a second time past EOF Just | ||
367 | * mark that we are at EOF and maybe decrement | ||
368 | * zft_seg_pos to stay in the same volume; | ||
369 | */ | ||
370 | zft_just_before_eof = 1; | ||
371 | zft_position_before_eof(&zft_pos, volume); | ||
372 | TRACE(ft_t_noise, "just before eof"); | ||
373 | } | ||
374 | } | ||
375 | zft_resid -= result; /* for MTSTATUS */ | ||
376 | TRACE_EXIT bytes_read; | ||
377 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-read.h b/drivers/char/ftape/zftape/zftape-read.h new file mode 100644 index 000000000000..42941de0c23a --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-read.h | |||
@@ -0,0 +1,53 @@ | |||
1 | #ifndef _ZFTAPE_READ_H | ||
2 | #define _ZFTAPE_READ_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996, 1997 Claus-Justus Heine | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-read.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:19:07 $ | ||
25 | * | ||
26 | * This file contains the definitions for the read functions | ||
27 | * for the zftape driver for Linux. | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include "../lowlevel/ftape-read.h" | ||
32 | |||
33 | /* ftape-read.c defined global vars. | ||
34 | */ | ||
35 | extern int zft_just_before_eof; | ||
36 | |||
37 | /* ftape-read.c defined global functions. | ||
38 | */ | ||
39 | extern void zft_zap_read_buffers(void); | ||
40 | extern int zft_read_header_segments(void); | ||
41 | extern int zft_fetch_segment_fraction(const unsigned int segment, | ||
42 | void *buffer, | ||
43 | const ft_read_mode_t read_mode, | ||
44 | const unsigned int start, | ||
45 | const unsigned int size); | ||
46 | #define zft_fetch_segment(segment, address, read_mode) \ | ||
47 | zft_fetch_segment_fraction(segment, address, read_mode, \ | ||
48 | 0, FT_SEGMENT_SIZE) | ||
49 | /* hook for the VFS interface | ||
50 | */ | ||
51 | extern int _zft_read(char __user *buff, int req_len); | ||
52 | |||
53 | #endif /* _ZFTAPE_READ_H */ | ||
diff --git a/drivers/char/ftape/zftape/zftape-rw.c b/drivers/char/ftape/zftape/zftape-rw.c new file mode 100644 index 000000000000..a61ef50f3dfc --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-rw.c | |||
@@ -0,0 +1,376 @@ | |||
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_qic113; /* conform to old specs. and old zftape */ | ||
49 | int zft_use_compression; | ||
50 | zft_position zft_pos = { | ||
51 | -1, /* seg_pos */ | ||
52 | 0, /* seg_byte_pos */ | ||
53 | 0, /* tape_pos */ | ||
54 | 0 /* volume_pos */ | ||
55 | }; | ||
56 | unsigned int zft_blk_sz = CONFIG_ZFT_DFLT_BLK_SZ; | ||
57 | __s64 zft_capacity; | ||
58 | |||
59 | unsigned int zft_written_segments; | ||
60 | int zft_label_changed; | ||
61 | |||
62 | /* Local vars. | ||
63 | */ | ||
64 | |||
65 | unsigned int zft_get_seg_sz(unsigned int segment) | ||
66 | { | ||
67 | int size; | ||
68 | TRACE_FUN(ft_t_any); | ||
69 | |||
70 | size = FT_SEGMENT_SIZE - | ||
71 | count_ones(ftape_get_bad_sector_entry(segment))*FT_SECTOR_SIZE; | ||
72 | if (size > 0) { | ||
73 | TRACE_EXIT (unsigned)size; | ||
74 | } else { | ||
75 | TRACE_EXIT 0; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /* ftape_set_flags(). Claus-Justus Heine, 1994/1995 | ||
80 | */ | ||
81 | void zft_set_flags(unsigned minor_unit) | ||
82 | { | ||
83 | TRACE_FUN(ft_t_flow); | ||
84 | |||
85 | zft_use_compression = zft_qic_mode = 0; | ||
86 | switch (minor_unit & ZFT_MINOR_OP_MASK) { | ||
87 | case (ZFT_Q80_MODE | ZFT_ZIP_MODE): | ||
88 | case ZFT_ZIP_MODE: | ||
89 | zft_use_compression = 1; | ||
90 | case 0: | ||
91 | case ZFT_Q80_MODE: | ||
92 | zft_qic_mode = 1; | ||
93 | if (zft_mt_compression) { /* override the default */ | ||
94 | zft_use_compression = 1; | ||
95 | } | ||
96 | break; | ||
97 | case ZFT_RAW_MODE: | ||
98 | TRACE(ft_t_noise, "switching to raw mode"); | ||
99 | break; | ||
100 | default: | ||
101 | TRACE(ft_t_warn, "Warning:\n" | ||
102 | KERN_INFO "Wrong combination of minor device bits.\n" | ||
103 | KERN_INFO "Switching to raw read-only mode."); | ||
104 | zft_write_protected = 1; | ||
105 | break; | ||
106 | } | ||
107 | TRACE_EXIT; | ||
108 | } | ||
109 | |||
110 | /* computes the segment and byte offset inside the segment | ||
111 | * corresponding to tape_pos. | ||
112 | * | ||
113 | * tape_pos gives the offset in bytes from the beginning of the | ||
114 | * ft_first_data_segment *seg_byte_pos is the offset in the current | ||
115 | * segment in bytes | ||
116 | * | ||
117 | * Of, if this routine was called often one should cache the last data | ||
118 | * pos it was called with, but actually this is only needed in | ||
119 | * ftape_seek_block(), that is, almost never. | ||
120 | */ | ||
121 | int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos) | ||
122 | { | ||
123 | int segment; | ||
124 | int seg_sz; | ||
125 | TRACE_FUN(ft_t_flow); | ||
126 | |||
127 | if (tape_pos == 0) { | ||
128 | *seg_byte_pos = 0; | ||
129 | segment = ft_first_data_segment; | ||
130 | } else { | ||
131 | seg_sz = 0; | ||
132 | |||
133 | for (segment = ft_first_data_segment; | ||
134 | ((tape_pos > 0) && (segment <= ft_last_data_segment)); | ||
135 | segment++) { | ||
136 | seg_sz = zft_get_seg_sz(segment); | ||
137 | tape_pos -= seg_sz; | ||
138 | } | ||
139 | if(tape_pos >= 0) { | ||
140 | /* the case tape_pos > != 0 means that the | ||
141 | * argument tape_pos lies beyond the EOT. | ||
142 | */ | ||
143 | *seg_byte_pos= 0; | ||
144 | } else { /* tape_pos < 0 */ | ||
145 | segment--; | ||
146 | *seg_byte_pos= tape_pos + seg_sz; | ||
147 | } | ||
148 | } | ||
149 | TRACE_EXIT(segment); | ||
150 | } | ||
151 | |||
152 | /* ftape_calc_tape_pos(). | ||
153 | * | ||
154 | * computes the offset in bytes from the beginning of the | ||
155 | * ft_first_data_segment inverse to ftape_calc_seg_byte_coord | ||
156 | * | ||
157 | * We should do some caching. But how: | ||
158 | * | ||
159 | * Each time the header segments are read in, this routine is called | ||
160 | * with ft_tracks_per_tape*segments_per_track argumnet. So this should be | ||
161 | * the time to reset the cache. | ||
162 | * | ||
163 | * Also, it might be in the future that the bad sector map gets | ||
164 | * changed. -> reset the cache | ||
165 | */ | ||
166 | static int seg_pos; | ||
167 | static __s64 tape_pos; | ||
168 | |||
169 | __s64 zft_get_capacity(void) | ||
170 | { | ||
171 | seg_pos = ft_first_data_segment; | ||
172 | tape_pos = 0; | ||
173 | |||
174 | while (seg_pos <= ft_last_data_segment) { | ||
175 | tape_pos += zft_get_seg_sz(seg_pos ++); | ||
176 | } | ||
177 | return tape_pos; | ||
178 | } | ||
179 | |||
180 | __s64 zft_calc_tape_pos(int segment) | ||
181 | { | ||
182 | int d1, d2, d3; | ||
183 | TRACE_FUN(ft_t_any); | ||
184 | |||
185 | if (segment > ft_last_data_segment) { | ||
186 | TRACE_EXIT zft_capacity; | ||
187 | } | ||
188 | if (segment < ft_first_data_segment) { | ||
189 | TRACE_EXIT 0; | ||
190 | } | ||
191 | d2 = segment - seg_pos; | ||
192 | if (-d2 > 10) { | ||
193 | d1 = segment - ft_first_data_segment; | ||
194 | if (-d2 > d1) { | ||
195 | tape_pos = 0; | ||
196 | seg_pos = ft_first_data_segment; | ||
197 | d2 = d1; | ||
198 | } | ||
199 | } | ||
200 | if (d2 > 10) { | ||
201 | d3 = ft_last_data_segment - segment; | ||
202 | if (d2 > d3) { | ||
203 | tape_pos = zft_capacity; | ||
204 | seg_pos = ft_last_data_segment + 1; | ||
205 | d2 = -d3; | ||
206 | } | ||
207 | } | ||
208 | if (d2 > 0) { | ||
209 | while (seg_pos < segment) { | ||
210 | tape_pos += zft_get_seg_sz(seg_pos++); | ||
211 | } | ||
212 | } else { | ||
213 | while (seg_pos > segment) { | ||
214 | tape_pos -= zft_get_seg_sz(--seg_pos); | ||
215 | } | ||
216 | } | ||
217 | TRACE(ft_t_noise, "new cached pos: %d", seg_pos); | ||
218 | |||
219 | TRACE_EXIT tape_pos; | ||
220 | } | ||
221 | |||
222 | /* copy Z-label string to buffer, keeps track of the correct offset in | ||
223 | * `buffer' | ||
224 | */ | ||
225 | void zft_update_label(__u8 *buffer) | ||
226 | { | ||
227 | TRACE_FUN(ft_t_flow); | ||
228 | |||
229 | if (strncmp(&buffer[FT_LABEL], ZFTAPE_LABEL, | ||
230 | sizeof(ZFTAPE_LABEL)-1) != 0) { | ||
231 | TRACE(ft_t_info, "updating label from \"%s\" to \"%s\"", | ||
232 | &buffer[FT_LABEL], ZFTAPE_LABEL); | ||
233 | strcpy(&buffer[FT_LABEL], ZFTAPE_LABEL); | ||
234 | memset(&buffer[FT_LABEL] + sizeof(ZFTAPE_LABEL) - 1, ' ', | ||
235 | FT_LABEL_SZ - sizeof(ZFTAPE_LABEL + 1)); | ||
236 | PUT4(buffer, FT_LABEL_DATE, 0); | ||
237 | zft_label_changed = zft_header_changed = 1; /* changed */ | ||
238 | } | ||
239 | TRACE_EXIT; | ||
240 | } | ||
241 | |||
242 | int zft_verify_write_segments(unsigned int segment, | ||
243 | __u8 *data, size_t size, | ||
244 | __u8 *buffer) | ||
245 | { | ||
246 | int result; | ||
247 | __u8 *write_buf; | ||
248 | __u8 *src_buf; | ||
249 | int single; | ||
250 | int seg_pos; | ||
251 | int seg_sz; | ||
252 | int remaining; | ||
253 | ft_write_mode_t write_mode; | ||
254 | TRACE_FUN(ft_t_flow); | ||
255 | |||
256 | seg_pos = segment; | ||
257 | seg_sz = zft_get_seg_sz(seg_pos); | ||
258 | src_buf = data; | ||
259 | single = size <= seg_sz; | ||
260 | remaining = size; | ||
261 | do { | ||
262 | TRACE(ft_t_noise, "\n" | ||
263 | KERN_INFO "remaining: %d\n" | ||
264 | KERN_INFO "seg_sz : %d\n" | ||
265 | KERN_INFO "segment : %d", | ||
266 | remaining, seg_sz, seg_pos); | ||
267 | if (remaining == seg_sz) { | ||
268 | write_buf = src_buf; | ||
269 | write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; | ||
270 | remaining = 0; | ||
271 | } else if (remaining > seg_sz) { | ||
272 | write_buf = src_buf; | ||
273 | write_mode = FT_WR_ASYNC; /* don't start tape */ | ||
274 | remaining -= seg_sz; | ||
275 | } else { /* remaining < seg_sz */ | ||
276 | write_buf = buffer; | ||
277 | memcpy(write_buf, src_buf, remaining); | ||
278 | memset(&write_buf[remaining],'\0',seg_sz-remaining); | ||
279 | write_mode = single ? FT_WR_SINGLE : FT_WR_MULTI; | ||
280 | remaining = 0; | ||
281 | } | ||
282 | if ((result = ftape_write_segment(seg_pos, | ||
283 | write_buf, | ||
284 | write_mode)) != seg_sz) { | ||
285 | TRACE(ft_t_err, "Error: " | ||
286 | "Couldn't write segment %d", seg_pos); | ||
287 | TRACE_EXIT result < 0 ? result : -EIO; /* bail out */ | ||
288 | } | ||
289 | zft_written_segments ++; | ||
290 | seg_sz = zft_get_seg_sz(++seg_pos); | ||
291 | src_buf += result; | ||
292 | } while (remaining > 0); | ||
293 | if (ftape_get_status()->fti_state == writing) { | ||
294 | TRACE_CATCH(ftape_loop_until_writes_done(),); | ||
295 | TRACE_CATCH(ftape_abort_operation(),); | ||
296 | zft_prevent_flush(); | ||
297 | } | ||
298 | seg_pos = segment; | ||
299 | src_buf = data; | ||
300 | remaining = size; | ||
301 | do { | ||
302 | TRACE_CATCH(result = ftape_read_segment(seg_pos, buffer, | ||
303 | single ? FT_RD_SINGLE | ||
304 | : FT_RD_AHEAD),); | ||
305 | if (memcmp(src_buf, buffer, | ||
306 | remaining > result ? result : remaining) != 0) { | ||
307 | TRACE_ABORT(-EIO, ft_t_err, | ||
308 | "Failed to verify written segment %d", | ||
309 | seg_pos); | ||
310 | } | ||
311 | remaining -= result; | ||
312 | TRACE(ft_t_noise, "verify successful:\n" | ||
313 | KERN_INFO "segment : %d\n" | ||
314 | KERN_INFO "segsize : %d\n" | ||
315 | KERN_INFO "remaining: %d", | ||
316 | seg_pos, result, remaining); | ||
317 | src_buf += seg_sz; | ||
318 | seg_pos++; | ||
319 | } while (remaining > 0); | ||
320 | TRACE_EXIT size; | ||
321 | } | ||
322 | |||
323 | |||
324 | /* zft_erase(). implemented compression-handling | ||
325 | * | ||
326 | * calculate the first data-segment when using/not using compression. | ||
327 | * | ||
328 | * update header-segment and compression-map-segment. | ||
329 | */ | ||
330 | int zft_erase(void) | ||
331 | { | ||
332 | int result = 0; | ||
333 | TRACE_FUN(ft_t_flow); | ||
334 | |||
335 | if (!zft_header_read) { | ||
336 | TRACE_CATCH(zft_vmalloc_once((void **)&zft_hseg_buf, | ||
337 | FT_SEGMENT_SIZE),); | ||
338 | /* no need to read the vtbl and compression map */ | ||
339 | TRACE_CATCH(ftape_read_header_segment(zft_hseg_buf),); | ||
340 | if ((zft_old_ftape = | ||
341 | zft_ftape_validate_label(&zft_hseg_buf[FT_LABEL]))) { | ||
342 | zft_ftape_extract_file_marks(zft_hseg_buf); | ||
343 | } | ||
344 | TRACE(ft_t_noise, | ||
345 | "ft_first_data_segment: %d, ft_last_data_segment: %d", | ||
346 | ft_first_data_segment, ft_last_data_segment); | ||
347 | zft_qic113 = (ft_format_code != fmt_normal && | ||
348 | ft_format_code != fmt_1100ft && | ||
349 | ft_format_code != fmt_425ft); | ||
350 | } | ||
351 | if (zft_old_ftape) { | ||
352 | zft_clear_ftape_file_marks(); | ||
353 | zft_old_ftape = 0; /* no longer old ftape */ | ||
354 | } | ||
355 | PUT2(zft_hseg_buf, FT_CMAP_START, 0); | ||
356 | zft_volume_table_changed = 1; | ||
357 | zft_capacity = zft_get_capacity(); | ||
358 | zft_init_vtbl(); | ||
359 | /* the rest must be done in ftape_update_header_segments | ||
360 | */ | ||
361 | zft_header_read = 1; | ||
362 | zft_header_changed = 1; /* force update of timestamp */ | ||
363 | result = zft_update_header_segments(); | ||
364 | |||
365 | ftape_abort_operation(); | ||
366 | |||
367 | zft_reset_position(&zft_pos); | ||
368 | zft_set_flags (zft_unit); | ||
369 | TRACE_EXIT result; | ||
370 | } | ||
371 | |||
372 | unsigned int zft_get_time(void) | ||
373 | { | ||
374 | unsigned int date = FT_TIME_STAMP(2097, 11, 30, 23, 59, 59); /* fun */ | ||
375 | return date; | ||
376 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-rw.h b/drivers/char/ftape/zftape/zftape-rw.h new file mode 100644 index 000000000000..14c07f086575 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-rw.h | |||
@@ -0,0 +1,102 @@ | |||
1 | #ifndef _ZFTAPE_RW_H | ||
2 | #define _ZFTAPE_RW_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996, 1997 Claus-Justus Heine. | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-rw.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:19:09 $ | ||
25 | * | ||
26 | * This file contains the definitions for the read and write | ||
27 | * functions for the QIC-117 floppy-tape driver for Linux. | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | #include <linux/config.h> /* for CONFIG_ZFT_DFLT_BLK_SZ */ | ||
32 | #include "../zftape/zftape-buffers.h" | ||
33 | |||
34 | #define SEGMENTS_PER_TAPE (ft_segments_per_track * ft_tracks_per_tape) | ||
35 | |||
36 | /* QIC-113 Rev. G says that `a maximum of 63488 raw bytes may be | ||
37 | * compressed into a single frame'. | ||
38 | * Maybe we should stick to 32kb to make it more `beautiful' | ||
39 | */ | ||
40 | #define ZFT_MAX_BLK_SZ (62*1024) /* bytes */ | ||
41 | #if !defined(CONFIG_ZFT_DFLT_BLK_SZ) | ||
42 | # define CONFIG_ZFT_DFLT_BLK_SZ (10*1024) /* bytes, default of gnu tar */ | ||
43 | #elif CONFIG_ZFT_DFLT_BLK_SZ == 0 | ||
44 | # undef CONFIG_ZFT_DFLT_BLK_SZ | ||
45 | # define CONFIG_ZFT_DFLT_BLK_SZ 1 | ||
46 | #elif (CONFIG_ZFT_DFLT_BLK_SZ % 1024) != 0 | ||
47 | # error CONFIG_ZFT_DFLT_BLK_SZ must be 1 or a multiple of 1024 | ||
48 | #endif | ||
49 | /* The *optional* compression routines need some overhead per tape | ||
50 | * block for their purposes. Instead of asking the actual compression | ||
51 | * implementation how much it needs, we restrict this overhead to be | ||
52 | * maximal of ZFT_CMPT_OVERHEAD size. We need this for EOT | ||
53 | * conditions. The tape is assumed to be logical at EOT when the | ||
54 | * distance from the physical EOT is less than | ||
55 | * one tape block + ZFT_CMPR_OVERHEAD | ||
56 | */ | ||
57 | #define ZFT_CMPR_OVERHEAD 16 /* bytes */ | ||
58 | |||
59 | typedef enum | ||
60 | { | ||
61 | zft_idle = 0, | ||
62 | zft_reading, | ||
63 | zft_writing, | ||
64 | } zft_status_enum; | ||
65 | |||
66 | typedef struct /* all values measured in bytes */ | ||
67 | { | ||
68 | int seg_pos; /* segment currently positioned at */ | ||
69 | int seg_byte_pos; /* offset in current segment */ | ||
70 | __s64 tape_pos; /* real offset from BOT */ | ||
71 | __s64 volume_pos; /* pos. in uncompressed data stream in | ||
72 | * current volume | ||
73 | */ | ||
74 | } zft_position; | ||
75 | |||
76 | extern zft_position zft_pos; | ||
77 | extern __u8 *zft_deblock_buf; | ||
78 | extern __u8 *zft_hseg_buf; | ||
79 | extern int zft_deblock_segment; | ||
80 | extern zft_status_enum zft_io_state; | ||
81 | extern int zft_header_changed; | ||
82 | extern int zft_qic113; /* conform to old specs. and old zftape */ | ||
83 | extern int zft_use_compression; | ||
84 | extern unsigned int zft_blk_sz; | ||
85 | extern __s64 zft_capacity; | ||
86 | extern unsigned int zft_written_segments; | ||
87 | extern int zft_label_changed; | ||
88 | |||
89 | /* zftape-rw.c exported functions | ||
90 | */ | ||
91 | extern unsigned int zft_get_seg_sz(unsigned int segment); | ||
92 | extern void zft_set_flags(unsigned int minor_unit); | ||
93 | extern int zft_calc_seg_byte_coord(int *seg_byte_pos, __s64 tape_pos); | ||
94 | extern __s64 zft_calc_tape_pos(int segment); | ||
95 | extern __s64 zft_get_capacity(void); | ||
96 | extern void zft_update_label(__u8 *buffer); | ||
97 | extern int zft_erase(void); | ||
98 | extern int zft_verify_write_segments(unsigned int segment, | ||
99 | __u8 *data, size_t size, __u8 *buffer); | ||
100 | extern unsigned int zft_get_time(void); | ||
101 | #endif /* _ZFTAPE_RW_H */ | ||
102 | |||
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.c b/drivers/char/ftape/zftape/zftape-vtbl.c new file mode 100644 index 000000000000..ad7f8be6340b --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-vtbl.c | |||
@@ -0,0 +1,757 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1995-1997 Claus-Justus Heine | ||
3 | |||
4 | This program is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 2, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | 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, | ||
17 | USA. | ||
18 | |||
19 | * | ||
20 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.c,v $ | ||
21 | * $Revision: 1.7.6.1 $ | ||
22 | * $Date: 1997/11/24 13:48:31 $ | ||
23 | * | ||
24 | * This file defines a volume table as defined in various QIC | ||
25 | * standards. | ||
26 | * | ||
27 | * This is a minimal implementation, just allowing ordinary DOS | ||
28 | * :( prgrams to identify the cartridge as used. | ||
29 | */ | ||
30 | |||
31 | #include <linux/errno.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/slab.h> | ||
34 | |||
35 | #include <linux/zftape.h> | ||
36 | #include "../zftape/zftape-init.h" | ||
37 | #include "../zftape/zftape-eof.h" | ||
38 | #include "../zftape/zftape-ctl.h" | ||
39 | #include "../zftape/zftape-write.h" | ||
40 | #include "../zftape/zftape-read.h" | ||
41 | #include "../zftape/zftape-rw.h" | ||
42 | #include "../zftape/zftape-vtbl.h" | ||
43 | |||
44 | #define ZFT_CMAP_HACK /* leave this defined to hide the compression map */ | ||
45 | |||
46 | /* | ||
47 | * global variables | ||
48 | */ | ||
49 | int zft_qic_mode = 1; /* use the vtbl */ | ||
50 | int zft_old_ftape; /* prevents old ftaped tapes to be overwritten */ | ||
51 | int zft_volume_table_changed; /* for write_header_segments() */ | ||
52 | |||
53 | /* | ||
54 | * private variables (only exported for inline functions) | ||
55 | */ | ||
56 | LIST_HEAD(zft_vtbl); | ||
57 | |||
58 | /* We could also allocate these dynamically when extracting the volume table | ||
59 | * sizeof(zft_volinfo) is about 32 or something close to that | ||
60 | */ | ||
61 | static zft_volinfo tape_vtbl; | ||
62 | static zft_volinfo eot_vtbl; | ||
63 | static zft_volinfo *cur_vtbl; | ||
64 | |||
65 | static inline void zft_new_vtbl_entry(void) | ||
66 | { | ||
67 | struct list_head *tmp = &zft_last_vtbl->node; | ||
68 | zft_volinfo *new = zft_kmalloc(sizeof(zft_volinfo)); | ||
69 | |||
70 | list_add(&new->node, tmp); | ||
71 | new->count = zft_eom_vtbl->count ++; | ||
72 | } | ||
73 | |||
74 | void zft_free_vtbl(void) | ||
75 | { | ||
76 | for (;;) { | ||
77 | struct list_head *tmp = zft_vtbl.prev; | ||
78 | zft_volinfo *vtbl; | ||
79 | |||
80 | if (tmp == &zft_vtbl) | ||
81 | break; | ||
82 | list_del(tmp); | ||
83 | vtbl = list_entry(tmp, zft_volinfo, node); | ||
84 | zft_kfree(vtbl, sizeof(zft_volinfo)); | ||
85 | } | ||
86 | INIT_LIST_HEAD(&zft_vtbl); | ||
87 | cur_vtbl = NULL; | ||
88 | } | ||
89 | |||
90 | /* initialize vtbl, called by ftape_new_cartridge() | ||
91 | */ | ||
92 | void zft_init_vtbl(void) | ||
93 | { | ||
94 | zft_volinfo *new; | ||
95 | |||
96 | zft_free_vtbl(); | ||
97 | |||
98 | /* Create the two dummy vtbl entries | ||
99 | */ | ||
100 | new = zft_kmalloc(sizeof(zft_volinfo)); | ||
101 | list_add(&new->node, &zft_vtbl); | ||
102 | new = zft_kmalloc(sizeof(zft_volinfo)); | ||
103 | list_add(&new->node, &zft_vtbl); | ||
104 | zft_head_vtbl->end_seg = ft_first_data_segment; | ||
105 | zft_head_vtbl->blk_sz = zft_blk_sz; | ||
106 | zft_head_vtbl->count = -1; | ||
107 | zft_eom_vtbl->start_seg = ft_first_data_segment + 1; | ||
108 | zft_eom_vtbl->end_seg = ft_last_data_segment + 1; | ||
109 | zft_eom_vtbl->blk_sz = zft_blk_sz; | ||
110 | zft_eom_vtbl->count = 0; | ||
111 | |||
112 | /* Reset the pointer for zft_find_volume() | ||
113 | */ | ||
114 | cur_vtbl = zft_eom_vtbl; | ||
115 | |||
116 | /* initialize the dummy vtbl entries for zft_qic_mode == 0 | ||
117 | */ | ||
118 | eot_vtbl.start_seg = ft_last_data_segment + 1; | ||
119 | eot_vtbl.end_seg = ft_last_data_segment + 1; | ||
120 | eot_vtbl.blk_sz = zft_blk_sz; | ||
121 | eot_vtbl.count = -1; | ||
122 | tape_vtbl.start_seg = ft_first_data_segment; | ||
123 | tape_vtbl.end_seg = ft_last_data_segment; | ||
124 | tape_vtbl.blk_sz = zft_blk_sz; | ||
125 | tape_vtbl.size = zft_capacity; | ||
126 | tape_vtbl.count = 0; | ||
127 | } | ||
128 | |||
129 | /* check for a valid VTBL signature. | ||
130 | */ | ||
131 | static int vtbl_signature_valid(__u8 signature[4]) | ||
132 | { | ||
133 | const char *vtbl_ids[] = VTBL_IDS; /* valid signatures */ | ||
134 | int j; | ||
135 | |||
136 | for (j = 0; | ||
137 | (j < NR_ITEMS(vtbl_ids)) && (memcmp(signature, vtbl_ids[j], 4) != 0); | ||
138 | j++); | ||
139 | return j < NR_ITEMS(vtbl_ids); | ||
140 | } | ||
141 | |||
142 | /* We used to store the block-size of the volume in the volume-label, | ||
143 | * using the keyword "blocksize". The blocksize written to the | ||
144 | * volume-label is in bytes. | ||
145 | * | ||
146 | * We use this now only for compatibility with old zftape version. We | ||
147 | * store the blocksize directly as binary number in the vendor | ||
148 | * extension part of the volume entry. | ||
149 | */ | ||
150 | static int check_volume_label(const char *label, int *blk_sz) | ||
151 | { | ||
152 | int valid_format; | ||
153 | char *blocksize; | ||
154 | TRACE_FUN(ft_t_flow); | ||
155 | |||
156 | TRACE(ft_t_noise, "called with \"%s\" / \"%s\"", label, ZFT_VOL_NAME); | ||
157 | if (strncmp(label, ZFT_VOL_NAME, strlen(ZFT_VOL_NAME)) != 0) { | ||
158 | *blk_sz = 1; /* smallest block size that we allow */ | ||
159 | valid_format = 0; | ||
160 | } else { | ||
161 | TRACE(ft_t_noise, "got old style zftape vtbl entry"); | ||
162 | /* get the default blocksize */ | ||
163 | /* use the kernel strstr() */ | ||
164 | blocksize= strstr(label, " blocksize "); | ||
165 | if (blocksize) { | ||
166 | blocksize += strlen(" blocksize "); | ||
167 | for(*blk_sz= 0; | ||
168 | *blocksize >= '0' && *blocksize <= '9'; | ||
169 | blocksize++) { | ||
170 | *blk_sz *= 10; | ||
171 | *blk_sz += *blocksize - '0'; | ||
172 | } | ||
173 | if (*blk_sz > ZFT_MAX_BLK_SZ) { | ||
174 | *blk_sz= 1; | ||
175 | valid_format= 0; | ||
176 | } else { | ||
177 | valid_format = 1; | ||
178 | } | ||
179 | } else { | ||
180 | *blk_sz= 1; | ||
181 | valid_format= 0; | ||
182 | } | ||
183 | } | ||
184 | TRACE_EXIT valid_format; | ||
185 | } | ||
186 | |||
187 | /* check for a zftape volume | ||
188 | */ | ||
189 | static int check_volume(__u8 *entry, zft_volinfo *volume) | ||
190 | { | ||
191 | TRACE_FUN(ft_t_flow); | ||
192 | |||
193 | if(strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
194 | strlen(ZFTAPE_SIG)) == 0) { | ||
195 | TRACE(ft_t_noise, "got new style zftape vtbl entry"); | ||
196 | volume->blk_sz = GET2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ); | ||
197 | volume->qic113 = entry[VTBL_EXT+EXT_ZFTAPE_QIC113]; | ||
198 | TRACE_EXIT 1; | ||
199 | } else { | ||
200 | TRACE_EXIT check_volume_label(&entry[VTBL_DESC], &volume->blk_sz); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | |||
205 | /* create zftape specific vtbl entry, the volume bounds are inserted | ||
206 | * in the calling function, zft_create_volume_headers() | ||
207 | */ | ||
208 | static void create_zft_volume(__u8 *entry, zft_volinfo *vtbl) | ||
209 | { | ||
210 | TRACE_FUN(ft_t_flow); | ||
211 | |||
212 | memset(entry, 0, VTBL_SIZE); | ||
213 | memcpy(&entry[VTBL_SIG], VTBL_ID, 4); | ||
214 | sprintf(&entry[VTBL_DESC], ZFT_VOL_NAME" %03d", vtbl->count); | ||
215 | entry[VTBL_FLAGS] = (VTBL_FL_NOT_VERIFIED | VTBL_FL_SEG_SPANNING); | ||
216 | entry[VTBL_M_NO] = 1; /* multi_cartridge_count */ | ||
217 | strcpy(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG); | ||
218 | PUT2(entry, VTBL_EXT+EXT_ZFTAPE_BLKSZ, vtbl->blk_sz); | ||
219 | if (zft_qic113) { | ||
220 | PUT8(entry, VTBL_DATA_SIZE, vtbl->size); | ||
221 | entry[VTBL_CMPR] = VTBL_CMPR_UNREG; | ||
222 | if (vtbl->use_compression) { /* use compression: */ | ||
223 | entry[VTBL_CMPR] |= VTBL_CMPR_USED; | ||
224 | } | ||
225 | entry[VTBL_EXT+EXT_ZFTAPE_QIC113] = 1; | ||
226 | } else { | ||
227 | PUT4(entry, VTBL_DATA_SIZE, vtbl->size); | ||
228 | entry[VTBL_K_CMPR] = VTBL_CMPR_UNREG; | ||
229 | if (vtbl->use_compression) { /* use compression: */ | ||
230 | entry[VTBL_K_CMPR] |= VTBL_CMPR_USED; | ||
231 | } | ||
232 | } | ||
233 | if (ft_format_code == fmt_big) { | ||
234 | /* SCSI like vtbl, store the number of used | ||
235 | * segments as 4 byte value | ||
236 | */ | ||
237 | PUT4(entry, VTBL_SCSI_SEGS, vtbl->end_seg-vtbl->start_seg + 1); | ||
238 | } else { | ||
239 | /* normal, QIC-80MC like vtbl | ||
240 | */ | ||
241 | PUT2(entry, VTBL_START, vtbl->start_seg); | ||
242 | PUT2(entry, VTBL_END, vtbl->end_seg); | ||
243 | } | ||
244 | TRACE_EXIT; | ||
245 | } | ||
246 | |||
247 | /* this one creates the volume headers for each volume. It is assumed | ||
248 | * that buffer already contains the old volume-table, so that vtbl | ||
249 | * entries without the zft_volume flag set can savely be ignored. | ||
250 | */ | ||
251 | static void zft_create_volume_headers(__u8 *buffer) | ||
252 | { | ||
253 | __u8 *entry; | ||
254 | struct list_head *tmp; | ||
255 | zft_volinfo *vtbl; | ||
256 | TRACE_FUN(ft_t_flow); | ||
257 | |||
258 | #ifdef ZFT_CMAP_HACK | ||
259 | if((strncmp(&buffer[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
260 | strlen(ZFTAPE_SIG)) == 0) && | ||
261 | buffer[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { | ||
262 | TRACE(ft_t_noise, "deleting cmap volume"); | ||
263 | memmove(buffer, buffer + VTBL_SIZE, | ||
264 | FT_SEGMENT_SIZE - VTBL_SIZE); | ||
265 | } | ||
266 | #endif | ||
267 | entry = buffer; | ||
268 | for (tmp = zft_head_vtbl->node.next; | ||
269 | tmp != &zft_eom_vtbl->node; | ||
270 | tmp = tmp->next) { | ||
271 | vtbl = list_entry(tmp, zft_volinfo, node); | ||
272 | /* we now fill in the values only for newly created volumes. | ||
273 | */ | ||
274 | if (vtbl->new_volume) { | ||
275 | create_zft_volume(entry, vtbl); | ||
276 | vtbl->new_volume = 0; /* clear the flag */ | ||
277 | } | ||
278 | |||
279 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], vtbl); | ||
280 | entry += VTBL_SIZE; | ||
281 | } | ||
282 | memset(entry, 0, FT_SEGMENT_SIZE - zft_eom_vtbl->count * VTBL_SIZE); | ||
283 | TRACE_EXIT; | ||
284 | } | ||
285 | |||
286 | /* write volume table to tape. Calls zft_create_volume_headers() | ||
287 | */ | ||
288 | int zft_update_volume_table(unsigned int segment) | ||
289 | { | ||
290 | int result = 0; | ||
291 | __u8 *verify_buf = NULL; | ||
292 | TRACE_FUN(ft_t_flow); | ||
293 | |||
294 | TRACE_CATCH(result = ftape_read_segment(ft_first_data_segment, | ||
295 | zft_deblock_buf, | ||
296 | FT_RD_SINGLE),); | ||
297 | zft_create_volume_headers(zft_deblock_buf); | ||
298 | TRACE(ft_t_noise, "writing volume table segment %d", segment); | ||
299 | if (zft_vmalloc_once(&verify_buf, FT_SEGMENT_SIZE) == 0) { | ||
300 | TRACE_CATCH(zft_verify_write_segments(segment, | ||
301 | zft_deblock_buf, result, | ||
302 | verify_buf), | ||
303 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE)); | ||
304 | zft_vfree(&verify_buf, FT_SEGMENT_SIZE); | ||
305 | } else { | ||
306 | TRACE_CATCH(ftape_write_segment(segment, zft_deblock_buf, | ||
307 | FT_WR_SINGLE),); | ||
308 | } | ||
309 | TRACE_EXIT 0; | ||
310 | } | ||
311 | |||
312 | /* non zftape volumes are handled in raw mode. Thus we need to | ||
313 | * calculate the raw amount of data contained in those segments. | ||
314 | */ | ||
315 | static void extract_alien_volume(__u8 *entry, zft_volinfo *vtbl) | ||
316 | { | ||
317 | TRACE_FUN(ft_t_flow); | ||
318 | |||
319 | vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) - | ||
320 | zft_calc_tape_pos(zft_last_vtbl->start_seg)); | ||
321 | vtbl->use_compression = 0; | ||
322 | vtbl->qic113 = zft_qic113; | ||
323 | if (vtbl->qic113) { | ||
324 | TRACE(ft_t_noise, | ||
325 | "Fake alien volume's size from " LL_X " to " LL_X, | ||
326 | LL(GET8(entry, VTBL_DATA_SIZE)), LL(vtbl->size)); | ||
327 | } else { | ||
328 | TRACE(ft_t_noise, | ||
329 | "Fake alien volume's size from %d to " LL_X, | ||
330 | (int)GET4(entry, VTBL_DATA_SIZE), LL(vtbl->size)); | ||
331 | } | ||
332 | TRACE_EXIT; | ||
333 | } | ||
334 | |||
335 | |||
336 | /* extract an zftape specific volume | ||
337 | */ | ||
338 | static void extract_zft_volume(__u8 *entry, zft_volinfo *vtbl) | ||
339 | { | ||
340 | TRACE_FUN(ft_t_flow); | ||
341 | |||
342 | if (vtbl->qic113) { | ||
343 | vtbl->size = GET8(entry, VTBL_DATA_SIZE); | ||
344 | vtbl->use_compression = | ||
345 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; | ||
346 | } else { | ||
347 | vtbl->size = GET4(entry, VTBL_DATA_SIZE); | ||
348 | if (entry[VTBL_K_CMPR] & VTBL_CMPR_UNREG) { | ||
349 | vtbl->use_compression = | ||
350 | (entry[VTBL_K_CMPR] & VTBL_CMPR_USED) != 0; | ||
351 | } else if (entry[VTBL_CMPR] & VTBL_CMPR_UNREG) { | ||
352 | vtbl->use_compression = | ||
353 | (entry[VTBL_CMPR] & VTBL_CMPR_USED) != 0; | ||
354 | } else { | ||
355 | TRACE(ft_t_warn, "Geeh! There is something wrong:\n" | ||
356 | KERN_INFO "QIC compression (Rev = K): %x\n" | ||
357 | KERN_INFO "QIC compression (Rev > K): %x", | ||
358 | entry[VTBL_K_CMPR], entry[VTBL_CMPR]); | ||
359 | } | ||
360 | } | ||
361 | TRACE_EXIT; | ||
362 | } | ||
363 | |||
364 | /* extract the volume table from buffer. "buffer" must already contain | ||
365 | * the vtbl-segment | ||
366 | */ | ||
367 | int zft_extract_volume_headers(__u8 *buffer) | ||
368 | { | ||
369 | __u8 *entry; | ||
370 | TRACE_FUN(ft_t_flow); | ||
371 | |||
372 | zft_init_vtbl(); | ||
373 | entry = buffer; | ||
374 | #ifdef ZFT_CMAP_HACK | ||
375 | if ((strncmp(&entry[VTBL_EXT+EXT_ZFTAPE_SIG], ZFTAPE_SIG, | ||
376 | strlen(ZFTAPE_SIG)) == 0) && | ||
377 | entry[VTBL_EXT+EXT_ZFTAPE_CMAP] != 0) { | ||
378 | TRACE(ft_t_noise, "ignoring cmap volume"); | ||
379 | entry += VTBL_SIZE; | ||
380 | } | ||
381 | #endif | ||
382 | /* the end of the vtbl is indicated by an invalid signature | ||
383 | */ | ||
384 | while (vtbl_signature_valid(&entry[VTBL_SIG]) && | ||
385 | (entry - buffer) < FT_SEGMENT_SIZE) { | ||
386 | zft_new_vtbl_entry(); | ||
387 | if (ft_format_code == fmt_big) { | ||
388 | /* SCSI like vtbl, stores only the number of | ||
389 | * segments used | ||
390 | */ | ||
391 | unsigned int num_segments= GET4(entry, VTBL_SCSI_SEGS); | ||
392 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
393 | zft_last_vtbl->end_seg = | ||
394 | zft_last_vtbl->start_seg + num_segments - 1; | ||
395 | } else { | ||
396 | /* `normal', QIC-80 like vtbl | ||
397 | */ | ||
398 | zft_last_vtbl->start_seg = GET2(entry, VTBL_START); | ||
399 | zft_last_vtbl->end_seg = GET2(entry, VTBL_END); | ||
400 | } | ||
401 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; | ||
402 | /* check if we created this volume and get the | ||
403 | * blk_sz | ||
404 | */ | ||
405 | zft_last_vtbl->zft_volume = check_volume(entry, zft_last_vtbl); | ||
406 | if (zft_last_vtbl->zft_volume == 0) { | ||
407 | extract_alien_volume(entry, zft_last_vtbl); | ||
408 | } else { | ||
409 | extract_zft_volume(entry, zft_last_vtbl); | ||
410 | } | ||
411 | DUMP_VOLINFO(ft_t_noise, &entry[VTBL_DESC], zft_last_vtbl); | ||
412 | entry +=VTBL_SIZE; | ||
413 | } | ||
414 | #if 0 | ||
415 | /* | ||
416 | * undefine to test end of tape handling | ||
417 | */ | ||
418 | zft_new_vtbl_entry(); | ||
419 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
420 | zft_last_vtbl->end_seg = ft_last_data_segment - 10; | ||
421 | zft_last_vtbl->blk_sz = zft_blk_sz; | ||
422 | zft_last_vtbl->zft_volume = 1; | ||
423 | zft_last_vtbl->qic113 = zft_qic113; | ||
424 | zft_last_vtbl->size = (zft_calc_tape_pos(zft_last_vtbl->end_seg+1) | ||
425 | - zft_calc_tape_pos(zft_last_vtbl->start_seg)); | ||
426 | #endif | ||
427 | TRACE_EXIT 0; | ||
428 | } | ||
429 | |||
430 | /* this functions translates the failed_sector_log, misused as | ||
431 | * EOF-marker list, into a virtual volume table. The table mustn't be | ||
432 | * written to tape, because this would occupy the first data segment, | ||
433 | * which should be the volume table, but is actually the first segment | ||
434 | * that is filled with data (when using standard ftape). We assume, | ||
435 | * that we get a non-empty failed_sector_log. | ||
436 | */ | ||
437 | int zft_fake_volume_headers (eof_mark_union *eof_map, int num_failed_sectors) | ||
438 | { | ||
439 | unsigned int segment, sector; | ||
440 | int have_eom = 0; | ||
441 | int vol_no; | ||
442 | TRACE_FUN(ft_t_flow); | ||
443 | |||
444 | if ((num_failed_sectors >= 2) && | ||
445 | (GET2(&eof_map[num_failed_sectors - 1].mark.segment, 0) | ||
446 | == | ||
447 | GET2(&eof_map[num_failed_sectors - 2].mark.segment, 0) + 1) && | ||
448 | (GET2(&eof_map[num_failed_sectors - 1].mark.date, 0) == 1)) { | ||
449 | /* this should be eom. We keep the remainder of the | ||
450 | * tape as another volume. | ||
451 | */ | ||
452 | have_eom = 1; | ||
453 | } | ||
454 | zft_init_vtbl(); | ||
455 | zft_eom_vtbl->start_seg = ft_first_data_segment; | ||
456 | for(vol_no = 0; vol_no < num_failed_sectors - have_eom; vol_no ++) { | ||
457 | zft_new_vtbl_entry(); | ||
458 | |||
459 | segment = GET2(&eof_map[vol_no].mark.segment, 0); | ||
460 | sector = GET2(&eof_map[vol_no].mark.date, 0); | ||
461 | |||
462 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
463 | zft_last_vtbl->end_seg = segment; | ||
464 | zft_eom_vtbl->start_seg = segment + 1; | ||
465 | zft_last_vtbl->blk_sz = 1; | ||
466 | zft_last_vtbl->size = | ||
467 | (zft_calc_tape_pos(zft_last_vtbl->end_seg) | ||
468 | - zft_calc_tape_pos(zft_last_vtbl->start_seg) | ||
469 | + (sector-1) * FT_SECTOR_SIZE); | ||
470 | TRACE(ft_t_noise, | ||
471 | "failed sector log: segment: %d, sector: %d", | ||
472 | segment, sector); | ||
473 | DUMP_VOLINFO(ft_t_noise, "Faked volume", zft_last_vtbl); | ||
474 | } | ||
475 | if (!have_eom) { | ||
476 | zft_new_vtbl_entry(); | ||
477 | zft_last_vtbl->start_seg = zft_eom_vtbl->start_seg; | ||
478 | zft_last_vtbl->end_seg = ft_last_data_segment; | ||
479 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; | ||
480 | zft_last_vtbl->size = zft_capacity; | ||
481 | zft_last_vtbl->size -= zft_calc_tape_pos(zft_last_vtbl->start_seg); | ||
482 | zft_last_vtbl->blk_sz = 1; | ||
483 | DUMP_VOLINFO(ft_t_noise, "Faked volume",zft_last_vtbl); | ||
484 | } | ||
485 | TRACE_EXIT 0; | ||
486 | } | ||
487 | |||
488 | /* update the internal volume table | ||
489 | * | ||
490 | * if before start of last volume: erase all following volumes if | ||
491 | * inside a volume: set end of volume to infinity | ||
492 | * | ||
493 | * this function is intended to be called every time _ftape_write() is | ||
494 | * called | ||
495 | * | ||
496 | * return: 0 if no new volume was created, 1 if a new volume was | ||
497 | * created | ||
498 | * | ||
499 | * NOTE: we don't need to check for zft_mode as ftape_write() does | ||
500 | * that already. This function gets never called without accessing | ||
501 | * zftape via the *qft* devices | ||
502 | */ | ||
503 | |||
504 | int zft_open_volume(zft_position *pos, int blk_sz, int use_compression) | ||
505 | { | ||
506 | TRACE_FUN(ft_t_flow); | ||
507 | |||
508 | if (!zft_qic_mode) { | ||
509 | TRACE_EXIT 0; | ||
510 | } | ||
511 | if (zft_tape_at_lbot(pos)) { | ||
512 | zft_init_vtbl(); | ||
513 | if(zft_old_ftape) { | ||
514 | /* clear old ftape's eof marks */ | ||
515 | zft_clear_ftape_file_marks(); | ||
516 | zft_old_ftape = 0; /* no longer old ftape */ | ||
517 | } | ||
518 | zft_reset_position(pos); | ||
519 | } | ||
520 | if (pos->seg_pos != zft_last_vtbl->end_seg + 1) { | ||
521 | TRACE_ABORT(-EIO, ft_t_bug, | ||
522 | "BUG: seg_pos: %d, zft_last_vtbl->end_seg: %d", | ||
523 | pos->seg_pos, zft_last_vtbl->end_seg); | ||
524 | } | ||
525 | TRACE(ft_t_noise, "create new volume"); | ||
526 | if (zft_eom_vtbl->count >= ZFT_MAX_VOLUMES) { | ||
527 | TRACE_ABORT(-ENOSPC, ft_t_err, | ||
528 | "Error: maxmimal number of volumes exhausted " | ||
529 | "(maxmimum is %d)", ZFT_MAX_VOLUMES); | ||
530 | } | ||
531 | zft_new_vtbl_entry(); | ||
532 | pos->volume_pos = pos->seg_byte_pos = 0; | ||
533 | zft_last_vtbl->start_seg = pos->seg_pos; | ||
534 | zft_last_vtbl->end_seg = ft_last_data_segment; /* infinity */ | ||
535 | zft_last_vtbl->blk_sz = blk_sz; | ||
536 | zft_last_vtbl->size = zft_capacity; | ||
537 | zft_last_vtbl->zft_volume = 1; | ||
538 | zft_last_vtbl->use_compression = use_compression; | ||
539 | zft_last_vtbl->qic113 = zft_qic113; | ||
540 | zft_last_vtbl->new_volume = 1; | ||
541 | zft_last_vtbl->open = 1; | ||
542 | zft_volume_table_changed = 1; | ||
543 | zft_eom_vtbl->start_seg = ft_last_data_segment + 1; | ||
544 | TRACE_EXIT 0; | ||
545 | } | ||
546 | |||
547 | /* perform mtfsf, mtbsf, not allowed without zft_qic_mode | ||
548 | */ | ||
549 | int zft_skip_volumes(int count, zft_position *pos) | ||
550 | { | ||
551 | const zft_volinfo *vtbl; | ||
552 | TRACE_FUN(ft_t_flow); | ||
553 | |||
554 | TRACE(ft_t_noise, "count: %d", count); | ||
555 | |||
556 | vtbl= zft_find_volume(pos->seg_pos); | ||
557 | while (count > 0 && vtbl != zft_eom_vtbl) { | ||
558 | vtbl = list_entry(vtbl->node.next, zft_volinfo, node); | ||
559 | count --; | ||
560 | } | ||
561 | while (count < 0 && vtbl != zft_first_vtbl) { | ||
562 | vtbl = list_entry(vtbl->node.prev, zft_volinfo, node); | ||
563 | count ++; | ||
564 | } | ||
565 | pos->seg_pos = vtbl->start_seg; | ||
566 | pos->seg_byte_pos = 0; | ||
567 | pos->volume_pos = 0; | ||
568 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
569 | zft_just_before_eof = vtbl->size == 0; | ||
570 | if (zft_cmpr_ops) { | ||
571 | (*zft_cmpr_ops->reset)(); | ||
572 | } | ||
573 | zft_deblock_segment = -1; /* no need to keep cache */ | ||
574 | TRACE(ft_t_noise, "repositioning to:\n" | ||
575 | KERN_INFO "zft_seg_pos : %d\n" | ||
576 | KERN_INFO "zft_seg_byte_pos : %d\n" | ||
577 | KERN_INFO "zft_tape_pos : " LL_X "\n" | ||
578 | KERN_INFO "zft_volume_pos : " LL_X "\n" | ||
579 | KERN_INFO "file number : %d", | ||
580 | pos->seg_pos, pos->seg_byte_pos, | ||
581 | LL(pos->tape_pos), LL(pos->volume_pos), vtbl->count); | ||
582 | zft_resid = count < 0 ? -count : count; | ||
583 | TRACE_EXIT zft_resid ? -EINVAL : 0; | ||
584 | } | ||
585 | |||
586 | /* the following simply returns the raw data position of the EOM | ||
587 | * marker, MTIOCSIZE ioctl | ||
588 | */ | ||
589 | __s64 zft_get_eom_pos(void) | ||
590 | { | ||
591 | if (zft_qic_mode) { | ||
592 | return zft_calc_tape_pos(zft_eom_vtbl->start_seg); | ||
593 | } else { | ||
594 | /* there is only one volume in raw mode */ | ||
595 | return zft_capacity; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | /* skip to eom, used for MTEOM | ||
600 | */ | ||
601 | void zft_skip_to_eom(zft_position *pos) | ||
602 | { | ||
603 | TRACE_FUN(ft_t_flow); | ||
604 | pos->seg_pos = zft_eom_vtbl->start_seg; | ||
605 | pos->seg_byte_pos = | ||
606 | pos->volume_pos = | ||
607 | zft_just_before_eof = 0; | ||
608 | pos->tape_pos = zft_calc_tape_pos(pos->seg_pos); | ||
609 | TRACE(ft_t_noise, "ftape positioned to segment %d, data pos " LL_X, | ||
610 | pos->seg_pos, LL(pos->tape_pos)); | ||
611 | TRACE_EXIT; | ||
612 | } | ||
613 | |||
614 | /* write an EOF-marker by setting zft_last_vtbl->end_seg to seg_pos. | ||
615 | * NOTE: this function assumes that zft_last_vtbl points to a valid | ||
616 | * vtbl entry | ||
617 | * | ||
618 | * NOTE: this routine always positions before the EOF marker | ||
619 | */ | ||
620 | int zft_close_volume(zft_position *pos) | ||
621 | { | ||
622 | TRACE_FUN(ft_t_any); | ||
623 | |||
624 | if (zft_vtbl_empty || !zft_last_vtbl->open) { /* should not happen */ | ||
625 | TRACE(ft_t_noise, "There are no volumes to finish"); | ||
626 | TRACE_EXIT -EIO; | ||
627 | } | ||
628 | if (pos->seg_byte_pos == 0 && | ||
629 | pos->seg_pos != zft_last_vtbl->start_seg) { | ||
630 | pos->seg_pos --; | ||
631 | pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); | ||
632 | } | ||
633 | zft_last_vtbl->end_seg = pos->seg_pos; | ||
634 | zft_last_vtbl->size = pos->volume_pos; | ||
635 | zft_volume_table_changed = 1; | ||
636 | zft_just_before_eof = 1; | ||
637 | zft_eom_vtbl->start_seg = zft_last_vtbl->end_seg + 1; | ||
638 | zft_last_vtbl->open = 0; /* closed */ | ||
639 | TRACE_EXIT 0; | ||
640 | } | ||
641 | |||
642 | /* write count file-marks at current position. | ||
643 | * | ||
644 | * The tape is positioned after the eof-marker, that is at byte 0 of | ||
645 | * the segment following the eof-marker | ||
646 | * | ||
647 | * this function is only allowed in zft_qic_mode | ||
648 | * | ||
649 | * Only allowed when tape is at BOT or EOD. | ||
650 | */ | ||
651 | int zft_weof(unsigned int count, zft_position *pos) | ||
652 | { | ||
653 | |||
654 | TRACE_FUN(ft_t_flow); | ||
655 | |||
656 | if (!count) { /* write zero EOF marks should be a real no-op */ | ||
657 | TRACE_EXIT 0; | ||
658 | } | ||
659 | zft_volume_table_changed = 1; | ||
660 | if (zft_tape_at_lbot(pos)) { | ||
661 | zft_init_vtbl(); | ||
662 | if(zft_old_ftape) { | ||
663 | /* clear old ftape's eof marks */ | ||
664 | zft_clear_ftape_file_marks(); | ||
665 | zft_old_ftape = 0; /* no longer old ftape */ | ||
666 | } | ||
667 | } | ||
668 | if (zft_last_vtbl->open) { | ||
669 | zft_close_volume(pos); | ||
670 | zft_move_past_eof(pos); | ||
671 | count --; | ||
672 | } | ||
673 | /* now it's easy, just append eof-marks, that is empty | ||
674 | * volumes, to the end of the already recorded media. | ||
675 | */ | ||
676 | while (count > 0 && | ||
677 | pos->seg_pos <= ft_last_data_segment && | ||
678 | zft_eom_vtbl->count < ZFT_MAX_VOLUMES) { | ||
679 | TRACE(ft_t_noise, | ||
680 | "Writing zero sized file at segment %d", pos->seg_pos); | ||
681 | zft_new_vtbl_entry(); | ||
682 | zft_last_vtbl->start_seg = pos->seg_pos; | ||
683 | zft_last_vtbl->end_seg = pos->seg_pos; | ||
684 | zft_last_vtbl->size = 0; | ||
685 | zft_last_vtbl->blk_sz = zft_blk_sz; | ||
686 | zft_last_vtbl->zft_volume = 1; | ||
687 | zft_last_vtbl->use_compression = 0; | ||
688 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos); | ||
689 | zft_eom_vtbl->start_seg = ++ pos->seg_pos; | ||
690 | count --; | ||
691 | } | ||
692 | if (count > 0) { | ||
693 | /* there are two possibilities: end of tape, or the | ||
694 | * maximum number of files is exhausted. | ||
695 | */ | ||
696 | zft_resid = count; | ||
697 | TRACE(ft_t_noise,"Number of marks NOT written: %d", zft_resid); | ||
698 | if (zft_eom_vtbl->count == ZFT_MAX_VOLUMES) { | ||
699 | TRACE_ABORT(-EINVAL, ft_t_warn, | ||
700 | "maximum allowed number of files " | ||
701 | "exhausted: %d", ZFT_MAX_VOLUMES); | ||
702 | } else { | ||
703 | TRACE_ABORT(-ENOSPC, | ||
704 | ft_t_noise, "reached end of tape"); | ||
705 | } | ||
706 | } | ||
707 | TRACE_EXIT 0; | ||
708 | } | ||
709 | |||
710 | const zft_volinfo *zft_find_volume(unsigned int seg_pos) | ||
711 | { | ||
712 | TRACE_FUN(ft_t_flow); | ||
713 | |||
714 | TRACE(ft_t_any, "called with seg_pos %d",seg_pos); | ||
715 | if (!zft_qic_mode) { | ||
716 | if (seg_pos > ft_last_data_segment) { | ||
717 | TRACE_EXIT &eot_vtbl; | ||
718 | } | ||
719 | tape_vtbl.blk_sz = zft_blk_sz; | ||
720 | TRACE_EXIT &tape_vtbl; | ||
721 | } | ||
722 | if (seg_pos < zft_first_vtbl->start_seg) { | ||
723 | TRACE_EXIT (cur_vtbl = zft_first_vtbl); | ||
724 | } | ||
725 | while (seg_pos > cur_vtbl->end_seg) { | ||
726 | cur_vtbl = list_entry(cur_vtbl->node.next, zft_volinfo, node); | ||
727 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); | ||
728 | } | ||
729 | while (seg_pos < cur_vtbl->start_seg) { | ||
730 | cur_vtbl = list_entry(cur_vtbl->node.prev, zft_volinfo, node); | ||
731 | TRACE(ft_t_noise, "%d - %d", cur_vtbl->start_seg, cur_vtbl->end_seg); | ||
732 | } | ||
733 | if (seg_pos > cur_vtbl->end_seg || seg_pos < cur_vtbl->start_seg) { | ||
734 | TRACE(ft_t_bug, "This cannot happen"); | ||
735 | } | ||
736 | DUMP_VOLINFO(ft_t_noise, "", cur_vtbl); | ||
737 | TRACE_EXIT cur_vtbl; | ||
738 | } | ||
739 | |||
740 | /* this function really assumes that we are just before eof | ||
741 | */ | ||
742 | void zft_move_past_eof(zft_position *pos) | ||
743 | { | ||
744 | TRACE_FUN(ft_t_flow); | ||
745 | |||
746 | TRACE(ft_t_noise, "old seg. pos: %d", pos->seg_pos); | ||
747 | pos->tape_pos += zft_get_seg_sz(pos->seg_pos++) - pos->seg_byte_pos; | ||
748 | pos->seg_byte_pos = 0; | ||
749 | pos->volume_pos = 0; | ||
750 | if (zft_cmpr_ops) { | ||
751 | (*zft_cmpr_ops->reset)(); | ||
752 | } | ||
753 | zft_just_before_eof = 0; | ||
754 | zft_deblock_segment = -1; /* no need to cache it anymore */ | ||
755 | TRACE(ft_t_noise, "new seg. pos: %d", pos->seg_pos); | ||
756 | TRACE_EXIT; | ||
757 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-vtbl.h b/drivers/char/ftape/zftape/zftape-vtbl.h new file mode 100644 index 000000000000..f31d196d1759 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-vtbl.h | |||
@@ -0,0 +1,227 @@ | |||
1 | #ifndef _ZFTAPE_VTBL_H | ||
2 | #define _ZFTAPE_VTBL_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (c) 1995-1997 Claus-Justus Heine | ||
6 | |||
7 | This program is free software; you can redistribute it and/or | ||
8 | modify it under the terms of the GNU General Public License as | ||
9 | published by the Free Software Foundation; either version 2, or (at | ||
10 | your option) any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, but | ||
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, | ||
20 | USA. | ||
21 | |||
22 | * | ||
23 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-vtbl.h,v $ | ||
24 | * $Revision: 1.3 $ | ||
25 | * $Date: 1997/10/28 14:30:09 $ | ||
26 | * | ||
27 | * This file defines a volume table as defined in the QIC-80 | ||
28 | * development standards. | ||
29 | */ | ||
30 | |||
31 | #include <linux/list.h> | ||
32 | |||
33 | #include "../lowlevel/ftape-tracing.h" | ||
34 | |||
35 | #include "../zftape/zftape-eof.h" | ||
36 | #include "../zftape/zftape-ctl.h" | ||
37 | #include "../zftape/zftape-rw.h" | ||
38 | |||
39 | #define VTBL_SIZE 128 /* bytes */ | ||
40 | |||
41 | /* The following are offsets in the vtbl. */ | ||
42 | #define VTBL_SIG 0 | ||
43 | #define VTBL_START 4 | ||
44 | #define VTBL_END 6 | ||
45 | #define VTBL_DESC 8 | ||
46 | #define VTBL_DATE 52 | ||
47 | #define VTBL_FLAGS 56 | ||
48 | #define VTBL_FL_VENDOR_SPECIFIC (1<<0) | ||
49 | #define VTBL_FL_MUTLI_CARTRIDGE (1<<1) | ||
50 | #define VTBL_FL_NOT_VERIFIED (1<<2) | ||
51 | #define VTBL_FL_REDIR_INHIBIT (1<<3) | ||
52 | #define VTBL_FL_SEG_SPANNING (1<<4) | ||
53 | #define VTBL_FL_DIRECTORY_LAST (1<<5) | ||
54 | #define VTBL_FL_RESERVED_6 (1<<6) | ||
55 | #define VTBL_FL_RESERVED_7 (1<<7) | ||
56 | #define VTBL_M_NO 57 | ||
57 | #define VTBL_EXT 58 | ||
58 | #define EXT_ZFTAPE_SIG 0 | ||
59 | #define EXT_ZFTAPE_BLKSZ 10 | ||
60 | #define EXT_ZFTAPE_CMAP 12 | ||
61 | #define EXT_ZFTAPE_QIC113 13 | ||
62 | #define VTBL_PWD 84 | ||
63 | #define VTBL_DIR_SIZE 92 | ||
64 | #define VTBL_DATA_SIZE 96 | ||
65 | #define VTBL_OS_VERSION 104 | ||
66 | #define VTBL_SRC_DRIVE 106 | ||
67 | #define VTBL_DEV 122 | ||
68 | #define VTBL_RESERVED_1 123 | ||
69 | #define VTBL_CMPR 124 | ||
70 | #define VTBL_CMPR_UNREG 0x3f | ||
71 | #define VTBL_CMPR_USED 0x80 | ||
72 | #define VTBL_FMT 125 | ||
73 | #define VTBL_RESERVED_2 126 | ||
74 | #define VTBL_RESERVED_3 127 | ||
75 | /* compatibility with pre revision K */ | ||
76 | #define VTBL_K_CMPR 120 | ||
77 | |||
78 | /* the next is used by QIC-3020 tapes with format code 6 (>2^16 | ||
79 | * segments) It is specified in QIC-113, Rev. G, Section 5 (SCSI | ||
80 | * volume table). The difference is simply, that we only store the | ||
81 | * number of segments used, not the starting segment. | ||
82 | */ | ||
83 | #define VTBL_SCSI_SEGS 4 /* is a 4 byte value */ | ||
84 | |||
85 | /* one vtbl is 128 bytes, that results in a maximum number of | ||
86 | * 29*1024/128 = 232 volumes. | ||
87 | */ | ||
88 | #define ZFT_MAX_VOLUMES (FT_SEGMENT_SIZE/VTBL_SIZE) | ||
89 | #define VTBL_ID "VTBL" | ||
90 | #define VTBL_IDS { VTBL_ID, "XTBL", "UTID", "EXVT" } /* other valid ids */ | ||
91 | #define ZFT_VOL_NAME "zftape volume" /* volume label used by me */ | ||
92 | #define ZFTAPE_SIG "LINUX ZFT" | ||
93 | |||
94 | /* global variables | ||
95 | */ | ||
96 | typedef struct zft_internal_vtbl | ||
97 | { | ||
98 | struct list_head node; | ||
99 | int count; | ||
100 | unsigned int start_seg; /* 32 bits are enough for now */ | ||
101 | unsigned int end_seg; /* 32 bits are enough for now */ | ||
102 | __s64 size; /* uncompressed size */ | ||
103 | unsigned int blk_sz; /* block size for this volume */ | ||
104 | unsigned int zft_volume :1; /* zftape created this volume */ | ||
105 | unsigned int use_compression:1; /* compressed volume */ | ||
106 | unsigned int qic113 :1; /* layout of compressed block | ||
107 | * info and vtbl conforms to | ||
108 | * QIC-113, Rev. G | ||
109 | */ | ||
110 | unsigned int new_volume :1; /* it was created by us, this | ||
111 | * run. this allows the | ||
112 | * fields that aren't really | ||
113 | * used by zftape to be filled | ||
114 | * in by some user level | ||
115 | * program. | ||
116 | */ | ||
117 | unsigned int open :1; /* just in progress of being | ||
118 | * written | ||
119 | */ | ||
120 | } zft_volinfo; | ||
121 | |||
122 | extern struct list_head zft_vtbl; | ||
123 | #define zft_head_vtbl list_entry(zft_vtbl.next, zft_volinfo, node) | ||
124 | #define zft_eom_vtbl list_entry(zft_vtbl.prev, zft_volinfo, node) | ||
125 | #define zft_last_vtbl list_entry(zft_eom_vtbl->node.prev, zft_volinfo, node) | ||
126 | #define zft_first_vtbl list_entry(zft_head_vtbl->node.next, zft_volinfo, node) | ||
127 | #define zft_vtbl_empty (zft_eom_vtbl->node.prev == &zft_head_vtbl->node) | ||
128 | |||
129 | #define DUMP_VOLINFO(level, desc, info) \ | ||
130 | { \ | ||
131 | char tmp[21]; \ | ||
132 | strlcpy(tmp, desc, sizeof(tmp)); \ | ||
133 | TRACE(level, "Volume %d:\n" \ | ||
134 | KERN_INFO "description : %s\n" \ | ||
135 | KERN_INFO "first segment: %d\n" \ | ||
136 | KERN_INFO "last segment: %d\n" \ | ||
137 | KERN_INFO "size : " LL_X "\n" \ | ||
138 | KERN_INFO "block size : %d\n" \ | ||
139 | KERN_INFO "compression : %d\n" \ | ||
140 | KERN_INFO "zftape volume: %d\n" \ | ||
141 | KERN_INFO "QIC-113 conf.: %d", \ | ||
142 | (info)->count, tmp, (info)->start_seg, (info)->end_seg, \ | ||
143 | LL((info)->size), (info)->blk_sz, \ | ||
144 | (info)->use_compression != 0, (info)->zft_volume != 0, \ | ||
145 | (info)->qic113 != 0); \ | ||
146 | } | ||
147 | |||
148 | extern int zft_qic_mode; | ||
149 | extern int zft_old_ftape; | ||
150 | extern int zft_volume_table_changed; | ||
151 | |||
152 | /* exported functions */ | ||
153 | extern void zft_init_vtbl (void); | ||
154 | extern void zft_free_vtbl (void); | ||
155 | extern int zft_extract_volume_headers(__u8 *buffer); | ||
156 | extern int zft_update_volume_table (unsigned int segment); | ||
157 | extern int zft_open_volume (zft_position *pos, | ||
158 | int blk_sz, int use_compression); | ||
159 | extern int zft_close_volume (zft_position *pos); | ||
160 | extern const zft_volinfo *zft_find_volume(unsigned int seg_pos); | ||
161 | extern int zft_skip_volumes (int count, zft_position *pos); | ||
162 | extern __s64 zft_get_eom_pos (void); | ||
163 | extern void zft_skip_to_eom (zft_position *pos); | ||
164 | extern int zft_fake_volume_headers (eof_mark_union *eof_map, | ||
165 | int num_failed_sectors); | ||
166 | extern int zft_weof (unsigned int count, zft_position *pos); | ||
167 | extern void zft_move_past_eof (zft_position *pos); | ||
168 | |||
169 | static inline int zft_tape_at_eod (const zft_position *pos); | ||
170 | static inline int zft_tape_at_lbot (const zft_position *pos); | ||
171 | static inline void zft_position_before_eof (zft_position *pos, | ||
172 | const zft_volinfo *volume); | ||
173 | static inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, | ||
174 | const zft_position *pos); | ||
175 | |||
176 | /* this function decrements the zft_seg_pos counter if we are right | ||
177 | * at the beginning of a segment. This is to handle fsfm/bsfm -- we | ||
178 | * need to position before the eof mark. NOTE: zft_tape_pos is not | ||
179 | * changed | ||
180 | */ | ||
181 | static inline void zft_position_before_eof(zft_position *pos, | ||
182 | const zft_volinfo *volume) | ||
183 | { | ||
184 | TRACE_FUN(ft_t_flow); | ||
185 | |||
186 | if (pos->seg_pos == volume->end_seg + 1 && pos->seg_byte_pos == 0) { | ||
187 | pos->seg_pos --; | ||
188 | pos->seg_byte_pos = zft_get_seg_sz(pos->seg_pos); | ||
189 | } | ||
190 | TRACE_EXIT; | ||
191 | } | ||
192 | |||
193 | /* Mmmh. Is the position at the end of the last volume, that is right | ||
194 | * before the last EOF mark also logical an EOD condition? | ||
195 | */ | ||
196 | static inline int zft_tape_at_eod(const zft_position *pos) | ||
197 | { | ||
198 | TRACE_FUN(ft_t_any); | ||
199 | |||
200 | if (zft_qic_mode) { | ||
201 | TRACE_EXIT (pos->seg_pos >= zft_eom_vtbl->start_seg || | ||
202 | zft_last_vtbl->open); | ||
203 | } else { | ||
204 | TRACE_EXIT pos->seg_pos > ft_last_data_segment; | ||
205 | } | ||
206 | } | ||
207 | |||
208 | static inline int zft_tape_at_lbot(const zft_position *pos) | ||
209 | { | ||
210 | if (zft_qic_mode) { | ||
211 | return (pos->seg_pos <= zft_first_vtbl->start_seg && | ||
212 | pos->volume_pos == 0); | ||
213 | } else { | ||
214 | return (pos->seg_pos <= ft_first_data_segment && | ||
215 | pos->volume_pos == 0); | ||
216 | } | ||
217 | } | ||
218 | |||
219 | /* This one checks for EOF. return remaing space (may be negative) | ||
220 | */ | ||
221 | static inline __s64 zft_check_for_eof(const zft_volinfo *vtbl, | ||
222 | const zft_position *pos) | ||
223 | { | ||
224 | return (__s64)(vtbl->size - pos->volume_pos); | ||
225 | } | ||
226 | |||
227 | #endif /* _ZFTAPE_VTBL_H */ | ||
diff --git a/drivers/char/ftape/zftape/zftape-write.c b/drivers/char/ftape/zftape/zftape-write.c new file mode 100644 index 000000000000..94327b8c97b9 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-write.c | |||
@@ -0,0 +1,483 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1996, 1997 Claus 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-write.c,v $ | ||
20 | * $Revision: 1.3 $ | ||
21 | * $Date: 1997/11/06 00:50:29 $ | ||
22 | * | ||
23 | * This file contains the writing code | ||
24 | * for the QIC-117 floppy-tape driver for Linux. | ||
25 | */ | ||
26 | |||
27 | #include <linux/errno.h> | ||
28 | #include <linux/mm.h> | ||
29 | |||
30 | #include <linux/zftape.h> | ||
31 | |||
32 | #include <asm/uaccess.h> | ||
33 | |||
34 | #include "../zftape/zftape-init.h" | ||
35 | #include "../zftape/zftape-eof.h" | ||
36 | #include "../zftape/zftape-ctl.h" | ||
37 | #include "../zftape/zftape-write.h" | ||
38 | #include "../zftape/zftape-read.h" | ||
39 | #include "../zftape/zftape-rw.h" | ||
40 | #include "../zftape/zftape-vtbl.h" | ||
41 | |||
42 | /* Global vars. | ||
43 | */ | ||
44 | |||
45 | /* Local vars. | ||
46 | */ | ||
47 | static int last_write_failed; | ||
48 | static int need_flush; | ||
49 | |||
50 | void zft_prevent_flush(void) | ||
51 | { | ||
52 | need_flush = 0; | ||
53 | } | ||
54 | |||
55 | static int zft_write_header_segments(__u8* buffer) | ||
56 | { | ||
57 | int header_1_ok = 0; | ||
58 | int header_2_ok = 0; | ||
59 | unsigned int time_stamp; | ||
60 | TRACE_FUN(ft_t_noise); | ||
61 | |||
62 | TRACE_CATCH(ftape_abort_operation(),); | ||
63 | ftape_seek_to_bot(); /* prevents extra rewind */ | ||
64 | if (GET4(buffer, 0) != FT_HSEG_MAGIC) { | ||
65 | TRACE_ABORT(-EIO, ft_t_err, | ||
66 | "wrong header signature found, aborting"); | ||
67 | } | ||
68 | /* Be optimistic: */ | ||
69 | PUT4(buffer, FT_SEG_CNT, | ||
70 | zft_written_segments + GET4(buffer, FT_SEG_CNT) + 2); | ||
71 | if ((time_stamp = zft_get_time()) != 0) { | ||
72 | PUT4(buffer, FT_WR_DATE, time_stamp); | ||
73 | if (zft_label_changed) { | ||
74 | PUT4(buffer, FT_LABEL_DATE, time_stamp); | ||
75 | } | ||
76 | } | ||
77 | TRACE(ft_t_noise, | ||
78 | "writing first header segment %d", ft_header_segment_1); | ||
79 | header_1_ok = zft_verify_write_segments(ft_header_segment_1, | ||
80 | buffer, FT_SEGMENT_SIZE, | ||
81 | zft_deblock_buf) >= 0; | ||
82 | TRACE(ft_t_noise, | ||
83 | "writing second header segment %d", ft_header_segment_2); | ||
84 | header_2_ok = zft_verify_write_segments(ft_header_segment_2, | ||
85 | buffer, FT_SEGMENT_SIZE, | ||
86 | zft_deblock_buf) >= 0; | ||
87 | if (!header_1_ok) { | ||
88 | TRACE(ft_t_warn, "Warning: " | ||
89 | "update of first header segment failed"); | ||
90 | } | ||
91 | if (!header_2_ok) { | ||
92 | TRACE(ft_t_warn, "Warning: " | ||
93 | "update of second header segment failed"); | ||
94 | } | ||
95 | if (!header_1_ok && !header_2_ok) { | ||
96 | TRACE_ABORT(-EIO, ft_t_err, "Error: " | ||
97 | "update of both header segments failed."); | ||
98 | } | ||
99 | TRACE_EXIT 0; | ||
100 | } | ||
101 | |||
102 | int zft_update_header_segments(void) | ||
103 | { | ||
104 | TRACE_FUN(ft_t_noise); | ||
105 | |||
106 | /* must NOT use zft_write_protected, as it also includes the | ||
107 | * file access mode. But we also want to update when soft | ||
108 | * write protection is enabled (O_RDONLY) | ||
109 | */ | ||
110 | if (ft_write_protected || zft_old_ftape) { | ||
111 | TRACE_ABORT(0, ft_t_noise, "Tape set read-only: no update"); | ||
112 | } | ||
113 | if (!zft_header_read) { | ||
114 | TRACE_ABORT(0, ft_t_noise, "Nothing to update"); | ||
115 | } | ||
116 | if (!zft_header_changed) { | ||
117 | zft_header_changed = zft_written_segments > 0; | ||
118 | } | ||
119 | if (!zft_header_changed && !zft_volume_table_changed) { | ||
120 | TRACE_ABORT(0, ft_t_noise, "Nothing to update"); | ||
121 | } | ||
122 | TRACE(ft_t_noise, "Updating header segments"); | ||
123 | if (ftape_get_status()->fti_state == writing) { | ||
124 | TRACE_CATCH(ftape_loop_until_writes_done(),); | ||
125 | } | ||
126 | TRACE_CATCH(ftape_abort_operation(),); | ||
127 | |||
128 | zft_deblock_segment = -1; /* invalidate the cache */ | ||
129 | if (zft_header_changed) { | ||
130 | TRACE_CATCH(zft_write_header_segments(zft_hseg_buf),); | ||
131 | } | ||
132 | if (zft_volume_table_changed) { | ||
133 | TRACE_CATCH(zft_update_volume_table(ft_first_data_segment),); | ||
134 | } | ||
135 | zft_header_changed = | ||
136 | zft_volume_table_changed = | ||
137 | zft_label_changed = | ||
138 | zft_written_segments = 0; | ||
139 | TRACE_CATCH(ftape_abort_operation(),); | ||
140 | ftape_seek_to_bot(); | ||
141 | TRACE_EXIT 0; | ||
142 | } | ||
143 | |||
144 | static int read_merge_buffer(int seg_pos, __u8 *buffer, int offset, int seg_sz) | ||
145 | { | ||
146 | int result = 0; | ||
147 | const ft_trace_t old_tracing = TRACE_LEVEL; | ||
148 | TRACE_FUN(ft_t_flow); | ||
149 | |||
150 | if (zft_qic_mode) { | ||
151 | /* writing in the middle of a volume is NOT allowed | ||
152 | * | ||
153 | */ | ||
154 | TRACE(ft_t_noise, "No need to read a segment"); | ||
155 | memset(buffer + offset, 0, seg_sz - offset); | ||
156 | TRACE_EXIT 0; | ||
157 | } | ||
158 | TRACE(ft_t_any, "waiting"); | ||
159 | ftape_start_writing(FT_WR_MULTI); | ||
160 | TRACE_CATCH(ftape_loop_until_writes_done(),); | ||
161 | |||
162 | TRACE(ft_t_noise, "trying to read segment %d from offset %d", | ||
163 | seg_pos, offset); | ||
164 | SET_TRACE_LEVEL(ft_t_bug); | ||
165 | result = zft_fetch_segment_fraction(seg_pos, buffer, | ||
166 | FT_RD_SINGLE, | ||
167 | offset, seg_sz - offset); | ||
168 | SET_TRACE_LEVEL(old_tracing); | ||
169 | if (result != (seg_sz - offset)) { | ||
170 | TRACE(ft_t_noise, "Ignore error: read_segment() result: %d", | ||
171 | result); | ||
172 | memset(buffer + offset, 0, seg_sz - offset); | ||
173 | } | ||
174 | TRACE_EXIT 0; | ||
175 | } | ||
176 | |||
177 | /* flush the write buffer to tape and write an eof-marker at the | ||
178 | * current position if not in raw mode. This function always | ||
179 | * positions the tape before the eof-marker. _ftape_close() should | ||
180 | * then advance to the next segment. | ||
181 | * | ||
182 | * the parameter "finish_volume" describes whether to position before | ||
183 | * or after the possibly created file-mark. We always position after | ||
184 | * the file-mark when called from ftape_close() and a flush was needed | ||
185 | * (that is ftape_write() was the last tape operation before calling | ||
186 | * ftape_flush) But we always position before the file-mark when this | ||
187 | * function get's called from outside ftape_close() | ||
188 | */ | ||
189 | int zft_flush_buffers(void) | ||
190 | { | ||
191 | int result; | ||
192 | int data_remaining; | ||
193 | int this_segs_size; | ||
194 | TRACE_FUN(ft_t_flow); | ||
195 | |||
196 | TRACE(ft_t_data_flow, | ||
197 | "entered, ftape_state = %d", ftape_get_status()->fti_state); | ||
198 | if (ftape_get_status()->fti_state != writing && !need_flush) { | ||
199 | TRACE_ABORT(0, ft_t_noise, "no need for flush"); | ||
200 | } | ||
201 | zft_io_state = zft_idle; /* triggers some initializations for the | ||
202 | * read and write routines | ||
203 | */ | ||
204 | if (last_write_failed) { | ||
205 | ftape_abort_operation(); | ||
206 | TRACE_EXIT -EIO; | ||
207 | } | ||
208 | TRACE(ft_t_noise, "flushing write buffers"); | ||
209 | this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); | ||
210 | if (this_segs_size == zft_pos.seg_byte_pos) { | ||
211 | zft_pos.seg_pos ++; | ||
212 | data_remaining = zft_pos.seg_byte_pos = 0; | ||
213 | } else { | ||
214 | data_remaining = zft_pos.seg_byte_pos; | ||
215 | } | ||
216 | /* If there is any data not written to tape yet, append zero's | ||
217 | * up to the end of the sector (if using compression) or merge | ||
218 | * it with the data existing on the tape Then write the | ||
219 | * segment(s) to tape. | ||
220 | */ | ||
221 | TRACE(ft_t_noise, "Position:\n" | ||
222 | KERN_INFO "seg_pos : %d\n" | ||
223 | KERN_INFO "byte pos : %d\n" | ||
224 | KERN_INFO "remaining: %d", | ||
225 | zft_pos.seg_pos, zft_pos.seg_byte_pos, data_remaining); | ||
226 | if (data_remaining > 0) { | ||
227 | do { | ||
228 | this_segs_size = zft_get_seg_sz(zft_pos.seg_pos); | ||
229 | if (this_segs_size > data_remaining) { | ||
230 | TRACE_CATCH(read_merge_buffer(zft_pos.seg_pos, | ||
231 | zft_deblock_buf, | ||
232 | data_remaining, | ||
233 | this_segs_size), | ||
234 | last_write_failed = 1); | ||
235 | } | ||
236 | result = ftape_write_segment(zft_pos.seg_pos, | ||
237 | zft_deblock_buf, | ||
238 | FT_WR_MULTI); | ||
239 | if (result != this_segs_size) { | ||
240 | TRACE(ft_t_err, "flush buffers failed"); | ||
241 | zft_pos.tape_pos -= zft_pos.seg_byte_pos; | ||
242 | zft_pos.seg_byte_pos = 0; | ||
243 | |||
244 | last_write_failed = 1; | ||
245 | TRACE_EXIT result; | ||
246 | } | ||
247 | zft_written_segments ++; | ||
248 | TRACE(ft_t_data_flow, | ||
249 | "flush, moved out buffer: %d", result); | ||
250 | /* need next segment for more data (empty segments?) | ||
251 | */ | ||
252 | if (result < data_remaining) { | ||
253 | if (result > 0) { | ||
254 | /* move remainder to buffer beginning | ||
255 | */ | ||
256 | memmove(zft_deblock_buf, | ||
257 | zft_deblock_buf + result, | ||
258 | FT_SEGMENT_SIZE - result); | ||
259 | } | ||
260 | } | ||
261 | data_remaining -= result; | ||
262 | zft_pos.seg_pos ++; | ||
263 | } while (data_remaining > 0); | ||
264 | TRACE(ft_t_any, "result: %d", result); | ||
265 | zft_deblock_segment = --zft_pos.seg_pos; | ||
266 | if (data_remaining == 0) { /* first byte next segment */ | ||
267 | zft_pos.seg_byte_pos = this_segs_size; | ||
268 | } else { /* after data previous segment, data_remaining < 0 */ | ||
269 | zft_pos.seg_byte_pos = data_remaining + result; | ||
270 | } | ||
271 | } else { | ||
272 | TRACE(ft_t_noise, "zft_deblock_buf empty"); | ||
273 | zft_pos.seg_pos --; | ||
274 | zft_pos.seg_byte_pos = zft_get_seg_sz (zft_pos.seg_pos); | ||
275 | ftape_start_writing(FT_WR_MULTI); | ||
276 | } | ||
277 | TRACE(ft_t_any, "waiting"); | ||
278 | if ((result = ftape_loop_until_writes_done()) < 0) { | ||
279 | /* that's really bad. What to to with zft_tape_pos? | ||
280 | */ | ||
281 | TRACE(ft_t_err, "flush buffers failed"); | ||
282 | } | ||
283 | TRACE(ft_t_any, "zft_seg_pos: %d, zft_seg_byte_pos: %d", | ||
284 | zft_pos.seg_pos, zft_pos.seg_byte_pos); | ||
285 | last_write_failed = | ||
286 | need_flush = 0; | ||
287 | TRACE_EXIT result; | ||
288 | } | ||
289 | |||
290 | /* return-value: the number of bytes removed from the user-buffer | ||
291 | * | ||
292 | * out: | ||
293 | * int *write_cnt: how much actually has been moved to the | ||
294 | * zft_deblock_buf | ||
295 | * int req_len : MUST NOT BE CHANGED, except at EOT, in | ||
296 | * which case it may be adjusted | ||
297 | * in : | ||
298 | * char *buff : the user buffer | ||
299 | * int buf_pos_write : copy of buf_len_wr int | ||
300 | * this_segs_size : the size in bytes of the actual segment | ||
301 | * char | ||
302 | * *zft_deblock_buf : zft_deblock_buf | ||
303 | */ | ||
304 | static int zft_simple_write(int *cnt, | ||
305 | __u8 *dst_buf, const int seg_sz, | ||
306 | const __u8 __user *src_buf, const int req_len, | ||
307 | const zft_position *pos,const zft_volinfo *volume) | ||
308 | { | ||
309 | int space_left; | ||
310 | TRACE_FUN(ft_t_flow); | ||
311 | |||
312 | /* volume->size holds the tape capacity while volume is open */ | ||
313 | if (pos->tape_pos + volume->blk_sz > volume->size) { | ||
314 | TRACE_EXIT -ENOSPC; | ||
315 | } | ||
316 | /* remaining space in this segment, NOT zft_deblock_buf | ||
317 | */ | ||
318 | space_left = seg_sz - pos->seg_byte_pos; | ||
319 | *cnt = req_len < space_left ? req_len : space_left; | ||
320 | if (copy_from_user(dst_buf + pos->seg_byte_pos, src_buf, *cnt) != 0) { | ||
321 | TRACE_EXIT -EFAULT; | ||
322 | } | ||
323 | TRACE_EXIT *cnt; | ||
324 | } | ||
325 | |||
326 | static int check_write_access(int req_len, | ||
327 | const zft_volinfo **volume, | ||
328 | zft_position *pos, | ||
329 | const unsigned int blk_sz) | ||
330 | { | ||
331 | int result; | ||
332 | TRACE_FUN(ft_t_flow); | ||
333 | |||
334 | if ((req_len % zft_blk_sz) != 0) { | ||
335 | TRACE_ABORT(-EINVAL, ft_t_info, | ||
336 | "write-count %d must be multiple of block-size %d", | ||
337 | req_len, blk_sz); | ||
338 | } | ||
339 | if (zft_io_state == zft_writing) { | ||
340 | /* all other error conditions have been checked earlier | ||
341 | */ | ||
342 | TRACE_EXIT 0; | ||
343 | } | ||
344 | zft_io_state = zft_idle; | ||
345 | TRACE_CATCH(zft_check_write_access(pos),); | ||
346 | /* If we haven't read the header segment yet, do it now. | ||
347 | * This will verify the configuration, get the bad sector | ||
348 | * table and read the volume table segment | ||
349 | */ | ||
350 | if (!zft_header_read) { | ||
351 | TRACE_CATCH(zft_read_header_segments(),); | ||
352 | } | ||
353 | /* fine. Now the tape is either at BOT or at EOD, | ||
354 | * Write start of volume now | ||
355 | */ | ||
356 | TRACE_CATCH(zft_open_volume(pos, blk_sz, zft_use_compression),); | ||
357 | *volume = zft_find_volume(pos->seg_pos); | ||
358 | DUMP_VOLINFO(ft_t_noise, "", *volume); | ||
359 | zft_just_before_eof = 0; | ||
360 | /* now merge with old data if necessary */ | ||
361 | if (!zft_qic_mode && pos->seg_byte_pos != 0){ | ||
362 | result = zft_fetch_segment(pos->seg_pos, | ||
363 | zft_deblock_buf, | ||
364 | FT_RD_SINGLE); | ||
365 | if (result < 0) { | ||
366 | if (result == -EINTR || result == -ENOSPC) { | ||
367 | TRACE_EXIT result; | ||
368 | } | ||
369 | TRACE(ft_t_noise, | ||
370 | "ftape_read_segment() result: %d. " | ||
371 | "This might be normal when using " | ||
372 | "a newly\nformatted tape", result); | ||
373 | memset(zft_deblock_buf, '\0', pos->seg_byte_pos); | ||
374 | } | ||
375 | } | ||
376 | zft_io_state = zft_writing; | ||
377 | TRACE_EXIT 0; | ||
378 | } | ||
379 | |||
380 | static int fill_deblock_buf(__u8 *dst_buf, const int seg_sz, | ||
381 | zft_position *pos, const zft_volinfo *volume, | ||
382 | const char __user *usr_buf, const int req_len) | ||
383 | { | ||
384 | int cnt = 0; | ||
385 | int result = 0; | ||
386 | TRACE_FUN(ft_t_flow); | ||
387 | |||
388 | if (seg_sz == 0) { | ||
389 | TRACE_ABORT(0, ft_t_data_flow, "empty segment"); | ||
390 | } | ||
391 | TRACE(ft_t_data_flow, "\n" | ||
392 | KERN_INFO "remaining req_len: %d\n" | ||
393 | KERN_INFO " buf_pos: %d", | ||
394 | req_len, pos->seg_byte_pos); | ||
395 | /* zft_deblock_buf will not contain a valid segment any longer */ | ||
396 | zft_deblock_segment = -1; | ||
397 | if (zft_use_compression) { | ||
398 | TRACE_CATCH(zft_cmpr_lock(1 /* try to load */),); | ||
399 | TRACE_CATCH(result= (*zft_cmpr_ops->write)(&cnt, | ||
400 | dst_buf, seg_sz, | ||
401 | usr_buf, req_len, | ||
402 | pos, volume),); | ||
403 | } else { | ||
404 | TRACE_CATCH(result= zft_simple_write(&cnt, | ||
405 | dst_buf, seg_sz, | ||
406 | usr_buf, req_len, | ||
407 | pos, volume),); | ||
408 | } | ||
409 | pos->volume_pos += result; | ||
410 | pos->seg_byte_pos += cnt; | ||
411 | pos->tape_pos += cnt; | ||
412 | TRACE(ft_t_data_flow, "\n" | ||
413 | KERN_INFO "removed from user-buffer : %d bytes.\n" | ||
414 | KERN_INFO "copied to zft_deblock_buf: %d bytes.\n" | ||
415 | KERN_INFO "zft_tape_pos : " LL_X " bytes.", | ||
416 | result, cnt, LL(pos->tape_pos)); | ||
417 | TRACE_EXIT result; | ||
418 | } | ||
419 | |||
420 | |||
421 | /* called by the kernel-interface routine "zft_write()" | ||
422 | */ | ||
423 | int _zft_write(const char __user *buff, int req_len) | ||
424 | { | ||
425 | int result = 0; | ||
426 | int written = 0; | ||
427 | int write_cnt; | ||
428 | int seg_sz; | ||
429 | static const zft_volinfo *volume = NULL; | ||
430 | TRACE_FUN(ft_t_flow); | ||
431 | |||
432 | zft_resid = req_len; | ||
433 | last_write_failed = 1; /* reset to 0 when successful */ | ||
434 | /* check if write is allowed | ||
435 | */ | ||
436 | TRACE_CATCH(check_write_access(req_len, &volume,&zft_pos,zft_blk_sz),); | ||
437 | while (req_len > 0) { | ||
438 | /* Allow us to escape from this loop with a signal ! | ||
439 | */ | ||
440 | FT_SIGNAL_EXIT(_DONT_BLOCK); | ||
441 | seg_sz = zft_get_seg_sz(zft_pos.seg_pos); | ||
442 | if ((write_cnt = fill_deblock_buf(zft_deblock_buf, | ||
443 | seg_sz, | ||
444 | &zft_pos, | ||
445 | volume, | ||
446 | buff, | ||
447 | req_len)) < 0) { | ||
448 | zft_resid -= written; | ||
449 | if (write_cnt == -ENOSPC) { | ||
450 | /* leave the remainder to flush_buffers() | ||
451 | */ | ||
452 | TRACE(ft_t_info, "No space left on device"); | ||
453 | last_write_failed = 0; | ||
454 | if (!need_flush) { | ||
455 | need_flush = written > 0; | ||
456 | } | ||
457 | TRACE_EXIT written > 0 ? written : -ENOSPC; | ||
458 | } else { | ||
459 | TRACE_EXIT result; | ||
460 | } | ||
461 | } | ||
462 | if (zft_pos.seg_byte_pos == seg_sz) { | ||
463 | TRACE_CATCH(ftape_write_segment(zft_pos.seg_pos, | ||
464 | zft_deblock_buf, | ||
465 | FT_WR_ASYNC), | ||
466 | zft_resid -= written); | ||
467 | zft_written_segments ++; | ||
468 | zft_pos.seg_byte_pos = 0; | ||
469 | zft_deblock_segment = zft_pos.seg_pos; | ||
470 | ++zft_pos.seg_pos; | ||
471 | } | ||
472 | written += write_cnt; | ||
473 | buff += write_cnt; | ||
474 | req_len -= write_cnt; | ||
475 | } /* while (req_len > 0) */ | ||
476 | TRACE(ft_t_data_flow, "remaining in blocking buffer: %d", | ||
477 | zft_pos.seg_byte_pos); | ||
478 | TRACE(ft_t_data_flow, "just written bytes: %d", written); | ||
479 | last_write_failed = 0; | ||
480 | zft_resid -= written; | ||
481 | need_flush = need_flush || written > 0; | ||
482 | TRACE_EXIT written; /* bytes written */ | ||
483 | } | ||
diff --git a/drivers/char/ftape/zftape/zftape-write.h b/drivers/char/ftape/zftape/zftape-write.h new file mode 100644 index 000000000000..ea887015b493 --- /dev/null +++ b/drivers/char/ftape/zftape/zftape-write.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef _ZFTAPE_WRITE_H | ||
2 | #define _ZFTAPE_WRITE_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (C) 1996, 1997 Claus-Justus Heine | ||
6 | |||
7 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2, or (at your option) | ||
10 | any later version. | ||
11 | |||
12 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | ||
16 | |||
17 | You should have received a copy of the GNU General Public License | ||
18 | along with this program; see the file COPYING. If not, write to | ||
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | |||
21 | * | ||
22 | * $Source: /homes/cvs/ftape-stacked/ftape/zftape/zftape-write.h,v $ | ||
23 | * $Revision: 1.2 $ | ||
24 | * $Date: 1997/10/05 19:19:13 $ | ||
25 | * | ||
26 | * This file contains the definitions for the write functions | ||
27 | * for the zftape driver for Linux. | ||
28 | * | ||
29 | */ | ||
30 | |||
31 | extern int zft_flush_buffers(void); | ||
32 | extern int zft_update_header_segments(void); | ||
33 | extern void zft_prevent_flush(void); | ||
34 | |||
35 | /* hook for the VFS interface | ||
36 | */ | ||
37 | extern int _zft_write(const char __user *buff, int req_len); | ||
38 | #endif /* _ZFTAPE_WRITE_H */ | ||
diff --git a/drivers/char/ftape/zftape/zftape_syms.c b/drivers/char/ftape/zftape/zftape_syms.c new file mode 100644 index 000000000000..2db1401682df --- /dev/null +++ b/drivers/char/ftape/zftape/zftape_syms.c | |||
@@ -0,0 +1,43 @@ | |||
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/zftape/zftape_syms.c,v $ | ||
20 | * $Revision: 1.3 $ | ||
21 | * $Date: 1997/10/05 19:19:14 $ | ||
22 | * | ||
23 | * This file contains the symbols that the zftape frontend to | ||
24 | * the ftape floppy tape driver exports | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | |||
29 | #include <linux/zftape.h> | ||
30 | |||
31 | #include "../zftape/zftape-init.h" | ||
32 | #include "../zftape/zftape-read.h" | ||
33 | #include "../zftape/zftape-buffers.h" | ||
34 | #include "../zftape/zftape-ctl.h" | ||
35 | |||
36 | /* zftape-init.c */ | ||
37 | EXPORT_SYMBOL(zft_cmpr_register); | ||
38 | /* zftape-read.c */ | ||
39 | EXPORT_SYMBOL(zft_fetch_segment_fraction); | ||
40 | /* zftape-buffers.c */ | ||
41 | EXPORT_SYMBOL(zft_vmalloc_once); | ||
42 | EXPORT_SYMBOL(zft_vmalloc_always); | ||
43 | EXPORT_SYMBOL(zft_vfree); | ||