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