11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/amiga/amiflop.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (C) 1993 Greg Harp 51da177e4SLinus Torvalds * Portions of this driver are based on code contributed by Brad Pepers 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * revised 28.5.95 by Joerg Dorchain 81da177e4SLinus Torvalds * - now no bugs(?) any more for both HD & DD 91da177e4SLinus Torvalds * - added support for 40 Track 5.25" drives, 80-track hopefully behaves 101da177e4SLinus Torvalds * like 3.5" dd (no way to test - are there any 5.25" drives out there 111da177e4SLinus Torvalds * that work on an A4000?) 121da177e4SLinus Torvalds * - wrote formatting routine (maybe dirty, but works) 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * june/july 1995 added ms-dos support by Joerg Dorchain 151da177e4SLinus Torvalds * (portions based on messydos.device and various contributors) 161da177e4SLinus Torvalds * - currently only 9 and 18 sector disks 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * - fixed a bug with the internal trackbuffer when using multiple 191da177e4SLinus Torvalds * disks the same time 201da177e4SLinus Torvalds * - made formatting a bit safer 211da177e4SLinus Torvalds * - added command line and machine based default for "silent" df0 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * december 1995 adapted for 1.2.13pl4 by Joerg Dorchain 241da177e4SLinus Torvalds * - works but I think it's inefficient. (look in redo_fd_request) 251da177e4SLinus Torvalds * But the changes were very efficient. (only three and a half lines) 261da177e4SLinus Torvalds * 271da177e4SLinus Torvalds * january 1996 added special ioctl for tracking down read/write problems 281da177e4SLinus Torvalds * - usage ioctl(d, RAW_TRACK, ptr); the raw track buffer (MFM-encoded data 291da177e4SLinus Torvalds * is copied to area. (area should be large enough since no checking is 301da177e4SLinus Torvalds * done - 30K is currently sufficient). return the actual size of the 311da177e4SLinus Torvalds * trackbuffer 321da177e4SLinus Torvalds * - replaced udelays() by a timer (CIAA timer B) for the waits 331da177e4SLinus Torvalds * needed for the disk mechanic. 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds * february 1996 fixed error recovery and multiple disk access 361da177e4SLinus Torvalds * - both got broken the first time I tampered with the driver :-( 371da177e4SLinus Torvalds * - still not safe, but better than before 381da177e4SLinus Torvalds * 391da177e4SLinus Torvalds * revised Marts 3rd, 1996 by Jes Sorensen for use in the 1.3.28 kernel. 401da177e4SLinus Torvalds * - Minor changes to accept the kdev_t. 411da177e4SLinus Torvalds * - Replaced some more udelays with ms_delays. Udelay is just a loop, 421da177e4SLinus Torvalds * and so the delay will be different depending on the given 431da177e4SLinus Torvalds * processor :-( 441da177e4SLinus Torvalds * - The driver could use a major cleanup because of the new 451da177e4SLinus Torvalds * major/minor handling that came with kdev_t. It seems to work for 461da177e4SLinus Torvalds * the time being, but I can't guarantee that it will stay like 471da177e4SLinus Torvalds * that when we start using 16 (24?) bit minors. 481da177e4SLinus Torvalds * 491da177e4SLinus Torvalds * restructured jan 1997 by Joerg Dorchain 501da177e4SLinus Torvalds * - Fixed Bug accessing multiple disks 511da177e4SLinus Torvalds * - some code cleanup 521da177e4SLinus Torvalds * - added trackbuffer for each drive to speed things up 531da177e4SLinus Torvalds * - fixed some race conditions (who finds the next may send it to me ;-) 541da177e4SLinus Torvalds */ 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds #include <linux/module.h> 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds #include <linux/fd.h> 591da177e4SLinus Torvalds #include <linux/hdreg.h> 601da177e4SLinus Torvalds #include <linux/delay.h> 611da177e4SLinus Torvalds #include <linux/init.h> 621da177e4SLinus Torvalds #include <linux/amifdreg.h> 631da177e4SLinus Torvalds #include <linux/amifd.h> 641da177e4SLinus Torvalds #include <linux/buffer_head.h> 651da177e4SLinus Torvalds #include <linux/blkdev.h> 661da177e4SLinus Torvalds #include <linux/elevator.h> 67b5dc7840SRoman Zippel #include <linux/interrupt.h> 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds #include <asm/setup.h> 701da177e4SLinus Torvalds #include <asm/uaccess.h> 711da177e4SLinus Torvalds #include <asm/amigahw.h> 721da177e4SLinus Torvalds #include <asm/amigaints.h> 731da177e4SLinus Torvalds #include <asm/irq.h> 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds #undef DEBUG /* print _LOTS_ of infos */ 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds #define RAW_IOCTL 781da177e4SLinus Torvalds #ifdef RAW_IOCTL 791da177e4SLinus Torvalds #define IOCTL_RAW_TRACK 0x5254524B /* 'RTRK' */ 801da177e4SLinus Torvalds #endif 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds /* 831da177e4SLinus Torvalds * Defines 841da177e4SLinus Torvalds */ 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* 871da177e4SLinus Torvalds * Error codes 881da177e4SLinus Torvalds */ 891da177e4SLinus Torvalds #define FD_OK 0 /* operation succeeded */ 901da177e4SLinus Torvalds #define FD_ERROR -1 /* general error (seek, read, write, etc) */ 911da177e4SLinus Torvalds #define FD_NOUNIT 1 /* unit does not exist */ 921da177e4SLinus Torvalds #define FD_UNITBUSY 2 /* unit already active */ 931da177e4SLinus Torvalds #define FD_NOTACTIVE 3 /* unit is not active */ 941da177e4SLinus Torvalds #define FD_NOTREADY 4 /* unit is not ready (motor not on/no disk) */ 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds #define MFM_NOSYNC 1 971da177e4SLinus Torvalds #define MFM_HEADER 2 981da177e4SLinus Torvalds #define MFM_DATA 3 991da177e4SLinus Torvalds #define MFM_TRACK 4 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds /* 1021da177e4SLinus Torvalds * Floppy ID values 1031da177e4SLinus Torvalds */ 1041da177e4SLinus Torvalds #define FD_NODRIVE 0x00000000 /* response when no unit is present */ 1051da177e4SLinus Torvalds #define FD_DD_3 0xffffffff /* double-density 3.5" (880K) drive */ 1061da177e4SLinus Torvalds #define FD_HD_3 0x55555555 /* high-density 3.5" (1760K) drive */ 1071da177e4SLinus Torvalds #define FD_DD_5 0xaaaaaaaa /* double-density 5.25" (440K) drive */ 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds static unsigned long int fd_def_df0 = FD_DD_3; /* default for df0 if it doesn't identify */ 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds module_param(fd_def_df0, ulong, 0); 1121da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds static struct request_queue *floppy_queue; 1151da177e4SLinus Torvalds #define QUEUE (floppy_queue) 1161da177e4SLinus Torvalds #define CURRENT elv_next_request(floppy_queue) 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds /* 1191da177e4SLinus Torvalds * Macros 1201da177e4SLinus Torvalds */ 1211da177e4SLinus Torvalds #define MOTOR_ON (ciab.prb &= ~DSKMOTOR) 1221da177e4SLinus Torvalds #define MOTOR_OFF (ciab.prb |= DSKMOTOR) 1231da177e4SLinus Torvalds #define SELECT(mask) (ciab.prb &= ~mask) 1241da177e4SLinus Torvalds #define DESELECT(mask) (ciab.prb |= mask) 1251da177e4SLinus Torvalds #define SELMASK(drive) (1 << (3 + (drive & 3))) 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds static struct fd_drive_type drive_types[] = { 1281da177e4SLinus Torvalds /* code name tr he rdsz wrsz sm pc1 pc2 sd st st*/ 1291da177e4SLinus Torvalds /* warning: times are now in milliseconds (ms) */ 1301da177e4SLinus Torvalds { FD_DD_3, "DD 3.5", 80, 2, 14716, 13630, 1, 80,161, 3, 18, 1}, 1311da177e4SLinus Torvalds { FD_HD_3, "HD 3.5", 80, 2, 28344, 27258, 2, 80,161, 3, 18, 1}, 1321da177e4SLinus Torvalds { FD_DD_5, "DD 5.25", 40, 2, 14716, 13630, 1, 40, 81, 6, 30, 2}, 1331da177e4SLinus Torvalds { FD_NODRIVE, "No Drive", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 1341da177e4SLinus Torvalds }; 135945f390fSTobias Klauser static int num_dr_types = ARRAY_SIZE(drive_types); 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds static int amiga_read(int), dos_read(int); 1381da177e4SLinus Torvalds static void amiga_write(int), dos_write(int); 1391da177e4SLinus Torvalds static struct fd_data_type data_types[] = { 1401da177e4SLinus Torvalds { "Amiga", 11 , amiga_read, amiga_write}, 1411da177e4SLinus Torvalds { "MS-Dos", 9, dos_read, dos_write} 1421da177e4SLinus Torvalds }; 1431da177e4SLinus Torvalds 1441da177e4SLinus Torvalds /* current info on each unit */ 1451da177e4SLinus Torvalds static struct amiga_floppy_struct unit[FD_MAX_UNITS]; 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct timer_list flush_track_timer[FD_MAX_UNITS]; 1481da177e4SLinus Torvalds static struct timer_list post_write_timer; 1491da177e4SLinus Torvalds static struct timer_list motor_on_timer; 1501da177e4SLinus Torvalds static struct timer_list motor_off_timer[FD_MAX_UNITS]; 1511da177e4SLinus Torvalds static int on_attempts; 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds /* Synchronization of FDC access */ 1541da177e4SLinus Torvalds /* request loop (trackbuffer) */ 1551da177e4SLinus Torvalds static volatile int fdc_busy = -1; 1561da177e4SLinus Torvalds static volatile int fdc_nested; 1571da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(fdc_wait); 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(motor_wait); 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds static volatile int selected = -1; /* currently selected drive */ 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds static int writepending; 1641da177e4SLinus Torvalds static int writefromint; 1651da177e4SLinus Torvalds static char *raw_buf; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds static DEFINE_SPINLOCK(amiflop_lock); 1681da177e4SLinus Torvalds 1691da177e4SLinus Torvalds #define RAW_BUF_SIZE 30000 /* size of raw disk data */ 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* 1721da177e4SLinus Torvalds * These are global variables, as that's the easiest way to give 1731da177e4SLinus Torvalds * information to interrupts. They are the data used for the current 1741da177e4SLinus Torvalds * request. 1751da177e4SLinus Torvalds */ 1761da177e4SLinus Torvalds static volatile char block_flag; 1771da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(wait_fd_block); 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds /* MS-Dos MFM Coding tables (should go quick and easy) */ 1801da177e4SLinus Torvalds static unsigned char mfmencode[16]={ 1811da177e4SLinus Torvalds 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15, 1821da177e4SLinus Torvalds 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55 1831da177e4SLinus Torvalds }; 1841da177e4SLinus Torvalds static unsigned char mfmdecode[128]; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds /* floppy internal millisecond timer stuff */ 1871da177e4SLinus Torvalds static volatile int ms_busy = -1; 1881da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(ms_wait); 1891da177e4SLinus Torvalds #define MS_TICKS ((amiga_eclock+50)/1000) 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds /* 1921da177e4SLinus Torvalds * Note that MAX_ERRORS=X doesn't imply that we retry every bad read 1931da177e4SLinus Torvalds * max X times - some types of errors increase the errorcount by 2 or 1941da177e4SLinus Torvalds * even 3, so we might actually retry only X/2 times before giving up. 1951da177e4SLinus Torvalds */ 1961da177e4SLinus Torvalds #define MAX_ERRORS 12 1971da177e4SLinus Torvalds 198b4290a23SAl Viro #define custom amiga_custom 199b4290a23SAl Viro 2001da177e4SLinus Torvalds /* Prevent "aliased" accesses. */ 2011da177e4SLinus Torvalds static int fd_ref[4] = { 0,0,0,0 }; 2021da177e4SLinus Torvalds static int fd_device[4] = { 0, 0, 0, 0 }; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* 2051da177e4SLinus Torvalds * Here come the actual hardware access and helper functions. 2061da177e4SLinus Torvalds * They are not reentrant and single threaded because all drives 2071da177e4SLinus Torvalds * share the same hardware and the same trackbuffer. 2081da177e4SLinus Torvalds */ 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds /* Milliseconds timer */ 2111da177e4SLinus Torvalds 2127d12e780SDavid Howells static irqreturn_t ms_isr(int irq, void *dummy) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds ms_busy = -1; 2151da177e4SLinus Torvalds wake_up(&ms_wait); 2161da177e4SLinus Torvalds return IRQ_HANDLED; 2171da177e4SLinus Torvalds } 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds /* all waits are queued up 2201da177e4SLinus Torvalds A more generic routine would do a schedule a la timer.device */ 2211da177e4SLinus Torvalds static void ms_delay(int ms) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds unsigned long flags; 2241da177e4SLinus Torvalds int ticks; 2251da177e4SLinus Torvalds if (ms > 0) { 2261da177e4SLinus Torvalds local_irq_save(flags); 2271da177e4SLinus Torvalds while (ms_busy == 0) 2281da177e4SLinus Torvalds sleep_on(&ms_wait); 2291da177e4SLinus Torvalds ms_busy = 0; 2301da177e4SLinus Torvalds local_irq_restore(flags); 2311da177e4SLinus Torvalds ticks = MS_TICKS*ms-1; 2321da177e4SLinus Torvalds ciaa.tblo=ticks%256; 2331da177e4SLinus Torvalds ciaa.tbhi=ticks/256; 2341da177e4SLinus Torvalds ciaa.crb=0x19; /*count eclock, force load, one-shoot, start */ 2351da177e4SLinus Torvalds sleep_on(&ms_wait); 2361da177e4SLinus Torvalds } 2371da177e4SLinus Torvalds } 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds /* Hardware semaphore */ 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds /* returns true when we would get the semaphore */ 2421da177e4SLinus Torvalds static inline int try_fdc(int drive) 2431da177e4SLinus Torvalds { 2441da177e4SLinus Torvalds drive &= 3; 2451da177e4SLinus Torvalds return ((fdc_busy < 0) || (fdc_busy == drive)); 2461da177e4SLinus Torvalds } 2471da177e4SLinus Torvalds 2481da177e4SLinus Torvalds static void get_fdc(int drive) 2491da177e4SLinus Torvalds { 2501da177e4SLinus Torvalds unsigned long flags; 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds drive &= 3; 2531da177e4SLinus Torvalds #ifdef DEBUG 2541da177e4SLinus Torvalds printk("get_fdc: drive %d fdc_busy %d fdc_nested %d\n",drive,fdc_busy,fdc_nested); 2551da177e4SLinus Torvalds #endif 2561da177e4SLinus Torvalds local_irq_save(flags); 2571da177e4SLinus Torvalds while (!try_fdc(drive)) 2581da177e4SLinus Torvalds sleep_on(&fdc_wait); 2591da177e4SLinus Torvalds fdc_busy = drive; 2601da177e4SLinus Torvalds fdc_nested++; 2611da177e4SLinus Torvalds local_irq_restore(flags); 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds 2641da177e4SLinus Torvalds static inline void rel_fdc(void) 2651da177e4SLinus Torvalds { 2661da177e4SLinus Torvalds #ifdef DEBUG 2671da177e4SLinus Torvalds if (fdc_nested == 0) 2681da177e4SLinus Torvalds printk("fd: unmatched rel_fdc\n"); 2691da177e4SLinus Torvalds printk("rel_fdc: fdc_busy %d fdc_nested %d\n",fdc_busy,fdc_nested); 2701da177e4SLinus Torvalds #endif 2711da177e4SLinus Torvalds fdc_nested--; 2721da177e4SLinus Torvalds if (fdc_nested == 0) { 2731da177e4SLinus Torvalds fdc_busy = -1; 2741da177e4SLinus Torvalds wake_up(&fdc_wait); 2751da177e4SLinus Torvalds } 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static void fd_select (int drive) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds unsigned char prb = ~0; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds drive&=3; 2831da177e4SLinus Torvalds #ifdef DEBUG 2841da177e4SLinus Torvalds printk("selecting %d\n",drive); 2851da177e4SLinus Torvalds #endif 2861da177e4SLinus Torvalds if (drive == selected) 2871da177e4SLinus Torvalds return; 2881da177e4SLinus Torvalds get_fdc(drive); 2891da177e4SLinus Torvalds selected = drive; 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds if (unit[drive].track % 2 != 0) 2921da177e4SLinus Torvalds prb &= ~DSKSIDE; 2931da177e4SLinus Torvalds if (unit[drive].motor == 1) 2941da177e4SLinus Torvalds prb &= ~DSKMOTOR; 2951da177e4SLinus Torvalds ciab.prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); 2961da177e4SLinus Torvalds ciab.prb = prb; 2971da177e4SLinus Torvalds prb &= ~SELMASK(drive); 2981da177e4SLinus Torvalds ciab.prb = prb; 2991da177e4SLinus Torvalds rel_fdc(); 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds static void fd_deselect (int drive) 3031da177e4SLinus Torvalds { 3041da177e4SLinus Torvalds unsigned char prb; 3051da177e4SLinus Torvalds unsigned long flags; 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds drive&=3; 3081da177e4SLinus Torvalds #ifdef DEBUG 3091da177e4SLinus Torvalds printk("deselecting %d\n",drive); 3101da177e4SLinus Torvalds #endif 3111da177e4SLinus Torvalds if (drive != selected) { 3121da177e4SLinus Torvalds printk(KERN_WARNING "Deselecting drive %d while %d was selected!\n",drive,selected); 3131da177e4SLinus Torvalds return; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds get_fdc(drive); 3171da177e4SLinus Torvalds local_irq_save(flags); 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds selected = -1; 3201da177e4SLinus Torvalds 3211da177e4SLinus Torvalds prb = ciab.prb; 3221da177e4SLinus Torvalds prb |= (SELMASK(0)|SELMASK(1)|SELMASK(2)|SELMASK(3)); 3231da177e4SLinus Torvalds ciab.prb = prb; 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds local_irq_restore (flags); 3261da177e4SLinus Torvalds rel_fdc(); 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds static void motor_on_callback(unsigned long nr) 3311da177e4SLinus Torvalds { 3321da177e4SLinus Torvalds if (!(ciaa.pra & DSKRDY) || --on_attempts == 0) { 3331da177e4SLinus Torvalds wake_up (&motor_wait); 3341da177e4SLinus Torvalds } else { 3351da177e4SLinus Torvalds motor_on_timer.expires = jiffies + HZ/10; 3361da177e4SLinus Torvalds add_timer(&motor_on_timer); 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds static int fd_motor_on(int nr) 3411da177e4SLinus Torvalds { 3421da177e4SLinus Torvalds nr &= 3; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds del_timer(motor_off_timer + nr); 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds if (!unit[nr].motor) { 3471da177e4SLinus Torvalds unit[nr].motor = 1; 3481da177e4SLinus Torvalds fd_select(nr); 3491da177e4SLinus Torvalds 3501da177e4SLinus Torvalds motor_on_timer.data = nr; 3511da177e4SLinus Torvalds mod_timer(&motor_on_timer, jiffies + HZ/2); 3521da177e4SLinus Torvalds 3531da177e4SLinus Torvalds on_attempts = 10; 3541da177e4SLinus Torvalds sleep_on (&motor_wait); 3551da177e4SLinus Torvalds fd_deselect(nr); 3561da177e4SLinus Torvalds } 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds if (on_attempts == 0) { 3591da177e4SLinus Torvalds on_attempts = -1; 3601da177e4SLinus Torvalds #if 0 3611da177e4SLinus Torvalds printk (KERN_ERR "motor_on failed, turning motor off\n"); 3621da177e4SLinus Torvalds fd_motor_off (nr); 3631da177e4SLinus Torvalds return 0; 3641da177e4SLinus Torvalds #else 3651da177e4SLinus Torvalds printk (KERN_WARNING "DSKRDY not set after 1.5 seconds - assuming drive is spinning notwithstanding\n"); 3661da177e4SLinus Torvalds #endif 3671da177e4SLinus Torvalds } 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds return 1; 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3721da177e4SLinus Torvalds static void fd_motor_off(unsigned long drive) 3731da177e4SLinus Torvalds { 3741da177e4SLinus Torvalds long calledfromint; 3751da177e4SLinus Torvalds #ifdef MODULE 3761da177e4SLinus Torvalds long decusecount; 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds decusecount = drive & 0x40000000; 3791da177e4SLinus Torvalds #endif 3801da177e4SLinus Torvalds calledfromint = drive & 0x80000000; 3811da177e4SLinus Torvalds drive&=3; 3821da177e4SLinus Torvalds if (calledfromint && !try_fdc(drive)) { 3831da177e4SLinus Torvalds /* We would be blocked in an interrupt, so try again later */ 3841da177e4SLinus Torvalds motor_off_timer[drive].expires = jiffies + 1; 3851da177e4SLinus Torvalds add_timer(motor_off_timer + drive); 3861da177e4SLinus Torvalds return; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds unit[drive].motor = 0; 3891da177e4SLinus Torvalds fd_select(drive); 3901da177e4SLinus Torvalds udelay (1); 3911da177e4SLinus Torvalds fd_deselect(drive); 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds static void floppy_off (unsigned int nr) 3951da177e4SLinus Torvalds { 3961da177e4SLinus Torvalds int drive; 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds drive = nr & 3; 3991da177e4SLinus Torvalds /* called this way it is always from interrupt */ 4001da177e4SLinus Torvalds motor_off_timer[drive].data = nr | 0x80000000; 4011da177e4SLinus Torvalds mod_timer(motor_off_timer + drive, jiffies + 3*HZ); 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds 4041da177e4SLinus Torvalds static int fd_calibrate(int drive) 4051da177e4SLinus Torvalds { 4061da177e4SLinus Torvalds unsigned char prb; 4071da177e4SLinus Torvalds int n; 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds drive &= 3; 4101da177e4SLinus Torvalds get_fdc(drive); 4111da177e4SLinus Torvalds if (!fd_motor_on (drive)) 4121da177e4SLinus Torvalds return 0; 4131da177e4SLinus Torvalds fd_select (drive); 4141da177e4SLinus Torvalds prb = ciab.prb; 4151da177e4SLinus Torvalds prb |= DSKSIDE; 4161da177e4SLinus Torvalds prb &= ~DSKDIREC; 4171da177e4SLinus Torvalds ciab.prb = prb; 4181da177e4SLinus Torvalds for (n = unit[drive].type->tracks/2; n != 0; --n) { 4191da177e4SLinus Torvalds if (ciaa.pra & DSKTRACK0) 4201da177e4SLinus Torvalds break; 4211da177e4SLinus Torvalds prb &= ~DSKSTEP; 4221da177e4SLinus Torvalds ciab.prb = prb; 4231da177e4SLinus Torvalds prb |= DSKSTEP; 4241da177e4SLinus Torvalds udelay (2); 4251da177e4SLinus Torvalds ciab.prb = prb; 4261da177e4SLinus Torvalds ms_delay(unit[drive].type->step_delay); 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds ms_delay (unit[drive].type->settle_time); 4291da177e4SLinus Torvalds prb |= DSKDIREC; 4301da177e4SLinus Torvalds n = unit[drive].type->tracks + 20; 4311da177e4SLinus Torvalds for (;;) { 4321da177e4SLinus Torvalds prb &= ~DSKSTEP; 4331da177e4SLinus Torvalds ciab.prb = prb; 4341da177e4SLinus Torvalds prb |= DSKSTEP; 4351da177e4SLinus Torvalds udelay (2); 4361da177e4SLinus Torvalds ciab.prb = prb; 4371da177e4SLinus Torvalds ms_delay(unit[drive].type->step_delay + 1); 4381da177e4SLinus Torvalds if ((ciaa.pra & DSKTRACK0) == 0) 4391da177e4SLinus Torvalds break; 4401da177e4SLinus Torvalds if (--n == 0) { 4411da177e4SLinus Torvalds printk (KERN_ERR "fd%d: calibrate failed, turning motor off\n", drive); 4421da177e4SLinus Torvalds fd_motor_off (drive); 4431da177e4SLinus Torvalds unit[drive].track = -1; 4441da177e4SLinus Torvalds rel_fdc(); 4451da177e4SLinus Torvalds return 0; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds unit[drive].track = 0; 4491da177e4SLinus Torvalds ms_delay(unit[drive].type->settle_time); 4501da177e4SLinus Torvalds 4511da177e4SLinus Torvalds rel_fdc(); 4521da177e4SLinus Torvalds fd_deselect(drive); 4531da177e4SLinus Torvalds return 1; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds 4561da177e4SLinus Torvalds static int fd_seek(int drive, int track) 4571da177e4SLinus Torvalds { 4581da177e4SLinus Torvalds unsigned char prb; 4591da177e4SLinus Torvalds int cnt; 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds #ifdef DEBUG 4621da177e4SLinus Torvalds printk("seeking drive %d to track %d\n",drive,track); 4631da177e4SLinus Torvalds #endif 4641da177e4SLinus Torvalds drive &= 3; 4651da177e4SLinus Torvalds get_fdc(drive); 4661da177e4SLinus Torvalds if (unit[drive].track == track) { 4671da177e4SLinus Torvalds rel_fdc(); 4681da177e4SLinus Torvalds return 1; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds if (!fd_motor_on(drive)) { 4711da177e4SLinus Torvalds rel_fdc(); 4721da177e4SLinus Torvalds return 0; 4731da177e4SLinus Torvalds } 4741da177e4SLinus Torvalds if (unit[drive].track < 0 && !fd_calibrate(drive)) { 4751da177e4SLinus Torvalds rel_fdc(); 4761da177e4SLinus Torvalds return 0; 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds fd_select (drive); 4801da177e4SLinus Torvalds cnt = unit[drive].track/2 - track/2; 4811da177e4SLinus Torvalds prb = ciab.prb; 4821da177e4SLinus Torvalds prb |= DSKSIDE | DSKDIREC; 4831da177e4SLinus Torvalds if (track % 2 != 0) 4841da177e4SLinus Torvalds prb &= ~DSKSIDE; 4851da177e4SLinus Torvalds if (cnt < 0) { 4861da177e4SLinus Torvalds cnt = - cnt; 4871da177e4SLinus Torvalds prb &= ~DSKDIREC; 4881da177e4SLinus Torvalds } 4891da177e4SLinus Torvalds ciab.prb = prb; 4901da177e4SLinus Torvalds if (track % 2 != unit[drive].track % 2) 4911da177e4SLinus Torvalds ms_delay (unit[drive].type->side_time); 4921da177e4SLinus Torvalds unit[drive].track = track; 4931da177e4SLinus Torvalds if (cnt == 0) { 4941da177e4SLinus Torvalds rel_fdc(); 4951da177e4SLinus Torvalds fd_deselect(drive); 4961da177e4SLinus Torvalds return 1; 4971da177e4SLinus Torvalds } 4981da177e4SLinus Torvalds do { 4991da177e4SLinus Torvalds prb &= ~DSKSTEP; 5001da177e4SLinus Torvalds ciab.prb = prb; 5011da177e4SLinus Torvalds prb |= DSKSTEP; 5021da177e4SLinus Torvalds udelay (1); 5031da177e4SLinus Torvalds ciab.prb = prb; 5041da177e4SLinus Torvalds ms_delay (unit[drive].type->step_delay); 5051da177e4SLinus Torvalds } while (--cnt != 0); 5061da177e4SLinus Torvalds ms_delay (unit[drive].type->settle_time); 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds rel_fdc(); 5091da177e4SLinus Torvalds fd_deselect(drive); 5101da177e4SLinus Torvalds return 1; 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds 5131da177e4SLinus Torvalds static unsigned long fd_get_drive_id(int drive) 5141da177e4SLinus Torvalds { 5151da177e4SLinus Torvalds int i; 5161da177e4SLinus Torvalds ulong id = 0; 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds drive&=3; 5191da177e4SLinus Torvalds get_fdc(drive); 5201da177e4SLinus Torvalds /* set up for ID */ 5211da177e4SLinus Torvalds MOTOR_ON; 5221da177e4SLinus Torvalds udelay(2); 5231da177e4SLinus Torvalds SELECT(SELMASK(drive)); 5241da177e4SLinus Torvalds udelay(2); 5251da177e4SLinus Torvalds DESELECT(SELMASK(drive)); 5261da177e4SLinus Torvalds udelay(2); 5271da177e4SLinus Torvalds MOTOR_OFF; 5281da177e4SLinus Torvalds udelay(2); 5291da177e4SLinus Torvalds SELECT(SELMASK(drive)); 5301da177e4SLinus Torvalds udelay(2); 5311da177e4SLinus Torvalds DESELECT(SELMASK(drive)); 5321da177e4SLinus Torvalds udelay(2); 5331da177e4SLinus Torvalds 5341da177e4SLinus Torvalds /* loop and read disk ID */ 5351da177e4SLinus Torvalds for (i=0; i<32; i++) { 5361da177e4SLinus Torvalds SELECT(SELMASK(drive)); 5371da177e4SLinus Torvalds udelay(2); 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds /* read and store value of DSKRDY */ 5401da177e4SLinus Torvalds id <<= 1; 5411da177e4SLinus Torvalds id |= (ciaa.pra & DSKRDY) ? 0 : 1; /* cia regs are low-active! */ 5421da177e4SLinus Torvalds 5431da177e4SLinus Torvalds DESELECT(SELMASK(drive)); 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 5461da177e4SLinus Torvalds rel_fdc(); 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds /* 5491da177e4SLinus Torvalds * RB: At least A500/A2000's df0: don't identify themselves. 5501da177e4SLinus Torvalds * As every (real) Amiga has at least a 3.5" DD drive as df0: 5511da177e4SLinus Torvalds * we default to that if df0: doesn't identify as a certain 5521da177e4SLinus Torvalds * type. 5531da177e4SLinus Torvalds */ 5541da177e4SLinus Torvalds if(drive == 0 && id == FD_NODRIVE) 5551da177e4SLinus Torvalds { 5561da177e4SLinus Torvalds id = fd_def_df0; 5571da177e4SLinus Torvalds printk(KERN_NOTICE "fd: drive 0 didn't identify, setting default %08lx\n", (ulong)fd_def_df0); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds /* return the ID value */ 5601da177e4SLinus Torvalds return (id); 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 5637d12e780SDavid Howells static irqreturn_t fd_block_done(int irq, void *dummy) 5641da177e4SLinus Torvalds { 5651da177e4SLinus Torvalds if (block_flag) 5661da177e4SLinus Torvalds custom.dsklen = 0x4000; 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds if (block_flag == 2) { /* writing */ 5691da177e4SLinus Torvalds writepending = 2; 5701da177e4SLinus Torvalds post_write_timer.expires = jiffies + 1; /* at least 2 ms */ 5711da177e4SLinus Torvalds post_write_timer.data = selected; 5721da177e4SLinus Torvalds add_timer(&post_write_timer); 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds else { /* reading */ 5751da177e4SLinus Torvalds block_flag = 0; 5761da177e4SLinus Torvalds wake_up (&wait_fd_block); 5771da177e4SLinus Torvalds } 5781da177e4SLinus Torvalds return IRQ_HANDLED; 5791da177e4SLinus Torvalds } 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds static void raw_read(int drive) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds drive&=3; 5841da177e4SLinus Torvalds get_fdc(drive); 5851da177e4SLinus Torvalds while (block_flag) 5861da177e4SLinus Torvalds sleep_on(&wait_fd_block); 5871da177e4SLinus Torvalds fd_select(drive); 5881da177e4SLinus Torvalds /* setup adkcon bits correctly */ 5891da177e4SLinus Torvalds custom.adkcon = ADK_MSBSYNC; 5901da177e4SLinus Torvalds custom.adkcon = ADK_SETCLR|ADK_WORDSYNC|ADK_FAST; 5911da177e4SLinus Torvalds 5921da177e4SLinus Torvalds custom.dsksync = MFM_SYNC; 5931da177e4SLinus Torvalds 5941da177e4SLinus Torvalds custom.dsklen = 0; 5951da177e4SLinus Torvalds custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); 5961da177e4SLinus Torvalds custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; 5971da177e4SLinus Torvalds custom.dsklen = unit[drive].type->read_size/sizeof(short) | DSKLEN_DMAEN; 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds block_flag = 1; 6001da177e4SLinus Torvalds 6011da177e4SLinus Torvalds while (block_flag) 6021da177e4SLinus Torvalds sleep_on (&wait_fd_block); 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds custom.dsklen = 0; 6051da177e4SLinus Torvalds fd_deselect(drive); 6061da177e4SLinus Torvalds rel_fdc(); 6071da177e4SLinus Torvalds } 6081da177e4SLinus Torvalds 6091da177e4SLinus Torvalds static int raw_write(int drive) 6101da177e4SLinus Torvalds { 6111da177e4SLinus Torvalds ushort adk; 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds drive&=3; 6141da177e4SLinus Torvalds get_fdc(drive); /* corresponds to rel_fdc() in post_write() */ 6151da177e4SLinus Torvalds if ((ciaa.pra & DSKPROT) == 0) { 6161da177e4SLinus Torvalds rel_fdc(); 6171da177e4SLinus Torvalds return 0; 6181da177e4SLinus Torvalds } 6191da177e4SLinus Torvalds while (block_flag) 6201da177e4SLinus Torvalds sleep_on(&wait_fd_block); 6211da177e4SLinus Torvalds fd_select(drive); 6221da177e4SLinus Torvalds /* clear adkcon bits */ 6231da177e4SLinus Torvalds custom.adkcon = ADK_PRECOMP1|ADK_PRECOMP0|ADK_WORDSYNC|ADK_MSBSYNC; 6241da177e4SLinus Torvalds /* set appropriate adkcon bits */ 6251da177e4SLinus Torvalds adk = ADK_SETCLR|ADK_FAST; 6261da177e4SLinus Torvalds if ((ulong)unit[drive].track >= unit[drive].type->precomp2) 6271da177e4SLinus Torvalds adk |= ADK_PRECOMP1; 6281da177e4SLinus Torvalds else if ((ulong)unit[drive].track >= unit[drive].type->precomp1) 6291da177e4SLinus Torvalds adk |= ADK_PRECOMP0; 6301da177e4SLinus Torvalds custom.adkcon = adk; 6311da177e4SLinus Torvalds 6321da177e4SLinus Torvalds custom.dsklen = DSKLEN_WRITE; 6331da177e4SLinus Torvalds custom.dskptr = (u_char *)ZTWO_PADDR((u_char *)raw_buf); 6341da177e4SLinus Torvalds custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; 6351da177e4SLinus Torvalds custom.dsklen = unit[drive].type->write_size/sizeof(short) | DSKLEN_DMAEN|DSKLEN_WRITE; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds block_flag = 2; 6381da177e4SLinus Torvalds return 1; 6391da177e4SLinus Torvalds } 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds /* 6421da177e4SLinus Torvalds * to be called at least 2ms after the write has finished but before any 6431da177e4SLinus Torvalds * other access to the hardware. 6441da177e4SLinus Torvalds */ 6451da177e4SLinus Torvalds static void post_write (unsigned long drive) 6461da177e4SLinus Torvalds { 6471da177e4SLinus Torvalds #ifdef DEBUG 6481da177e4SLinus Torvalds printk("post_write for drive %ld\n",drive); 6491da177e4SLinus Torvalds #endif 6501da177e4SLinus Torvalds drive &= 3; 6511da177e4SLinus Torvalds custom.dsklen = 0; 6521da177e4SLinus Torvalds block_flag = 0; 6531da177e4SLinus Torvalds writepending = 0; 6541da177e4SLinus Torvalds writefromint = 0; 6551da177e4SLinus Torvalds unit[drive].dirty = 0; 6561da177e4SLinus Torvalds wake_up(&wait_fd_block); 6571da177e4SLinus Torvalds fd_deselect(drive); 6581da177e4SLinus Torvalds rel_fdc(); /* corresponds to get_fdc() in raw_write */ 6591da177e4SLinus Torvalds } 6601da177e4SLinus Torvalds 6611da177e4SLinus Torvalds 6621da177e4SLinus Torvalds /* 6631da177e4SLinus Torvalds * The following functions are to convert the block contents into raw data 6641da177e4SLinus Torvalds * written to disk and vice versa. 6651da177e4SLinus Torvalds * (Add other formats here ;-)) 6661da177e4SLinus Torvalds */ 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds static unsigned long scan_sync(unsigned long raw, unsigned long end) 6691da177e4SLinus Torvalds { 6701da177e4SLinus Torvalds ushort *ptr = (ushort *)raw, *endp = (ushort *)end; 6711da177e4SLinus Torvalds 6721da177e4SLinus Torvalds while (ptr < endp && *ptr++ != 0x4489) 6731da177e4SLinus Torvalds ; 6741da177e4SLinus Torvalds if (ptr < endp) { 6751da177e4SLinus Torvalds while (*ptr == 0x4489 && ptr < endp) 6761da177e4SLinus Torvalds ptr++; 6771da177e4SLinus Torvalds return (ulong)ptr; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds return 0; 6801da177e4SLinus Torvalds } 6811da177e4SLinus Torvalds 6821da177e4SLinus Torvalds static inline unsigned long checksum(unsigned long *addr, int len) 6831da177e4SLinus Torvalds { 6841da177e4SLinus Torvalds unsigned long csum = 0; 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds len /= sizeof(*addr); 6871da177e4SLinus Torvalds while (len-- > 0) 6881da177e4SLinus Torvalds csum ^= *addr++; 6891da177e4SLinus Torvalds csum = ((csum>>1) & 0x55555555) ^ (csum & 0x55555555); 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds return csum; 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds static unsigned long decode (unsigned long *data, unsigned long *raw, 6951da177e4SLinus Torvalds int len) 6961da177e4SLinus Torvalds { 6971da177e4SLinus Torvalds ulong *odd, *even; 6981da177e4SLinus Torvalds 6991da177e4SLinus Torvalds /* convert length from bytes to longwords */ 7001da177e4SLinus Torvalds len >>= 2; 7011da177e4SLinus Torvalds odd = raw; 7021da177e4SLinus Torvalds even = odd + len; 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds /* prepare return pointer */ 7051da177e4SLinus Torvalds raw += len * 2; 7061da177e4SLinus Torvalds 7071da177e4SLinus Torvalds do { 7081da177e4SLinus Torvalds *data++ = ((*odd++ & 0x55555555) << 1) | (*even++ & 0x55555555); 7091da177e4SLinus Torvalds } while (--len != 0); 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds return (ulong)raw; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds struct header { 7151da177e4SLinus Torvalds unsigned char magic; 7161da177e4SLinus Torvalds unsigned char track; 7171da177e4SLinus Torvalds unsigned char sect; 7181da177e4SLinus Torvalds unsigned char ord; 7191da177e4SLinus Torvalds unsigned char labels[16]; 7201da177e4SLinus Torvalds unsigned long hdrchk; 7211da177e4SLinus Torvalds unsigned long datachk; 7221da177e4SLinus Torvalds }; 7231da177e4SLinus Torvalds 7241da177e4SLinus Torvalds static int amiga_read(int drive) 7251da177e4SLinus Torvalds { 7261da177e4SLinus Torvalds unsigned long raw; 7271da177e4SLinus Torvalds unsigned long end; 7281da177e4SLinus Torvalds int scnt; 7291da177e4SLinus Torvalds unsigned long csum; 7301da177e4SLinus Torvalds struct header hdr; 7311da177e4SLinus Torvalds 7321da177e4SLinus Torvalds drive&=3; 7331da177e4SLinus Torvalds raw = (long) raw_buf; 7341da177e4SLinus Torvalds end = raw + unit[drive].type->read_size; 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds for (scnt = 0;scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { 7371da177e4SLinus Torvalds if (!(raw = scan_sync(raw, end))) { 7381da177e4SLinus Torvalds printk (KERN_INFO "can't find sync for sector %d\n", scnt); 7391da177e4SLinus Torvalds return MFM_NOSYNC; 7401da177e4SLinus Torvalds } 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.magic, (ulong *)raw, 4); 7431da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.labels, (ulong *)raw, 16); 7441da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.hdrchk, (ulong *)raw, 4); 7451da177e4SLinus Torvalds raw = decode ((ulong *)&hdr.datachk, (ulong *)raw, 4); 7461da177e4SLinus Torvalds csum = checksum((ulong *)&hdr, 7471da177e4SLinus Torvalds (char *)&hdr.hdrchk-(char *)&hdr); 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds #ifdef DEBUG 7501da177e4SLinus Torvalds printk ("(%x,%d,%d,%d) (%lx,%lx,%lx,%lx) %lx %lx\n", 7511da177e4SLinus Torvalds hdr.magic, hdr.track, hdr.sect, hdr.ord, 7521da177e4SLinus Torvalds *(ulong *)&hdr.labels[0], *(ulong *)&hdr.labels[4], 7531da177e4SLinus Torvalds *(ulong *)&hdr.labels[8], *(ulong *)&hdr.labels[12], 7541da177e4SLinus Torvalds hdr.hdrchk, hdr.datachk); 7551da177e4SLinus Torvalds #endif 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds if (hdr.hdrchk != csum) { 7581da177e4SLinus Torvalds printk(KERN_INFO "MFM_HEADER: %08lx,%08lx\n", hdr.hdrchk, csum); 7591da177e4SLinus Torvalds return MFM_HEADER; 7601da177e4SLinus Torvalds } 7611da177e4SLinus Torvalds 7621da177e4SLinus Torvalds /* verify track */ 7631da177e4SLinus Torvalds if (hdr.track != unit[drive].track) { 7641da177e4SLinus Torvalds printk(KERN_INFO "MFM_TRACK: %d, %d\n", hdr.track, unit[drive].track); 7651da177e4SLinus Torvalds return MFM_TRACK; 7661da177e4SLinus Torvalds } 7671da177e4SLinus Torvalds 7681da177e4SLinus Torvalds raw = decode ((ulong *)(unit[drive].trackbuf + hdr.sect*512), 7691da177e4SLinus Torvalds (ulong *)raw, 512); 7701da177e4SLinus Torvalds csum = checksum((ulong *)(unit[drive].trackbuf + hdr.sect*512), 512); 7711da177e4SLinus Torvalds 7721da177e4SLinus Torvalds if (hdr.datachk != csum) { 7731da177e4SLinus Torvalds printk(KERN_INFO "MFM_DATA: (%x:%d:%d:%d) sc=%d %lx, %lx\n", 7741da177e4SLinus Torvalds hdr.magic, hdr.track, hdr.sect, hdr.ord, scnt, 7751da177e4SLinus Torvalds hdr.datachk, csum); 7761da177e4SLinus Torvalds printk (KERN_INFO "data=(%lx,%lx,%lx,%lx)\n", 7771da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[0], 7781da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[1], 7791da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[2], 7801da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+hdr.sect*512))[3]); 7811da177e4SLinus Torvalds return MFM_DATA; 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds return 0; 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds static void encode(unsigned long data, unsigned long *dest) 7891da177e4SLinus Torvalds { 7901da177e4SLinus Torvalds unsigned long data2; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds data &= 0x55555555; 7931da177e4SLinus Torvalds data2 = data ^ 0x55555555; 7941da177e4SLinus Torvalds data |= ((data2 >> 1) | 0x80000000) & (data2 << 1); 7951da177e4SLinus Torvalds 7961da177e4SLinus Torvalds if (*(dest - 1) & 0x00000001) 7971da177e4SLinus Torvalds data &= 0x7FFFFFFF; 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds *dest = data; 8001da177e4SLinus Torvalds } 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds static void encode_block(unsigned long *dest, unsigned long *src, int len) 8031da177e4SLinus Torvalds { 8041da177e4SLinus Torvalds int cnt, to_cnt = 0; 8051da177e4SLinus Torvalds unsigned long data; 8061da177e4SLinus Torvalds 8071da177e4SLinus Torvalds /* odd bits */ 8081da177e4SLinus Torvalds for (cnt = 0; cnt < len / 4; cnt++) { 8091da177e4SLinus Torvalds data = src[cnt] >> 1; 8101da177e4SLinus Torvalds encode(data, dest + to_cnt++); 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds /* even bits */ 8141da177e4SLinus Torvalds for (cnt = 0; cnt < len / 4; cnt++) { 8151da177e4SLinus Torvalds data = src[cnt]; 8161da177e4SLinus Torvalds encode(data, dest + to_cnt++); 8171da177e4SLinus Torvalds } 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds static unsigned long *putsec(int disk, unsigned long *raw, int cnt) 8211da177e4SLinus Torvalds { 8221da177e4SLinus Torvalds struct header hdr; 8231da177e4SLinus Torvalds int i; 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds disk&=3; 8261da177e4SLinus Torvalds *raw = (raw[-1]&1) ? 0x2AAAAAAA : 0xAAAAAAAA; 8271da177e4SLinus Torvalds raw++; 8281da177e4SLinus Torvalds *raw++ = 0x44894489; 8291da177e4SLinus Torvalds 8301da177e4SLinus Torvalds hdr.magic = 0xFF; 8311da177e4SLinus Torvalds hdr.track = unit[disk].track; 8321da177e4SLinus Torvalds hdr.sect = cnt; 8331da177e4SLinus Torvalds hdr.ord = unit[disk].dtype->sects * unit[disk].type->sect_mult - cnt; 8341da177e4SLinus Torvalds for (i = 0; i < 16; i++) 8351da177e4SLinus Torvalds hdr.labels[i] = 0; 8361da177e4SLinus Torvalds hdr.hdrchk = checksum((ulong *)&hdr, 8371da177e4SLinus Torvalds (char *)&hdr.hdrchk-(char *)&hdr); 8381da177e4SLinus Torvalds hdr.datachk = checksum((ulong *)(unit[disk].trackbuf+cnt*512), 512); 8391da177e4SLinus Torvalds 8401da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.magic, 4); 8411da177e4SLinus Torvalds raw += 2; 8421da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.labels, 16); 8431da177e4SLinus Torvalds raw += 8; 8441da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.hdrchk, 4); 8451da177e4SLinus Torvalds raw += 2; 8461da177e4SLinus Torvalds encode_block(raw, (ulong *)&hdr.datachk, 4); 8471da177e4SLinus Torvalds raw += 2; 8481da177e4SLinus Torvalds encode_block(raw, (ulong *)(unit[disk].trackbuf+cnt*512), 512); 8491da177e4SLinus Torvalds raw += 256; 8501da177e4SLinus Torvalds 8511da177e4SLinus Torvalds return raw; 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds static void amiga_write(int disk) 8551da177e4SLinus Torvalds { 8561da177e4SLinus Torvalds unsigned int cnt; 8571da177e4SLinus Torvalds unsigned long *ptr = (unsigned long *)raw_buf; 8581da177e4SLinus Torvalds 8591da177e4SLinus Torvalds disk&=3; 8601da177e4SLinus Torvalds /* gap space */ 8611da177e4SLinus Torvalds for (cnt = 0; cnt < 415 * unit[disk].type->sect_mult; cnt++) 8621da177e4SLinus Torvalds *ptr++ = 0xaaaaaaaa; 8631da177e4SLinus Torvalds 8641da177e4SLinus Torvalds /* sectors */ 8651da177e4SLinus Torvalds for (cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) 8661da177e4SLinus Torvalds ptr = putsec (disk, ptr, cnt); 8671da177e4SLinus Torvalds *(ushort *)ptr = (ptr[-1]&1) ? 0x2AA8 : 0xAAA8; 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds struct dos_header { 8721da177e4SLinus Torvalds unsigned char track, /* 0-80 */ 8731da177e4SLinus Torvalds side, /* 0-1 */ 8741da177e4SLinus Torvalds sec, /* 0-...*/ 8751da177e4SLinus Torvalds len_desc;/* 2 */ 8761da177e4SLinus Torvalds unsigned short crc; /* on 68000 we got an alignment problem, 8771da177e4SLinus Torvalds but this compiler solves it by adding silently 8781da177e4SLinus Torvalds adding a pad byte so data won't fit 8791da177e4SLinus Torvalds and this took about 3h to discover.... */ 8801da177e4SLinus Torvalds unsigned char gap1[22]; /* for longword-alignedness (0x4e) */ 8811da177e4SLinus Torvalds }; 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds /* crc routines are borrowed from the messydos-handler */ 8841da177e4SLinus Torvalds 8851da177e4SLinus Torvalds /* excerpt from the messydos-device 8861da177e4SLinus Torvalds ; The CRC is computed not only over the actual data, but including 8871da177e4SLinus Torvalds ; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb). 8881da177e4SLinus Torvalds ; As we don't read or encode these fields into our buffers, we have to 8891da177e4SLinus Torvalds ; preload the registers containing the CRC with the values they would have 8901da177e4SLinus Torvalds ; after stepping over these fields. 8911da177e4SLinus Torvalds ; 8921da177e4SLinus Torvalds ; How CRCs "really" work: 8931da177e4SLinus Torvalds ; 8941da177e4SLinus Torvalds ; First, you should regard a bitstring as a series of coefficients of 8951da177e4SLinus Torvalds ; polynomials. We calculate with these polynomials in modulo-2 8961da177e4SLinus Torvalds ; arithmetic, in which both add and subtract are done the same as 8971da177e4SLinus Torvalds ; exclusive-or. Now, we modify our data (a very long polynomial) in 8981da177e4SLinus Torvalds ; such a way that it becomes divisible by the CCITT-standard 16-bit 8991da177e4SLinus Torvalds ; 16 12 5 9001da177e4SLinus Torvalds ; polynomial: x + x + x + 1, represented by $11021. The easiest 9011da177e4SLinus Torvalds ; way to do this would be to multiply (using proper arithmetic) our 9021da177e4SLinus Torvalds ; datablock with $11021. So we have: 9031da177e4SLinus Torvalds ; data * $11021 = 9041da177e4SLinus Torvalds ; data * ($10000 + $1021) = 9051da177e4SLinus Torvalds ; data * $10000 + data * $1021 9061da177e4SLinus Torvalds ; The left part of this is simple: Just add two 0 bytes. But then 9071da177e4SLinus Torvalds ; the right part (data $1021) remains difficult and even could have 9081da177e4SLinus Torvalds ; a carry into the left part. The solution is to use a modified 9091da177e4SLinus Torvalds ; multiplication, which has a result that is not correct, but with 9101da177e4SLinus Torvalds ; a difference of any multiple of $11021. We then only need to keep 9111da177e4SLinus Torvalds ; the 16 least significant bits of the result. 9121da177e4SLinus Torvalds ; 9131da177e4SLinus Torvalds ; The following algorithm does this for us: 9141da177e4SLinus Torvalds ; 9151da177e4SLinus Torvalds ; unsigned char *data, c, crclo, crchi; 9161da177e4SLinus Torvalds ; while (not done) { 9171da177e4SLinus Torvalds ; c = *data++ + crchi; 9181da177e4SLinus Torvalds ; crchi = (@ c) >> 8 + crclo; 9191da177e4SLinus Torvalds ; crclo = @ c; 9201da177e4SLinus Torvalds ; } 9211da177e4SLinus Torvalds ; 9221da177e4SLinus Torvalds ; Remember, + is done with EOR, the @ operator is in two tables (high 9231da177e4SLinus Torvalds ; and low byte separately), which is calculated as 9241da177e4SLinus Torvalds ; 9251da177e4SLinus Torvalds ; $1021 * (c & $F0) 9261da177e4SLinus Torvalds ; xor $1021 * (c & $0F) 9271da177e4SLinus Torvalds ; xor $1021 * (c >> 4) (* is regular multiplication) 9281da177e4SLinus Torvalds ; 9291da177e4SLinus Torvalds ; 9301da177e4SLinus Torvalds ; Anyway, the end result is the same as the remainder of the division of 9311da177e4SLinus Torvalds ; the data by $11021. I am afraid I need to study theory a bit more... 9321da177e4SLinus Torvalds 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds my only works was to code this from manx to C.... 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds */ 9371da177e4SLinus Torvalds 9381da177e4SLinus Torvalds static ushort dos_crc(void * data_a3, int data_d0, int data_d1, int data_d3) 9391da177e4SLinus Torvalds { 9401da177e4SLinus Torvalds static unsigned char CRCTable1[] = { 9411da177e4SLinus Torvalds 0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70,0x81,0x91,0xa1,0xb1,0xc1,0xd1,0xe1,0xf1, 9421da177e4SLinus Torvalds 0x12,0x02,0x32,0x22,0x52,0x42,0x72,0x62,0x93,0x83,0xb3,0xa3,0xd3,0xc3,0xf3,0xe3, 9431da177e4SLinus Torvalds 0x24,0x34,0x04,0x14,0x64,0x74,0x44,0x54,0xa5,0xb5,0x85,0x95,0xe5,0xf5,0xc5,0xd5, 9441da177e4SLinus Torvalds 0x36,0x26,0x16,0x06,0x76,0x66,0x56,0x46,0xb7,0xa7,0x97,0x87,0xf7,0xe7,0xd7,0xc7, 9451da177e4SLinus Torvalds 0x48,0x58,0x68,0x78,0x08,0x18,0x28,0x38,0xc9,0xd9,0xe9,0xf9,0x89,0x99,0xa9,0xb9, 9461da177e4SLinus Torvalds 0x5a,0x4a,0x7a,0x6a,0x1a,0x0a,0x3a,0x2a,0xdb,0xcb,0xfb,0xeb,0x9b,0x8b,0xbb,0xab, 9471da177e4SLinus Torvalds 0x6c,0x7c,0x4c,0x5c,0x2c,0x3c,0x0c,0x1c,0xed,0xfd,0xcd,0xdd,0xad,0xbd,0x8d,0x9d, 9481da177e4SLinus Torvalds 0x7e,0x6e,0x5e,0x4e,0x3e,0x2e,0x1e,0x0e,0xff,0xef,0xdf,0xcf,0xbf,0xaf,0x9f,0x8f, 9491da177e4SLinus Torvalds 0x91,0x81,0xb1,0xa1,0xd1,0xc1,0xf1,0xe1,0x10,0x00,0x30,0x20,0x50,0x40,0x70,0x60, 9501da177e4SLinus Torvalds 0x83,0x93,0xa3,0xb3,0xc3,0xd3,0xe3,0xf3,0x02,0x12,0x22,0x32,0x42,0x52,0x62,0x72, 9511da177e4SLinus Torvalds 0xb5,0xa5,0x95,0x85,0xf5,0xe5,0xd5,0xc5,0x34,0x24,0x14,0x04,0x74,0x64,0x54,0x44, 9521da177e4SLinus Torvalds 0xa7,0xb7,0x87,0x97,0xe7,0xf7,0xc7,0xd7,0x26,0x36,0x06,0x16,0x66,0x76,0x46,0x56, 9531da177e4SLinus Torvalds 0xd9,0xc9,0xf9,0xe9,0x99,0x89,0xb9,0xa9,0x58,0x48,0x78,0x68,0x18,0x08,0x38,0x28, 9541da177e4SLinus Torvalds 0xcb,0xdb,0xeb,0xfb,0x8b,0x9b,0xab,0xbb,0x4a,0x5a,0x6a,0x7a,0x0a,0x1a,0x2a,0x3a, 9551da177e4SLinus Torvalds 0xfd,0xed,0xdd,0xcd,0xbd,0xad,0x9d,0x8d,0x7c,0x6c,0x5c,0x4c,0x3c,0x2c,0x1c,0x0c, 9561da177e4SLinus Torvalds 0xef,0xff,0xcf,0xdf,0xaf,0xbf,0x8f,0x9f,0x6e,0x7e,0x4e,0x5e,0x2e,0x3e,0x0e,0x1e 9571da177e4SLinus Torvalds }; 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds static unsigned char CRCTable2[] = { 9601da177e4SLinus Torvalds 0x00,0x21,0x42,0x63,0x84,0xa5,0xc6,0xe7,0x08,0x29,0x4a,0x6b,0x8c,0xad,0xce,0xef, 9611da177e4SLinus Torvalds 0x31,0x10,0x73,0x52,0xb5,0x94,0xf7,0xd6,0x39,0x18,0x7b,0x5a,0xbd,0x9c,0xff,0xde, 9621da177e4SLinus Torvalds 0x62,0x43,0x20,0x01,0xe6,0xc7,0xa4,0x85,0x6a,0x4b,0x28,0x09,0xee,0xcf,0xac,0x8d, 9631da177e4SLinus Torvalds 0x53,0x72,0x11,0x30,0xd7,0xf6,0x95,0xb4,0x5b,0x7a,0x19,0x38,0xdf,0xfe,0x9d,0xbc, 9641da177e4SLinus Torvalds 0xc4,0xe5,0x86,0xa7,0x40,0x61,0x02,0x23,0xcc,0xed,0x8e,0xaf,0x48,0x69,0x0a,0x2b, 9651da177e4SLinus Torvalds 0xf5,0xd4,0xb7,0x96,0x71,0x50,0x33,0x12,0xfd,0xdc,0xbf,0x9e,0x79,0x58,0x3b,0x1a, 9661da177e4SLinus Torvalds 0xa6,0x87,0xe4,0xc5,0x22,0x03,0x60,0x41,0xae,0x8f,0xec,0xcd,0x2a,0x0b,0x68,0x49, 9671da177e4SLinus Torvalds 0x97,0xb6,0xd5,0xf4,0x13,0x32,0x51,0x70,0x9f,0xbe,0xdd,0xfc,0x1b,0x3a,0x59,0x78, 9681da177e4SLinus Torvalds 0x88,0xa9,0xca,0xeb,0x0c,0x2d,0x4e,0x6f,0x80,0xa1,0xc2,0xe3,0x04,0x25,0x46,0x67, 9691da177e4SLinus Torvalds 0xb9,0x98,0xfb,0xda,0x3d,0x1c,0x7f,0x5e,0xb1,0x90,0xf3,0xd2,0x35,0x14,0x77,0x56, 9701da177e4SLinus Torvalds 0xea,0xcb,0xa8,0x89,0x6e,0x4f,0x2c,0x0d,0xe2,0xc3,0xa0,0x81,0x66,0x47,0x24,0x05, 9711da177e4SLinus Torvalds 0xdb,0xfa,0x99,0xb8,0x5f,0x7e,0x1d,0x3c,0xd3,0xf2,0x91,0xb0,0x57,0x76,0x15,0x34, 9721da177e4SLinus Torvalds 0x4c,0x6d,0x0e,0x2f,0xc8,0xe9,0x8a,0xab,0x44,0x65,0x06,0x27,0xc0,0xe1,0x82,0xa3, 9731da177e4SLinus Torvalds 0x7d,0x5c,0x3f,0x1e,0xf9,0xd8,0xbb,0x9a,0x75,0x54,0x37,0x16,0xf1,0xd0,0xb3,0x92, 9741da177e4SLinus Torvalds 0x2e,0x0f,0x6c,0x4d,0xaa,0x8b,0xe8,0xc9,0x26,0x07,0x64,0x45,0xa2,0x83,0xe0,0xc1, 9751da177e4SLinus Torvalds 0x1f,0x3e,0x5d,0x7c,0x9b,0xba,0xd9,0xf8,0x17,0x36,0x55,0x74,0x93,0xb2,0xd1,0xf0 9761da177e4SLinus Torvalds }; 9771da177e4SLinus Torvalds 9781da177e4SLinus Torvalds /* look at the asm-code - what looks in C a bit strange is almost as good as handmade */ 9791da177e4SLinus Torvalds register int i; 9801da177e4SLinus Torvalds register unsigned char *CRCT1, *CRCT2, *data, c, crch, crcl; 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds CRCT1=CRCTable1; 9831da177e4SLinus Torvalds CRCT2=CRCTable2; 9841da177e4SLinus Torvalds data=data_a3; 9851da177e4SLinus Torvalds crcl=data_d1; 9861da177e4SLinus Torvalds crch=data_d0; 9871da177e4SLinus Torvalds for (i=data_d3; i>=0; i--) { 9881da177e4SLinus Torvalds c = (*data++) ^ crch; 9891da177e4SLinus Torvalds crch = CRCT1[c] ^ crcl; 9901da177e4SLinus Torvalds crcl = CRCT2[c]; 9911da177e4SLinus Torvalds } 9921da177e4SLinus Torvalds return (crch<<8)|crcl; 9931da177e4SLinus Torvalds } 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds static inline ushort dos_hdr_crc (struct dos_header *hdr) 9961da177e4SLinus Torvalds { 9971da177e4SLinus Torvalds return dos_crc(&(hdr->track), 0xb2, 0x30, 3); /* precomputed magic */ 9981da177e4SLinus Torvalds } 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds static inline ushort dos_data_crc(unsigned char *data) 10011da177e4SLinus Torvalds { 10021da177e4SLinus Torvalds return dos_crc(data, 0xe2, 0x95 ,511); /* precomputed magic */ 10031da177e4SLinus Torvalds } 10041da177e4SLinus Torvalds 10051da177e4SLinus Torvalds static inline unsigned char dos_decode_byte(ushort word) 10061da177e4SLinus Torvalds { 10071da177e4SLinus Torvalds register ushort w2; 10081da177e4SLinus Torvalds register unsigned char byte; 10091da177e4SLinus Torvalds register unsigned char *dec = mfmdecode; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds w2=word; 10121da177e4SLinus Torvalds w2>>=8; 10131da177e4SLinus Torvalds w2&=127; 10141da177e4SLinus Torvalds byte = dec[w2]; 10151da177e4SLinus Torvalds byte <<= 4; 10161da177e4SLinus Torvalds w2 = word & 127; 10171da177e4SLinus Torvalds byte |= dec[w2]; 10181da177e4SLinus Torvalds return byte; 10191da177e4SLinus Torvalds } 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds static unsigned long dos_decode(unsigned char *data, unsigned short *raw, int len) 10221da177e4SLinus Torvalds { 10231da177e4SLinus Torvalds int i; 10241da177e4SLinus Torvalds 10251da177e4SLinus Torvalds for (i = 0; i < len; i++) 10261da177e4SLinus Torvalds *data++=dos_decode_byte(*raw++); 10271da177e4SLinus Torvalds return ((ulong)raw); 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds #ifdef DEBUG 10311da177e4SLinus Torvalds static void dbg(unsigned long ptr) 10321da177e4SLinus Torvalds { 10331da177e4SLinus Torvalds printk("raw data @%08lx: %08lx, %08lx ,%08lx, %08lx\n", ptr, 10341da177e4SLinus Torvalds ((ulong *)ptr)[0], ((ulong *)ptr)[1], 10351da177e4SLinus Torvalds ((ulong *)ptr)[2], ((ulong *)ptr)[3]); 10361da177e4SLinus Torvalds } 10371da177e4SLinus Torvalds #endif 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds static int dos_read(int drive) 10401da177e4SLinus Torvalds { 10411da177e4SLinus Torvalds unsigned long end; 10421da177e4SLinus Torvalds unsigned long raw; 10431da177e4SLinus Torvalds int scnt; 10441da177e4SLinus Torvalds unsigned short crc,data_crc[2]; 10451da177e4SLinus Torvalds struct dos_header hdr; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds drive&=3; 10481da177e4SLinus Torvalds raw = (long) raw_buf; 10491da177e4SLinus Torvalds end = raw + unit[drive].type->read_size; 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds for (scnt=0; scnt < unit[drive].dtype->sects * unit[drive].type->sect_mult; scnt++) { 10521da177e4SLinus Torvalds do { /* search for the right sync of each sec-hdr */ 10531da177e4SLinus Torvalds if (!(raw = scan_sync (raw, end))) { 10541da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no hdr sync on " 10551da177e4SLinus Torvalds "track %d, unit %d for sector %d\n", 10561da177e4SLinus Torvalds unit[drive].track,drive,scnt); 10571da177e4SLinus Torvalds return MFM_NOSYNC; 10581da177e4SLinus Torvalds } 10591da177e4SLinus Torvalds #ifdef DEBUG 10601da177e4SLinus Torvalds dbg(raw); 10611da177e4SLinus Torvalds #endif 10621da177e4SLinus Torvalds } while (*((ushort *)raw)!=0x5554); /* loop usually only once done */ 10631da177e4SLinus Torvalds raw+=2; /* skip over headermark */ 10641da177e4SLinus Torvalds raw = dos_decode((unsigned char *)&hdr,(ushort *) raw,8); 10651da177e4SLinus Torvalds crc = dos_hdr_crc(&hdr); 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds #ifdef DEBUG 10681da177e4SLinus Torvalds printk("(%3d,%d,%2d,%d) %x\n", hdr.track, hdr.side, 10691da177e4SLinus Torvalds hdr.sec, hdr.len_desc, hdr.crc); 10701da177e4SLinus Torvalds #endif 10711da177e4SLinus Torvalds 10721da177e4SLinus Torvalds if (crc != hdr.crc) { 10731da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_HEADER %04x,%04x\n", 10741da177e4SLinus Torvalds hdr.crc, crc); 10751da177e4SLinus Torvalds return MFM_HEADER; 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds if (hdr.track != unit[drive].track/unit[drive].type->heads) { 10781da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_TRACK %d, %d\n", 10791da177e4SLinus Torvalds hdr.track, 10801da177e4SLinus Torvalds unit[drive].track/unit[drive].type->heads); 10811da177e4SLinus Torvalds return MFM_TRACK; 10821da177e4SLinus Torvalds } 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds if (hdr.side != unit[drive].track%unit[drive].type->heads) { 10851da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_SIDE %d, %d\n", 10861da177e4SLinus Torvalds hdr.side, 10871da177e4SLinus Torvalds unit[drive].track%unit[drive].type->heads); 10881da177e4SLinus Torvalds return MFM_TRACK; 10891da177e4SLinus Torvalds } 10901da177e4SLinus Torvalds 10911da177e4SLinus Torvalds if (hdr.len_desc != 2) { 10921da177e4SLinus Torvalds printk(KERN_INFO "dos_read: unknown sector len " 10931da177e4SLinus Torvalds "descriptor %d\n", hdr.len_desc); 10941da177e4SLinus Torvalds return MFM_DATA; 10951da177e4SLinus Torvalds } 10961da177e4SLinus Torvalds #ifdef DEBUG 10971da177e4SLinus Torvalds printk("hdr accepted\n"); 10981da177e4SLinus Torvalds #endif 10991da177e4SLinus Torvalds if (!(raw = scan_sync (raw, end))) { 11001da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no data sync on track " 11011da177e4SLinus Torvalds "%d, unit %d for sector%d, disk sector %d\n", 11021da177e4SLinus Torvalds unit[drive].track, drive, scnt, hdr.sec); 11031da177e4SLinus Torvalds return MFM_NOSYNC; 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds #ifdef DEBUG 11061da177e4SLinus Torvalds dbg(raw); 11071da177e4SLinus Torvalds #endif 11081da177e4SLinus Torvalds 11091da177e4SLinus Torvalds if (*((ushort *)raw)!=0x5545) { 11101da177e4SLinus Torvalds printk(KERN_INFO "dos_read: no data mark after " 11111da177e4SLinus Torvalds "sync (%d,%d,%d,%d) sc=%d\n", 11121da177e4SLinus Torvalds hdr.track,hdr.side,hdr.sec,hdr.len_desc,scnt); 11131da177e4SLinus Torvalds return MFM_NOSYNC; 11141da177e4SLinus Torvalds } 11151da177e4SLinus Torvalds 11161da177e4SLinus Torvalds raw+=2; /* skip data mark (included in checksum) */ 11171da177e4SLinus Torvalds raw = dos_decode((unsigned char *)(unit[drive].trackbuf + (hdr.sec - 1) * 512), (ushort *) raw, 512); 11181da177e4SLinus Torvalds raw = dos_decode((unsigned char *)data_crc,(ushort *) raw,4); 11191da177e4SLinus Torvalds crc = dos_data_crc(unit[drive].trackbuf + (hdr.sec - 1) * 512); 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds if (crc != data_crc[0]) { 11221da177e4SLinus Torvalds printk(KERN_INFO "dos_read: MFM_DATA (%d,%d,%d,%d) " 11231da177e4SLinus Torvalds "sc=%d, %x %x\n", hdr.track, hdr.side, 11241da177e4SLinus Torvalds hdr.sec, hdr.len_desc, scnt,data_crc[0], crc); 11251da177e4SLinus Torvalds printk(KERN_INFO "data=(%lx,%lx,%lx,%lx,...)\n", 11261da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[0], 11271da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[1], 11281da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[2], 11291da177e4SLinus Torvalds ((ulong *)(unit[drive].trackbuf+(hdr.sec-1)*512))[3]); 11301da177e4SLinus Torvalds return MFM_DATA; 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds } 11331da177e4SLinus Torvalds return 0; 11341da177e4SLinus Torvalds } 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds static inline ushort dos_encode_byte(unsigned char byte) 11371da177e4SLinus Torvalds { 11381da177e4SLinus Torvalds register unsigned char *enc, b2, b1; 11391da177e4SLinus Torvalds register ushort word; 11401da177e4SLinus Torvalds 11411da177e4SLinus Torvalds enc=mfmencode; 11421da177e4SLinus Torvalds b1=byte; 11431da177e4SLinus Torvalds b2=b1>>4; 11441da177e4SLinus Torvalds b1&=15; 11451da177e4SLinus Torvalds word=enc[b2] <<8 | enc [b1]; 11461da177e4SLinus Torvalds return (word|((word&(256|64)) ? 0: 128)); 11471da177e4SLinus Torvalds } 11481da177e4SLinus Torvalds 11491da177e4SLinus Torvalds static void dos_encode_block(ushort *dest, unsigned char *src, int len) 11501da177e4SLinus Torvalds { 11511da177e4SLinus Torvalds int i; 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds for (i = 0; i < len; i++) { 11541da177e4SLinus Torvalds *dest=dos_encode_byte(*src++); 11551da177e4SLinus Torvalds *dest|=((dest[-1]&1)||(*dest&0x4000))? 0: 0x8000; 11561da177e4SLinus Torvalds dest++; 11571da177e4SLinus Torvalds } 11581da177e4SLinus Torvalds } 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds static unsigned long *ms_putsec(int drive, unsigned long *raw, int cnt) 11611da177e4SLinus Torvalds { 11621da177e4SLinus Torvalds static struct dos_header hdr={0,0,0,2,0, 11631da177e4SLinus Torvalds {78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78,78}}; 11641da177e4SLinus Torvalds int i; 11651da177e4SLinus Torvalds static ushort crc[2]={0,0x4e4e}; 11661da177e4SLinus Torvalds 11671da177e4SLinus Torvalds drive&=3; 11681da177e4SLinus Torvalds /* id gap 1 */ 11691da177e4SLinus Torvalds /* the MFM word before is always 9254 */ 11701da177e4SLinus Torvalds for(i=0;i<6;i++) 11711da177e4SLinus Torvalds *raw++=0xaaaaaaaa; 11721da177e4SLinus Torvalds /* 3 sync + 1 headermark */ 11731da177e4SLinus Torvalds *raw++=0x44894489; 11741da177e4SLinus Torvalds *raw++=0x44895554; 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds /* fill in the variable parts of the header */ 11771da177e4SLinus Torvalds hdr.track=unit[drive].track/unit[drive].type->heads; 11781da177e4SLinus Torvalds hdr.side=unit[drive].track%unit[drive].type->heads; 11791da177e4SLinus Torvalds hdr.sec=cnt+1; 11801da177e4SLinus Torvalds hdr.crc=dos_hdr_crc(&hdr); 11811da177e4SLinus Torvalds 11821da177e4SLinus Torvalds /* header (without "magic") and id gap 2*/ 11831da177e4SLinus Torvalds dos_encode_block((ushort *)raw,(unsigned char *) &hdr.track,28); 11841da177e4SLinus Torvalds raw+=14; 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds /*id gap 3 */ 11871da177e4SLinus Torvalds for(i=0;i<6;i++) 11881da177e4SLinus Torvalds *raw++=0xaaaaaaaa; 11891da177e4SLinus Torvalds 11901da177e4SLinus Torvalds /* 3 syncs and 1 datamark */ 11911da177e4SLinus Torvalds *raw++=0x44894489; 11921da177e4SLinus Torvalds *raw++=0x44895545; 11931da177e4SLinus Torvalds 11941da177e4SLinus Torvalds /* data */ 11951da177e4SLinus Torvalds dos_encode_block((ushort *)raw, 11961da177e4SLinus Torvalds (unsigned char *)unit[drive].trackbuf+cnt*512,512); 11971da177e4SLinus Torvalds raw+=256; 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds /*data crc + jd's special gap (long words :-/) */ 12001da177e4SLinus Torvalds crc[0]=dos_data_crc(unit[drive].trackbuf+cnt*512); 12011da177e4SLinus Torvalds dos_encode_block((ushort *) raw,(unsigned char *)crc,4); 12021da177e4SLinus Torvalds raw+=2; 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds /* data gap */ 12051da177e4SLinus Torvalds for(i=0;i<38;i++) 12061da177e4SLinus Torvalds *raw++=0x92549254; 12071da177e4SLinus Torvalds 12081da177e4SLinus Torvalds return raw; /* wrote 652 MFM words */ 12091da177e4SLinus Torvalds } 12101da177e4SLinus Torvalds 12111da177e4SLinus Torvalds static void dos_write(int disk) 12121da177e4SLinus Torvalds { 12131da177e4SLinus Torvalds int cnt; 12141da177e4SLinus Torvalds unsigned long raw = (unsigned long) raw_buf; 12151da177e4SLinus Torvalds unsigned long *ptr=(unsigned long *)raw; 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds disk&=3; 12181da177e4SLinus Torvalds /* really gap4 + indexgap , but we write it first and round it up */ 12191da177e4SLinus Torvalds for (cnt=0;cnt<425;cnt++) 12201da177e4SLinus Torvalds *ptr++=0x92549254; 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds /* the following is just guessed */ 12231da177e4SLinus Torvalds if (unit[disk].type->sect_mult==2) /* check for HD-Disks */ 12241da177e4SLinus Torvalds for(cnt=0;cnt<473;cnt++) 12251da177e4SLinus Torvalds *ptr++=0x92549254; 12261da177e4SLinus Torvalds 12271da177e4SLinus Torvalds /* now the index marks...*/ 12281da177e4SLinus Torvalds for (cnt=0;cnt<20;cnt++) 12291da177e4SLinus Torvalds *ptr++=0x92549254; 12301da177e4SLinus Torvalds for (cnt=0;cnt<6;cnt++) 12311da177e4SLinus Torvalds *ptr++=0xaaaaaaaa; 12321da177e4SLinus Torvalds *ptr++=0x52245224; 12331da177e4SLinus Torvalds *ptr++=0x52245552; 12341da177e4SLinus Torvalds for (cnt=0;cnt<20;cnt++) 12351da177e4SLinus Torvalds *ptr++=0x92549254; 12361da177e4SLinus Torvalds 12371da177e4SLinus Torvalds /* sectors */ 12381da177e4SLinus Torvalds for(cnt = 0; cnt < unit[disk].dtype->sects * unit[disk].type->sect_mult; cnt++) 12391da177e4SLinus Torvalds ptr=ms_putsec(disk,ptr,cnt); 12401da177e4SLinus Torvalds 12411da177e4SLinus Torvalds *(ushort *)ptr = 0xaaa8; /* MFM word before is always 0x9254 */ 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds 12441da177e4SLinus Torvalds /* 12451da177e4SLinus Torvalds * Here comes the high level stuff (i.e. the filesystem interface) 12461da177e4SLinus Torvalds * and helper functions. 12471da177e4SLinus Torvalds * Normally this should be the only part that has to be adapted to 12481da177e4SLinus Torvalds * different kernel versions. 12491da177e4SLinus Torvalds */ 12501da177e4SLinus Torvalds 12511da177e4SLinus Torvalds /* FIXME: this assumes the drive is still spinning - 12521da177e4SLinus Torvalds * which is only true if we complete writing a track within three seconds 12531da177e4SLinus Torvalds */ 12541da177e4SLinus Torvalds static void flush_track_callback(unsigned long nr) 12551da177e4SLinus Torvalds { 12561da177e4SLinus Torvalds nr&=3; 12571da177e4SLinus Torvalds writefromint = 1; 12581da177e4SLinus Torvalds if (!try_fdc(nr)) { 12591da177e4SLinus Torvalds /* we might block in an interrupt, so try again later */ 12601da177e4SLinus Torvalds flush_track_timer[nr].expires = jiffies + 1; 12611da177e4SLinus Torvalds add_timer(flush_track_timer + nr); 12621da177e4SLinus Torvalds return; 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds get_fdc(nr); 12651da177e4SLinus Torvalds (*unit[nr].dtype->write_fkt)(nr); 12661da177e4SLinus Torvalds if (!raw_write(nr)) { 12671da177e4SLinus Torvalds printk (KERN_NOTICE "floppy disk write protected\n"); 12681da177e4SLinus Torvalds writefromint = 0; 12691da177e4SLinus Torvalds writepending = 0; 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds rel_fdc(); 12721da177e4SLinus Torvalds } 12731da177e4SLinus Torvalds 12741da177e4SLinus Torvalds static int non_int_flush_track (unsigned long nr) 12751da177e4SLinus Torvalds { 12761da177e4SLinus Torvalds unsigned long flags; 12771da177e4SLinus Torvalds 12781da177e4SLinus Torvalds nr&=3; 12791da177e4SLinus Torvalds writefromint = 0; 12801da177e4SLinus Torvalds del_timer(&post_write_timer); 12811da177e4SLinus Torvalds get_fdc(nr); 12821da177e4SLinus Torvalds if (!fd_motor_on(nr)) { 12831da177e4SLinus Torvalds writepending = 0; 12841da177e4SLinus Torvalds rel_fdc(); 12851da177e4SLinus Torvalds return 0; 12861da177e4SLinus Torvalds } 12871da177e4SLinus Torvalds local_irq_save(flags); 12881da177e4SLinus Torvalds if (writepending != 2) { 12891da177e4SLinus Torvalds local_irq_restore(flags); 12901da177e4SLinus Torvalds (*unit[nr].dtype->write_fkt)(nr); 12911da177e4SLinus Torvalds if (!raw_write(nr)) { 12921da177e4SLinus Torvalds printk (KERN_NOTICE "floppy disk write protected " 12931da177e4SLinus Torvalds "in write!\n"); 12941da177e4SLinus Torvalds writepending = 0; 12951da177e4SLinus Torvalds return 0; 12961da177e4SLinus Torvalds } 12971da177e4SLinus Torvalds while (block_flag == 2) 12981da177e4SLinus Torvalds sleep_on (&wait_fd_block); 12991da177e4SLinus Torvalds } 13001da177e4SLinus Torvalds else { 13011da177e4SLinus Torvalds local_irq_restore(flags); 13021da177e4SLinus Torvalds ms_delay(2); /* 2 ms post_write delay */ 13031da177e4SLinus Torvalds post_write(nr); 13041da177e4SLinus Torvalds } 13051da177e4SLinus Torvalds rel_fdc(); 13061da177e4SLinus Torvalds return 1; 13071da177e4SLinus Torvalds } 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds static int get_track(int drive, int track) 13101da177e4SLinus Torvalds { 13111da177e4SLinus Torvalds int error, errcnt; 13121da177e4SLinus Torvalds 13131da177e4SLinus Torvalds drive&=3; 13141da177e4SLinus Torvalds if (unit[drive].track == track) 13151da177e4SLinus Torvalds return 0; 13161da177e4SLinus Torvalds get_fdc(drive); 13171da177e4SLinus Torvalds if (!fd_motor_on(drive)) { 13181da177e4SLinus Torvalds rel_fdc(); 13191da177e4SLinus Torvalds return -1; 13201da177e4SLinus Torvalds } 13211da177e4SLinus Torvalds 13221da177e4SLinus Torvalds if (unit[drive].dirty == 1) { 13231da177e4SLinus Torvalds del_timer (flush_track_timer + drive); 13241da177e4SLinus Torvalds non_int_flush_track (drive); 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds errcnt = 0; 13271da177e4SLinus Torvalds while (errcnt < MAX_ERRORS) { 13281da177e4SLinus Torvalds if (!fd_seek(drive, track)) 13291da177e4SLinus Torvalds return -1; 13301da177e4SLinus Torvalds raw_read(drive); 13311da177e4SLinus Torvalds error = (*unit[drive].dtype->read_fkt)(drive); 13321da177e4SLinus Torvalds if (error == 0) { 13331da177e4SLinus Torvalds rel_fdc(); 13341da177e4SLinus Torvalds return 0; 13351da177e4SLinus Torvalds } 13361da177e4SLinus Torvalds /* Read Error Handling: recalibrate and try again */ 13371da177e4SLinus Torvalds unit[drive].track = -1; 13381da177e4SLinus Torvalds errcnt++; 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds rel_fdc(); 13411da177e4SLinus Torvalds return -1; 13421da177e4SLinus Torvalds } 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds static void redo_fd_request(void) 13451da177e4SLinus Torvalds { 13461da177e4SLinus Torvalds unsigned int cnt, block, track, sector; 13471da177e4SLinus Torvalds int drive; 13481da177e4SLinus Torvalds struct amiga_floppy_struct *floppy; 13491da177e4SLinus Torvalds char *data; 13501da177e4SLinus Torvalds unsigned long flags; 13511da177e4SLinus Torvalds 13521da177e4SLinus Torvalds repeat: 13531da177e4SLinus Torvalds if (!CURRENT) { 13541da177e4SLinus Torvalds /* Nothing left to do */ 13551da177e4SLinus Torvalds return; 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds 13581da177e4SLinus Torvalds floppy = CURRENT->rq_disk->private_data; 13591da177e4SLinus Torvalds drive = floppy - unit; 13601da177e4SLinus Torvalds 13611da177e4SLinus Torvalds /* Here someone could investigate to be more efficient */ 13621da177e4SLinus Torvalds for (cnt = 0; cnt < CURRENT->current_nr_sectors; cnt++) { 13631da177e4SLinus Torvalds #ifdef DEBUG 13641da177e4SLinus Torvalds printk("fd: sector %ld + %d requested for %s\n", 13651da177e4SLinus Torvalds CURRENT->sector,cnt, 1366e654bc43SBoaz Harrosh (rq_data_dir(CURRENT) == READ) ? "read" : "write"); 13671da177e4SLinus Torvalds #endif 13681da177e4SLinus Torvalds block = CURRENT->sector + cnt; 13691da177e4SLinus Torvalds if ((int)block > floppy->blocks) { 13701da177e4SLinus Torvalds end_request(CURRENT, 0); 13711da177e4SLinus Torvalds goto repeat; 13721da177e4SLinus Torvalds } 13731da177e4SLinus Torvalds 13741da177e4SLinus Torvalds track = block / (floppy->dtype->sects * floppy->type->sect_mult); 13751da177e4SLinus Torvalds sector = block % (floppy->dtype->sects * floppy->type->sect_mult); 13761da177e4SLinus Torvalds data = CURRENT->buffer + 512 * cnt; 13771da177e4SLinus Torvalds #ifdef DEBUG 13781da177e4SLinus Torvalds printk("access to track %d, sector %d, with buffer at " 13791da177e4SLinus Torvalds "0x%08lx\n", track, sector, data); 13801da177e4SLinus Torvalds #endif 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds if ((rq_data_dir(CURRENT) != READ) && (rq_data_dir(CURRENT) != WRITE)) { 13831da177e4SLinus Torvalds printk(KERN_WARNING "do_fd_request: unknown command\n"); 13841da177e4SLinus Torvalds end_request(CURRENT, 0); 13851da177e4SLinus Torvalds goto repeat; 13861da177e4SLinus Torvalds } 13871da177e4SLinus Torvalds if (get_track(drive, track) == -1) { 13881da177e4SLinus Torvalds end_request(CURRENT, 0); 13891da177e4SLinus Torvalds goto repeat; 13901da177e4SLinus Torvalds } 13911da177e4SLinus Torvalds 13921da177e4SLinus Torvalds switch (rq_data_dir(CURRENT)) { 13931da177e4SLinus Torvalds case READ: 13941da177e4SLinus Torvalds memcpy(data, floppy->trackbuf + sector * 512, 512); 13951da177e4SLinus Torvalds break; 13961da177e4SLinus Torvalds 13971da177e4SLinus Torvalds case WRITE: 13981da177e4SLinus Torvalds memcpy(floppy->trackbuf + sector * 512, data, 512); 13991da177e4SLinus Torvalds 14001da177e4SLinus Torvalds /* keep the drive spinning while writes are scheduled */ 14011da177e4SLinus Torvalds if (!fd_motor_on(drive)) { 14021da177e4SLinus Torvalds end_request(CURRENT, 0); 14031da177e4SLinus Torvalds goto repeat; 14041da177e4SLinus Torvalds } 14051da177e4SLinus Torvalds /* 14061da177e4SLinus Torvalds * setup a callback to write the track buffer 14071da177e4SLinus Torvalds * after a short (1 tick) delay. 14081da177e4SLinus Torvalds */ 14091da177e4SLinus Torvalds local_irq_save(flags); 14101da177e4SLinus Torvalds 14111da177e4SLinus Torvalds floppy->dirty = 1; 14121da177e4SLinus Torvalds /* reset the timer */ 14131da177e4SLinus Torvalds mod_timer (flush_track_timer + drive, jiffies + 1); 14141da177e4SLinus Torvalds local_irq_restore(flags); 14151da177e4SLinus Torvalds break; 14161da177e4SLinus Torvalds } 14171da177e4SLinus Torvalds } 14181da177e4SLinus Torvalds CURRENT->nr_sectors -= CURRENT->current_nr_sectors; 14191da177e4SLinus Torvalds CURRENT->sector += CURRENT->current_nr_sectors; 14201da177e4SLinus Torvalds 14211da177e4SLinus Torvalds end_request(CURRENT, 1); 14221da177e4SLinus Torvalds goto repeat; 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds 1425165125e1SJens Axboe static void do_fd_request(struct request_queue * q) 14261da177e4SLinus Torvalds { 14271da177e4SLinus Torvalds redo_fd_request(); 14281da177e4SLinus Torvalds } 14291da177e4SLinus Torvalds 1430a885c8c4SChristoph Hellwig static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo) 1431a885c8c4SChristoph Hellwig { 1432a885c8c4SChristoph Hellwig int drive = MINOR(bdev->bd_dev) & 3; 1433a885c8c4SChristoph Hellwig 1434a885c8c4SChristoph Hellwig geo->heads = unit[drive].type->heads; 1435a885c8c4SChristoph Hellwig geo->sectors = unit[drive].dtype->sects * unit[drive].type->sect_mult; 1436a885c8c4SChristoph Hellwig geo->cylinders = unit[drive].type->tracks; 1437a885c8c4SChristoph Hellwig return 0; 1438a885c8c4SChristoph Hellwig } 1439a885c8c4SChristoph Hellwig 144047225db5SAl Viro static int fd_ioctl(struct block_device *bdev, fmode_t mode, 14411da177e4SLinus Torvalds unsigned int cmd, unsigned long param) 14421da177e4SLinus Torvalds { 144347225db5SAl Viro struct amiga_floppy_struct *p = bdev->bd_disk->private_data; 144447225db5SAl Viro int drive = p - unit; 14451da177e4SLinus Torvalds static struct floppy_struct getprm; 14468a423e54SAl Viro void __user *argp = (void __user *)param; 14471da177e4SLinus Torvalds 14481da177e4SLinus Torvalds switch(cmd){ 14491da177e4SLinus Torvalds case FDFMTBEG: 14501da177e4SLinus Torvalds get_fdc(drive); 14511da177e4SLinus Torvalds if (fd_ref[drive] > 1) { 14521da177e4SLinus Torvalds rel_fdc(); 14531da177e4SLinus Torvalds return -EBUSY; 14541da177e4SLinus Torvalds } 145547225db5SAl Viro fsync_bdev(bdev); 14561da177e4SLinus Torvalds if (fd_motor_on(drive) == 0) { 14571da177e4SLinus Torvalds rel_fdc(); 14581da177e4SLinus Torvalds return -ENODEV; 14591da177e4SLinus Torvalds } 14601da177e4SLinus Torvalds if (fd_calibrate(drive) == 0) { 14611da177e4SLinus Torvalds rel_fdc(); 14621da177e4SLinus Torvalds return -ENXIO; 14631da177e4SLinus Torvalds } 14641da177e4SLinus Torvalds floppy_off(drive); 14651da177e4SLinus Torvalds rel_fdc(); 14661da177e4SLinus Torvalds break; 14671da177e4SLinus Torvalds case FDFMTTRK: 146847225db5SAl Viro if (param < p->type->tracks * p->type->heads) 14691da177e4SLinus Torvalds { 14701da177e4SLinus Torvalds get_fdc(drive); 14711da177e4SLinus Torvalds if (fd_seek(drive,param) != 0){ 147247225db5SAl Viro memset(p->trackbuf, FD_FILL_BYTE, 147347225db5SAl Viro p->dtype->sects * p->type->sect_mult * 512); 14741da177e4SLinus Torvalds non_int_flush_track(drive); 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds floppy_off(drive); 14771da177e4SLinus Torvalds rel_fdc(); 14781da177e4SLinus Torvalds } 14791da177e4SLinus Torvalds else 14801da177e4SLinus Torvalds return -EINVAL; 14811da177e4SLinus Torvalds break; 14821da177e4SLinus Torvalds case FDFMTEND: 14831da177e4SLinus Torvalds floppy_off(drive); 148447225db5SAl Viro invalidate_bdev(bdev); 14851da177e4SLinus Torvalds break; 14861da177e4SLinus Torvalds case FDGETPRM: 14871da177e4SLinus Torvalds memset((void *)&getprm, 0, sizeof (getprm)); 148847225db5SAl Viro getprm.track=p->type->tracks; 148947225db5SAl Viro getprm.head=p->type->heads; 149047225db5SAl Viro getprm.sect=p->dtype->sects * p->type->sect_mult; 149147225db5SAl Viro getprm.size=p->blocks; 14928a423e54SAl Viro if (copy_to_user(argp, &getprm, sizeof(struct floppy_struct))) 14931da177e4SLinus Torvalds return -EFAULT; 14941da177e4SLinus Torvalds break; 14951da177e4SLinus Torvalds case FDSETPRM: 14961da177e4SLinus Torvalds case FDDEFPRM: 14971da177e4SLinus Torvalds return -EINVAL; 14981da177e4SLinus Torvalds case FDFLUSH: /* unconditionally, even if not needed */ 14991da177e4SLinus Torvalds del_timer (flush_track_timer + drive); 15001da177e4SLinus Torvalds non_int_flush_track(drive); 15011da177e4SLinus Torvalds break; 15021da177e4SLinus Torvalds #ifdef RAW_IOCTL 15031da177e4SLinus Torvalds case IOCTL_RAW_TRACK: 150447225db5SAl Viro if (copy_to_user(argp, raw_buf, p->type->read_size)) 15051da177e4SLinus Torvalds return -EFAULT; 15061da177e4SLinus Torvalds else 150747225db5SAl Viro return p->type->read_size; 15081da177e4SLinus Torvalds #endif 15091da177e4SLinus Torvalds default: 15101da177e4SLinus Torvalds printk(KERN_DEBUG "fd_ioctl: unknown cmd %d for drive %d.", 15111da177e4SLinus Torvalds cmd, drive); 15121da177e4SLinus Torvalds return -ENOSYS; 15131da177e4SLinus Torvalds } 15141da177e4SLinus Torvalds return 0; 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds static void fd_probe(int dev) 15181da177e4SLinus Torvalds { 15191da177e4SLinus Torvalds unsigned long code; 15201da177e4SLinus Torvalds int type; 15211da177e4SLinus Torvalds int drive; 15221da177e4SLinus Torvalds 15231da177e4SLinus Torvalds drive = dev & 3; 15241da177e4SLinus Torvalds code = fd_get_drive_id(drive); 15251da177e4SLinus Torvalds 15261da177e4SLinus Torvalds /* get drive type */ 15271da177e4SLinus Torvalds for (type = 0; type < num_dr_types; type++) 15281da177e4SLinus Torvalds if (drive_types[type].code == code) 15291da177e4SLinus Torvalds break; 15301da177e4SLinus Torvalds 15311da177e4SLinus Torvalds if (type >= num_dr_types) { 15321da177e4SLinus Torvalds printk(KERN_WARNING "fd_probe: unsupported drive type " 15331da177e4SLinus Torvalds "%08lx found\n", code); 15341da177e4SLinus Torvalds unit[drive].type = &drive_types[num_dr_types-1]; /* FD_NODRIVE */ 15351da177e4SLinus Torvalds return; 15361da177e4SLinus Torvalds } 15371da177e4SLinus Torvalds 15381da177e4SLinus Torvalds unit[drive].type = drive_types + type; 15391da177e4SLinus Torvalds unit[drive].track = -1; 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds unit[drive].disk = -1; 15421da177e4SLinus Torvalds unit[drive].motor = 0; 15431da177e4SLinus Torvalds unit[drive].busy = 0; 15441da177e4SLinus Torvalds unit[drive].status = -1; 15451da177e4SLinus Torvalds } 15461da177e4SLinus Torvalds 15471da177e4SLinus Torvalds /* 15481da177e4SLinus Torvalds * floppy_open check for aliasing (/dev/fd0 can be the same as 15491da177e4SLinus Torvalds * /dev/PS0 etc), and disallows simultaneous access to the same 15501da177e4SLinus Torvalds * drive with different device numbers. 15511da177e4SLinus Torvalds */ 155247225db5SAl Viro static int floppy_open(struct block_device *bdev, fmode_t mode) 15531da177e4SLinus Torvalds { 155447225db5SAl Viro int drive = MINOR(bdev->bd_dev) & 3; 155547225db5SAl Viro int system = (MINOR(bdev->bd_dev) & 4) >> 2; 15561da177e4SLinus Torvalds int old_dev; 15571da177e4SLinus Torvalds unsigned long flags; 15581da177e4SLinus Torvalds 15591da177e4SLinus Torvalds old_dev = fd_device[drive]; 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds if (fd_ref[drive] && old_dev != system) 15621da177e4SLinus Torvalds return -EBUSY; 15631da177e4SLinus Torvalds 156447225db5SAl Viro if (mode & (FMODE_READ|FMODE_WRITE)) { 156547225db5SAl Viro check_disk_change(bdev); 156647225db5SAl Viro if (mode & FMODE_WRITE) { 15671da177e4SLinus Torvalds int wrprot; 15681da177e4SLinus Torvalds 15691da177e4SLinus Torvalds get_fdc(drive); 15701da177e4SLinus Torvalds fd_select (drive); 15711da177e4SLinus Torvalds wrprot = !(ciaa.pra & DSKPROT); 15721da177e4SLinus Torvalds fd_deselect (drive); 15731da177e4SLinus Torvalds rel_fdc(); 15741da177e4SLinus Torvalds 15751da177e4SLinus Torvalds if (wrprot) 15761da177e4SLinus Torvalds return -EROFS; 15771da177e4SLinus Torvalds } 15781da177e4SLinus Torvalds } 15791da177e4SLinus Torvalds 15801da177e4SLinus Torvalds local_irq_save(flags); 15811da177e4SLinus Torvalds fd_ref[drive]++; 15821da177e4SLinus Torvalds fd_device[drive] = system; 15831da177e4SLinus Torvalds local_irq_restore(flags); 15841da177e4SLinus Torvalds 15851da177e4SLinus Torvalds unit[drive].dtype=&data_types[system]; 15861da177e4SLinus Torvalds unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks* 15871da177e4SLinus Torvalds data_types[system].sects*unit[drive].type->sect_mult; 15881da177e4SLinus Torvalds set_capacity(unit[drive].gendisk, unit[drive].blocks); 15891da177e4SLinus Torvalds 15901da177e4SLinus Torvalds printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive, 15911da177e4SLinus Torvalds unit[drive].type->name, data_types[system].name); 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds return 0; 15941da177e4SLinus Torvalds } 15951da177e4SLinus Torvalds 159647225db5SAl Viro static int floppy_release(struct gendisk *disk, fmode_t mode) 15971da177e4SLinus Torvalds { 159847225db5SAl Viro struct amiga_floppy_struct *p = disk->private_data; 159947225db5SAl Viro int drive = p - unit; 16001da177e4SLinus Torvalds 16011da177e4SLinus Torvalds if (unit[drive].dirty == 1) { 16021da177e4SLinus Torvalds del_timer (flush_track_timer + drive); 16031da177e4SLinus Torvalds non_int_flush_track (drive); 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds 16061da177e4SLinus Torvalds if (!fd_ref[drive]--) { 16071da177e4SLinus Torvalds printk(KERN_CRIT "floppy_release with fd_ref == 0"); 16081da177e4SLinus Torvalds fd_ref[drive] = 0; 16091da177e4SLinus Torvalds } 16101da177e4SLinus Torvalds #ifdef MODULE 16111da177e4SLinus Torvalds /* the mod_use counter is handled this way */ 16121da177e4SLinus Torvalds floppy_off (drive | 0x40000000); 16131da177e4SLinus Torvalds #endif 16141da177e4SLinus Torvalds return 0; 16151da177e4SLinus Torvalds } 16161da177e4SLinus Torvalds 16171da177e4SLinus Torvalds /* 16181da177e4SLinus Torvalds * floppy-change is never called from an interrupt, so we can relax a bit 16191da177e4SLinus Torvalds * here, sleep etc. Note that floppy-on tries to set current_DOR to point 16201da177e4SLinus Torvalds * to the desired drive, but it will probably not survive the sleep if 16211da177e4SLinus Torvalds * several floppies are used at the same time: thus the loop. 16221da177e4SLinus Torvalds */ 16231da177e4SLinus Torvalds static int amiga_floppy_change(struct gendisk *disk) 16241da177e4SLinus Torvalds { 16251da177e4SLinus Torvalds struct amiga_floppy_struct *p = disk->private_data; 16261da177e4SLinus Torvalds int drive = p - unit; 16271da177e4SLinus Torvalds int changed; 16281da177e4SLinus Torvalds static int first_time = 1; 16291da177e4SLinus Torvalds 16301da177e4SLinus Torvalds if (first_time) 16311da177e4SLinus Torvalds changed = first_time--; 16321da177e4SLinus Torvalds else { 16331da177e4SLinus Torvalds get_fdc(drive); 16341da177e4SLinus Torvalds fd_select (drive); 16351da177e4SLinus Torvalds changed = !(ciaa.pra & DSKCHANGE); 16361da177e4SLinus Torvalds fd_deselect (drive); 16371da177e4SLinus Torvalds rel_fdc(); 16381da177e4SLinus Torvalds } 16391da177e4SLinus Torvalds 16401da177e4SLinus Torvalds if (changed) { 16411da177e4SLinus Torvalds fd_probe(drive); 16421da177e4SLinus Torvalds p->track = -1; 16431da177e4SLinus Torvalds p->dirty = 0; 16441da177e4SLinus Torvalds writepending = 0; /* if this was true before, too bad! */ 16451da177e4SLinus Torvalds writefromint = 0; 16461da177e4SLinus Torvalds return 1; 16471da177e4SLinus Torvalds } 16481da177e4SLinus Torvalds return 0; 16491da177e4SLinus Torvalds } 16501da177e4SLinus Torvalds 16511da177e4SLinus Torvalds static struct block_device_operations floppy_fops = { 16521da177e4SLinus Torvalds .owner = THIS_MODULE, 165347225db5SAl Viro .open = floppy_open, 165447225db5SAl Viro .release = floppy_release, 165547225db5SAl Viro .locked_ioctl = fd_ioctl, 1656a885c8c4SChristoph Hellwig .getgeo = fd_getgeo, 16571da177e4SLinus Torvalds .media_changed = amiga_floppy_change, 16581da177e4SLinus Torvalds }; 16591da177e4SLinus Torvalds 16601da177e4SLinus Torvalds static int __init fd_probe_drives(void) 16611da177e4SLinus Torvalds { 16621da177e4SLinus Torvalds int drive,drives,nomem; 16631da177e4SLinus Torvalds 16641da177e4SLinus Torvalds printk(KERN_INFO "FD: probing units\n" KERN_INFO "found "); 16651da177e4SLinus Torvalds drives=0; 16661da177e4SLinus Torvalds nomem=0; 16671da177e4SLinus Torvalds for(drive=0;drive<FD_MAX_UNITS;drive++) { 16681da177e4SLinus Torvalds struct gendisk *disk; 16691da177e4SLinus Torvalds fd_probe(drive); 16701da177e4SLinus Torvalds if (unit[drive].type->code == FD_NODRIVE) 16711da177e4SLinus Torvalds continue; 16721da177e4SLinus Torvalds disk = alloc_disk(1); 16731da177e4SLinus Torvalds if (!disk) { 16741da177e4SLinus Torvalds unit[drive].type->code = FD_NODRIVE; 16751da177e4SLinus Torvalds continue; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds unit[drive].gendisk = disk; 16781da177e4SLinus Torvalds drives++; 16791da177e4SLinus Torvalds if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) { 16801da177e4SLinus Torvalds printk("no mem for "); 16811da177e4SLinus Torvalds unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */ 16821da177e4SLinus Torvalds drives--; 16831da177e4SLinus Torvalds nomem = 1; 16841da177e4SLinus Torvalds } 16851da177e4SLinus Torvalds printk("fd%d ",drive); 16861da177e4SLinus Torvalds disk->major = FLOPPY_MAJOR; 16871da177e4SLinus Torvalds disk->first_minor = drive; 16881da177e4SLinus Torvalds disk->fops = &floppy_fops; 16891da177e4SLinus Torvalds sprintf(disk->disk_name, "fd%d", drive); 16901da177e4SLinus Torvalds disk->private_data = &unit[drive]; 16911da177e4SLinus Torvalds disk->queue = floppy_queue; 16921da177e4SLinus Torvalds set_capacity(disk, 880*2); 16931da177e4SLinus Torvalds add_disk(disk); 16941da177e4SLinus Torvalds } 16951da177e4SLinus Torvalds if ((drives > 0) || (nomem == 0)) { 16961da177e4SLinus Torvalds if (drives == 0) 16971da177e4SLinus Torvalds printk("no drives"); 16981da177e4SLinus Torvalds printk("\n"); 16991da177e4SLinus Torvalds return drives; 17001da177e4SLinus Torvalds } 17011da177e4SLinus Torvalds printk("\n"); 17021da177e4SLinus Torvalds return -ENOMEM; 17031da177e4SLinus Torvalds } 17041da177e4SLinus Torvalds 17051da177e4SLinus Torvalds static struct kobject *floppy_find(dev_t dev, int *part, void *data) 17061da177e4SLinus Torvalds { 17071da177e4SLinus Torvalds int drive = *part & 3; 17081da177e4SLinus Torvalds if (unit[drive].type->code == FD_NODRIVE) 17091da177e4SLinus Torvalds return NULL; 17101da177e4SLinus Torvalds *part = 0; 17111da177e4SLinus Torvalds return get_disk(unit[drive].gendisk); 17121da177e4SLinus Torvalds } 17131da177e4SLinus Torvalds 17142db5f59cSAl Viro static int __init amiga_floppy_init(void) 17151da177e4SLinus Torvalds { 17161da177e4SLinus Torvalds int i, ret; 17171da177e4SLinus Torvalds 17182db5f59cSAl Viro if (!MACH_IS_AMIGA) 1719fd5b462fSGeert Uytterhoeven return -ENODEV; 17202db5f59cSAl Viro 17211da177e4SLinus Torvalds if (!AMIGAHW_PRESENT(AMI_FLOPPY)) 1722fd5b462fSGeert Uytterhoeven return -ENODEV; 17231da177e4SLinus Torvalds 17241da177e4SLinus Torvalds if (register_blkdev(FLOPPY_MAJOR,"fd")) 17251da177e4SLinus Torvalds return -EBUSY; 17261da177e4SLinus Torvalds 17271da177e4SLinus Torvalds /* 17281da177e4SLinus Torvalds * We request DSKPTR, DSKLEN and DSKDATA only, because the other 17291da177e4SLinus Torvalds * floppy registers are too spreaded over the custom register space 17301da177e4SLinus Torvalds */ 17311da177e4SLinus Torvalds ret = -EBUSY; 17321da177e4SLinus Torvalds if (!request_mem_region(CUSTOM_PHYSADDR+0x20, 8, "amiflop [Paula]")) { 17331da177e4SLinus Torvalds printk("fd: cannot get floppy registers\n"); 17341da177e4SLinus Torvalds goto out_blkdev; 17351da177e4SLinus Torvalds } 17361da177e4SLinus Torvalds 17371da177e4SLinus Torvalds ret = -ENOMEM; 17381da177e4SLinus Torvalds if ((raw_buf = (char *)amiga_chip_alloc (RAW_BUF_SIZE, "Floppy")) == 17391da177e4SLinus Torvalds NULL) { 17401da177e4SLinus Torvalds printk("fd: cannot get chip mem buffer\n"); 17411da177e4SLinus Torvalds goto out_memregion; 17421da177e4SLinus Torvalds } 17431da177e4SLinus Torvalds 17441da177e4SLinus Torvalds ret = -EBUSY; 17451da177e4SLinus Torvalds if (request_irq(IRQ_AMIGA_DSKBLK, fd_block_done, 0, "floppy_dma", NULL)) { 17461da177e4SLinus Torvalds printk("fd: cannot get irq for dma\n"); 17471da177e4SLinus Torvalds goto out_irq; 17481da177e4SLinus Torvalds } 17491da177e4SLinus Torvalds 17501da177e4SLinus Torvalds if (request_irq(IRQ_AMIGA_CIAA_TB, ms_isr, 0, "floppy_timer", NULL)) { 17511da177e4SLinus Torvalds printk("fd: cannot get irq for timer\n"); 17521da177e4SLinus Torvalds goto out_irq2; 17531da177e4SLinus Torvalds } 17541da177e4SLinus Torvalds 17551da177e4SLinus Torvalds ret = -ENOMEM; 17561da177e4SLinus Torvalds floppy_queue = blk_init_queue(do_fd_request, &amiflop_lock); 17571da177e4SLinus Torvalds if (!floppy_queue) 17581da177e4SLinus Torvalds goto out_queue; 17591da177e4SLinus Torvalds 1760fd5b462fSGeert Uytterhoeven ret = -ENODEV; 17611da177e4SLinus Torvalds if (fd_probe_drives() < 1) /* No usable drives */ 17621da177e4SLinus Torvalds goto out_probe; 17631da177e4SLinus Torvalds 17641da177e4SLinus Torvalds blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE, 17651da177e4SLinus Torvalds floppy_find, NULL, NULL); 17661da177e4SLinus Torvalds 17671da177e4SLinus Torvalds /* initialize variables */ 17681da177e4SLinus Torvalds init_timer(&motor_on_timer); 17691da177e4SLinus Torvalds motor_on_timer.expires = 0; 17701da177e4SLinus Torvalds motor_on_timer.data = 0; 17711da177e4SLinus Torvalds motor_on_timer.function = motor_on_callback; 17721da177e4SLinus Torvalds for (i = 0; i < FD_MAX_UNITS; i++) { 17731da177e4SLinus Torvalds init_timer(&motor_off_timer[i]); 17741da177e4SLinus Torvalds motor_off_timer[i].expires = 0; 17751da177e4SLinus Torvalds motor_off_timer[i].data = i|0x80000000; 17761da177e4SLinus Torvalds motor_off_timer[i].function = fd_motor_off; 17771da177e4SLinus Torvalds init_timer(&flush_track_timer[i]); 17781da177e4SLinus Torvalds flush_track_timer[i].expires = 0; 17791da177e4SLinus Torvalds flush_track_timer[i].data = i; 17801da177e4SLinus Torvalds flush_track_timer[i].function = flush_track_callback; 17811da177e4SLinus Torvalds 17821da177e4SLinus Torvalds unit[i].track = -1; 17831da177e4SLinus Torvalds } 17841da177e4SLinus Torvalds 17851da177e4SLinus Torvalds init_timer(&post_write_timer); 17861da177e4SLinus Torvalds post_write_timer.expires = 0; 17871da177e4SLinus Torvalds post_write_timer.data = 0; 17881da177e4SLinus Torvalds post_write_timer.function = post_write; 17891da177e4SLinus Torvalds 17901da177e4SLinus Torvalds for (i = 0; i < 128; i++) 17911da177e4SLinus Torvalds mfmdecode[i]=255; 17921da177e4SLinus Torvalds for (i = 0; i < 16; i++) 17931da177e4SLinus Torvalds mfmdecode[mfmencode[i]]=i; 17941da177e4SLinus Torvalds 17951da177e4SLinus Torvalds /* make sure that disk DMA is enabled */ 17961da177e4SLinus Torvalds custom.dmacon = DMAF_SETCLR | DMAF_DISK; 17971da177e4SLinus Torvalds 17981da177e4SLinus Torvalds /* init ms timer */ 17991da177e4SLinus Torvalds ciaa.crb = 8; /* one-shot, stop */ 18001da177e4SLinus Torvalds return 0; 18011da177e4SLinus Torvalds 18021da177e4SLinus Torvalds out_probe: 18031da177e4SLinus Torvalds blk_cleanup_queue(floppy_queue); 18041da177e4SLinus Torvalds out_queue: 18051da177e4SLinus Torvalds free_irq(IRQ_AMIGA_CIAA_TB, NULL); 18061da177e4SLinus Torvalds out_irq2: 18071da177e4SLinus Torvalds free_irq(IRQ_AMIGA_DSKBLK, NULL); 18081da177e4SLinus Torvalds out_irq: 18091da177e4SLinus Torvalds amiga_chip_free(raw_buf); 18101da177e4SLinus Torvalds out_memregion: 18111da177e4SLinus Torvalds release_mem_region(CUSTOM_PHYSADDR+0x20, 8); 18121da177e4SLinus Torvalds out_blkdev: 18131da177e4SLinus Torvalds unregister_blkdev(FLOPPY_MAJOR,"fd"); 18141da177e4SLinus Torvalds return ret; 18151da177e4SLinus Torvalds } 18161da177e4SLinus Torvalds 18172db5f59cSAl Viro module_init(amiga_floppy_init); 18181da177e4SLinus Torvalds #ifdef MODULE 18191da177e4SLinus Torvalds 18201da177e4SLinus Torvalds #if 0 /* not safe to unload */ 18211da177e4SLinus Torvalds void cleanup_module(void) 18221da177e4SLinus Torvalds { 18231da177e4SLinus Torvalds int i; 18241da177e4SLinus Torvalds 18251da177e4SLinus Torvalds for( i = 0; i < FD_MAX_UNITS; i++) { 18261da177e4SLinus Torvalds if (unit[i].type->code != FD_NODRIVE) { 18271da177e4SLinus Torvalds del_gendisk(unit[i].gendisk); 18281da177e4SLinus Torvalds put_disk(unit[i].gendisk); 18291da177e4SLinus Torvalds kfree(unit[i].trackbuf); 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds } 18321da177e4SLinus Torvalds blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256); 18331da177e4SLinus Torvalds free_irq(IRQ_AMIGA_CIAA_TB, NULL); 18341da177e4SLinus Torvalds free_irq(IRQ_AMIGA_DSKBLK, NULL); 18351da177e4SLinus Torvalds custom.dmacon = DMAF_DISK; /* disable DMA */ 18361da177e4SLinus Torvalds amiga_chip_free(raw_buf); 18371da177e4SLinus Torvalds blk_cleanup_queue(floppy_queue); 18381da177e4SLinus Torvalds release_mem_region(CUSTOM_PHYSADDR+0x20, 8); 18391da177e4SLinus Torvalds unregister_blkdev(FLOPPY_MAJOR, "fd"); 18401da177e4SLinus Torvalds } 18411da177e4SLinus Torvalds #endif 184263907435SAl Viro 184363907435SAl Viro #else 184463907435SAl Viro static int __init amiga_floppy_setup (char *str) 184563907435SAl Viro { 184663907435SAl Viro int n; 184763907435SAl Viro if (!MACH_IS_AMIGA) 184863907435SAl Viro return 0; 184963907435SAl Viro if (!get_option(&str, &n)) 185063907435SAl Viro return 0; 185163907435SAl Viro printk (KERN_INFO "amiflop: Setting default df0 to %x\n", n); 185263907435SAl Viro fd_def_df0 = n; 18539b41046cSOGAWA Hirofumi return 1; 185463907435SAl Viro } 185563907435SAl Viro 185663907435SAl Viro __setup("floppy=", amiga_floppy_setup); 18571da177e4SLinus Torvalds #endif 1858