linux/drivers/block/floppy.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 *  linux/drivers/block/floppy.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 *  Copyright (C) 1993, 1994  Alain Knaff
 *  Copyright (C) 1998 Alan Cox
 */

/*
 * 02.12.91 - Changed to static variables to indicate need for reset
 * and recalibrate. This makes some things easier (output_byte reset
 * checking etc), and means less interrupt jumping in case of errors,
 * so the code is hopefully easier to understand.
 */

/*
 * This file is certainly a mess. I've tried my best to get it working,
 * but I don't like programming floppies, and I have only one anyway.
 * Urgel. I should check for more errors, and do more graceful error
 * recovery. Seems there are problems with several drives. I've tried to
 * correct them. No promises.
 */

/*
 * As with hd.c, all routines within this file can (and will) be called
 * by interrupts, so extreme caution is needed. A hardware interrupt
 * handler may not sleep, or a kernel panic will happen. Thus I cannot
 * call "floppy-on" directly, but have to set a special timer interrupt
 * etc.
 */

/*
 * 28.02.92 - made track-buffering routines, based on the routines written
 * by [email protected] (Lawrence Foard). Linus.
 */

/*
 * Automatic floppy-detection and formatting written by Werner Almesberger
 * ([email protected]), who also corrected some problems with
 * the floppy-change signal detection.
 */

/*
 * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
 * FDC data overrun bug, added some preliminary stuff for vertical
 * recording support.
 *
 * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
 *
 * TODO: Errors are still not counted properly.
 */

/* 1992/9/20
 * Modifications for ``Sector Shifting'' by Rob Hooft ([email protected])
 * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
 * Christoph H. Hochst\"atter.
 * I have fixed the shift values to the ones I always use. Maybe a new
 * ioctl() should be created to be able to modify them.
 * There is a bug in the driver that makes it impossible to format a
 * floppy as the first thing after bootup.
 */

/*
 * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
 * this helped the floppy driver as well. Much cleaner, and still seems to
 * work.
 */

/* 1994/6/24 --bbroad-- added the floppy table entries and made
 * minor modifications to allow 2.88 floppies to be run.
 */

/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
 * disk types.
 */

/*
 * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
 * format bug fixes, but unfortunately some new bugs too...
 */

/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
 * errors to allow safe writing by specialized programs.
 */

/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
 * by defining bit 1 of the "stretch" parameter to mean put sectors on the
 * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
 * drives are "upside-down").
 */

/*
 * 1995/8/26 -- Andreas Busse -- added Mips support.
 */

/*
 * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
 * features to asm/floppy.h.
 */

/*
 * 1998/1/21 -- Richard Gooch <[email protected]> -- 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 &
 * use of '0' for NULL.
 */

/*
 * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation
 * failures.
 */

/*
 * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives.
 */

/*
 * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24
 * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were
 * being used to store jiffies, which are unsigned longs).
 */

/*
 * 2000/08/28 -- Arnaldo Carvalho de Melo <[email protected]>
 * - get rid of check_region
 * - s/suser/capable/
 */

/*
 * 2001/08/26 -- Paul Gortmaker - fix insmod oops on machines with no
 * floppy controller (lingering task on list after module is gone... boom.)
 */

/*
 * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range
 * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix
 * requires many non-obvious changes in arch dependent code.
 */

/* 2003/07/28 -- Daniele Bellucci <[email protected]>.
 * Better audit of register_blkdev.
 */

#define REALLY_SLOW_IO

#define DEBUGT

#define DPRINT(format, args...)

#define DCL_DEBUG
#ifdef DCL_DEBUG
#define debug_dcl(test, fmt, args...)
#else
#define debug_dcl
#endif

/* do print messages for unexpected interrupts */
static int print_unex =;
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/fdreg.h>
#include <linux/fd.h>
#include <linux/hdreg.h>
#include <linux/errno.h>
#include <linux/slab.h>
#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/major.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/async.h>
#include <linux/compat.h>

/*
 * PS/2 floppies have much slower step rates than regular floppies.
 * It's been recommended that take about 1/4 of the default speed
 * in some more extreme cases.
 */
static DEFINE_MUTEX(floppy_mutex);
static int slow_floppy;

#include <asm/dma.h>
#include <asm/irq.h>

static int FLOPPY_IRQ =;
static int FLOPPY_DMA =;
static int can_use_virtual_dma =;
/* =======
 * can use virtual DMA:
 * 0 = use of virtual DMA disallowed by config
 * 1 = use of virtual DMA prescribed by config
 * 2 = no virtual DMA preference configured.  By default try hard DMA,
 * but fall back on virtual DMA when not enough memory available
 */

static int use_virtual_dma;
/* =======
 * use virtual DMA
 * 0 using hard DMA
 * 1 using virtual DMA
 * This variable is set to virtual when a DMA mem problem arises, and
 * reset back in floppy_grab_irq_and_dma.
 * It is not safe to reset it in other circumstances, because the floppy
 * driver may have several buffers in use at once, and we do currently not
 * record each buffers capabilities
 */

static DEFINE_SPINLOCK(floppy_lock);

static unsigned short virtual_dma_port =;
irqreturn_t floppy_interrupt(int irq, void *dev_id);
static int set_dor(int fdc, char mask, char data);

#define K_64

/* the following is the mask of allowed drives. By default units 2 and
 * 3 of both floppy controllers are disabled, because switching on the
 * motor of these drives causes system hangs on some PCI computers. drive
 * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
 * a drive is allowed.
 *
 * NOTE: This must come before we include the arch floppy header because
 *       some ports reference this variable from there. -DaveM
 */

static int allowed_drive_mask =;

#include <asm/floppy.h>

static int irqdma_allocated;

#include <linux/blk-mq.h>
#include <linux/blkpg.h>
#include <linux/cdrom.h>	/* for the compatibility eject ioctl */
#include <linux/completion.h>

static LIST_HEAD(floppy_reqs);
static struct request *current_req;
static int set_next_request(void);

#ifndef fd_get_dma_residue
#define fd_get_dma_residue
#endif

/* Dma Memory related stuff */

#ifndef fd_dma_mem_free
#define fd_dma_mem_free
#endif

#ifndef fd_dma_mem_alloc
#define fd_dma_mem_alloc
#endif

#ifndef fd_cacheflush
#define fd_cacheflush(addr, size)
#endif

static inline void fallback_on_nodma_alloc(char **addr, size_t l)
{}

/* End dma memory related stuff */

static unsigned long fake_change;
static bool initialized;

#define ITYPE(x)
#define TOMINOR(x)
#define UNIT(x)
#define FDC(x)
	/* reverse mapping from unit and fdc to drive */
#define REVDRIVE(fdc, unit)

#define PH_HEAD(floppy, head)
#define STRETCH(floppy)

/* read/write commands */
#define COMMAND
#define DR_SELECT
#define TRACK
#define HEAD
#define SECTOR
#define SIZECODE
#define SECT_PER_TRACK
#define GAP
#define SIZECODE2
#define NR_RW

/* format commands */
#define F_SIZECODE
#define F_SECT_PER_TRACK
#define F_GAP
#define F_FILL
#define NR_F

/*
 * Maximum disk size (in kilobytes).
 * This default is used whenever the current disk size is unknown.
 * [Now it is rather a minimum]
 */
#define MAX_DISK_SIZE

/*
 * globals used by 'result()'
 */
static unsigned char reply_buffer[FD_RAW_REPLY_SIZE];
static int inr;		/* size of reply buffer, when called from interrupt */
#define ST0
#define ST1
#define ST2
#define ST3
#define R_TRACK
#define R_HEAD
#define R_SECTOR
#define R_SIZECODE

#define SEL_DLY

/*
 * this struct defines the different floppy drive types.
 */
static struct {} default_drive_params[] =;

static struct floppy_drive_params drive_params[N_DRIVE];
static struct floppy_drive_struct drive_state[N_DRIVE];
static struct floppy_write_errors write_errors[N_DRIVE];
static struct timer_list motor_off_timer[N_DRIVE];
static struct blk_mq_tag_set tag_sets[N_DRIVE];
static struct gendisk *opened_disk[N_DRIVE];
static DEFINE_MUTEX(open_lock);
static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;

/*
 * This struct defines the different floppy types.
 *
 * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
 * types (e.g. 360kB diskette in 1.2MB drive, etc.).  Bit 1 of 'stretch'
 * tells if the disk is in Commodore 1581 format, which means side 0 sectors
 * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
 * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
 * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
 * side 0 is on physical side 0 (but with the misnamed sector IDs).
 * 'stretch' should probably be renamed to something more general, like
 * 'options'.
 *
 * Bits 2 through 9 of 'stretch' tell the number of the first sector.
 * The LSB (bit 2) is flipped. For most disks, the first sector
 * is 1 (represented by 0x00<<2).  For some CP/M and music sampler
 * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2).
 * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2).
 *
 * Other parameters should be self-explanatory (see also setfdprm(8)).
 */
/*
	    Size
	     |  Sectors per track
	     |  | Head
	     |  | |  Tracks
	     |  | |  | Stretch
	     |  | |  | |  Gap 1 size
	     |  | |  | |    |  Data rate, | 0x40 for perp
	     |  | |  | |    |    |  Spec1 (stepping rate, head unload
	     |  | |  | |    |    |    |    /fmt gap (gap2) */
static struct floppy_struct floppy_type[32] =;

static struct gendisk *disks[N_DRIVE][ARRAY_SIZE(floppy_type)];

#define SECTSIZE

/* Auto-detection: Disk type used until the next media change occurs. */
static struct floppy_struct *current_type[N_DRIVE];

/*
 * User-provided type information. current_type points to
 * the respective entry of this array.
 */
static struct floppy_struct user_params[N_DRIVE];

static sector_t floppy_sizes[256];

static char floppy_device_name[] =;

/*
 * The driver is trying to determine the correct media format
 * while probing is set. rw_interrupt() clears it after a
 * successful access.
 */
static int probing;

/* Synchronization of FDC access. */
#define FD_COMMAND_NONE
#define FD_COMMAND_ERROR
#define FD_COMMAND_OKAY

static volatile int command_status =;
static unsigned long fdc_busy;
static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
static DECLARE_WAIT_QUEUE_HEAD(command_done);

/* errors encountered on the current (or last) request */
static int floppy_errors;

/* Format request descriptor. */
static struct format_descr format_req;

/*
 * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
 * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
 * H is head unload time (1=16ms, 2=32ms, etc)
 */

/*
 * Track buffer
 * Because these are written to by the DMA controller, they must
 * not contain a 64k byte boundary crossing, or data will be
 * corrupted/lost.
 */
static char *floppy_track_buffer;
static int max_buffer_sectors;

static const struct cont_t {} *cont;

static void floppy_ready(void);
static void floppy_start(void);
static void process_fd_request(void);
static void recalibrate_floppy(void);
static void floppy_shutdown(struct work_struct *);

static int floppy_request_regions(int);
static void floppy_release_regions(int);
static int floppy_grab_irq_and_dma(void);
static void floppy_release_irq_and_dma(void);

/*
 * The "reset" variable should be tested whenever an interrupt is scheduled,
 * after the commands have been sent. This is to ensure that the driver doesn't
 * get wedged when the interrupt doesn't come because of a failed command.
 * reset doesn't need to be tested before sending commands, because
 * output_byte is automatically disabled when reset is set.
 */
static void reset_fdc(void);
static int floppy_revalidate(struct gendisk *disk);

/*
 * These are global variables, as that's the easiest way to give
 * information to interrupts. They are the data used for the current
 * request.
 */
#define NO_TRACK
#define NEED_1_RECAL
#define NEED_2_RECAL

static atomic_t usage_count =;

/* buffer related variables */
static int buffer_track =;
static int buffer_drive =;
static int buffer_min =;
static int buffer_max =;

/* fdc related variables, should end up in a struct */
static struct floppy_fdc_state fdc_state[N_FDC];
static int current_fdc;			/* current fdc */

static struct workqueue_struct *floppy_wq;

static struct floppy_struct *_floppy =;
static unsigned char current_drive;
static long current_count_sectors;
static unsigned char fsector_t;	/* sector in track */
static unsigned char in_sector_offset;	/* offset within physical sector,
					 * expressed in units of 512 bytes */

static inline unsigned char fdc_inb(int fdc, int reg)
{}

static inline void fdc_outb(unsigned char value, int fdc, int reg)
{}

static inline bool drive_no_geom(int drive)
{}

#ifndef fd_eject
static inline int fd_eject(int drive)
{}
#endif

/*
 * Debugging
 * =========
 */
#ifdef DEBUGT
static long unsigned debugtimer;

static inline void set_debugt(void)
{}

static inline void debugt(const char *func, const char *msg)
{}
#else
static inline void set_debugt(void) { }
static inline void debugt(const char *func, const char *msg) { }
#endif /* DEBUGT */


static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown);
static const char *timeout_message;

static void is_alive(const char *func, const char *message)
{}

static void (*do_floppy)(void) =;

#define OLOGSIZE

static void (*lasthandler)(void);
static unsigned long interruptjiffies;
static unsigned long resultjiffies;
static int resultsize;
static unsigned long lastredo;

static struct output_log {} output_log[OLOGSIZE];

static int output_log_pos;

#define MAXTIMEOUT

static void __reschedule_timeout(int drive, const char *message)
{}

static void reschedule_timeout(int drive, const char *message)
{}

#define INFBOUND(a, b)
#define SUPBOUND(a, b)

/*
 * Bottom half floppy driver.
 * ==========================
 *
 * This part of the file contains the code talking directly to the hardware,
 * and also the main service loop (seek-configure-spinup-command)
 */

/*
 * disk change.
 * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
 * and the last_checked date.
 *
 * last_checked is the date of the last check which showed 'no disk change'
 * FD_DISK_CHANGE is set under two conditions:
 * 1. The floppy has been changed after some i/o to that floppy already
 *    took place.
 * 2. No floppy disk is in the drive. This is done in order to ensure that
 *    requests are quickly flushed in case there is no disk in the drive. It
 *    follows that FD_DISK_CHANGE can only be cleared if there is a disk in
 *    the drive.
 *
 * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
 * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
 *  each seek. If a disk is present, the disk change line should also be
 *  cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
 *  change line is set, this means either that no disk is in the drive, or
 *  that it has been removed since the last seek.
 *
 * This means that we really have a third possibility too:
 *  The floppy has been changed after the last seek.
 */

static int disk_change(int drive)
{}

static inline int is_selected(int dor, int unit)
{}

static bool is_ready_state(int status)
{}

static int set_dor(int fdc, char mask, char data)
{}

static void twaddle(int fdc, int drive)
{}

/*
 * Reset all driver information about the specified fdc.
 * This is needed after a reset, and after a raw command.
 */
static void reset_fdc_info(int fdc, int mode)
{}

/*
 * selects the fdc and drive, and enables the fdc's input/dma.
 * Both current_drive and current_fdc are changed to match the new drive.
 */
static void set_fdc(int drive)
{}

/*
 * locks the driver.
 * Both current_drive and current_fdc are changed to match the new drive.
 */
static int lock_fdc(int drive)
{}

/* unlocks the driver */
static void unlock_fdc(void)
{}

/* switches the motor off after a given timeout */
static void motor_off_callback(struct timer_list *t)
{}

/* schedules motor off */
static void floppy_off(unsigned int drive)
{}

/*
 * cycle through all N_DRIVE floppy drives, for disk change testing.
 * stopping at current drive. This is done before any long operation, to
 * be sure to have up to date disk change information.
 */
static void scandrives(void)
{}

static void empty(void)
{}

static void empty_done(int result)
{}

static void (*floppy_work_fn)(void);

static void floppy_work_workfn(struct work_struct *work)
{}

static DECLARE_WORK(floppy_work, floppy_work_workfn);

static void schedule_bh(void (*handler)(void))
{}

static void (*fd_timer_fn)(void) =;

static void fd_timer_workfn(struct work_struct *work)
{}

static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn);

static void cancel_activity(void)
{}

/* this function makes sure that the disk stays in the drive during the
 * transfer */
static void fd_watchdog(void)
{}

static void main_command_interrupt(void)
{}

/* waits for a delay (spinup or select) to pass */
static int fd_wait_for_completion(unsigned long expires,
				  void (*function)(void))
{}

static void setup_DMA(void)
{}

static void show_floppy(int fdc);

/* waits until the fdc becomes ready */
static int wait_til_ready(int fdc)
{}

/* sends a command byte to the fdc */
static int output_byte(int fdc, char byte)
{}

/* gets the response from the fdc */
static int result(int fdc)
{}

#define MORE_OUTPUT
/* does the fdc need more output? */
static int need_more_output(int fdc)
{}

/* Set perpendicular mode as required, based on data rate, if supported.
 * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
 */
static void perpendicular_mode(int fdc)
{}				/* perpendicular_mode */

static int fifo_depth =;
static int no_fifo;

static int fdc_configure(int fdc)
{}

#define NOMINAL_DTR

/* Issue a "SPECIFY" command to set the step rate time, head unload time,
 * head load time, and DMA disable flag to values needed by floppy.
 *
 * The value "dtr" is the data transfer rate in Kbps.  It is needed
 * to account for the data rate-based scaling done by the 82072 and 82077
 * FDC types.  This parameter is ignored for other types of FDCs (i.e.
 * 8272a).
 *
 * Note that changing the data transfer rate has a (probably deleterious)
 * effect on the parameters subject to scaling for 82072/82077 FDCs, so
 * fdc_specify is called again after each data transfer rate
 * change.
 *
 * srt: 1000 to 16000 in microseconds
 * hut: 16 to 240 milliseconds
 * hlt: 2 to 254 milliseconds
 *
 * These values are rounded up to the next highest available delay time.
 */
static void fdc_specify(int fdc, int drive)
{}				/* fdc_specify */

/* Set the FDC's data transfer rate on behalf of the specified drive.
 * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
 * of the specify command (i.e. using the fdc_specify function).
 */
static int fdc_dtr(void)
{}				/* fdc_dtr */

static void tell_sector(void)
{}				/* tell_sector */

static void print_errors(void)
{}

/*
 * OK, this error interpreting routine is called after a
 * DMA read/write has succeeded
 * or failed, so we check the results, and copy any buffers.
 * hhb: Added better error reporting.
 * ak: Made this into a separate routine.
 */
static int interpret_errors(void)
{}

/*
 * This routine is called when everything should be correctly set up
 * for the transfer (i.e. floppy motor is on, the correct floppy is
 * selected, and the head is sitting on the right track).
 */
static void setup_rw_floppy(void)
{}

static int blind_seek;

/*
 * This is the routine called after every seek (or recalibrate) interrupt
 * from the floppy controller.
 */
static void seek_interrupt(void)
{}

static void check_wp(int fdc, int drive)
{}

static void seek_floppy(void)
{}

static void recal_interrupt(void)
{}

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)
{}

static void recalibrate_floppy(void)
{}

/*
 * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
 */
static void reset_interrupt(void)
{}

/*
 * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
 * or by setting the self clearing bit 7 of STATUS (newer FDCs).
 * This WILL trigger an interrupt, causing the handlers in the current
 * cont's ->redo() to be called via reset_interrupt().
 */
static void reset_fdc(void)
{}

static void show_floppy(int fdc)
{}

static void floppy_shutdown(struct work_struct *arg)
{}

/* start motor, check media-changed condition and write protection */
static int start_motor(void (*function)(void))
{}

static void floppy_ready(void)
{}

static void floppy_start(void)
{}

/*
 * ========================================================================
 * here ends the bottom half. Exported routines are:
 * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
 * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
 * Initialization also uses output_byte, result, set_dor, floppy_interrupt
 * and set_dor.
 * ========================================================================
 */
/*
 * General purpose continuations.
 * ==============================
 */

static void do_wakeup(void)
{}

static const struct cont_t wakeup_cont =;

static const struct cont_t intr_cont =;

/* schedules handler, waiting for completion. May be interrupted, will then
 * return -EINTR, in which case the driver will automatically be unlocked.
 */
static int wait_til_done(void (*handler)(void), bool interruptible)
{}

static void generic_done(int result)
{}

static void generic_success(void)
{}

static void generic_failure(void)
{}

static void success_and_wakeup(void)
{}

/*
 * formatting and rw support.
 * ==========================
 */

static int next_valid_format(int drive)
{}

static void bad_flp_intr(void)
{}

static void set_floppy(int drive)
{}

/*
 * formatting support.
 * ===================
 */
static void format_interrupt(void)
{}

#define FM_MODE(x, y)
#define CT(x)

static void setup_format_params(int track)
{}

static void redo_format(void)
{}

static const struct cont_t format_cont =;

static int do_format(int drive, struct format_descr *tmp_format_req)
{}

/*
 * Buffer read/write and support
 * =============================
 */

static void floppy_end_request(struct request *req, blk_status_t error)
{}

/* new request_done. Can handle physical sectors which are smaller than a
 * logical buffer */
static void request_done(int uptodate)
{}

/* Interrupt handler evaluating the result of the r/w operation */
static void rw_interrupt(void)
{}

/* Compute the maximal transfer size */
static int transfer_size(int ssize, int max_sector, int max_size)
{}

/*
 * Move data from/to the track buffer to/from the buffer cache.
 */
static void copy_buffer(int ssize, int max_sector, int max_sector_2)
{}

/* work around a bug in pseudo DMA
 * (on some FDCs) pseudo DMA does not stop when the CPU stops
 * sending data.  Hence we need a different way to signal the
 * transfer length:  We use raw_cmd->cmd[SECT_PER_TRACK].  Unfortunately, this
 * does not work with MT, hence we can only transfer one head at
 * a time
 */
static void virtualdmabug_workaround(void)
{}

/*
 * Formulate a read/write request.
 * this routine decides where to load the data (directly to buffer, or to
 * tmp floppy area), how much data to load (the size of the buffer, the whole
 * track, or a single sector)
 * All floppy_track_buffer handling goes in here. If we ever add track buffer
 * allocation on the fly, it should be done here. No other part should need
 * modification.
 */

static int make_raw_rw_request(void)
{}

static int set_next_request(void)
{}

/* Starts or continues processing request. Will automatically unlock the
 * driver at end of request.
 */
static void redo_fd_request(void)
{}

static const struct cont_t rw_cont =;

/* schedule the request and automatically unlock the driver on completion */
static void process_fd_request(void)
{}

static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx,
				    const struct blk_mq_queue_data *bd)
{}

static const struct cont_t poll_cont =;

static int poll_drive(bool interruptible, int flag)
{}

/*
 * User triggered reset
 * ====================
 */

static void reset_intr(void)
{}

static const struct cont_t reset_cont =;

/*
 * Resets the FDC connected to drive <drive>.
 * Both current_drive and current_fdc are changed to match the new drive.
 */
static int user_reset_fdc(int drive, int arg, bool interruptible)
{}

/*
 * Misc Ioctl's and support
 * ========================
 */
static inline int fd_copyout(void __user *param, const void *address,
			     unsigned long size)
{}

static inline int fd_copyin(void __user *param, void *address,
			    unsigned long size)
{}

static const char *drive_name(int type, int drive)
{}

#ifdef CONFIG_BLK_DEV_FD_RAWCMD

/* raw commands */
static void raw_cmd_done(int flag)
{}

static const struct cont_t raw_cmd_cont =;

static int raw_cmd_copyout(int cmd, void __user *param,
				  struct floppy_raw_cmd *ptr)
{}

static void raw_cmd_free(struct floppy_raw_cmd **ptr)
{}

#define MAX_LEN

static int raw_cmd_copyin(int cmd, void __user *param,
				 struct floppy_raw_cmd **rcmd)
{}

static int raw_cmd_ioctl(int cmd, void __user *param)
{}

static int floppy_raw_cmd_ioctl(int type, int drive, int cmd,
				void __user *param)
{}

#else /* CONFIG_BLK_DEV_FD_RAWCMD */

static int floppy_raw_cmd_ioctl(int type, int drive, int cmd,
				void __user *param)
{
	return -EOPNOTSUPP;
}

#endif

static int invalidate_drive(struct gendisk *disk)
{}

static int set_geometry(unsigned int cmd, struct floppy_struct *g,
			       int drive, int type, struct block_device *bdev)
{}

/* handle obsolete ioctl's */
static unsigned int ioctl_table[] =;

static int normalize_ioctl(unsigned int *cmd, int *size)
{}

static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
{}

static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{}

static bool valid_floppy_drive_params(const short autodetect[FD_AUTODETECT_SIZE],
		int native_format)
{}

static int fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode,
		unsigned int cmd, unsigned long param)
{}

static int fd_ioctl(struct block_device *bdev, blk_mode_t mode,
			     unsigned int cmd, unsigned long param)
{}

#ifdef CONFIG_COMPAT

struct compat_floppy_drive_params {};

struct compat_floppy_drive_struct {};

struct compat_floppy_fdc_state {};

struct compat_floppy_write_errors {};

#define FDSETPRM32
#define FDDEFPRM32
#define FDSETDRVPRM32
#define FDGETDRVPRM32
#define FDGETDRVSTAT32
#define FDPOLLDRVSTAT32
#define FDGETFDCSTAT32
#define FDWERRORGET32

static int compat_set_geometry(struct block_device *bdev, blk_mode_t mode,
		unsigned int cmd, struct compat_floppy_struct __user *arg)
{}

static int compat_get_prm(int drive,
			  struct compat_floppy_struct __user *arg)
{}

static int compat_setdrvprm(int drive,
			    struct compat_floppy_drive_params __user *arg)
{}

static int compat_getdrvprm(int drive,
			    struct compat_floppy_drive_params __user *arg)
{}

static int compat_getdrvstat(int drive, bool poll,
			    struct compat_floppy_drive_struct __user *arg)
{}

static int compat_getfdcstat(int drive,
			    struct compat_floppy_fdc_state __user *arg)
{}

static int compat_werrorget(int drive,
			    struct compat_floppy_write_errors __user *arg)
{}

static int fd_compat_ioctl(struct block_device *bdev, blk_mode_t mode,
		unsigned int cmd, unsigned long param)
{}
#endif

static void __init config_types(void)
{}

static void floppy_release(struct gendisk *disk)
{}

/*
 * floppy_open check for aliasing (/dev/fd0 can be the same as
 * /dev/PS0 etc), and disallows simultaneous access to the same
 * drive with different device numbers.
 */
static int floppy_open(struct gendisk *disk, blk_mode_t mode)
{}

/*
 * Check if the disk has been changed or if a change has been faked.
 */
static unsigned int floppy_check_events(struct gendisk *disk,
					unsigned int clearing)
{}

/*
 * This implements "read block 0" for floppy_revalidate().
 * Needed for format autodetection, checking whether there is
 * a disk in the drive, and whether that disk is writable.
 */

struct rb0_cbdata {};

static void floppy_rb0_cb(struct bio *bio)
{}

static int __floppy_read_block_0(struct block_device *bdev, int drive)
{}

/* revalidate the floppy disk, i.e. trigger format autodetection by reading
 * the bootblock (block 0). "Autodetection" is also needed to check whether
 * there is a disk in the drive at all... Thus we also do it for fixed
 * geometry formats */
static int floppy_revalidate(struct gendisk *disk)
{}

static const struct block_device_operations floppy_fops =;

/*
 * Floppy Driver initialization
 * =============================
 */

/* Determine the floppy disk controller type */
/* This routine was written by David C. Niemi */
static char __init get_fdc_version(int fdc)
{}				/* get_fdc_version */

/* lilo configuration */

static void __init floppy_set_flags(int *ints, int param, int param2)
{}

static void __init daring(int *ints, int param, int param2)
{}

static void __init set_cmos(int *ints, int dummy, int dummy2)
{}

static struct param_table {} config_params[] __initdata =;

static int __init floppy_setup(char *str)
{}

static int have_no_fdc =;

static ssize_t floppy_cmos_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{}

static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL);

static struct attribute *floppy_dev_attrs[] =;

ATTRIBUTE_GROUPS();

static void floppy_device_release(struct device *dev)
{}

static int floppy_resume(struct device *dev)
{}

static const struct dev_pm_ops floppy_pm_ops =;

static struct platform_driver floppy_driver =;

static const struct blk_mq_ops floppy_mq_ops =;

static struct platform_device floppy_device[N_DRIVE];
static bool registered[N_DRIVE];

static bool floppy_available(int drive)
{}

static int floppy_alloc_disk(unsigned int drive, unsigned int type)
{}

static DEFINE_MUTEX(floppy_probe_lock);

static void floppy_probe(dev_t dev)
{}

static int __init do_floppy_init(void)
{}

#ifndef MODULE
static __init void floppy_async_init(void *data, async_cookie_t cookie)
{}
#endif

static int __init floppy_init(void)
{}

static const struct io_region {} io_regions[] =;

static void floppy_release_allocated_regions(int fdc, const struct io_region *p)
{}

#define ARRAY_END(X)

static int floppy_request_regions(int fdc)
{}

static void floppy_release_regions(int fdc)
{}

static int floppy_grab_irq_and_dma(void)
{}

static void floppy_release_irq_and_dma(void)
{}

#ifdef MODULE

static char *floppy;

static void __init parse_floppy_cfg_string(char *cfg)
{
	char *ptr;

	while (*cfg) {
		ptr = cfg;
		while (*cfg && *cfg != ' ' && *cfg != '\t')
			cfg++;
		if (*cfg) {
			*cfg = '\0';
			cfg++;
		}
		if (*ptr)
			floppy_setup(ptr);
	}
}

static int __init floppy_module_init(void)
{
	if (floppy)
		parse_floppy_cfg_string(floppy);
	return floppy_init();
}
module_init(floppy_module_init);

static void __exit floppy_module_exit(void)
{
	int drive, i;

	unregister_blkdev(FLOPPY_MAJOR, "fd");
	platform_driver_unregister(&floppy_driver);

	destroy_workqueue(floppy_wq);

	for (drive = 0; drive < N_DRIVE; drive++) {
		del_timer_sync(&motor_off_timer[drive]);

		if (floppy_available(drive)) {
			for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
				if (disks[drive][i])
					del_gendisk(disks[drive][i]);
			}
			if (registered[drive])
				platform_device_unregister(&floppy_device[drive]);
		}
		for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
			if (disks[drive][i])
				put_disk(disks[drive][i]);
		}
		blk_mq_free_tag_set(&tag_sets[drive]);
	}

	cancel_delayed_work_sync(&fd_timeout);
	cancel_delayed_work_sync(&fd_timer);

	if (atomic_read(&usage_count))
		floppy_release_irq_and_dma();

	/* eject disk, if any */
	fd_eject(0);
}

module_exit(floppy_module_exit);

module_param(floppy, charp, 0);
module_param(FLOPPY_IRQ, int, 0);
module_param(FLOPPY_DMA, int, 0);
MODULE_AUTHOR("Alain L. Knaff");
MODULE_DESCRIPTION("Normal floppy disk support");
MODULE_LICENSE("GPL");

/* This doesn't actually get used other than for module information */
static const struct pnp_device_id floppy_pnpids[] = {
	{"PNP0700", 0},
	{}
};

MODULE_DEVICE_TABLE(pnp, floppy_pnpids);

#else

__setup();
module_init()
#endif

MODULE_ALIAS_BLOCKDEV_MAJOR();