fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / block / floppy.c
index 2ef418b..3f1b382 100644 (file)
  * features to asm/floppy.h.
  */
 
+/*
+ * 1998/1/21 -- Richard Gooch <rgooch@atnf.csiro.au> -- devfs support
+ */
+
 /*
  * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of
  * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting &
@@ -155,14 +159,9 @@ static int print_unex = 1;
 #include <linux/kernel.h>
 #include <linux/timer.h>
 #include <linux/workqueue.h>
-#include <linux/version.h>
 #define FDPATCHES
 #include <linux/fdreg.h>
 
-/*
- * 1998/1/21 -- Richard Gooch <rgooch@atnf.csiro.au> -- devfs support
- */
-
 #include <linux/fd.h>
 #include <linux/hdreg.h>
 
@@ -171,15 +170,16 @@ static int print_unex = 1;
 #include <linux/mm.h>
 #include <linux/bio.h>
 #include <linux/string.h>
+#include <linux/jiffies.h>
 #include <linux/fcntl.h>
 #include <linux/delay.h>
 #include <linux/mc146818rtc.h> /* CMOS defines */
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
-#include <linux/devfs_fs_kernel.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/buffer_head.h> /* for invalidate_buffers() */
+#include <linux/mutex.h>
 
 /*
  * PS/2 floppies have much slower step rates than regular floppies.
@@ -217,13 +217,12 @@ static int use_virtual_dma;
  * record each buffers capabilities
  */
 
-static spinlock_t floppy_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(floppy_lock);
 static struct completion device_release;
 
 static unsigned short virtual_dma_port = 0x3f0;
-irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+irqreturn_t floppy_interrupt(int irq, void *dev_id);
 static int set_dor(int fdc, char mask, char data);
-static void register_devfs_entries(int drive) __init;
 
 #define K_64   0x10000         /* 64KB */
 
@@ -243,7 +242,6 @@ static int allowed_drive_mask = 0x33;
 
 static int irqdma_allocated;
 
-#define LOCAL_END_REQUEST
 #define DEVICE_NAME "floppy"
 
 #include <linux/blkdev.h>
@@ -415,7 +413,7 @@ static struct floppy_write_errors write_errors[N_DRIVE];
 static struct timer_list motor_off_timer[N_DRIVE];
 static struct gendisk *disks[N_DRIVE];
 static struct block_device *opened_bdev[N_DRIVE];
-static DECLARE_MUTEX(open_lock);
+static DEFINE_MUTEX(open_lock);
 static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
 
 /*
@@ -480,7 +478,6 @@ static struct floppy_struct floppy_type[32] = {
        { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5"    */
 };
 
-#define        NUMBER(x)       (sizeof(x) / sizeof(*(x)))
 #define SECTSIZE (_FD_SECTSIZE(*floppy))
 
 /* Auto-detection: Disk type used until the next media change occurs. */
@@ -494,6 +491,8 @@ static struct floppy_struct user_params[N_DRIVE];
 
 static sector_t floppy_sizes[256];
 
+static char floppy_device_name[] = "floppy";
+
 /*
  * The driver is trying to determine the correct media format
  * while probing is set. rw_interrupt() clears it after a
@@ -604,31 +603,30 @@ static inline int fd_eject(int drive)
 }
 #endif
 
-#ifdef DEBUGT
-static long unsigned debugtimer;
-#endif
-
 /*
  * Debugging
  * =========
  */
+#ifdef DEBUGT
+static long unsigned debugtimer;
+
 static inline void set_debugt(void)
 {
-#ifdef DEBUGT
        debugtimer = jiffies;
-#endif
 }
 
 static inline void debugt(const char *message)
 {
-#ifdef DEBUGT
        if (DP->flags & DEBUGT)
                printk("%s dtime=%lu\n", message, jiffies - debugtimer);
-#endif
 }
+#else
+static inline void set_debugt(void) { }
+static inline void debugt(const char *message) { }
+#endif /* DEBUGT */
 
 typedef void (*timeout_fn) (unsigned long);
-static struct timer_list fd_timeout = TIMER_INITIALIZER(floppy_shutdown, 0, 0);
+static DEFINE_TIMER(fd_timeout, floppy_shutdown, 0, 0);
 
 static const char *timeout_message;
 
@@ -736,7 +734,7 @@ static int disk_change(int drive)
 {
        int fdc = FDC(drive);
 #ifdef FLOPPY_SANITY_CHECK
-       if (jiffies - UDRS->select_date < UDP->select_delay)
+       if (time_before(jiffies, UDRS->select_date + UDP->select_delay))
                DPRINT("WARNING disk change called early\n");
        if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
            (FDCS->dor & 3) != UNIT(drive) || fdc != FDC(drive)) {
@@ -816,15 +814,6 @@ static int set_dor(int fdc, char mask, char data)
                        UDRS->select_date = jiffies;
                }
        }
-       /*
-        *      We should propagate failures to grab the resources back
-        *      nicely from here. Actually we ought to rewrite the fd
-        *      driver some day too.
-        */
-       if (newdor & FLOPPY_MOTOR_MASK)
-               floppy_grab_irq_and_dma();
-       if (olddor & FLOPPY_MOTOR_MASK)
-               floppy_release_irq_and_dma();
        return olddor;
 }
 
@@ -882,8 +871,6 @@ static int _lock_fdc(int drive, int interruptible, int line)
                       line);
                return -1;
        }
-       if (floppy_grab_irq_and_dma() == -1)
-               return -EBUSY;
 
        if (test_and_set_bit(0, &fdc_busy)) {
                DECLARE_WAITQUEUE(wait, current);
@@ -905,6 +892,8 @@ static int _lock_fdc(int drive, int interruptible, int line)
 
                set_current_state(TASK_RUNNING);
                remove_wait_queue(&fdc_wait, &wait);
+
+               flush_scheduled_work();
        }
        command_status = FD_COMMAND_NONE;
 
@@ -923,7 +912,7 @@ static inline void unlock_fdc(void)
 {
        unsigned long flags;
 
-       raw_cmd = 0;
+       raw_cmd = NULL;
        if (!test_bit(0, &fdc_busy))
                DPRINT("FDC access conflict!\n");
 
@@ -938,7 +927,6 @@ static inline void unlock_fdc(void)
        if (elv_next_request(floppy_queue))
                do_fd_request(floppy_queue);
        spin_unlock_irqrestore(&floppy_lock, flags);
-       floppy_release_irq_and_dma();
        wake_up(&fdc_wait);
 }
 
@@ -1004,15 +992,15 @@ static void empty(void)
 {
 }
 
-static DECLARE_WORK(floppy_work, NULL, NULL);
+static DECLARE_WORK(floppy_work, NULL);
 
 static void schedule_bh(void (*handler) (void))
 {
-       PREPARE_WORK(&floppy_work, (void (*)(void *))handler, NULL);
+       PREPARE_WORK(&floppy_work, (work_func_t)handler);
        schedule_work(&floppy_work);
 }
 
-static struct timer_list fd_timer = TIMER_INITIALIZER(NULL, 0, 0);
+static DEFINE_TIMER(fd_timer, NULL, 0, 0);
 
 static void cancel_activity(void)
 {
@@ -1020,7 +1008,7 @@ static void cancel_activity(void)
 
        spin_lock_irqsave(&floppy_lock, flags);
        do_floppy = NULL;
-       PREPARE_WORK(&floppy_work, (void *)empty, NULL);
+       PREPARE_WORK(&floppy_work, (work_func_t)empty);
        del_timer(&fd_timer);
        spin_unlock_irqrestore(&floppy_lock, flags);
 }
@@ -1064,7 +1052,7 @@ static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
                return 1;
        }
 
-       if ((signed)(jiffies - delay) < 0) {
+       if (time_before(jiffies, delay)) {
                del_timer(&fd_timer);
                fd_timer.function = function;
                fd_timer.expires = delay;
@@ -1074,7 +1062,7 @@ static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
        return 0;
 }
 
-static spinlock_t floppy_hlt_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(floppy_hlt_lock);
 static int hlt_disabled;
 static void floppy_disable_hlt(void)
 {
@@ -1524,7 +1512,7 @@ static void setup_rw_floppy(void)
                 * again just before spinup completion. Beware that
                 * after scandrives, we must again wait for selection.
                 */
-               if ((signed)(ready_date - jiffies) > DP->select_delay) {
+               if (time_after(ready_date, jiffies + DP->select_delay)) {
                        ready_date -= DP->select_delay;
                        function = (timeout_fn) floppy_start;
                } else
@@ -1546,9 +1534,8 @@ static void setup_rw_floppy(void)
        for (i = 0; i < raw_cmd->cmd_count; i++)
                r |= output_byte(raw_cmd->cmd[i]);
 
-#ifdef DEBUGT
        debugt("rw_command: ");
-#endif
+
        if (r) {
                cont->error();
                reset_fdc();
@@ -1570,9 +1557,7 @@ static int blind_seek;
  */
 static void seek_interrupt(void)
 {
-#ifdef DEBUGT
        debugt("seek interrupt:");
-#endif
        if (inr != 2 || (ST0 & 0xF8) != 0x20) {
                DPRINT("seek failed\n");
                DRS->track = NEED_2_RECAL;
@@ -1676,24 +1661,18 @@ static void seek_floppy(void)
        output_byte(FD_SEEK);
        output_byte(UNIT(current_drive));
        LAST_OUT(track);
-#ifdef DEBUGT
        debugt("seek command:");
-#endif
 }
 
 static void recal_interrupt(void)
 {
-#ifdef DEBUGT
        debugt("recal interrupt:");
-#endif
        if (inr != 2)
                FDCS->reset = 1;
        else if (ST0 & ST0_ECE) {
                switch (DRS->track) {
                case NEED_1_RECAL:
-#ifdef DEBUGT
                        debugt("recal interrupt need 1 recal:");
-#endif
                        /* after a second recalibrate, we still haven't
                         * reached track 0. Probably no drive. Raise an
                         * error, as failing immediately might upset
@@ -1702,9 +1681,7 @@ static void recal_interrupt(void)
                        cont->redo();
                        return;
                case NEED_2_RECAL:
-#ifdef DEBUGT
                        debugt("recal interrupt need 2 recal:");
-#endif
                        /* If we already did a recalibrate,
                         * and we are not at track 0, this
                         * means we have moved. (The only way
@@ -1722,9 +1699,7 @@ static void recal_interrupt(void)
                        DRS->select_date = jiffies;
                        /* fall through */
                default:
-#ifdef DEBUGT
                        debugt("recal interrupt default:");
-#endif
                        /* Recalibrate moves the head by at
                         * most 80 steps. If after one
                         * recalibrate we don't have reached
@@ -1751,7 +1726,7 @@ static void print_result(char *message, int inr)
 }
 
 /* interrupt handler. Note that this can be called externally on the Sparc */
-irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t floppy_interrupt(int irq, void *dev_id)
 {
        void (*handler) (void) = do_floppy;
        int do_print;
@@ -1801,10 +1776,11 @@ irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2
                         && max_sensei);
        }
-       if (handler)
-               schedule_bh(handler);
-       else
+       if (!handler) {
                FDCS->reset = 1;
+               return IRQ_NONE;
+       }
+       schedule_bh(handler);
        is_alive("normal interrupt end");
 
        /* FIXME! Was it really for us? */
@@ -1813,9 +1789,7 @@ irqreturn_t floppy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
 static void recalibrate_floppy(void)
 {
-#ifdef DEBUGT
        debugt("recalibrate floppy:");
-#endif
        do_floppy = recal_interrupt;
        output_byte(FD_RECALIBRATE);
        LAST_OUT(UNIT(current_drive));
@@ -1826,9 +1800,7 @@ static void recalibrate_floppy(void)
  */
 static void reset_interrupt(void)
 {
-#ifdef DEBUGT
        debugt("reset interrupt:");
-#endif
        result();               /* get the status ready for set_fdc */
        if (FDCS->reset) {
                printk("reset set in interrupt, calling %p\n", cont->error);
@@ -1896,7 +1868,7 @@ static void show_floppy(void)
        printk("fdc_busy=%lu\n", fdc_busy);
        if (do_floppy)
                printk("do_floppy=%p\n", do_floppy);
-       if (floppy_work.pending)
+       if (work_pending(&floppy_work))
                printk("floppy_work.func=%p\n", floppy_work.func);
        if (timer_pending(&fd_timer))
                printk("fd_timer.function=%p\n", fd_timer.function);
@@ -2039,7 +2011,7 @@ static void floppy_start(void)
 static void do_wakeup(void)
 {
        reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
-       cont = 0;
+       cont = NULL;
        command_status += 2;
        wake_up(&command_done);
 }
@@ -2266,9 +2238,7 @@ static void redo_format(void)
        buffer_track = -1;
        setup_format_params(format_req.track << STRETCH(_floppy));
        floppy_start();
-#ifdef DEBUGT
        debugt("queue format request");
-#endif
 }
 
 static struct cont_t format_cont = {
@@ -2319,7 +2289,7 @@ static void floppy_end_request(struct request *req, int uptodate)
        add_disk_randomness(req->rq_disk);
        floppy_off((long)req->rq_disk->private_data);
        blkdev_dequeue_request(req);
-       end_that_request_last(req);
+       end_that_request_last(req, uptodate);
 
        /* We're done with the request */
        current_req = NULL;
@@ -2992,9 +2962,7 @@ static void redo_fd_request(void)
                if (TESTF(FD_NEED_TWADDLE))
                        twaddle();
                schedule_bh(floppy_start);
-#ifdef DEBUGT
                debugt("queue fd request");
-#endif
                return;
        }
 #undef REPEAT
@@ -3023,8 +2991,8 @@ static void do_fd_request(request_queue_t * q)
        if (usage_count == 0) {
                printk("warning: usage count=0, current_req=%p exiting\n",
                       current_req);
-               printk("sect=%ld flags=%lx\n", (long)current_req->sector,
-                      current_req->flags);
+               printk("sect=%ld type=%x flags=%x\n", (long)current_req->sector,
+                      current_req->cmd_type, current_req->cmd_flags);
                return;
        }
        if (test_bit(0, &fdc_busy)) {
@@ -3101,19 +3069,19 @@ static int user_reset_fdc(int drive, int arg, int interruptible)
  * Misc Ioctl's and support
  * ========================
  */
-static inline int fd_copyout(void *param, const void *address,
+static inline int fd_copyout(void __user *param, const void *address,
                             unsigned long size)
 {
        return copy_to_user(param, address, size) ? -EFAULT : 0;
 }
 
-static inline int fd_copyin(void *param, void *address, unsigned long size)
+static inline int fd_copyin(void __user *param, void *address, unsigned long size)
 {
        return copy_from_user(address, param, size) ? -EFAULT : 0;
 }
 
-#define _COPYOUT(x) (copy_to_user((void *)param, &(x), sizeof(x)) ? -EFAULT : 0)
-#define _COPYIN(x) (copy_from_user(&(x), (void *)param, sizeof(x)) ? -EFAULT : 0)
+#define _COPYOUT(x) (copy_to_user((void __user *)param, &(x), sizeof(x)) ? -EFAULT : 0)
+#define _COPYIN(x) (copy_from_user(&(x), (void __user *)param, sizeof(x)) ? -EFAULT : 0)
 
 #define COPYOUT(x) ECALL(_COPYOUT(x))
 #define COPYIN(x) ECALL(_COPYIN(x))
@@ -3188,7 +3156,7 @@ static struct cont_t raw_cmd_cont = {
        .done           = raw_cmd_done
 };
 
-static inline int raw_cmd_copyout(int cmd, char *param,
+static inline int raw_cmd_copyout(int cmd, char __user *param,
                                  struct floppy_raw_cmd *ptr)
 {
        int ret;
@@ -3213,7 +3181,7 @@ static void raw_cmd_free(struct floppy_raw_cmd **ptr)
        struct floppy_raw_cmd *next, *this;
 
        this = *ptr;
-       *ptr = 0;
+       *ptr = NULL;
        while (this) {
                if (this->buffer_length) {
                        fd_dma_mem_free((unsigned long)this->kernel_data,
@@ -3226,14 +3194,14 @@ static void raw_cmd_free(struct floppy_raw_cmd **ptr)
        }
 }
 
-static inline int raw_cmd_copyin(int cmd, char *param,
+static inline int raw_cmd_copyin(int cmd, char __user *param,
                                 struct floppy_raw_cmd **rcmd)
 {
        struct floppy_raw_cmd *ptr;
        int ret;
        int i;
 
-       *rcmd = 0;
+       *rcmd = NULL;
        while (1) {
                ptr = (struct floppy_raw_cmd *)
                    kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
@@ -3241,7 +3209,7 @@ static inline int raw_cmd_copyin(int cmd, char *param,
                        return -ENOMEM;
                *rcmd = ptr;
                COPYIN(*ptr);
-               ptr->next = 0;
+               ptr->next = NULL;
                ptr->buffer_length = 0;
                param += sizeof(struct floppy_raw_cmd);
                if (ptr->cmd_count > 33)
@@ -3258,7 +3226,7 @@ static inline int raw_cmd_copyin(int cmd, char *param,
                for (i = 0; i < 16; i++)
                        ptr->reply[i] = 0;
                ptr->resultcode = 0;
-               ptr->kernel_data = 0;
+               ptr->kernel_data = NULL;
 
                if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
                        if (ptr->length <= 0)
@@ -3280,7 +3248,7 @@ static inline int raw_cmd_copyin(int cmd, char *param,
        }
 }
 
-static int raw_cmd_ioctl(int cmd, void *param)
+static int raw_cmd_ioctl(int cmd, void __user *param)
 {
        int drive, ret, ret2;
        struct floppy_raw_cmd *my_raw_cmd;
@@ -3340,11 +3308,6 @@ static int invalidate_drive(struct block_device *bdev)
        return 0;
 }
 
-static inline void clear_write_error(int drive)
-{
-       CLEARSTRUCT(UDRWE);
-}
-
 static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
                               int drive, int type, struct block_device *bdev)
 {
@@ -3360,7 +3323,7 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
        if (type) {
                if (!capable(CAP_SYS_ADMIN))
                        return -EPERM;
-               down(&open_lock);
+               mutex_lock(&open_lock);
                LOCK_FDC(drive, 1);
                floppy_type[type] = *g;
                floppy_type[type].name = "user format";
@@ -3372,9 +3335,9 @@ static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
                        struct block_device *bdev = opened_bdev[cnt];
                        if (!bdev || ITYPE(drive_state[cnt].fd_device) != type)
                                continue;
-                       __invalidate_device(bdev, 0);
+                       __invalidate_device(bdev);
                }
-               up(&open_lock);
+               mutex_unlock(&open_lock);
        } else {
                int oldStretch;
                LOCK_FDC(drive, 1);
@@ -3470,6 +3433,23 @@ static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
        return 0;
 }
 
+static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+{
+       int drive = (long)bdev->bd_disk->private_data;
+       int type = ITYPE(drive_state[drive].fd_device);
+       struct floppy_struct *g;
+       int ret;
+
+       ret = get_floppy_geometry(drive, type, &g);
+       if (ret)
+               return ret;
+
+       geo->heads = g->head;
+       geo->sectors = g->sect;
+       geo->cylinders = g->track;
+       return 0;
+}
+
 static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                    unsigned long param)
 {
@@ -3499,23 +3479,6 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                cmd = FDEJECT;
        }
 
-       /* generic block device ioctls */
-       switch (cmd) {
-               /* the following have been inspired by the corresponding
-                * code for other block devices. */
-               struct floppy_struct *g;
-       case HDIO_GETGEO:
-               {
-                       struct hd_geometry loc;
-                       ECALL(get_floppy_geometry(drive, type, &g));
-                       loc.heads = g->head;
-                       loc.sectors = g->sect;
-                       loc.cylinders = g->track;
-                       loc.start = 0;
-                       return _COPYOUT(loc);
-               }
-       }
-
        /* convert the old style command into a new style command */
        if ((cmd & 0xff00) == 0x0200) {
                ECALL(normalize_ioctl(&cmd, &size));
@@ -3530,7 +3493,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
        /* copyin */
        CLEARSTRUCT(&inparam);
        if (_IOC_DIR(cmd) & _IOC_WRITE)
-           ECALL(fd_copyin((void *)param, &inparam, size))
+           ECALL(fd_copyin((void __user *)param, &inparam, size))
 
                switch (cmd) {
                case FDEJECT:
@@ -3626,7 +3589,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                                return -EINVAL;
                        LOCK_FDC(drive, 1);
                        set_floppy(drive);
-                       CALL(i = raw_cmd_ioctl(cmd, (void *)param));
+                       CALL(i = raw_cmd_ioctl(cmd, (void __user *)param));
                        process_fd_request();
                        return i;
 
@@ -3641,7 +3604,7 @@ static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
                }
 
        if (_IOC_DIR(cmd) & _IOC_READ)
-               return fd_copyout((void *)param, outparam, size);
+               return fd_copyout((void __user *)param, outparam, size);
        else
                return 0;
 #undef OUT
@@ -3670,7 +3633,7 @@ static void __init config_types(void)
                const char *name = NULL;
                static char temparea[32];
 
-               if (type < NUMBER(default_drive_params)) {
+               if (type < ARRAY_SIZE(default_drive_params)) {
                        params = &default_drive_params[type].params;
                        if (type) {
                                name = default_drive_params[type].name;
@@ -3689,7 +3652,6 @@ static void __init config_types(void)
                                first = 0;
                        }
                        printk("%s fd%d is %s", prepend, drive, name);
-                       register_devfs_entries(drive);
                }
                *UDP = *params;
        }
@@ -3701,7 +3663,7 @@ static int floppy_release(struct inode *inode, struct file *filp)
 {
        int drive = (long)inode->i_bdev->bd_disk->private_data;
 
-       down(&open_lock);
+       mutex_lock(&open_lock);
        if (UDRS->fd_ref < 0)
                UDRS->fd_ref = 0;
        else if (!UDRS->fd_ref--) {
@@ -3710,8 +3672,8 @@ static int floppy_release(struct inode *inode, struct file *filp)
        }
        if (!UDRS->fd_ref)
                opened_bdev[drive] = NULL;
-       floppy_release_irq_and_dma();
-       up(&open_lock);
+       mutex_unlock(&open_lock);
+
        return 0;
 }
 
@@ -3729,7 +3691,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
        char *tmp;
 
        filp->private_data = (void *)0;
-       down(&open_lock);
+       mutex_lock(&open_lock);
        old_dev = UDRS->fd_device;
        if (opened_bdev[drive] && opened_bdev[drive] != inode->i_bdev)
                goto out2;
@@ -3742,9 +3704,6 @@ static int floppy_open(struct inode *inode, struct file *filp)
        if (UDRS->fd_ref == -1 || (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
                goto out2;
 
-       if (floppy_grab_irq_and_dma())
-               goto out2;
-
        if (filp->f_flags & O_EXCL)
                UDRS->fd_ref = -1;
        else
@@ -3795,8 +3754,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
        /* Allow ioctls if we have write-permissions even if read-only open.
         * Needed so that programs such as fdrawcmd still can work on write
         * protected disks */
-       if (filp->f_mode & 2
-           || permission(filp->f_dentry->d_inode, 2, NULL) == 0)
+       if ((filp->f_mode & FMODE_WRITE) || !file_permission(filp, MAY_WRITE))
                filp->private_data = (void *)8;
 
        if (UFDCS->rawcmd == 1)
@@ -3813,7 +3771,7 @@ static int floppy_open(struct inode *inode, struct file *filp)
                if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
                        goto out;
        }
-       up(&open_lock);
+       mutex_unlock(&open_lock);
        return 0;
 out:
        if (UDRS->fd_ref < 0)
@@ -3822,9 +3780,8 @@ out:
                UDRS->fd_ref--;
        if (!UDRS->fd_ref)
                opened_bdev[drive] = NULL;
-       floppy_release_irq_and_dma();
 out2:
-       up(&open_lock);
+       mutex_unlock(&open_lock);
        return res;
 }
 
@@ -3838,15 +3795,10 @@ static int check_floppy_change(struct gendisk *disk)
        if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
                return 1;
 
-       if (UDP->checkfreq < (int)(jiffies - UDRS->last_checked)) {
-               if (floppy_grab_irq_and_dma()) {
-                       return 1;
-               }
-
+       if (time_after(jiffies, UDRS->last_checked + UDP->checkfreq)) {
                lock_fdc(drive, 0);
                poll_drive(0, 0);
                process_fd_request();
-               floppy_release_irq_and_dma();
        }
 
        if (UTESTF(FD_DISK_CHANGED) ||
@@ -3964,40 +3916,10 @@ static struct block_device_operations floppy_fops = {
        .open           = floppy_open,
        .release        = floppy_release,
        .ioctl          = fd_ioctl,
+       .getgeo         = fd_getgeo,
        .media_changed  = check_floppy_change,
        .revalidate_disk = floppy_revalidate,
 };
-static char *table[] = {
-       "", "d360", "h1200", "u360", "u720", "h360", "h720",
-       "u1440", "u2880", "CompaQ", "h1440", "u1680", "h410",
-       "u820", "h1476", "u1722", "h420", "u830", "h1494", "u1743",
-       "h880", "u1040", "u1120", "h1600", "u1760", "u1920",
-       "u3200", "u3520", "u3840", "u1840", "u800", "u1600",
-       NULL
-};
-static int t360[] = { 1, 0 },
-       t1200[] = { 2, 5, 6, 10, 12, 14, 16, 18, 20, 23, 0 },
-       t3in[] = { 8, 9, 26, 27, 28, 7, 11, 15, 19, 24, 25, 29, 31, 3, 4, 13,
-                       17, 21, 22, 30, 0 };
-static int *table_sup[] =
-    { NULL, t360, t1200, t3in + 5 + 8, t3in + 5, t3in, t3in };
-
-static void __init register_devfs_entries(int drive)
-{
-       int base_minor = (drive < 4) ? drive : (124 + drive);
-
-       if (UDP->cmos < NUMBER(default_drive_params)) {
-               int i = 0;
-               do {
-                       int minor = base_minor + (table_sup[UDP->cmos][i] << 2);
-
-                       devfs_mk_bdev(MKDEV(FLOPPY_MAJOR, minor),
-                                     S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |
-                                     S_IWGRP, "floppy/%d%s", drive,
-                                     table[table_sup[UDP->cmos][i]]);
-               } while (table_sup[UDP->cmos][i++]);
-       }
-}
 
 /*
  * Floppy Driver initialization
@@ -4146,33 +4068,33 @@ static struct param_table {
        int *var;
        int def_param;
        int param2;
-} config_params[] = {
-       {"allowed_drive_mask", 0, &allowed_drive_mask, 0xff, 0}, /* obsolete */
-       {"all_drives", 0, &allowed_drive_mask, 0xff, 0},        /* obsolete */
-       {"asus_pci", 0, &allowed_drive_mask, 0x33, 0},
-       {"irq", 0, &FLOPPY_IRQ, 6, 0},
-       {"dma", 0, &FLOPPY_DMA, 2, 0},
-       {"daring", daring, 0, 1, 0},
+} config_params[] __initdata = {
+       {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
+       {"all_drives", NULL, &allowed_drive_mask, 0xff, 0},     /* obsolete */
+       {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0},
+       {"irq", NULL, &FLOPPY_IRQ, 6, 0},
+       {"dma", NULL, &FLOPPY_DMA, 2, 0},
+       {"daring", daring, NULL, 1, 0},
 #if N_FDC > 1
-       {"two_fdc", 0, &FDC2, 0x370, 0},
-       {"one_fdc", 0, &FDC2, 0, 0},
+       {"two_fdc", NULL, &FDC2, 0x370, 0},
+       {"one_fdc", NULL, &FDC2, 0, 0},
 #endif
-       {"thinkpad", floppy_set_flags, 0, 1, FD_INVERTED_DCL},
-       {"broken_dcl", floppy_set_flags, 0, 1, FD_BROKEN_DCL},
-       {"messages", floppy_set_flags, 0, 1, FTD_MSG},
-       {"silent_dcl_clear", floppy_set_flags, 0, 1, FD_SILENT_DCL_CLEAR},
-       {"debug", floppy_set_flags, 0, 1, FD_DEBUG},
-       {"nodma", 0, &can_use_virtual_dma, 1, 0},
-       {"omnibook", 0, &can_use_virtual_dma, 1, 0},
-       {"yesdma", 0, &can_use_virtual_dma, 0, 0},
-       {"fifo_depth", 0, &fifo_depth, 0xa, 0},
-       {"nofifo", 0, &no_fifo, 0x20, 0},
-       {"usefifo", 0, &no_fifo, 0, 0},
-       {"cmos", set_cmos, 0, 0, 0},
-       {"slow", 0, &slow_floppy, 1, 0},
-       {"unexpected_interrupts", 0, &print_unex, 1, 0},
-       {"no_unexpected_interrupts", 0, &print_unex, 0, 0},
-       {"L40SX", 0, &print_unex, 0, 0}
+       {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL},
+       {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL},
+       {"messages", floppy_set_flags, NULL, 1, FTD_MSG},
+       {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR},
+       {"debug", floppy_set_flags, NULL, 1, FD_DEBUG},
+       {"nodma", NULL, &can_use_virtual_dma, 1, 0},
+       {"omnibook", NULL, &can_use_virtual_dma, 1, 0},
+       {"yesdma", NULL, &can_use_virtual_dma, 0, 0},
+       {"fifo_depth", NULL, &fifo_depth, 0xa, 0},
+       {"nofifo", NULL, &no_fifo, 0x20, 0},
+       {"usefifo", NULL, &no_fifo, 0, 0},
+       {"cmos", set_cmos, NULL, 0, 0},
+       {"slow", NULL, &slow_floppy, 1, 0},
+       {"unexpected_interrupts", NULL, &print_unex, 1, 0},
+       {"no_unexpected_interrupts", NULL, &print_unex, 0, 0},
+       {"L40SX", NULL, &print_unex, 0, 0}
 
        EXTRA_FLOPPY_PARAMS
 };
@@ -4212,24 +4134,30 @@ static int __init floppy_setup(char *str)
                printk("\n");
        } else
                DPRINT("botched floppy option\n");
-       DPRINT("Read linux/Documentation/floppy.txt\n");
+       DPRINT("Read Documentation/floppy.txt\n");
        return 0;
 }
 
 static int have_no_fdc = -ENODEV;
 
+static ssize_t floppy_cmos_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct platform_device *p;
+       int drive;
+
+       p = container_of(dev, struct platform_device,dev);
+       drive = p->id;
+       return sprintf(buf, "%X\n", UDP->cmos);
+}
+DEVICE_ATTR(cmos,S_IRUGO,floppy_cmos_show,NULL);
+
 static void floppy_device_release(struct device *dev)
 {
        complete(&device_release);
 }
 
-static struct platform_device floppy_device = {
-       .name           = "floppy",
-       .id             = 0,
-       .dev            = {
-                       .release = floppy_device_release,
-                       }
-};
+static struct platform_device floppy_device[N_DRIVE];
 
 static struct kobject *floppy_find(dev_t dev, int *part, void *data)
 {
@@ -4238,44 +4166,51 @@ static struct kobject *floppy_find(dev_t dev, int *part, void *data)
            !(allowed_drive_mask & (1 << drive)) ||
            fdc_state[FDC(drive)].version == FDC_NONE)
                return NULL;
-       if (((*part >> 2) & 0x1f) >= NUMBER(floppy_type))
+       if (((*part >> 2) & 0x1f) >= ARRAY_SIZE(floppy_type))
                return NULL;
        *part = 0;
        return get_disk(disks[drive]);
 }
 
-int __init floppy_init(void)
+static int __init floppy_init(void)
 {
        int i, unit, drive;
-       int err;
+       int err, dr;
+
+#if defined(CONFIG_PPC_MERGE)
+       if (check_legacy_ioport(FDC1))
+               return -ENODEV;
+#endif
 
        raw_cmd = NULL;
 
-       for (i = 0; i < N_DRIVE; i++) {
-               disks[i] = alloc_disk(1);
-               if (!disks[i])
-                       goto Enomem;
+       for (dr = 0; dr < N_DRIVE; dr++) {
+               disks[dr] = alloc_disk(1);
+               if (!disks[dr]) {
+                       err = -ENOMEM;
+                       goto out_put_disk;
+               }
 
-               disks[i]->major = FLOPPY_MAJOR;
-               disks[i]->first_minor = TOMINOR(i);
-               disks[i]->fops = &floppy_fops;
-               sprintf(disks[i]->disk_name, "fd%d", i);
+               disks[dr]->major = FLOPPY_MAJOR;
+               disks[dr]->first_minor = TOMINOR(dr);
+               disks[dr]->fops = &floppy_fops;
+               sprintf(disks[dr]->disk_name, "fd%d", dr);
 
-               init_timer(&motor_off_timer[i]);
-               motor_off_timer[i].data = i;
-               motor_off_timer[i].function = motor_off_callback;
+               init_timer(&motor_off_timer[dr]);
+               motor_off_timer[dr].data = dr;
+               motor_off_timer[dr].function = motor_off_callback;
        }
 
-       devfs_mk_dir("floppy");
-       if ((err = register_blkdev(FLOPPY_MAJOR, "fd")))
-               goto out;
+       err = register_blkdev(FLOPPY_MAJOR, "fd");
+       if (err)
+               goto out_put_disk;
 
        floppy_queue = blk_init_queue(do_fd_request, &floppy_lock);
-       blk_queue_max_sectors(floppy_queue, 64);
        if (!floppy_queue) {
                err = -ENOMEM;
-               goto fail_queue;
+               goto out_unreg_blkdev;
        }
+       blk_queue_max_sectors(floppy_queue, 64);
 
        blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
                            floppy_find, NULL, NULL);
@@ -4306,17 +4241,20 @@ int __init floppy_init(void)
        use_virtual_dma = can_use_virtual_dma & 1;
        fdc_state[0].address = FDC1;
        if (fdc_state[0].address == -1) {
+               del_timer(&fd_timeout);
                err = -ENODEV;
-               goto out1;
+               goto out_unreg_region;
        }
 #if N_FDC > 1
        fdc_state[1].address = FDC2;
 #endif
 
        fdc = 0;                /* reset fdc in case of unexpected interrupt */
-       if (floppy_grab_irq_and_dma()) {
+       err = floppy_grab_irq_and_dma();
+       if (err) {
+               del_timer(&fd_timeout);
                err = -EBUSY;
-               goto out1;
+               goto out_unreg_region;
        }
 
        /* initialise drive state */
@@ -4330,6 +4268,12 @@ int __init floppy_init(void)
                floppy_track_buffer = NULL;
                max_buffer_sectors = 0;
        }
+       /*
+        * Small 10 msec delay to let through any interrupt that
+        * initialization might have triggered, to not
+        * confuse detection:
+        */
+       msleep(10);
 
        for (i = 0; i < N_FDC; i++) {
                fdc = i;
@@ -4369,15 +4313,11 @@ int __init floppy_init(void)
        fdc = 0;
        del_timer(&fd_timeout);
        current_drive = 0;
-       floppy_release_irq_and_dma();
        initialising = 0;
        if (have_no_fdc) {
                DPRINT("no floppy controllers found\n");
-               flush_scheduled_work();
-               if (usage_count)
-                       floppy_release_irq_and_dma();
                err = have_no_fdc;
-               goto out2;
+               goto out_flush_work;
        }
 
        for (drive = 0; drive < N_DRIVE; drive++) {
@@ -4385,36 +4325,44 @@ int __init floppy_init(void)
                        continue;
                if (fdc_state[FDC(drive)].version == FDC_NONE)
                        continue;
+
+               floppy_device[drive].name = floppy_device_name;
+               floppy_device[drive].id = drive;
+               floppy_device[drive].dev.release = floppy_device_release;
+
+               err = platform_device_register(&floppy_device[drive]);
+               if (err)
+                       goto out_flush_work;
+
+               device_create_file(&floppy_device[drive].dev,&dev_attr_cmos);
                /* to be cleaned up... */
                disks[drive]->private_data = (void *)(long)drive;
                disks[drive]->queue = floppy_queue;
                disks[drive]->flags |= GENHD_FL_REMOVABLE;
+               disks[drive]->driverfs_dev = &floppy_device[drive].dev;
                add_disk(disks[drive]);
        }
 
-       platform_device_register(&floppy_device);
        return 0;
 
-out1:
-       del_timer(&fd_timeout);
-out2:
+out_flush_work:
+       flush_scheduled_work();
+       if (usage_count)
+               floppy_release_irq_and_dma();
+out_unreg_region:
        blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
        blk_cleanup_queue(floppy_queue);
-fail_queue:
+out_unreg_blkdev:
        unregister_blkdev(FLOPPY_MAJOR, "fd");
-out:
-       for (i = 0; i < N_DRIVE; i++)
-               put_disk(disks[i]);
-       devfs_remove("floppy");
+out_put_disk:
+       while (dr--) {
+               del_timer(&motor_off_timer[dr]);
+               put_disk(disks[dr]);
+       }
        return err;
-
-Enomem:
-       while (i--)
-               put_disk(disks[i]);
-       return -ENOMEM;
 }
 
-static spinlock_t floppy_usage_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(floppy_usage_lock);
 
 static int floppy_grab_irq_and_dma(void)
 {
@@ -4426,6 +4374,13 @@ static int floppy_grab_irq_and_dma(void)
                return 0;
        }
        spin_unlock_irqrestore(&floppy_usage_lock, flags);
+
+       /*
+        * We might have scheduled a free_irq(), wait it to
+        * drain first:
+        */
+       flush_scheduled_work();
+
        if (fd_request_irq()) {
                DPRINT("Unable to grab IRQ%d for the floppy driver\n",
                       FLOPPY_IRQ);
@@ -4543,7 +4498,7 @@ static void floppy_release_irq_and_dma(void)
                printk("floppy timer still active:%s\n", timeout_message);
        if (timer_pending(&fd_timer))
                printk("auxiliary floppy timer still active\n");
-       if (floppy_work.pending)
+       if (work_pending(&floppy_work))
                printk("work still pending\n");
 #endif
        old_fdc = fdc;
@@ -4557,20 +4512,7 @@ static void floppy_release_irq_and_dma(void)
 
 #ifdef MODULE
 
-char *floppy;
-
-static void unregister_devfs_entries(int drive)
-{
-       int i;
-
-       if (UDP->cmos < NUMBER(default_drive_params)) {
-               i = 0;
-               do {
-                       devfs_remove("floppy/%d%s", drive,
-                                    table[table_sup[UDP->cmos][i]]);
-               } while (table_sup[UDP->cmos][i++]);
-       }
-}
+static char *floppy;
 
 static void __init parse_floppy_cfg_string(char *cfg)
 {
@@ -4587,10 +4529,8 @@ static void __init parse_floppy_cfg_string(char *cfg)
        }
 }
 
-int init_module(void)
+int __init init_module(void)
 {
-       printk(KERN_INFO "inserting floppy driver for " UTS_RELEASE "\n");
-
        if (floppy)
                parse_floppy_cfg_string(floppy);
        return floppy_init();
@@ -4601,7 +4541,6 @@ void cleanup_module(void)
        int drive;
 
        init_completion(&device_release);
-       platform_device_unregister(&floppy_device);
        blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
        unregister_blkdev(FLOPPY_MAJOR, "fd");
 
@@ -4611,11 +4550,11 @@ void cleanup_module(void)
                if ((allowed_drive_mask & (1 << drive)) &&
                    fdc_state[FDC(drive)].version != FDC_NONE) {
                        del_gendisk(disks[drive]);
-                       unregister_devfs_entries(drive);
+                       device_remove_file(&floppy_device[drive].dev, &dev_attr_cmos);
+                       platform_device_unregister(&floppy_device[drive]);
                }
                put_disk(disks[drive]);
        }
-       devfs_remove("floppy");
 
        del_timer_sync(&fd_timeout);
        del_timer_sync(&fd_timer);
@@ -4630,9 +4569,9 @@ void cleanup_module(void)
        wait_for_completion(&device_release);
 }
 
-MODULE_PARM(floppy, "s");
-MODULE_PARM(FLOPPY_IRQ, "i");
-MODULE_PARM(FLOPPY_DMA, "i");
+module_param(floppy, charp, 0);
+module_param(FLOPPY_IRQ, int, 0);
+module_param(FLOPPY_DMA, int, 0);
 MODULE_AUTHOR("Alain L. Knaff");
 MODULE_SUPPORTED_DEVICE("fd");
 MODULE_LICENSE("GPL");