diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/block/amiflop.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/block/amiflop.c')
-rw-r--r-- | drivers/block/amiflop.c | 1850 |
1 files changed, 1850 insertions, 0 deletions
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c new file mode 100644 index 000000000000..1468e8cf712d --- /dev/null +++ b/drivers/block/amiflop.c | |||
@@ -0,0 +1,1850 @@ | |||
1 | /* | ||
2 | * linux/amiga/amiflop.c | ||
3 | * | ||
4 | * Copyright (C) 1993 Greg Harp | ||
5 | * Portions of this driver are based on code contributed by Brad Pepers | ||
6 | * | ||
7 | * revised 28.5.95 by Joerg Dorchain | ||
8 | * - now no bugs(?) any more for both HD & DD | ||
9 | * - added support for 40 Track 5.25" drives, 80-track hopefully behaves | ||
10 | * like 3.5" dd (no way to test - are there any 5.25" drives out there | ||
11 | * that work on an A4000?) | ||
12 | * - wrote formatting routine (maybe dirty, but works) | ||
13 | * | ||
14 | * june/july 1995 added ms-dos support by Joerg Dorchain | ||
15 | * (portions based on messydos.device and various contributors) | ||
16 | * - currently only 9 and 18 sector disks | ||
17 | * | ||
18 | * - fixed a bug with the internal trackbuffer when using multiple | ||
19 | * disks the same time | ||
20 | * - made formatting a bit safer | ||
21 | * - added command line and machine based default for "silent" df0 | ||
22 | * | ||
23 | * december 1995 adapted for 1.2.13pl4 by Joerg Dorchain | ||
24 | * - works but I think it's inefficient. (look in redo_fd_request) | ||
25 | * But the changes were very efficient. (only three and a half lines) | ||
26 | * | ||
27 | * january 1996 added special ioctl for tracking down read/write problems | ||
28 | * - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data | ||
29 | * is copied to area. (area should be large enough since no checking is | ||
30 | * done - 30K is currently sufficient). return the actual size of the | ||
31 | * trackbuffer | ||
32 | * - replaced udelays() by a timer (CIAA timer B) for the waits | ||
33 | * needed for the disk mechanic. | ||
34 | * | ||
35 | * february 1996 fixed error recovery and multiple disk access | ||
36 | * - both got broken the first time I tampered with the driver :-( | ||
37 | * - still not safe, but better than before | ||
38 | * | ||
39 | * revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel. | ||
40 | * - Minor changes to accept the kdev_t. | ||
41 | * - Replaced some more udelays with ms_delays. Udelay is just a loop, | ||
42 | * and so the delay will be different depending on the given | ||
43 | * processor :-( | ||
44 | * - The driver could use a major cleanup because of the new | ||
45 | * major/minor handling that came with kdev_t. It seems to work for | ||
46 | * the time being, but I can't guarantee that it will stay like | ||
47 | * that when we start using 16 (24?) bit minors. | ||
48 | * | ||
49 | * restructured jan 1997 by Joerg Dorchain | ||
50 | * - Fixed Bug accessing multiple disks | ||
51 | * - some code cleanup | ||
52 | * - added trackbuffer for each drive to speed things up | ||
53 | * - fixed some race conditions (who finds the next may send it to me ;-) | ||
54 | */ | ||
55 | |||
56 | #include <linux/module.h> | ||
57 | |||
58 | #include <linux/fd.h> | ||
59 | #include <linux/hdreg.h> | ||
60 | #include <linux/delay.h> | ||
61 | #include <linux/init.h> | ||
62 | #include <linux/amifdreg.h> | ||
63 | #include <linux/amifd.h> | ||
64 | #include <linux/buffer_head.h> | ||
65 | #include <linux/blkdev.h> | ||
66 | #include <linux/elevator.h> | ||
67 | |||
68 | #include <asm/setup.h> | ||
69 | #include <asm/uaccess.h> | ||
70 | #include <asm/amigahw.h> | ||
71 | #include <asm/amigaints.h> | ||
72 | #include <asm/irq.h> | ||
73 | |||
74 | #undef DEBUG /* print _LOTS_ of infos */ | ||
75 | |||
76 | #define RAW_IOCTL | ||
77 | #ifdef RAW_IOCTL | ||
78 | #define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */ | ||
79 | #endif | ||
80 | |||
81 | /* | ||
82 | * Defines | ||
83 | */ | ||
84 | |||
85 | /* | ||
86 | * Error codes | ||
87 | */ | ||
88 | #define FD_OK 0 /* operation succeeded */ | ||
89 | #define FD_ERROR -1 /* general error (seek, read, write, etc) */ | ||
90 | #define FD_NOUNIT 1 /* unit does not exist */ | ||
91 | #define FD_UNITBUSY 2 /* unit already active */ | ||
92 | #define FD_NOTACTIVE 3 /* unit is not active */ | ||
93 | #define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */ | ||
94 | |||
95 | #define MFM_NOSYNC 1 | ||
96 | #define MFM_HEADER 2 | ||
97 | #define MFM_DATA 3 | ||
98 | #define MFM_TRACK 4 | ||
99 | |||
100 | /* | ||
101 | * Floppy ID values | ||
102 | */ | ||
103 | #define FD_NODRIVE 0x00000000 /* response when no unit is present */ | ||
104 | #define FD_DD_3 0xffffffff /* double-density 3.5" (880K) drive */ | ||
105 | #define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */ | ||
106 | #define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */ | ||
107 | |||
108 | static unsigned long int fd_def_df0 = FD_DD_3; /* default for df0 if it doesn't identify */ | ||
109 | |||
110 | module_param(fd_def_df0, ulong, 0); | ||
111 | MODULE_LICENSE("GPL"); | ||
112 | |||
113 | static struct request_queue *floppy_queue; | ||
114 | #define QUEUE (floppy_queue) | ||
115 | #define CURRENT elv_next_request(floppy_queue) | ||
116 | |||
117 | /* | ||
118 | * Macros | ||
119 | */ | ||
120 | #define MOTOR_ON (ciab.prb &= ~DSKMOTOR) | ||
121 | #define MOTOR_OFF (ciab.prb |= DSKMOTOR) | ||
122 | #define SELECT(mask) (ciab.prb &= ~mask) | ||
123 | #define DESELECT(mask) (ciab.prb |= mask) | ||
124 | #define SELMASK(drive) (1 << (3 + (drive & 3))) | ||
125 | |||
126 | static struct fd_drive_type drive_types[] = { | ||
127 | /* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/ | ||
128 | /* warning: times are now in milliseconds (ms) */ | ||
129 | { FD_DD_3, "DD 3.5", 80, 2, 14716, 13630, 1, 80,161, 3, 18, 1}, | ||
130 | { FD_HD_3, "HD 3.5", 80, 2, 28344, 27258, 2, 80,161, 3, 18, 1}, | ||
131 | { FD_DD_5, "DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2}, | ||
132 | { FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} | ||
133 | }; | ||
134 | static int num_dr_types = sizeof(drive_types) / sizeof(drive_types[0]); | ||
135 | |||
136 | static int amiga_read(int), dos_read(int); | ||
137 | static void amiga_write(int), dos_write(int); | ||
138 | static struct fd_data_type data_types[] = { | ||
139 | { "Amiga", 11 , amiga_read, amiga_write}, | ||
140 | { "MS-Dos", 9, dos_read, dos_write} | ||
141 | }; | ||
142 | |||
143 | /* current info on each unit */ | ||
144 | static struct amiga_floppy_struct unit[FD_MAX_UNITS]; | ||
145 | |||
146 | static struct timer_list flush_track_timer[FD_MAX_UNITS]; | ||
147 | static struct timer_list post_write_timer; | ||
148 | static struct timer_list motor_on_timer; | ||
149 | static struct timer_list motor_off_timer[FD_MAX_UNITS]; | ||
150 | static int on_attempts; | ||
151 | |||
152 | /* Synchronization of FDC access */ | ||
153 | /* request loop (trackbuffer) */ | ||
154 | static volatile int fdc_busy = -1; | ||
155 | static volatile int fdc_nested; | ||
156 | static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); | ||
157 | |||
158 | static DECLARE_WAIT_QUEUE_HEAD(motor_wait); | ||
159 | |||
160 | static volatile int selected = -1; /* currently selected drive */ | ||
161 | |||
162 | static int writepending; | ||
163 | static int writefromint; | ||
164 | static char *raw_buf; | ||
165 | |||
166 | static DEFINE_SPINLOCK(amiflop_lock); | ||
167 | |||
168 | #define RAW_BUF_SIZE 30000 /* size of raw disk data */ | ||
169 | |||
170 | /* | ||
171 | * These are global variables, as that's the easiest way to give | ||
172 | * information to interrupts. They are the data used for the current | ||
173 | * request. | ||
174 | */ | ||
175 | static volatile char block_flag; | ||
176 | static DECLARE_WAIT_QUEUE_HEAD(wait_fd_block); | ||
177 | |||
178 | /* MS-Dos MFM Coding tables (should go quick and easy) */ | ||
179 | static unsigned char mfmencode[16]={ | ||
180 | 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, | ||
181 | 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55 | ||
182 | }; | ||
183 | static unsigned char mfmdecode[128]; | ||
184 | |||
185 | /* floppy internal millisecond timer stuff */ | ||
186 | static volatile int ms_busy = -1; | ||
187 | static DECLARE_WAIT_QUEUE_HEAD(ms_wait); | ||
188 | #define MS_TICKS ((amiga_eclock+50)/1000) | ||
189 | |||
190 | /* | ||
191 | * Note that MAX_ERRORS=X doesn't imply that we retry every bad read | ||
192 | * max X times - some types of errors increase the errorcount by 2 or | ||
193 | * even 3, so we might actually retry only X/2 times before giving up. | ||
194 | */ | ||
195 | #define MAX_ERRORS 12 | ||
196 | |||
197 | /* Prevent "aliased" accesses. */ | ||
198 | static int fd_ref[4] = { 0,0,0,0 }; | ||
199 | static int fd_device[4] = { 0, 0, 0, 0 }; | ||
200 | |||
201 | /* | ||
202 | * Here come the actual hardware access and helper functions. | ||
203 | * They are not reentrant and single threaded because all drives | ||
204 | * share the same hardware and the same trackbuffer. | ||
205 | */ | ||
206 | |||
207 | /* Milliseconds timer */ | ||
208 | |||
209 | static irqreturn_t ms_isr(int irq, void *dummy, struct pt_regs *fp) | ||
210 | { | ||
211 | ms_busy = -1; | ||
212 | wake_up(&ms_wait); | ||
213 | return IRQ_HANDLED; | ||
214 | } | ||
215 | |||
216 | /* all waits are queued up | ||
217 | A more generic routine would do a schedule a la timer.device */ | ||
218 | static void ms_delay(int ms) | ||
219 | { | ||
220 | unsigned long flags; | ||
221 | int ticks; | ||
222 | if (ms > 0) { | ||
223 | local_irq_save(flags); | ||
224 | while (ms_busy == 0) | ||
225 | sleep_on(&ms_wait); | ||
226 | ms_busy = 0; | ||
227 | local_irq_restore(flags); | ||
228 | ticks = MS_TICKS*ms-1; | ||
229 | ciaa.tblo=ticks%256; | ||
230 | ciaa.tbhi=ticks/256; | ||
231 | ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */ | ||
232 | sleep_on(&ms_wait); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /* Hardware semaphore */ | ||
237 | |||
238 | /* returns true when we would get the semaphore */ | ||
239 | static inline int try_fdc(int drive) | ||
240 | { | ||
241 | drive &= 3; | ||
242 | return ((fdc_busy < 0) || (fdc_busy == drive)); | ||
243 | } | ||
244 | |||
245 | static void get_fdc(int drive) | ||
246 | { | ||
247 | unsigned long flags; | ||
248 | |||
249 | drive &= 3; | ||
250 | #ifdef DEBUG | ||
251 | printk("get_fdc: drive %d fdc_busy %d fdc_nested %d\n",drive,fdc_busy,fdc_nested); | ||
252 | #endif | ||
253 | local_irq_save(flags); | ||
254 | while (!try_fdc(drive)) | ||
255 | sleep_on(&fdc_wait); | ||
256 | fdc_busy = drive; | ||
257 | fdc_nested++; | ||
258 | local_irq_restore(flags); | ||
259 | } | ||
260 | |||
261 | static inline void rel_fdc(void) | ||
262 | { | ||
263 | #ifdef DEBUG | ||
264 | if (fdc_nested == 0) | ||
265 | printk("fd: unmatched rel_fdc\n"); | ||
266 | printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested); | ||
267 | #endif | ||
268 | fdc_nested--; | ||
269 | if (fdc_nested == 0) { | ||
270 | fdc_busy = -1; | ||
271 | wake_up(&fdc_wait); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | static void fd_select (int drive) | ||
276 | { | ||
277 | unsigned char prb = ~0; | ||
278 | |||
279 | drive&=3; | ||
280 | #ifdef DEBUG | ||
281 | printk("selecting %d\n",drive); | ||
282 | #endif | ||
283 | if (drive == selected) | ||
284 | return; | ||
285 | get_fdc(drive); | ||
286 | selected = drive; | ||
287 | |||
288 | if (unit[drive].track % 2 != 0) | ||
289 | prb &= ~DSKSIDE; | ||
290 | if (unit[drive].motor == 1) | ||
291 | prb &= ~DSKMOTOR; | ||
292 | ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); | ||
293 | ciab.prb = prb; | ||
294 | prb &= ~SELMASK(drive); | ||
295 | ciab.prb = prb; | ||
296 | rel_fdc(); | ||
297 | } | ||
298 | |||
299 | static void fd_deselect (int drive) | ||
300 | { | ||
301 | unsigned char prb; | ||
302 | unsigned long flags; | ||
303 | |||
304 | drive&=3; | ||
305 | #ifdef DEBUG | ||
306 | printk("deselecting %d\n",drive); | ||
307 | #endif | ||
308 | if (drive != selected) { | ||
309 | printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected); | ||
310 | return; | ||
311 | } | ||
312 | |||
313 | get_fdc(drive); | ||
314 | local_irq_save(flags); | ||
315 | |||
316 | selected = -1; | ||
317 | |||
318 | prb = ciab.prb; | ||
319 | prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); | ||
320 | ciab.prb = prb; | ||
321 | |||
322 | local_irq_restore (flags); | ||
323 | rel_fdc(); | ||
324 | |||
325 | } | ||
326 | |||
327 | static void motor_on_callback(unsigned long nr) | ||
328 | { | ||
329 | if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) { | ||
330 | wake_up (&motor_wait); | ||
331 | } else { | ||
332 | motor_on_timer.expires = jiffies + HZ/10; | ||
333 | add_timer(&motor_on_timer); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | static int fd_motor_on(int nr) | ||
338 | { | ||
339 | nr &= 3; | ||
340 | |||
341 | del_timer(motor_off_timer + nr); | ||
342 | |||
343 | if (!unit[nr].motor) { | ||
344 | unit[nr].motor = 1; | ||
345 | fd_select(nr); | ||
346 | |||
347 | motor_on_timer.data = nr; | ||
348 | mod_timer(&motor_on_timer, jiffies + HZ/2); | ||
349 | |||
350 | on_attempts = 10; | ||
351 | sleep_on (&motor_wait); | ||
352 | fd_deselect(nr); | ||
353 | } | ||
354 | |||
355 | if (on_attempts == 0) { | ||
356 | on_attempts = -1; | ||
357 | #if 0 | ||
358 | printk (KERN_ERR "motor_on failed, turning motor off\n"); | ||
359 | fd_motor_off (nr); | ||
360 | return 0; | ||
361 | #else | ||
362 | printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n"); | ||
363 | #endif | ||
364 | } | ||
365 | |||
366 | return 1; | ||
367 | } | ||
368 | |||
369 | static void fd_motor_off(unsigned long drive) | ||
370 | { | ||
371 | long calledfromint; | ||
372 | #ifdef MODULE | ||
373 | long decusecount; | ||
374 | |||
375 | decusecount = drive & 0x40000000; | ||
376 | #endif | ||
377 | calledfromint = drive & 0x80000000; | ||
378 | drive&=3; | ||
379 | if (calledfromint && !try_fdc(drive)) { | ||
380 | /* We would be blocked in an interrupt, so try again later */ | ||
381 | motor_off_timer[drive].expires = jiffies + 1; | ||
382 | add_timer(motor_off_timer + drive); | ||
383 | return; | ||
384 | } | ||
385 | unit[drive].motor = 0; | ||
386 | fd_select(drive); | ||
387 | udelay (1); | ||
388 | fd_deselect(drive); | ||
389 | } | ||
390 | |||
391 | static void floppy_off (unsigned int nr) | ||
392 | { | ||
393 | int drive; | ||
394 | |||
395 | drive = nr & 3; | ||
396 | /* called this way it is always from interrupt */ | ||
397 | motor_off_timer[drive].data = nr | 0x80000000; | ||
398 | mod_timer(motor_off_timer + drive, jiffies + 3*HZ); | ||
399 | } | ||
400 | |||
401 | static int fd_calibrate(int drive) | ||
402 | { | ||
403 | unsigned char prb; | ||
404 | int n; | ||
405 | |||
406 | drive &= 3; | ||
407 | get_fdc(drive); | ||
408 | if (!fd_motor_on (drive)) | ||
409 | return 0; | ||
410 | fd_select (drive); | ||
411 | prb = ciab.prb; | ||
412 | prb |= DSKSIDE; | ||
413 | prb &= ~DSKDIREC; | ||
414 | ciab.prb = prb; | ||
415 | for (n = unit[drive].type->tracks/2; n != 0; --n) { | ||
416 | if (ciaa.pra & DSKTRACK0) | ||
417 | break; | ||
418 | prb &= ~DSKSTEP; | ||
419 | ciab.prb = prb; | ||
420 | prb |= DSKSTEP; | ||
421 | udelay (2); | ||
422 | ciab.prb = prb; | ||
423 | ms_delay(unit[drive].type->step_delay); | ||
424 | } | ||
425 | ms_delay (unit[drive].type->settle_time); | ||
426 | prb |= DSKDIREC; | ||
427 | n = unit[drive].type->tracks + 20; | ||
428 | for (;;) { | ||
429 | prb &= ~DSKSTEP; | ||
430 | ciab.prb = prb; | ||
431 | prb |= DSKSTEP; | ||
432 | udelay (2); | ||
433 | ciab.prb = prb; | ||
434 | ms_delay(unit[drive].type->step_delay + 1); | ||
435 | if ((ciaa.pra & DSKTRACK0) == 0) | ||
436 | break; | ||
437 | if (--n == 0) { | ||
438 | printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive); | ||
439 | fd_motor_off (drive); | ||
440 | unit[drive].track = -1; | ||
441 | rel_fdc(); | ||
442 | return 0; | ||
443 | } | ||
444 | } | ||
445 | unit[drive].track = 0; | ||
446 | ms_delay(unit[drive].type->settle_time); | ||
447 | |||
448 | rel_fdc(); | ||
449 | fd_deselect(drive); | ||
450 | return 1; | ||
451 | } | ||
452 | |||
453 | static int fd_seek(int drive, int track) | ||
454 | { | ||
455 | unsigned char prb; | ||
456 | int cnt; | ||
457 | |||
458 | #ifdef DEBUG | ||
459 | printk("seeking drive %d to track %d\n",drive,track); | ||
460 | #endif | ||
461 | drive &= 3; | ||
462 | get_fdc(drive); | ||
463 | if (unit[drive].track == track) { | ||
464 | rel_fdc(); | ||
465 | return 1; | ||
466 | } | ||
467 | if (!fd_motor_on(drive)) { | ||
468 | rel_fdc(); | ||
469 | return 0; | ||
470 | } | ||
471 | if (unit[drive].track < 0 && !fd_calibrate(drive)) { | ||
472 | rel_fdc(); | ||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | fd_select (drive); | ||
477 | cnt = unit[drive].track/2 - track/2; | ||
478 | prb = ciab.prb; | ||
479 | prb |= DSKSIDE | DSKDIREC; | ||
480 | if (track % 2 != 0) | ||
481 | prb &= ~DSKSIDE; | ||
482 | if (cnt < 0) { | ||
483 | cnt = - cnt; | ||
484 | prb &= ~DSKDIREC; | ||
485 | } | ||
486 | ciab.prb = prb; | ||
487 | if (track % 2 != unit[drive].track % 2) | ||
488 | ms_delay (unit[drive].type->side_time); | ||
489 | unit[drive].track = track; | ||
490 | if (cnt == 0) { | ||
491 | rel_fdc(); | ||
492 | fd_deselect(drive); | ||
493 | return 1; | ||
494 | } | ||
495 | do { | ||
496 | prb &= ~DSKSTEP; | ||
497 | ciab.prb = prb; | ||
498 | prb |= DSKSTEP; | ||
499 | udelay (1); | ||
500 | ciab.prb = prb; | ||
501 | ms_delay (unit[drive].type->step_delay); | ||
502 | } while (--cnt != 0); | ||
503 | ms_delay (unit[drive].type->settle_time); | ||
504 | |||
505 | rel_fdc(); | ||
506 | fd_deselect(drive); | ||
507 | return 1; | ||
508 | } | ||
509 | |||
510 | static unsigned long fd_get_drive_id(int drive) | ||
511 | { | ||
512 | int i; | ||
513 | ulong id = 0; | ||
514 | |||
515 | drive&=3; | ||
516 | get_fdc(drive); | ||
517 | /* set up for ID */ | ||
518 | MOTOR_ON; | ||
519 | udelay(2); | ||
520 | SELECT(SELMASK(drive)); | ||
521 | udelay(2); | ||
522 | DESELECT(SELMASK(drive)); | ||
523 | udelay(2); | ||
524 | MOTOR_OFF; | ||
525 | udelay(2); | ||
526 | SELECT(SELMASK(drive)); | ||
527 | udelay(2); | ||
528 | DESELECT(SELMASK(drive)); | ||
529 | udelay(2); | ||
530 | |||
531 | /* loop and read disk ID */ | ||
532 | for (i=0; i<32; i++) { | ||
533 | SELECT(SELMASK(drive)); | ||
534 | udelay(2); | ||
535 | |||
536 | /* read and store value of DSKRDY */ | ||
537 | id <<= 1; | ||
538 | id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */ | ||
539 | |||
540 | DESELECT(SELMASK(drive)); | ||
541 | } | ||
542 | |||
543 | rel_fdc(); | ||
544 | |||
545 | /* | ||
546 | * RB: At least A500/A2000's df0: don't identify themselves. | ||
547 | * As every (real) Amiga has at least a 3.5" DD drive as df0: | ||
548 | * we default to that if df0: doesn't identify as a certain | ||
549 | * type. | ||
550 | */ | ||
551 | if(drive == 0 && id == FD_NODRIVE) | ||
552 | { | ||
553 | id = fd_def_df0; | ||
554 | printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0); | ||
555 | } | ||
556 | /* return the ID value */ | ||
557 | return (id); | ||
558 | } | ||
559 | |||
560 | static irqreturn_t fd_block_done(int irq, void *dummy, struct pt_regs *fp) | ||
561 | { | ||
562 | if (block_flag) | ||
563 | custom.dsklen = 0x4000; | ||
564 | |||
565 | if (block_flag == 2) { /* writing */ | ||
566 | writepending = 2; | ||
567 | post_write_timer.expires = jiffies + 1; /* at least 2 ms */ | ||
568 | post_write_timer.data = selected; | ||
569 | add_timer(&post_write_timer); | ||
570 | } | ||
571 | else { /* reading */ | ||
572 | block_flag = 0; | ||
573 | wake_up (&wait_fd_block); | ||
574 | } | ||
575 | return IRQ_HANDLED; | ||
576 | } | ||
577 | |||
578 | static void raw_read(int drive) | ||
579 | { | ||
580 | drive&=3; | ||
581 | get_fdc(drive); | ||
582 | while (block_flag) | ||
583 | sleep_on(&wait_fd_block); | ||
584 | fd_select(drive); | ||
585 | /* setup adkcon bits correctly */ | ||
586 | custom.adkcon = ADK_MSBSYNC; | ||
587 | custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST; | ||
588 | |||
589 | custom.dsksync = MFM_SYNC; | ||
590 | |||
591 | custom.dsklen = 0; | ||
592 | custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); | ||
593 | custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; | ||
594 | custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; | ||
595 | |||
596 | block_flag = 1; | ||
597 | |||
598 | while (block_flag) | ||
599 | sleep_on (&wait_fd_block); | ||
600 | |||
601 | custom.dsklen = 0; | ||
602 | fd_deselect(drive); | ||
603 | rel_fdc(); | ||
604 | } | ||
605 | |||
606 | static int raw_write(int drive) | ||
607 | { | ||
608 | ushort adk; | ||
609 | |||
610 | drive&=3; | ||
611 | get_fdc(drive); /* corresponds to rel_fdc() in post_write() */ | ||
612 | if ((ciaa.pra & DSKPROT) == 0) { | ||
613 | rel_fdc(); | ||
614 | return 0; | ||
615 | } | ||
616 | while (block_flag) | ||
617 | sleep_on(&wait_fd_block); | ||
618 | fd_select(drive); | ||
619 | /* clear adkcon bits */ | ||
620 | custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC; | ||
621 | /* set appropriate adkcon bits */ | ||
622 | adk = ADK_SETCLR|ADK_FAST; | ||
623 | if ((ulong)unit[drive].track >= unit[drive].type->precomp2) | ||
624 | adk |= ADK_PRECOMP1; | ||
625 | else if ((ulong)unit[drive].track >= unit[drive].type->precomp1) | ||
626 | adk |= ADK_PRECOMP0; | ||
627 | custom.adkcon = adk; | ||
628 | |||
629 | custom.dsklen = DSKLEN_WRITE; | ||
630 | custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); | ||
631 | custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; | ||
632 | custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; | ||
633 | |||
634 | block_flag = 2; | ||
635 | return 1; | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * to be called at least 2ms after the write has finished but before any | ||
640 | * other access to the hardware. | ||
641 | */ | ||
642 | static void post_write (unsigned long drive) | ||
643 | { | ||
644 | #ifdef DEBUG | ||
645 | printk("post_write for drive %ld\n",drive); | ||
646 | #endif | ||
647 | drive &= 3; | ||
648 | custom.dsklen = 0; | ||
649 | block_flag = 0; | ||
650 | writepending = 0; | ||
651 | writefromint = 0; | ||
652 | unit[drive].dirty = 0; | ||
653 | wake_up(&wait_fd_block); | ||
654 | fd_deselect(drive); | ||
655 | rel_fdc(); /* corresponds to get_fdc() in raw_write */ | ||
656 | } | ||
657 | |||
658 | |||
659 | /* | ||
660 | * The following functions are to convert the block contents into raw data | ||
661 | * written to disk and vice versa. | ||
662 | * (Add other formats here ;-)) | ||
663 | */ | ||
664 | |||
665 | static unsigned long scan_sync(unsigned long raw, unsigned long end) | ||
666 | { | ||
667 | ushort *ptr = (ushort *)raw, *endp = (ushort *)end; | ||
668 | |||
669 | while (ptr < endp && *ptr++ != 0x4489) | ||
670 | ; | ||
671 | if (ptr < endp) { | ||
672 | while (*ptr == 0x4489 && ptr < endp) | ||
673 | ptr++; | ||
674 | return (ulong)ptr; | ||
675 | } | ||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static inline unsigned long checksum(unsigned long *addr, int len) | ||
680 | { | ||
681 | unsigned long csum = 0; | ||
682 | |||
683 | len /= sizeof(*addr); | ||
684 | while (len-- > 0) | ||
685 | csum ^= *addr++; | ||
686 | csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555); | ||
687 | |||
688 | return csum; | ||
689 | } | ||
690 | |||
691 | static unsigned long decode (unsigned long *data, unsigned long *raw, | ||
692 | int len) | ||
693 | { | ||
694 | ulong *odd, *even; | ||
695 | |||
696 | /* convert length from bytes to longwords */ | ||
697 | len >>= 2; | ||
698 | odd = raw; | ||
699 | even = odd + len; | ||
700 | |||
701 | /* prepare return pointer */ | ||
702 | raw += len * 2; | ||
703 | |||
704 | do { | ||
705 | *data++ = ((*odd++ & 0x55555555) << 1) | (*even++ & 0x55555555); | ||
706 | } while (--len != 0); | ||
707 | |||
708 | return (ulong)raw; | ||
709 | } | ||
710 | |||
711 | struct header { | ||
712 | unsigned char magic; | ||
713 | unsigned char track; | ||
714 | unsigned char sect; | ||
715 | unsigned char ord; | ||
716 | unsigned char labels[16]; | ||
717 | unsigned long hdrchk; | ||
718 | unsigned long datachk; | ||
719 | }; | ||
720 | |||
721 | static int amiga_read(int drive) | ||
722 | { | ||
723 | unsigned long raw; | ||
724 | unsigned long end; | ||
725 | int scnt; | ||
726 | unsigned long csum; | ||
727 | struct header hdr; | ||
728 | |||
729 | drive&=3; | ||
730 | raw = (long) raw_buf; | ||
731 | end = raw + unit[drive].type->read_size; | ||
732 | |||
733 | for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { | ||
734 | if (!(raw = scan_sync(raw, end))) { | ||
735 | printk (KERN_INFO "can't find sync for sector %d\n", scnt); | ||
736 | return MFM_NOSYNC; | ||
737 | } | ||
738 | |||
739 | raw = decode ((ulong *)&hdr.magic, (ulong *)raw, 4); | ||
740 | raw = decode ((ulong *)&hdr.labels, (ulong *)raw, 16); | ||
741 | raw = decode ((ulong *)&hdr.hdrchk, (ulong *)raw, 4); | ||
742 | raw = decode ((ulong *)&hdr.datachk, (ulong *)raw, 4); | ||
743 | csum = checksum((ulong *)&hdr, | ||
744 | (char *)&hdr.hdrchk-(char *)&hdr); | ||
745 | |||
746 | #ifdef DEBUG | ||
747 | printk ("(%x,%d,%d,%d) (%lx,%lx,%lx,%lx) %lx %lx\n", | ||
748 | hdr.magic, hdr.track, hdr.sect, hdr.ord, | ||
749 | *(ulong *)&hdr.labels[0], *(ulong *)&hdr.labels[4], | ||
750 | *(ulong *)&hdr.labels[8], *(ulong *)&hdr.labels[12], | ||
751 | hdr.hdrchk, hdr.datachk); | ||
752 | #endif | ||
753 | |||
754 | if (hdr.hdrchk != csum) { | ||
755 | printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum); | ||
756 | return MFM_HEADER; | ||
757 | } | ||
758 | |||
759 | /* verify track */ | ||
760 | if (hdr.track != unit[drive].track) { | ||
761 | printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track); | ||
762 | return MFM_TRACK; | ||
763 | } | ||
764 | |||
765 | raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512), | ||
766 | (ulong *)raw, 512); | ||
767 | csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512); | ||
768 | |||
769 | if (hdr.datachk != csum) { | ||
770 | printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n", | ||
771 | hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt, | ||
772 | hdr.datachk, csum); | ||
773 | printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n", | ||
774 | ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0], | ||
775 | ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1], | ||
776 | ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2], | ||
777 | ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]); | ||
778 | return MFM_DATA; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | return 0; | ||
783 | } | ||
784 | |||
785 | static void encode(unsigned long data, unsigned long *dest) | ||
786 | { | ||
787 | unsigned long data2; | ||
788 | |||
789 | data &= 0x55555555; | ||
790 | data2 = data ^ 0x55555555; | ||
791 | data |= ((data2 >> 1) | 0x80000000) & (data2 << 1); | ||
792 | |||
793 | if (*(dest - 1) & 0x00000001) | ||
794 | data &= 0x7FFFFFFF; | ||
795 | |||
796 | *dest = data; | ||
797 | } | ||
798 | |||
799 | static void encode_block(unsigned long *dest, unsigned long *src, int len) | ||
800 | { | ||
801 | int cnt, to_cnt = 0; | ||
802 | unsigned long data; | ||
803 | |||
804 | /* odd bits */ | ||
805 | for (cnt = 0; cnt < len / 4; cnt++) { | ||
806 | data = src[cnt] >> 1; | ||
807 | encode(data, dest + to_cnt++); | ||
808 | } | ||
809 | |||
810 | /* even bits */ | ||
811 | for (cnt = 0; cnt < len / 4; cnt++) { | ||
812 | data = src[cnt]; | ||
813 | encode(data, dest + to_cnt++); | ||
814 | } | ||
815 | } | ||
816 | |||
817 | static unsigned long *putsec(int disk, unsigned long *raw, int cnt) | ||
818 | { | ||
819 | struct header hdr; | ||
820 | int i; | ||
821 | |||
822 | disk&=3; | ||
823 | *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA; | ||
824 | raw++; | ||
825 | *raw++ = 0x44894489; | ||
826 | |||
827 | hdr.magic = 0xFF; | ||
828 | hdr.track = unit[disk].track; | ||
829 | hdr.sect = cnt; | ||
830 | hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt; | ||
831 | for (i = 0; i < 16; i++) | ||
832 | hdr.labels[i] = 0; | ||
833 | hdr.hdrchk = checksum((ulong *)&hdr, | ||
834 | (char *)&hdr.hdrchk-(char *)&hdr); | ||
835 | hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512); | ||
836 | |||
837 | encode_block(raw, (ulong *)&hdr.magic, 4); | ||
838 | raw += 2; | ||
839 | encode_block(raw, (ulong *)&hdr.labels, 16); | ||
840 | raw += 8; | ||
841 | encode_block(raw, (ulong *)&hdr.hdrchk, 4); | ||
842 | raw += 2; | ||
843 | encode_block(raw, (ulong *)&hdr.datachk, 4); | ||
844 | raw += 2; | ||
845 | encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512); | ||
846 | raw += 256; | ||
847 | |||
848 | return raw; | ||
849 | } | ||
850 | |||
851 | static void amiga_write(int disk) | ||
852 | { | ||
853 | unsigned int cnt; | ||
854 | unsigned long *ptr = (unsigned long *)raw_buf; | ||
855 | |||
856 | disk&=3; | ||
857 | /* gap space */ | ||
858 | for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++) | ||
859 | *ptr++ = 0xaaaaaaaa; | ||
860 | |||
861 | /* sectors */ | ||
862 | for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) | ||
863 | ptr = putsec (disk, ptr, cnt); | ||
864 | *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8; | ||
865 | } | ||
866 | |||
867 | |||
868 | struct dos_header { | ||
869 | unsigned char track, /* 0-80 */ | ||
870 | side, /* 0-1 */ | ||
871 | sec, /* 0-...*/ | ||
872 | len_desc;/* 2 */ | ||
873 | unsigned short crc; /* on 68000 we got an alignment problem, | ||
874 | but this compiler solves it by adding silently | ||
875 | adding a pad byte so data won't fit | ||
876 | and this took about 3h to discover.... */ | ||
877 | unsigned char gap1[22]; /* for longword-alignedness (0x4e) */ | ||
878 | }; | ||
879 | |||
880 | /* crc routines are borrowed from the messydos-handler */ | ||
881 | |||
882 | /* excerpt from the messydos-device | ||
883 | ; The CRC is computed not only over the actual data, but including | ||
884 | ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb). | ||
885 | ; As we don't read or encode these fields into our buffers, we have to | ||
886 | ; preload the registers containing the CRC with the values they would have | ||
887 | ; after stepping over these fields. | ||
888 | ; | ||
889 | ; How CRCs "really" work: | ||
890 | ; | ||
891 | ; First, you should regard a bitstring as a series of coefficients of | ||
892 | ; polynomials. We calculate with these polynomials in modulo-2 | ||
893 | ; arithmetic, in which both add and subtract are done the same as | ||
894 | ; exclusive-or. Now, we modify our data (a very long polynomial) in | ||
895 | ; such a way that it becomes divisible by the CCITT-standard 16-bit | ||
896 | ; 16 12 5 | ||
897 | ; polynomial: x + x + x + 1, represented by $11021. The easiest | ||
898 | ; way to do this would be to multiply (using proper arithmetic) our | ||
899 | ; datablock with $11021. So we have: | ||
900 | ; data * $11021 = | ||
901 | ; data * ($10000 + $1021) = | ||
902 | ; data * $10000 + data * $1021 | ||
903 | ; The left part of this is simple: Just add two 0 bytes. But then | ||
904 | ; the right part (data $1021) remains difficult and even could have | ||
905 | ; a carry into the left part. The solution is to use a modified | ||
906 | ; multiplication, which has a result that is not correct, but with | ||
907 | ; a difference of any multiple of $11021. We then only need to keep | ||
908 | ; the 16 least significant bits of the result. | ||
909 | ; | ||
910 | ; The following algorithm does this for us: | ||
911 | ; | ||
912 | ; unsigned char *data, c, crclo, crchi; | ||
913 | ; while (not done) { | ||
914 | ; c = *data++ + crchi; | ||
915 | ; crchi = (@ c) >> 8 + crclo; | ||
916 | ; crclo = @ c; | ||
917 | ; } | ||
918 | ; | ||
919 | ; Remember, + is done with EOR, the @ operator is in two tables (high | ||
920 | ; and low byte separately), which is calculated as | ||
921 | ; | ||
922 | ; $1021 * (c & $F0) | ||
923 | ; xor $1021 * (c & $0F) | ||
924 | ; xor $1021 * (c >> 4) (* is regular multiplication) | ||
925 | ; | ||
926 | ; | ||
927 | ; Anyway, the end result is the same as the remainder of the division of | ||
928 | ; the data by $11021. I am afraid I need to study theory a bit more... | ||
929 | |||
930 | |||
931 | my only works was to code this from manx to C.... | ||
932 | |||
933 | */ | ||
934 | |||
935 | static ushort dos_crc(void * data_a3, int data_d0, int data_d1, int data_d3) | ||
936 | { | ||
937 | static unsigned char CRCTable1[] = { | ||
938 | 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xa1,0xb1,0xc1,0xd1,0xe1,0xf1, | ||
939 | 0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xb3,0xa3,0xd3,0xc3,0xf3,0xe3, | ||
940 | 0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xa5,0xb5,0x85,0x95,0xe5,0xf5,0xc5,0xd5, | ||
941 | 0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xb7,0xa7,0x97,0x87,0xf7,0xe7,0xd7,0xc7, | ||
942 | 0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xc9,0xd9,0xe9,0xf9,0x89,0x99,0xa9,0xb9, | ||
943 | 0x5a,0x4a,0x7a,0x6a,0x1a,0x0a,0x3a,0x2a,0xdb,0xcb,0xfb,0xeb,0x9b,0x8b,0xbb,0xab, | ||
944 | 0x6c,0x7c,0x4c,0x5c,0x2c,0x3c,0x0c,0x1c,0xed,0xfd,0xcd,0xdd,0xad,0xbd,0x8d,0x9d, | ||
945 | 0x7e,0x6e,0x5e,0x4e,0x3e,0x2e,0x1e,0x0e,0xff,0xef,0xdf,0xcf,0xbf,0xaf,0x9f,0x8f, | ||
946 | 0x91,0x81,0xb1,0xa1,0xd1,0xc1,0xf1,0xe1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60, | ||
947 | 0x83,0x93,0xa3,0xb3,0xc3,0xd3,0xe3,0xf3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72, | ||
948 | 0xb5,0xa5,0x95,0x85,0xf5,0xe5,0xd5,0xc5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44, | ||
949 | 0xa7,0xb7,0x87,0x97,0xe7,0xf7,0xc7,0xd7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56, | ||
950 | 0xd9,0xc9,0xf9,0xe9,0x99,0x89,0xb9,0xa9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28, | ||
951 | 0xcb,0xdb,0xeb,0xfb,0x8b,0x9b,0xab,0xbb,0x4a,0x5a,0x6a,0x7a,0x0a,0x1a,0x2a,0x3a, | ||
952 | 0xfd,0xed,0xdd,0xcd,0xbd,0xad,0x9d,0x8d,0x7c,0x6c,0x5c,0x4c,0x3c,0x2c,0x1c,0x0c, | ||
953 | 0xef,0xff,0xcf,0xdf,0xaf,0xbf,0x8f,0x9f,0x6e,0x7e,0x4e,0x5e,0x2e,0x3e,0x0e,0x1e | ||
954 | }; | ||
955 | |||
956 | static unsigned char CRCTable2[] = { | ||
957 | 0x00,0x21,0x42,0x63,0x84,0xa5,0xc6,0xe7,0x08,0x29,0x4a,0x6b,0x8c,0xad,0xce,0xef, | ||
958 | 0x31,0x10,0x73,0x52,0xb5,0x94,0xf7,0xd6,0x39,0x18,0x7b,0x5a,0xbd,0x9c,0xff,0xde, | ||
959 | 0x62,0x43,0x20,0x01,0xe6,0xc7,0xa4,0x85,0x6a,0x4b,0x28,0x09,0xee,0xcf,0xac,0x8d, | ||
960 | 0x53,0x72,0x11,0x30,0xd7,0xf6,0x95,0xb4,0x5b,0x7a,0x19,0x38,0xdf,0xfe,0x9d,0xbc, | ||
961 | 0xc4,0xe5,0x86,0xa7,0x40,0x61,0x02,0x23,0xcc,0xed,0x8e,0xaf,0x48,0x69,0x0a,0x2b, | ||
962 | 0xf5,0xd4,0xb7,0x96,0x71,0x50,0x33,0x12,0xfd,0xdc,0xbf,0x9e,0x79,0x58,0x3b,0x1a, | ||
963 | 0xa6,0x87,0xe4,0xc5,0x22,0x03,0x60,0x41,0xae,0x8f,0xec,0xcd,0x2a,0x0b,0x68,0x49, | ||
964 | 0x97,0xb6,0xd5,0xf4,0x13,0x32,0x51,0x70,0x9f,0xbe,0xdd,0xfc,0x1b,0x3a,0x59,0x78, | ||
965 | 0x88,0xa9,0xca,0xeb,0x0c,0x2d,0x4e,0x6f,0x80,0xa1,0xc2,0xe3,0x04,0x25,0x46,0x67, | ||
966 | 0xb9,0x98,0xfb,0xda,0x3d,0x1c,0x7f,0x5e,0xb1,0x90,0xf3,0xd2,0x35,0x14,0x77,0x56, | ||
967 | 0xea,0xcb,0xa8,0x89,0x6e,0x4f,0x2c,0x0d,0xe2,0xc3,0xa0,0x81,0x66,0x47,0x24,0x05, | ||
968 | 0xdb,0xfa,0x99,0xb8,0x5f,0x7e,0x1d,0x3c,0xd3,0xf2,0x91,0xb0,0x57,0x76,0x15,0x34, | ||
969 | 0x4c,0x6d,0x0e,0x2f,0xc8,0xe9,0x8a,0xab,0x44,0x65,0x06,0x27,0xc0,0xe1,0x82,0xa3, | ||
970 | 0x7d,0x5c,0x3f,0x1e,0xf9,0xd8,0xbb,0x9a,0x75,0x54,0x37,0x16,0xf1,0xd0,0xb3,0x92, | ||
971 | 0x2e,0x0f,0x6c,0x4d,0xaa,0x8b,0xe8,0xc9,0x26,0x07,0x64,0x45,0xa2,0x83,0xe0,0xc1, | ||
972 | 0x1f,0x3e,0x5d,0x7c,0x9b,0xba,0xd9,0xf8,0x17,0x36,0x55,0x74,0x93,0xb2,0xd1,0xf0 | ||
973 | }; | ||
974 | |||
975 | /* look at the asm-code - what looks in C a bit strange is almost as good as handmade */ | ||
976 | register int i; | ||
977 | register unsigned char *CRCT1, *CRCT2, *data, c, crch, crcl; | ||
978 | |||
979 | CRCT1=CRCTable1; | ||
980 | CRCT2=CRCTable2; | ||
981 | data=data_a3; | ||
982 | crcl=data_d1; | ||
983 | crch=data_d0; | ||
984 | for (i=data_d3; i>=0; i--) { | ||
985 | c = (*data++) ^ crch; | ||
986 | crch = CRCT1[c] ^ crcl; | ||
987 | crcl = CRCT2[c]; | ||
988 | } | ||
989 | return (crch<<8)|crcl; | ||
990 | } | ||
991 | |||
992 | static inline ushort dos_hdr_crc (struct dos_header *hdr) | ||
993 | { | ||
994 | return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */ | ||
995 | } | ||
996 | |||
997 | static inline ushort dos_data_crc(unsigned char *data) | ||
998 | { | ||
999 | return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */ | ||
1000 | } | ||
1001 | |||
1002 | static inline unsigned char dos_decode_byte(ushort word) | ||
1003 | { | ||
1004 | register ushort w2; | ||
1005 | register unsigned char byte; | ||
1006 | register unsigned char *dec = mfmdecode; | ||
1007 | |||
1008 | w2=word; | ||
1009 | w2>>=8; | ||
1010 | w2&=127; | ||
1011 | byte = dec[w2]; | ||
1012 | byte <<= 4; | ||
1013 | w2 = word & 127; | ||
1014 | byte |= dec[w2]; | ||
1015 | return byte; | ||
1016 | } | ||
1017 | |||
1018 | static unsigned long dos_decode(unsigned char *data, unsigned short *raw, int len) | ||
1019 | { | ||
1020 | int i; | ||
1021 | |||
1022 | for (i = 0; i < len; i++) | ||
1023 | *data++=dos_decode_byte(*raw++); | ||
1024 | return ((ulong)raw); | ||
1025 | } | ||
1026 | |||
1027 | #ifdef DEBUG | ||
1028 | static void dbg(unsigned long ptr) | ||
1029 | { | ||
1030 | printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n", ptr, | ||
1031 | ((ulong *)ptr)[0], ((ulong *)ptr)[1], | ||
1032 | ((ulong *)ptr)[2], ((ulong *)ptr)[3]); | ||
1033 | } | ||
1034 | #endif | ||
1035 | |||
1036 | static int dos_read(int drive) | ||
1037 | { | ||
1038 | unsigned long end; | ||
1039 | unsigned long raw; | ||
1040 | int scnt; | ||
1041 | unsigned short crc,data_crc[2]; | ||
1042 | struct dos_header hdr; | ||
1043 | |||
1044 | drive&=3; | ||
1045 | raw = (long) raw_buf; | ||
1046 | end = raw + unit[drive].type->read_size; | ||
1047 | |||
1048 | for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { | ||
1049 | do { /* search for the right sync of each sec-hdr */ | ||
1050 | if (!(raw = scan_sync (raw, end))) { | ||
1051 | printk(KERN_INFO "dos_read: no hdr sync on " | ||
1052 | "track %d, unit %d for sector %d\n", | ||
1053 | unit[drive].track,drive,scnt); | ||
1054 | return MFM_NOSYNC; | ||
1055 | } | ||
1056 | #ifdef DEBUG | ||
1057 | dbg(raw); | ||
1058 | #endif | ||
1059 | } while (*((ushort *)raw)!=0x5554); /* loop usually only once done */ | ||
1060 | raw+=2; /* skip over headermark */ | ||
1061 | raw = dos_decode((unsigned char *)&hdr,(ushort *) raw,8); | ||
1062 | crc = dos_hdr_crc(&hdr); | ||
1063 | |||
1064 | #ifdef DEBUG | ||
1065 | printk("(%3d,%d,%2d,%d) %x\n", hdr.track, hdr.side, | ||
1066 | hdr.sec, hdr.len_desc, hdr.crc); | ||
1067 | #endif | ||
1068 | |||
1069 | if (crc != hdr.crc) { | ||
1070 | printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", | ||
1071 | hdr.crc, crc); | ||
1072 | return MFM_HEADER; | ||
1073 | } | ||
1074 | if (hdr.track != unit[drive].track/unit[drive].type->heads) { | ||
1075 | printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", | ||
1076 | hdr.track, | ||
1077 | unit[drive].track/unit[drive].type->heads); | ||
1078 | return MFM_TRACK; | ||
1079 | } | ||
1080 | |||
1081 | if (hdr.side != unit[drive].track%unit[drive].type->heads) { | ||
1082 | printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", | ||
1083 | hdr.side, | ||
1084 | unit[drive].track%unit[drive].type->heads); | ||
1085 | return MFM_TRACK; | ||
1086 | } | ||
1087 | |||
1088 | if (hdr.len_desc != 2) { | ||
1089 | printk(KERN_INFO "dos_read: unknown sector len " | ||
1090 | "descriptor %d\n", hdr.len_desc); | ||
1091 | return MFM_DATA; | ||
1092 | } | ||
1093 | #ifdef DEBUG | ||
1094 | printk("hdr accepted\n"); | ||
1095 | #endif | ||
1096 | if (!(raw = scan_sync (raw, end))) { | ||
1097 | printk(KERN_INFO "dos_read: no data sync on track " | ||
1098 | "%d, unit %d for sector%d, disk sector %d\n", | ||
1099 | unit[drive].track, drive, scnt, hdr.sec); | ||
1100 | return MFM_NOSYNC; | ||
1101 | } | ||
1102 | #ifdef DEBUG | ||
1103 | dbg(raw); | ||
1104 | #endif | ||
1105 | |||
1106 | if (*((ushort *)raw)!=0x5545) { | ||
1107 | printk(KERN_INFO "dos_read: no data mark after " | ||
1108 | "sync (%d,%d,%d,%d) sc=%d\n", | ||
1109 | hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt); | ||
1110 | return MFM_NOSYNC; | ||
1111 | } | ||
1112 | |||
1113 | raw+=2; /* skip data mark (included in checksum) */ | ||
1114 | raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512); | ||
1115 | raw = dos_decode((unsigned char *)data_crc,(ushort *) raw,4); | ||
1116 | crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512); | ||
1117 | |||
1118 | if (crc != data_crc[0]) { | ||
1119 | printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) " | ||
1120 | "sc=%d, %x %x\n", hdr.track, hdr.side, | ||
1121 | hdr.sec, hdr.len_desc, scnt,data_crc[0], crc); | ||
1122 | printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n", | ||
1123 | ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0], | ||
1124 | ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1], | ||
1125 | ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2], | ||
1126 | ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]); | ||
1127 | return MFM_DATA; | ||
1128 | } | ||
1129 | } | ||
1130 | return 0; | ||
1131 | } | ||
1132 | |||
1133 | static inline ushort dos_encode_byte(unsigned char byte) | ||
1134 | { | ||
1135 | register unsigned char *enc, b2, b1; | ||
1136 | register ushort word; | ||
1137 | |||
1138 | enc=mfmencode; | ||
1139 | b1=byte; | ||
1140 | b2=b1>>4; | ||
1141 | b1&=15; | ||
1142 | word=enc[b2] <<8 | enc [b1]; | ||
1143 | return (word|((word&(256|64)) ? 0: 128)); | ||
1144 | } | ||
1145 | |||
1146 | static void dos_encode_block(ushort *dest, unsigned char *src, int len) | ||
1147 | { | ||
1148 | int i; | ||
1149 | |||
1150 | for (i = 0; i < len; i++) { | ||
1151 | *dest=dos_encode_byte(*src++); | ||
1152 | *dest|=((dest[-1]&1)||(*dest&0x4000))? 0: 0x8000; | ||
1153 | dest++; | ||
1154 | } | ||
1155 | } | ||
1156 | |||
1157 | static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt) | ||
1158 | { | ||
1159 | static struct dos_header hdr={0,0,0,2,0, | ||
1160 | {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}}; | ||
1161 | int i; | ||
1162 | static ushort crc[2]={0,0x4e4e}; | ||
1163 | |||
1164 | drive&=3; | ||
1165 | /* id gap 1 */ | ||
1166 | /* the MFM word before is always 9254 */ | ||
1167 | for(i=0;i<6;i++) | ||
1168 | *raw++=0xaaaaaaaa; | ||
1169 | /* 3 sync + 1 headermark */ | ||
1170 | *raw++=0x44894489; | ||
1171 | *raw++=0x44895554; | ||
1172 | |||
1173 | /* fill in the variable parts of the header */ | ||
1174 | hdr.track=unit[drive].track/unit[drive].type->heads; | ||
1175 | hdr.side=unit[drive].track%unit[drive].type->heads; | ||
1176 | hdr.sec=cnt+1; | ||
1177 | hdr.crc=dos_hdr_crc(&hdr); | ||
1178 | |||
1179 | /* header (without "magic") and id gap 2*/ | ||
1180 | dos_encode_block((ushort *)raw,(unsigned char *) &hdr.track,28); | ||
1181 | raw+=14; | ||
1182 | |||
1183 | /*id gap 3 */ | ||
1184 | for(i=0;i<6;i++) | ||
1185 | *raw++=0xaaaaaaaa; | ||
1186 | |||
1187 | /* 3 syncs and 1 datamark */ | ||
1188 | *raw++=0x44894489; | ||
1189 | *raw++=0x44895545; | ||
1190 | |||
1191 | /* data */ | ||
1192 | dos_encode_block((ushort *)raw, | ||
1193 | (unsigned char *)unit[drive].trackbuf+cnt*512,512); | ||
1194 | raw+=256; | ||
1195 | |||
1196 | /*data crc + jd's special gap (long words :-/) */ | ||
1197 | crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512); | ||
1198 | dos_encode_block((ushort *) raw,(unsigned char *)crc,4); | ||
1199 | raw+=2; | ||
1200 | |||
1201 | /* data gap */ | ||
1202 | for(i=0;i<38;i++) | ||
1203 | *raw++=0x92549254; | ||
1204 | |||
1205 | return raw; /* wrote 652 MFM words */ | ||
1206 | } | ||
1207 | |||
1208 | static void dos_write(int disk) | ||
1209 | { | ||
1210 | int cnt; | ||
1211 | unsigned long raw = (unsigned long) raw_buf; | ||
1212 | unsigned long *ptr=(unsigned long *)raw; | ||
1213 | |||
1214 | disk&=3; | ||
1215 | /* really gap4 + indexgap , but we write it first and round it up */ | ||
1216 | for (cnt=0;cnt<425;cnt++) | ||
1217 | *ptr++=0x92549254; | ||
1218 | |||
1219 | /* the following is just guessed */ | ||
1220 | if (unit[disk].type->sect_mult==2) /* check for HD-Disks */ | ||
1221 | for(cnt=0;cnt<473;cnt++) | ||
1222 | *ptr++=0x92549254; | ||
1223 | |||
1224 | /* now the index marks...*/ | ||
1225 | for (cnt=0;cnt<20;cnt++) | ||
1226 | *ptr++=0x92549254; | ||
1227 | for (cnt=0;cnt<6;cnt++) | ||
1228 | *ptr++=0xaaaaaaaa; | ||
1229 | *ptr++=0x52245224; | ||
1230 | *ptr++=0x52245552; | ||
1231 | for (cnt=0;cnt<20;cnt++) | ||
1232 | *ptr++=0x92549254; | ||
1233 | |||
1234 | /* sectors */ | ||
1235 | for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) | ||
1236 | ptr=ms_putsec(disk,ptr,cnt); | ||
1237 | |||
1238 | *(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */ | ||
1239 | } | ||
1240 | |||
1241 | /* | ||
1242 | * Here comes the high level stuff (i.e. the filesystem interface) | ||
1243 | * and helper functions. | ||
1244 | * Normally this should be the only part that has to be adapted to | ||
1245 | * different kernel versions. | ||
1246 | */ | ||
1247 | |||
1248 | /* FIXME: this assumes the drive is still spinning - | ||
1249 | * which is only true if we complete writing a track within three seconds | ||
1250 | */ | ||
1251 | static void flush_track_callback(unsigned long nr) | ||
1252 | { | ||
1253 | nr&=3; | ||
1254 | writefromint = 1; | ||
1255 | if (!try_fdc(nr)) { | ||
1256 | /* we might block in an interrupt, so try again later */ | ||
1257 | flush_track_timer[nr].expires = jiffies + 1; | ||
1258 | add_timer(flush_track_timer + nr); | ||
1259 | return; | ||
1260 | } | ||
1261 | get_fdc(nr); | ||
1262 | (*unit[nr].dtype->write_fkt)(nr); | ||
1263 | if (!raw_write(nr)) { | ||
1264 | printk (KERN_NOTICE "floppy disk write protected\n"); | ||
1265 | writefromint = 0; | ||
1266 | writepending = 0; | ||
1267 | } | ||
1268 | rel_fdc(); | ||
1269 | } | ||
1270 | |||
1271 | static int non_int_flush_track (unsigned long nr) | ||
1272 | { | ||
1273 | unsigned long flags; | ||
1274 | |||
1275 | nr&=3; | ||
1276 | writefromint = 0; | ||
1277 | del_timer(&post_write_timer); | ||
1278 | get_fdc(nr); | ||
1279 | if (!fd_motor_on(nr)) { | ||
1280 | writepending = 0; | ||
1281 | rel_fdc(); | ||
1282 | return 0; | ||
1283 | } | ||
1284 | local_irq_save(flags); | ||
1285 | if (writepending != 2) { | ||
1286 | local_irq_restore(flags); | ||
1287 | (*unit[nr].dtype->write_fkt)(nr); | ||
1288 | if (!raw_write(nr)) { | ||
1289 | printk (KERN_NOTICE "floppy disk write protected " | ||
1290 | "in write!\n"); | ||
1291 | writepending = 0; | ||
1292 | return 0; | ||
1293 | } | ||
1294 | while (block_flag == 2) | ||
1295 | sleep_on (&wait_fd_block); | ||
1296 | } | ||
1297 | else { | ||
1298 | local_irq_restore(flags); | ||
1299 | ms_delay(2); /* 2 ms post_write delay */ | ||
1300 | post_write(nr); | ||
1301 | } | ||
1302 | rel_fdc(); | ||
1303 | return 1; | ||
1304 | } | ||
1305 | |||
1306 | static int get_track(int drive, int track) | ||
1307 | { | ||
1308 | int error, errcnt; | ||
1309 | |||
1310 | drive&=3; | ||
1311 | if (unit[drive].track == track) | ||
1312 | return 0; | ||
1313 | get_fdc(drive); | ||
1314 | if (!fd_motor_on(drive)) { | ||
1315 | rel_fdc(); | ||
1316 | return -1; | ||
1317 | } | ||
1318 | |||
1319 | if (unit[drive].dirty == 1) { | ||
1320 | del_timer (flush_track_timer + drive); | ||
1321 | non_int_flush_track (drive); | ||
1322 | } | ||
1323 | errcnt = 0; | ||
1324 | while (errcnt < MAX_ERRORS) { | ||
1325 | if (!fd_seek(drive, track)) | ||
1326 | return -1; | ||
1327 | raw_read(drive); | ||
1328 | error = (*unit[drive].dtype->read_fkt)(drive); | ||
1329 | if (error == 0) { | ||
1330 | rel_fdc(); | ||
1331 | return 0; | ||
1332 | } | ||
1333 | /* Read Error Handling: recalibrate and try again */ | ||
1334 | unit[drive].track = -1; | ||
1335 | errcnt++; | ||
1336 | } | ||
1337 | rel_fdc(); | ||
1338 | return -1; | ||
1339 | } | ||
1340 | |||
1341 | static void redo_fd_request(void) | ||
1342 | { | ||
1343 | unsigned int cnt, block, track, sector; | ||
1344 | int drive; | ||
1345 | struct amiga_floppy_struct *floppy; | ||
1346 | char *data; | ||
1347 | unsigned long flags; | ||
1348 | |||
1349 | repeat: | ||
1350 | if (!CURRENT) { | ||
1351 | /* Nothing left to do */ | ||
1352 | return; | ||
1353 | } | ||
1354 | |||
1355 | floppy = CURRENT->rq_disk->private_data; | ||
1356 | drive = floppy - unit; | ||
1357 | |||
1358 | /* Here someone could investigate to be more efficient */ | ||
1359 | for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) { | ||
1360 | #ifdef DEBUG | ||
1361 | printk("fd: sector %ld + %d requested for %s\n", | ||
1362 | CURRENT->sector,cnt, | ||
1363 | (CURRENT->cmd==READ)?"read":"write"); | ||
1364 | #endif | ||
1365 | block = CURRENT->sector + cnt; | ||
1366 | if ((int)block > floppy->blocks) { | ||
1367 | end_request(CURRENT, 0); | ||
1368 | goto repeat; | ||
1369 | } | ||
1370 | |||
1371 | track = block / (floppy->dtype->sects * floppy->type->sect_mult); | ||
1372 | sector = block % (floppy->dtype->sects * floppy->type->sect_mult); | ||
1373 | data = CURRENT->buffer + 512 * cnt; | ||
1374 | #ifdef DEBUG | ||
1375 | printk("access to track %d, sector %d, with buffer at " | ||
1376 | "0x%08lx\n", track, sector, data); | ||
1377 | #endif | ||
1378 | |||
1379 | if ((rq_data_dir(CURRENT) != READ) && (rq_data_dir(CURRENT) != WRITE)) { | ||
1380 | printk(KERN_WARNING "do_fd_request: unknown command\n"); | ||
1381 | end_request(CURRENT, 0); | ||
1382 | goto repeat; | ||
1383 | } | ||
1384 | if (get_track(drive, track) == -1) { | ||
1385 | end_request(CURRENT, 0); | ||
1386 | goto repeat; | ||
1387 | } | ||
1388 | |||
1389 | switch (rq_data_dir(CURRENT)) { | ||
1390 | case READ: | ||
1391 | memcpy(data, floppy->trackbuf + sector * 512, 512); | ||
1392 | break; | ||
1393 | |||
1394 | case WRITE: | ||
1395 | memcpy(floppy->trackbuf + sector * 512, data, 512); | ||
1396 | |||
1397 | /* keep the drive spinning while writes are scheduled */ | ||
1398 | if (!fd_motor_on(drive)) { | ||
1399 | end_request(CURRENT, 0); | ||
1400 | goto repeat; | ||
1401 | } | ||
1402 | /* | ||
1403 | * setup a callback to write the track buffer | ||
1404 | * after a short (1 tick) delay. | ||
1405 | */ | ||
1406 | local_irq_save(flags); | ||
1407 | |||
1408 | floppy->dirty = 1; | ||
1409 | /* reset the timer */ | ||
1410 | mod_timer (flush_track_timer + drive, jiffies + 1); | ||
1411 | local_irq_restore(flags); | ||
1412 | break; | ||
1413 | } | ||
1414 | } | ||
1415 | CURRENT->nr_sectors -= CURRENT->current_nr_sectors; | ||
1416 | CURRENT->sector += CURRENT->current_nr_sectors; | ||
1417 | |||
1418 | end_request(CURRENT, 1); | ||
1419 | goto repeat; | ||
1420 | } | ||
1421 | |||
1422 | static void do_fd_request(request_queue_t * q) | ||
1423 | { | ||
1424 | redo_fd_request(); | ||
1425 | } | ||
1426 | |||
1427 | static int fd_ioctl(struct inode *inode, struct file *filp, | ||
1428 | unsigned int cmd, unsigned long param) | ||
1429 | { | ||
1430 | int drive = iminor(inode) & 3; | ||
1431 | static struct floppy_struct getprm; | ||
1432 | |||
1433 | switch(cmd){ | ||
1434 | case HDIO_GETGEO: | ||
1435 | { | ||
1436 | struct hd_geometry loc; | ||
1437 | loc.heads = unit[drive].type->heads; | ||
1438 | loc.sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult; | ||
1439 | loc.cylinders = unit[drive].type->tracks; | ||
1440 | loc.start = 0; | ||
1441 | if (copy_to_user((void *)param, (void *)&loc, | ||
1442 | sizeof(struct hd_geometry))) | ||
1443 | return -EFAULT; | ||
1444 | break; | ||
1445 | } | ||
1446 | case FDFMTBEG: | ||
1447 | get_fdc(drive); | ||
1448 | if (fd_ref[drive] > 1) { | ||
1449 | rel_fdc(); | ||
1450 | return -EBUSY; | ||
1451 | } | ||
1452 | fsync_bdev(inode->i_bdev); | ||
1453 | if (fd_motor_on(drive) == 0) { | ||
1454 | rel_fdc(); | ||
1455 | return -ENODEV; | ||
1456 | } | ||
1457 | if (fd_calibrate(drive) == 0) { | ||
1458 | rel_fdc(); | ||
1459 | return -ENXIO; | ||
1460 | } | ||
1461 | floppy_off(drive); | ||
1462 | rel_fdc(); | ||
1463 | break; | ||
1464 | case FDFMTTRK: | ||
1465 | if (param < unit[drive].type->tracks * unit[drive].type->heads) | ||
1466 | { | ||
1467 | get_fdc(drive); | ||
1468 | if (fd_seek(drive,param) != 0){ | ||
1469 | memset(unit[drive].trackbuf, FD_FILL_BYTE, | ||
1470 | unit[drive].dtype->sects * unit[drive].type->sect_mult * 512); | ||
1471 | non_int_flush_track(drive); | ||
1472 | } | ||
1473 | floppy_off(drive); | ||
1474 | rel_fdc(); | ||
1475 | } | ||
1476 | else | ||
1477 | return -EINVAL; | ||
1478 | break; | ||
1479 | case FDFMTEND: | ||
1480 | floppy_off(drive); | ||
1481 | invalidate_bdev(inode->i_bdev, 0); | ||
1482 | break; | ||
1483 | case FDGETPRM: | ||
1484 | memset((void *)&getprm, 0, sizeof (getprm)); | ||
1485 | getprm.track=unit[drive].type->tracks; | ||
1486 | getprm.head=unit[drive].type->heads; | ||
1487 | getprm.sect=unit[drive].dtype->sects * unit[drive].type->sect_mult; | ||
1488 | getprm.size=unit[drive].blocks; | ||
1489 | if (copy_to_user((void *)param, | ||
1490 | (void *)&getprm, | ||
1491 | sizeof(struct floppy_struct))) | ||
1492 | return -EFAULT; | ||
1493 | break; | ||
1494 | case FDSETPRM: | ||
1495 | case FDDEFPRM: | ||
1496 | return -EINVAL; | ||
1497 | case FDFLUSH: /* unconditionally, even if not needed */ | ||
1498 | del_timer (flush_track_timer + drive); | ||
1499 | non_int_flush_track(drive); | ||
1500 | break; | ||
1501 | #ifdef RAW_IOCTL | ||
1502 | case IOCTL_RAW_TRACK: | ||
1503 | if (copy_to_user((void *)param, raw_buf, | ||
1504 | unit[drive].type->read_size)) | ||
1505 | return -EFAULT; | ||
1506 | else | ||
1507 | return unit[drive].type->read_size; | ||
1508 | #endif | ||
1509 | default: | ||
1510 | printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.", | ||
1511 | cmd, drive); | ||
1512 | return -ENOSYS; | ||
1513 | } | ||
1514 | return 0; | ||
1515 | } | ||
1516 | |||
1517 | static void fd_probe(int dev) | ||
1518 | { | ||
1519 | unsigned long code; | ||
1520 | int type; | ||
1521 | int drive; | ||
1522 | |||
1523 | drive = dev & 3; | ||
1524 | code = fd_get_drive_id(drive); | ||
1525 | |||
1526 | /* get drive type */ | ||
1527 | for (type = 0; type < num_dr_types; type++) | ||
1528 | if (drive_types[type].code == code) | ||
1529 | break; | ||
1530 | |||
1531 | if (type >= num_dr_types) { | ||
1532 | printk(KERN_WARNING "fd_probe: unsupported drive type " | ||
1533 | "%08lx found\n", code); | ||
1534 | unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */ | ||
1535 | return; | ||
1536 | } | ||
1537 | |||
1538 | unit[drive].type = drive_types + type; | ||
1539 | unit[drive].track = -1; | ||
1540 | |||
1541 | unit[drive].disk = -1; | ||
1542 | unit[drive].motor = 0; | ||
1543 | unit[drive].busy = 0; | ||
1544 | unit[drive].status = -1; | ||
1545 | } | ||
1546 | |||
1547 | /* | ||
1548 | * floppy_open check for aliasing (/dev/fd0 can be the same as | ||
1549 | * /dev/PS0 etc), and disallows simultaneous access to the same | ||
1550 | * drive with different device numbers. | ||
1551 | */ | ||
1552 | static int floppy_open(struct inode *inode, struct file *filp) | ||
1553 | { | ||
1554 | int drive = iminor(inode) & 3; | ||
1555 | int system = (iminor(inode) & 4) >> 2; | ||
1556 | int old_dev; | ||
1557 | unsigned long flags; | ||
1558 | |||
1559 | old_dev = fd_device[drive]; | ||
1560 | |||
1561 | if (fd_ref[drive] && old_dev != system) | ||
1562 | return -EBUSY; | ||
1563 | |||
1564 | if (filp && filp->f_mode & 3) { | ||
1565 | check_disk_change(inode->i_bdev); | ||
1566 | if (filp->f_mode & 2 ) { | ||
1567 | int wrprot; | ||
1568 | |||
1569 | get_fdc(drive); | ||
1570 | fd_select (drive); | ||
1571 | wrprot = !(ciaa.pra & DSKPROT); | ||
1572 | fd_deselect (drive); | ||
1573 | rel_fdc(); | ||
1574 | |||
1575 | if (wrprot) | ||
1576 | return -EROFS; | ||
1577 | } | ||
1578 | } | ||
1579 | |||
1580 | local_irq_save(flags); | ||
1581 | fd_ref[drive]++; | ||
1582 | fd_device[drive] = system; | ||
1583 | local_irq_restore(flags); | ||
1584 | |||
1585 | unit[drive].dtype=&data_types[system]; | ||
1586 | unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks* | ||
1587 | data_types[system].sects*unit[drive].type->sect_mult; | ||
1588 | set_capacity(unit[drive].gendisk, unit[drive].blocks); | ||
1589 | |||
1590 | printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive, | ||
1591 | unit[drive].type->name, data_types[system].name); | ||
1592 | |||
1593 | return 0; | ||
1594 | } | ||
1595 | |||
1596 | static int floppy_release(struct inode * inode, struct file * filp) | ||
1597 | { | ||
1598 | int drive = iminor(inode) & 3; | ||
1599 | |||
1600 | if (unit[drive].dirty == 1) { | ||
1601 | del_timer (flush_track_timer + drive); | ||
1602 | non_int_flush_track (drive); | ||
1603 | } | ||
1604 | |||
1605 | if (!fd_ref[drive]--) { | ||
1606 | printk(KERN_CRIT "floppy_release with fd_ref == 0"); | ||
1607 | fd_ref[drive] = 0; | ||
1608 | } | ||
1609 | #ifdef MODULE | ||
1610 | /* the mod_use counter is handled this way */ | ||
1611 | floppy_off (drive | 0x40000000); | ||
1612 | #endif | ||
1613 | return 0; | ||
1614 | } | ||
1615 | |||
1616 | /* | ||
1617 | * floppy-change is never called from an interrupt, so we can relax a bit | ||
1618 | * here, sleep etc. Note that floppy-on tries to set current_DOR to point | ||
1619 | * to the desired drive, but it will probably not survive the sleep if | ||
1620 | * several floppies are used at the same time: thus the loop. | ||
1621 | */ | ||
1622 | static int amiga_floppy_change(struct gendisk *disk) | ||
1623 | { | ||
1624 | struct amiga_floppy_struct *p = disk->private_data; | ||
1625 | int drive = p - unit; | ||
1626 | int changed; | ||
1627 | static int first_time = 1; | ||
1628 | |||
1629 | if (first_time) | ||
1630 | changed = first_time--; | ||
1631 | else { | ||
1632 | get_fdc(drive); | ||
1633 | fd_select (drive); | ||
1634 | changed = !(ciaa.pra & DSKCHANGE); | ||
1635 | fd_deselect (drive); | ||
1636 | rel_fdc(); | ||
1637 | } | ||
1638 | |||
1639 | if (changed) { | ||
1640 | fd_probe(drive); | ||
1641 | p->track = -1; | ||
1642 | p->dirty = 0; | ||
1643 | writepending = 0; /* if this was true before, too bad! */ | ||
1644 | writefromint = 0; | ||
1645 | return 1; | ||
1646 | } | ||
1647 | return 0; | ||
1648 | } | ||
1649 | |||
1650 | static struct block_device_operations floppy_fops = { | ||
1651 | .owner = THIS_MODULE, | ||
1652 | .open = floppy_open, | ||
1653 | .release = floppy_release, | ||
1654 | .ioctl = fd_ioctl, | ||
1655 | .media_changed = amiga_floppy_change, | ||
1656 | }; | ||
1657 | |||
1658 | void __init amiga_floppy_setup (char *str, int *ints) | ||
1659 | { | ||
1660 | printk (KERN_INFO "amiflop: Setting default df0 to %x\n", ints[1]); | ||
1661 | fd_def_df0 = ints[1]; | ||
1662 | } | ||
1663 | |||
1664 | static int __init fd_probe_drives(void) | ||
1665 | { | ||
1666 | int drive,drives,nomem; | ||
1667 | |||
1668 | printk(KERN_INFO "FD: probing units\n" KERN_INFO "found "); | ||
1669 | drives=0; | ||
1670 | nomem=0; | ||
1671 | for(drive=0;drive<FD_MAX_UNITS;drive++) { | ||
1672 | struct gendisk *disk; | ||
1673 | fd_probe(drive); | ||
1674 | if (unit[drive].type->code == FD_NODRIVE) | ||
1675 | continue; | ||
1676 | disk = alloc_disk(1); | ||
1677 | if (!disk) { | ||
1678 | unit[drive].type->code = FD_NODRIVE; | ||
1679 | continue; | ||
1680 | } | ||
1681 | unit[drive].gendisk = disk; | ||
1682 | drives++; | ||
1683 | if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) { | ||
1684 | printk("no mem for "); | ||
1685 | unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */ | ||
1686 | drives--; | ||
1687 | nomem = 1; | ||
1688 | } | ||
1689 | printk("fd%d ",drive); | ||
1690 | disk->major = FLOPPY_MAJOR; | ||
1691 | disk->first_minor = drive; | ||
1692 | disk->fops = &floppy_fops; | ||
1693 | sprintf(disk->disk_name, "fd%d", drive); | ||
1694 | disk->private_data = &unit[drive]; | ||
1695 | disk->queue = floppy_queue; | ||
1696 | set_capacity(disk, 880*2); | ||
1697 | add_disk(disk); | ||
1698 | } | ||
1699 | if ((drives > 0) || (nomem == 0)) { | ||
1700 | if (drives == 0) | ||
1701 | printk("no drives"); | ||
1702 | printk("\n"); | ||
1703 | return drives; | ||
1704 | } | ||
1705 | printk("\n"); | ||
1706 | return -ENOMEM; | ||
1707 | } | ||
1708 | |||
1709 | static struct kobject *floppy_find(dev_t dev, int *part, void *data) | ||
1710 | { | ||
1711 | int drive = *part & 3; | ||
1712 | if (unit[drive].type->code == FD_NODRIVE) | ||
1713 | return NULL; | ||
1714 | *part = 0; | ||
1715 | return get_disk(unit[drive].gendisk); | ||
1716 | } | ||
1717 | |||
1718 | int __init amiga_floppy_init(void) | ||
1719 | { | ||
1720 | int i, ret; | ||
1721 | |||
1722 | if (!AMIGAHW_PRESENT(AMI_FLOPPY)) | ||
1723 | return -ENXIO; | ||
1724 | |||
1725 | if (register_blkdev(FLOPPY_MAJOR,"fd")) | ||
1726 | return -EBUSY; | ||
1727 | |||
1728 | /* | ||
1729 | * We request DSKPTR, DSKLEN and DSKDATA only, because the other | ||
1730 | * floppy registers are too spreaded over the custom register space | ||
1731 | */ | ||
1732 | ret = -EBUSY; | ||
1733 | if (!request_mem_region(CUSTOM_PHYSADDR+0x20, 8, "amiflop [Paula]")) { | ||
1734 | printk("fd: cannot get floppy registers\n"); | ||
1735 | goto out_blkdev; | ||
1736 | } | ||
1737 | |||
1738 | ret = -ENOMEM; | ||
1739 | if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE, "Floppy")) == | ||
1740 | NULL) { | ||
1741 | printk("fd: cannot get chip mem buffer\n"); | ||
1742 | goto out_memregion; | ||
1743 | } | ||
1744 | |||
1745 | ret = -EBUSY; | ||
1746 | if (request_irq(IRQ_AMIGA_DSKBLK, fd_block_done, 0, "floppy_dma", NULL)) { | ||
1747 | printk("fd: cannot get irq for dma\n"); | ||
1748 | goto out_irq; | ||
1749 | } | ||
1750 | |||
1751 | if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL)) { | ||
1752 | printk("fd: cannot get irq for timer\n"); | ||
1753 | goto out_irq2; | ||
1754 | } | ||
1755 | |||
1756 | ret = -ENOMEM; | ||
1757 | floppy_queue = blk_init_queue(do_fd_request, &amiflop_lock); | ||
1758 | if (!floppy_queue) | ||
1759 | goto out_queue; | ||
1760 | |||
1761 | ret = -ENXIO; | ||
1762 | if (fd_probe_drives() < 1) /* No usable drives */ | ||
1763 | goto out_probe; | ||
1764 | |||
1765 | blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, | ||
1766 | floppy_find, NULL, NULL); | ||
1767 | |||
1768 | /* initialize variables */ | ||
1769 | init_timer(&motor_on_timer); | ||
1770 | motor_on_timer.expires = 0; | ||
1771 | motor_on_timer.data = 0; | ||
1772 | motor_on_timer.function = motor_on_callback; | ||
1773 | for (i = 0; i < FD_MAX_UNITS; i++) { | ||
1774 | init_timer(&motor_off_timer[i]); | ||
1775 | motor_off_timer[i].expires = 0; | ||
1776 | motor_off_timer[i].data = i|0x80000000; | ||
1777 | motor_off_timer[i].function = fd_motor_off; | ||
1778 | init_timer(&flush_track_timer[i]); | ||
1779 | flush_track_timer[i].expires = 0; | ||
1780 | flush_track_timer[i].data = i; | ||
1781 | flush_track_timer[i].function = flush_track_callback; | ||
1782 | |||
1783 | unit[i].track = -1; | ||
1784 | } | ||
1785 | |||
1786 | init_timer(&post_write_timer); | ||
1787 | post_write_timer.expires = 0; | ||
1788 | post_write_timer.data = 0; | ||
1789 | post_write_timer.function = post_write; | ||
1790 | |||
1791 | for (i = 0; i < 128; i++) | ||
1792 | mfmdecode[i]=255; | ||
1793 | for (i = 0; i < 16; i++) | ||
1794 | mfmdecode[mfmencode[i]]=i; | ||
1795 | |||
1796 | /* make sure that disk DMA is enabled */ | ||
1797 | custom.dmacon = DMAF_SETCLR | DMAF_DISK; | ||
1798 | |||
1799 | /* init ms timer */ | ||
1800 | ciaa.crb = 8; /* one-shot, stop */ | ||
1801 | return 0; | ||
1802 | |||
1803 | out_probe: | ||
1804 | blk_cleanup_queue(floppy_queue); | ||
1805 | out_queue: | ||
1806 | free_irq(IRQ_AMIGA_CIAA_TB, NULL); | ||
1807 | out_irq2: | ||
1808 | free_irq(IRQ_AMIGA_DSKBLK, NULL); | ||
1809 | out_irq: | ||
1810 | amiga_chip_free(raw_buf); | ||
1811 | out_memregion: | ||
1812 | release_mem_region(CUSTOM_PHYSADDR+0x20, 8); | ||
1813 | out_blkdev: | ||
1814 | unregister_blkdev(FLOPPY_MAJOR,"fd"); | ||
1815 | return ret; | ||
1816 | } | ||
1817 | |||
1818 | #ifdef MODULE | ||
1819 | #include <linux/version.h> | ||
1820 | |||
1821 | int init_module(void) | ||
1822 | { | ||
1823 | if (!MACH_IS_AMIGA) | ||
1824 | return -ENXIO; | ||
1825 | return amiga_floppy_init(); | ||
1826 | } | ||
1827 | |||
1828 | #if 0 /* not safe to unload */ | ||
1829 | void cleanup_module(void) | ||
1830 | { | ||
1831 | int i; | ||
1832 | |||
1833 | for( i = 0; i < FD_MAX_UNITS; i++) { | ||
1834 | if (unit[i].type->code != FD_NODRIVE) { | ||
1835 | del_gendisk(unit[i].gendisk); | ||
1836 | put_disk(unit[i].gendisk); | ||
1837 | kfree(unit[i].trackbuf); | ||
1838 | } | ||
1839 | } | ||
1840 | blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); | ||
1841 | free_irq(IRQ_AMIGA_CIAA_TB, NULL); | ||
1842 | free_irq(IRQ_AMIGA_DSKBLK, NULL); | ||
1843 | custom.dmacon = DMAF_DISK; /* disable DMA */ | ||
1844 | amiga_chip_free(raw_buf); | ||
1845 | blk_cleanup_queue(floppy_queue); | ||
1846 | release_mem_region(CUSTOM_PHYSADDR+0x20, 8); | ||
1847 | unregister_blkdev(FLOPPY_MAJOR, "fd"); | ||
1848 | } | ||
1849 | #endif | ||
1850 | #endif | ||