/* * 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 entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
*/
/* * Automatic floppy-detection and formatting written by Werner Almesberger * (almesber@nessie.cs.id.ethz.ch), 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 (hooft@chem.ruu.nl) * 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/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent * 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 & * 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 <acme@conectiva.com.br> * - 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.
*/
/* * 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); staticint slow_floppy;
#include <asm/dma.h> #include <asm/irq.h>
staticint FLOPPY_IRQ = 6; staticint FLOPPY_DMA = 2; staticint can_use_virtual_dma = 2; /* ======= * 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
*/
staticint 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
*/
/* 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
*/
staticint allowed_drive_mask = 0x33;
#include <asm/floppy.h>
staticint irqdma_allocated;
#include <linux/blk-mq.h> #include <linux/blkpg.h> #include <linux/cdrom.h> /* for the compatibility eject ioctl */ #include <linux/completion.h>
/* * 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 4 /* 3984 */
/* * globals used by 'result()'
*/ staticunsignedchar reply_buffer[FD_RAW_REPLY_SIZE]; staticint inr; /* size of reply buffer, when called from interrupt */ #define ST0 0 #define ST1 1 #define ST2 2 #define ST3 0 /* result of GETSTATUS */ #define R_TRACK 3 #define R_HEAD 4 #define R_SECTOR 5 #define R_SIZECODE 6
#define SEL_DLY (2 * HZ / 100)
/* * this struct defines the different floppy drive types.
*/ staticstruct { struct floppy_drive_params params; constchar *name; /* name printed while booting */
} default_drive_params[] = { /* NOTE: the time values in jiffies should be in msec! CMOS drive type | Maximum data rate supported by drive type | | Head load time, msec | | | Head unload time, msec (not used) | | | | Step rate interval, usec | | | | | Time needed for spinup time (jiffies) | | | | | | Timeout for spinning down (jiffies) | | | | | | | Spindown offset (where disk stops) | | | | | | | | Select delay | | | | | | | | | RPS | | | | | | | | | | Max number of tracks | | | | | | | | | | | Interrupt timeout | | | | | | | | | | | | Max nonintlv. sectors
| | | | | | | | | | | | | -Max Errors- flags */
{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
/* * 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) */ staticstruct floppy_struct floppy_type[32] = {
{ 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
{ 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
{ 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
{ 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
{ 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
{ 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
{ 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
{ 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
{ 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
{ 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
/* Auto-detection: Disk type used until the next media change occurs. */ staticstruct floppy_struct *current_type[N_DRIVE];
/* * User-provided type information. current_type points to * the respective entry of this array.
*/ staticstruct floppy_struct user_params[N_DRIVE];
static sector_t floppy_sizes[256];
staticchar floppy_device_name[] = "floppy";
/* * The driver is trying to determine the correct media format * while probing is set. rw_interrupt() clears it after a * successful access.
*/ staticint probing;
/* errors encountered on the current (or last) request */ staticint floppy_errors;
/* Format request descriptor. */ staticstruct 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.
*/ staticchar *floppy_track_buffer; staticint max_buffer_sectors;
staticconststruct cont_t { void (*interrupt)(void); /* this is called after the interrupt of the
* main command */ void (*redo)(void); /* this is called to retry the operation */ void (*error)(void); /* this is called to tally an error */ void (*done)(int); /* this is called to say if the operation has
* succeeded/failed */
} *cont;
/* * 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.
*/ staticvoid reset_fdc(void); staticint 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 -1 #define NEED_1_RECAL -2 #define NEED_2_RECAL -3
#define INFBOUND(a, b) (a) = max_t(int, a, b) #define SUPBOUND(a, b) (a) = min_t(int, 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.
*/
staticint disk_change(int drive)
{ int fdc = FDC(drive);
if (time_before(jiffies, drive_state[drive].select_date + drive_params[drive].select_delay))
DPRINT("WARNING disk change called early\n"); if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))) ||
(fdc_state[fdc].dor & 3) != UNIT(drive) || fdc != FDC(drive)) {
DPRINT("probing disk change on unselected drive\n");
DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive),
(unsignedint)fdc_state[fdc].dor);
}
debug_dcl(drive_params[drive].flags, "checking disk change line for drive %d\n", drive);
debug_dcl(drive_params[drive].flags, "jiffies=%lu\n", jiffies);
debug_dcl(drive_params[drive].flags, "disk change line=%x\n",
fdc_inb(fdc, FD_DIR) & 0x80);
debug_dcl(drive_params[drive].flags, "flags=%lx\n",
drive_state[drive].flags);
if (drive_state[drive].maxblock) /* mark it changed */
set_bit(FD_DISK_CHANGED_BIT,
&drive_state[drive].flags);
/* invalidate its geometry */ if (drive_state[drive].keep_data >= 0) { if ((drive_params[drive].flags & FTD_MSG) &&
current_type[drive] != NULL)
DPRINT("Disk type is undefined after disk change\n");
current_type[drive] = NULL;
floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1;
}
/* * Reset all driver information about the specified fdc. * This is needed after a reset, and after a raw command.
*/ staticvoid reset_fdc_info(int fdc, int mode)
{ int drive;
/* * 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.
*/ staticvoid set_fdc(int drive)
{ unsignedint fdc;
if (drive < 0 || drive >= N_DRIVE) {
pr_info("bad drive value %d\n", drive); return;
}
/* * locks the driver. * Both current_drive and current_fdc are changed to match the new drive.
*/ staticint lock_fdc(int drive)
{ if (WARN(atomic_read(&usage_count) == 0, "Trying to lock fdc while usage count=0\n")) return -1;
if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy))) return -EINTR;
/* switches the motor off after a given timeout */ staticvoid motor_off_callback(struct timer_list *t)
{ unsignedlong nr = t - motor_off_timer; unsignedchar mask = ~(0x10 << UNIT(nr));
if (WARN_ON_ONCE(nr >= N_DRIVE)) return;
set_dor(FDC(nr), mask, 0);
}
/* schedules motor off */ staticvoid floppy_off(unsignedint drive)
{ unsignedlongvolatile delta; int fdc = FDC(drive);
if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive)))) return;
timer_delete(motor_off_timer + drive);
/* make spindle stop in a position which minimizes spinup time
* next time */ if (drive_params[drive].rps) {
delta = jiffies - drive_state[drive].first_read_date + HZ -
drive_params[drive].spindown_offset;
delta = ((delta * drive_params[drive].rps) % HZ) / drive_params[drive].rps;
motor_off_timer[drive].expires =
jiffies + drive_params[drive].spindown - delta;
}
add_timer(motor_off_timer + 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.
*/ staticvoid scandrives(void)
{ int i; int drive; int saved_drive;
if (drive_params[current_drive].select_delay) return;
saved_drive = current_drive; for (i = 0; i < N_DRIVE; i++) {
drive = (saved_drive + i + 1) % N_DRIVE; if (drive_state[drive].fd_ref == 0 || drive_params[drive].select_delay != 0) continue; /* skip closed drives */
set_fdc(drive); if (!(set_dor(current_fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
(0x10 << UNIT(drive)))) /* switch the motor off again, if it was off to
* begin with */
set_dor(current_fdc, ~(0x10 << UNIT(drive)), 0);
}
set_fdc(saved_drive);
}
/* this function makes sure that the disk stays in the drive during the
* transfer */ staticvoid fd_watchdog(void)
{
debug_dcl(drive_params[current_drive].flags, "calling disk change from watchdog\n");
/* waits for a delay (spinup or select) to pass */ staticint fd_wait_for_completion(unsignedlong expires, void (*function)(void))
{ if (fdc_state[current_fdc].reset) {
reset_fdc(); /* do the reset during sleep to win time * if we don't need to sleep, it's a good
* occasion anyways */ return 1;
}
/* gets the response from the fdc */ staticint result(int fdc)
{ int i; int status = 0;
for (i = 0; i < FD_RAW_REPLY_SIZE; i++) {
status = wait_til_ready(fdc); if (status < 0) break;
status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA; if ((status & ~STATUS_BUSY) == STATUS_READY) {
resultjiffies = jiffies;
resultsize = i; return i;
} if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY))
reply_buffer[i] = fdc_inb(fdc, FD_DATA); else break;
} if (initialized) {
DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
fdc, status, i);
show_floppy(fdc);
}
fdc_state[fdc].reset = 1; return -1;
}
#define MORE_OUTPUT -2 /* does the fdc need more output? */ staticint need_more_output(int fdc)
{ int status = wait_til_ready(fdc);
if (status < 0) return -1;
if (is_ready_state(status)) return MORE_OUTPUT;
return result(fdc);
}
/* Set perpendicular mode as required, based on data rate, if supported. * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
*/ staticvoid perpendicular_mode(int fdc)
{ unsignedchar perp_mode;
if (raw_cmd->rate & 0x40) { switch (raw_cmd->rate & 3) { case 0:
perp_mode = 2; break; case 3:
perp_mode = 3; break; default:
DPRINT("Invalid data rate for perpendicular mode!\n");
cont->done(0);
fdc_state[fdc].reset = 1; /* * convenient way to return to * redo without too much hassle * (deep stack et al.)
*/ return;
}
} else
perp_mode = 0;
if (fdc_state[fdc].perp_mode == perp_mode) return; if (fdc_state[fdc].version >= FDC_82077_ORIG) {
output_byte(fdc, FD_PERPENDICULAR);
output_byte(fdc, perp_mode);
fdc_state[fdc].perp_mode = perp_mode;
} elseif (perp_mode) {
DPRINT("perpendicular mode not supported by this FDC.\n");
}
} /* perpendicular_mode */
/* 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.
*/ staticvoid fdc_specify(int fdc, int drive)
{ unsignedchar spec1; unsignedchar spec2; unsignedlong srt; unsignedlong hlt; unsignedlong hut; unsignedlong dtr = NOMINAL_DTR; unsignedlong scale_dtr = NOMINAL_DTR; int hlt_max_code = 0x7f; int hut_max_code = 0xf;
/* If these parameters did not change, just return with success */ if (fdc_state[fdc].spec1 != spec1 ||
fdc_state[fdc].spec2 != spec2) { /* Go ahead and set spec1 and spec2 */
output_byte(fdc, FD_SPECIFY);
output_byte(fdc, fdc_state[fdc].spec1 = spec1);
output_byte(fdc, fdc_state[fdc].spec2 = spec2);
}
} /* 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).
*/ staticint fdc_dtr(void)
{ /* If data rate not already set to desired value, set it. */ if ((raw_cmd->rate & 3) == fdc_state[current_fdc].dtr) return 0;
/* Set dtr */
fdc_outb(raw_cmd->rate & 3, current_fdc, FD_DCR);
/* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) * need a stabilization period of several milliseconds to be * enforced after data rate changes before R/W operations. * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
*/
fdc_state[current_fdc].dtr = raw_cmd->rate & 3; return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready);
} /* fdc_dtr */
/* * 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.
*/ staticint interpret_errors(void)
{ char bad;
/* check IC to find cause of interrupt */ switch (reply_buffer[ST0] & ST0_INTR) { case 0x40: /* error occurred during command execution */ if (reply_buffer[ST1] & ST1_EOC) return 0; /* occurs with pseudo-DMA */
bad = 1; if (reply_buffer[ST1] & ST1_WP) {
DPRINT("Drive is write protected\n");
clear_bit(FD_DISK_WRITABLE_BIT,
&drive_state[current_drive].flags);
cont->done(0);
bad = 2;
} elseif (reply_buffer[ST1] & ST1_ND) {
set_bit(FD_NEED_TWADDLE_BIT,
&drive_state[current_drive].flags);
} elseif (reply_buffer[ST1] & ST1_OR) { if (drive_params[current_drive].flags & FTD_MSG)
DPRINT("Over/Underrun - retrying\n");
bad = 0;
} elseif (floppy_errors >= drive_params[current_drive].max_errors.reporting) {
print_errors();
} if (reply_buffer[ST2] & ST2_WC || reply_buffer[ST2] & ST2_BC) /* wrong cylinder => recal */
drive_state[current_drive].track = NEED_2_RECAL; return bad; case 0x80: /* invalid command given */
DPRINT("Invalid FDC command given!\n");
cont->done(0); return 2; case 0xc0:
DPRINT("Abnormal termination caused by polling\n");
cont->error(); return 2; default: /* (0) Normal command termination */ return 0;
}
}
/* * 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).
*/ staticvoid setup_rw_floppy(void)
{ int i; int r; int flags; unsignedlong ready_date; void (*function)(void);
if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) {
ready_date = drive_state[current_drive].spinup_date + drive_params[current_drive].spinup; /* If spinup will take a long time, rerun scandrives * again just before spinup completion. Beware that * after scandrives, we must again wait for selection.
*/ if (time_after(ready_date, jiffies + drive_params[current_drive].select_delay)) {
ready_date -= drive_params[current_drive].select_delay;
function = floppy_start;
} else
function = setup_rw_floppy;
/* wait until the floppy is spinning fast enough */ if (fd_wait_for_completion(ready_date, function)) return;
} if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
setup_DMA();
if (flags & FD_RAW_INTR)
do_floppy = main_command_interrupt;
r = 0; for (i = 0; i < raw_cmd->cmd_count; i++)
r |= output_byte(current_fdc, raw_cmd->fullcmd[i]);
/* * This is the routine called after every seek (or recalibrate) interrupt * from the floppy controller.
*/ staticvoid seek_interrupt(void)
{
debugt(__func__, ""); if (inr != 2 || (reply_buffer[ST0] & 0xF8) != 0x20) {
DPRINT("seek failed\n");
drive_state[current_drive].track = NEED_2_RECAL;
cont->error();
cont->redo(); return;
} if (drive_state[current_drive].track >= 0 &&
drive_state[current_drive].track != reply_buffer[ST1] &&
!blind_seek) {
debug_dcl(drive_params[current_drive].flags, "clearing NEWCHANGE flag because of effective seek\n");
debug_dcl(drive_params[current_drive].flags, "jiffies=%lu\n",
jiffies);
clear_bit(FD_DISK_NEWCHANGE_BIT,
&drive_state[current_drive].flags); /* effective seek */
drive_state[current_drive].select_date = jiffies;
}
drive_state[current_drive].track = reply_buffer[ST1];
floppy_ready();
}
staticvoid check_wp(int fdc, int drive)
{ if (test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)) { /* check write protection */
output_byte(fdc, FD_GETSTATUS);
output_byte(fdc, UNIT(drive)); if (result(fdc) != 1) {
fdc_state[fdc].reset = 1; return;
}
clear_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
clear_bit(FD_NEED_TWADDLE_BIT,
&drive_state[drive].flags);
debug_dcl(drive_params[drive].flags, "checking whether disk is write protected\n");
debug_dcl(drive_params[drive].flags, "wp=%x\n",
reply_buffer[ST3] & 0x40); if (!(reply_buffer[ST3] & 0x40))
set_bit(FD_DISK_WRITABLE_BIT,
&drive_state[drive].flags); else
clear_bit(FD_DISK_WRITABLE_BIT,
&drive_state[drive].flags);
}
}
staticvoid seek_floppy(void)
{ int track;
blind_seek = 0;
debug_dcl(drive_params[current_drive].flags, "calling disk change from %s\n", __func__);
if (!test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) { /* the media changed flag should be cleared after the seek. * If it isn't, this means that there is really no disk in * the drive.
*/
set_bit(FD_DISK_CHANGED_BIT,
&drive_state[current_drive].flags);
cont->done(0);
cont->redo(); return;
} if (drive_state[current_drive].track <= NEED_1_RECAL) {
recalibrate_floppy(); return;
} elseif (test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
(raw_cmd->flags & FD_RAW_NEED_DISK) &&
(drive_state[current_drive].track <= NO_TRACK || drive_state[current_drive].track == raw_cmd->track)) { /* we seek to clear the media-changed condition. Does anybody
* know a more elegant way, which works on all drives? */ if (raw_cmd->track)
track = raw_cmd->track - 1; else { if (drive_params[current_drive].flags & FD_SILENT_DCL_CLEAR) {
set_dor(current_fdc, ~(0x10 << UNIT(current_drive)), 0);
blind_seek = 1;
raw_cmd->flags |= FD_RAW_NEED_SEEK;
}
track = 1;
}
} else {
check_wp(current_fdc, current_drive); if (raw_cmd->track != drive_state[current_drive].track &&
(raw_cmd->flags & FD_RAW_NEED_SEEK))
track = raw_cmd->track; else {
setup_rw_floppy(); return;
}
}
staticvoid recal_interrupt(void)
{
debugt(__func__, ""); if (inr != 2)
fdc_state[current_fdc].reset = 1; elseif (reply_buffer[ST0] & ST0_ECE) { switch (drive_state[current_drive].track) { case NEED_1_RECAL:
debugt(__func__, "need 1 recal"); /* after a second recalibrate, we still haven't * reached track 0. Probably no drive. Raise an * error, as failing immediately might upset
* computers possessed by the Devil :-) */
cont->error();
cont->redo(); return; case NEED_2_RECAL:
debugt(__func__, "need 2 recal"); /* If we already did a recalibrate, * and we are not at track 0, this * means we have moved. (The only way * not to move at recalibration is to * be already at track 0.) Clear the
* new change flag */
debug_dcl(drive_params[current_drive].flags, "clearing NEWCHANGE flag because of second recalibrate\n");
clear_bit(FD_DISK_NEWCHANGE_BIT,
&drive_state[current_drive].flags);
drive_state[current_drive].select_date = jiffies;
fallthrough; default:
debugt(__func__, "default"); /* Recalibrate moves the head by at * most 80 steps. If after one * recalibrate we don't have reached * track 0, this might mean that we * started beyond track 80. Try
* again. */
drive_state[current_drive].track = NEED_1_RECAL; break;
}
} else
drive_state[current_drive].track = reply_buffer[ST1];
floppy_ready();
}
staticvoid print_result(char *message, int inr)
{ int i;
DPRINT("%s ", message); if (inr >= 0) for (i = 0; i < inr; i++)
pr_cont("repl[%d]=%x ", i, reply_buffer[i]);
pr_cont("\n");
}
/* interrupt handler. Note that this can be called externally on the Sparc */
irqreturn_t floppy_interrupt(int irq, void *dev_id)
{ int do_print; unsignedlong f; void (*handler)(void) = do_floppy;
f = claim_dma_lock();
fd_disable_dma();
release_dma_lock(f);
do_floppy = NULL; if (current_fdc >= N_FDC || fdc_state[current_fdc].address == -1) { /* we don't even know which FDC is the culprit */
pr_info("DOR0=%x\n", fdc_state[0].dor);
pr_info("floppy interrupt on bizarre fdc %d\n", current_fdc);
pr_info("handler=%ps\n", handler);
is_alive(__func__, "bizarre fdc"); return IRQ_NONE;
}
fdc_state[current_fdc].reset = 0; /* We have to clear the reset flag here, because apparently on boxes * with level triggered interrupts (PS/2, Sparc, ...), it is needed to * emit SENSEI's to clear the interrupt line. And fdc_state[fdc].reset * blocks the emission of the SENSEI's. * It is OK to emit floppy commands because we are in an interrupt * handler here, and thus we have to fear no interference of other * activity.
*/
do_print = !handler && print_unex && initialized;
inr = result(current_fdc); if (do_print)
print_result("unexpected interrupt", inr); if (inr == 0) { int max_sensei = 4; do {
output_byte(current_fdc, FD_SENSEI);
inr = result(current_fdc); if (do_print)
print_result("sensei", inr);
max_sensei--;
} while ((reply_buffer[ST0] & 0x83) != UNIT(current_drive) &&
inr == 2 && max_sensei);
} if (!handler) {
fdc_state[current_fdc].reset = 1; return IRQ_NONE;
}
schedule_bh(handler);
is_alive(__func__, "normal interrupt end");
/* FIXME! Was it really for us? */ return IRQ_HANDLED;
}
/* * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
*/ staticvoid reset_interrupt(void)
{
debugt(__func__, "");
result(current_fdc); /* get the status ready for set_fdc */ if (fdc_state[current_fdc].reset) {
pr_info("reset set in interrupt, calling %ps\n", cont->error);
cont->error(); /* a reset just after a reset. BAD! */
}
cont->redo();
}
/* * 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().
*/ staticvoid reset_fdc(void)
{ unsignedlong flags;
/* avoid dma going to a random drive after shutdown */
if (initialized)
DPRINT("floppy timeout called\n");
fdc_state[current_fdc].reset = 1; if (cont) {
cont->done(0);
cont->redo(); /* this will recall reset when needed */
} else {
pr_info("no cont in shutdown!\n");
process_fd_request();
}
is_alive(__func__, "");
}
/* start motor, check media-changed condition and write protection */ staticint start_motor(void (*function)(void))
{ int mask; int data;
mask = 0xfc;
data = UNIT(current_drive); if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) { if (!(fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))) {
set_debugt(); /* no read since this drive is running */
drive_state[current_drive].first_read_date = 0; /* note motor start time if motor is not yet running */
drive_state[current_drive].spinup_date = jiffies;
data |= (0x10 << UNIT(current_drive));
}
} elseif (fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))
mask &= ~(0x10 << UNIT(current_drive));
/* starts motor and selects floppy */
timer_delete(motor_off_timer + current_drive);
set_dor(current_fdc, mask, data);
/* wait_for_completion also schedules reset if needed. */ return fd_wait_for_completion(drive_state[current_drive].select_date + drive_params[current_drive].select_delay,
function);
}
staticvoid floppy_ready(void)
{ if (fdc_state[current_fdc].reset) {
reset_fdc(); return;
} if (start_motor(floppy_ready)) return; if (fdc_dtr()) return;
debug_dcl(drive_params[current_drive].flags, "calling disk change from floppy_ready\n"); if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
disk_change(current_drive) && !drive_params[current_drive].select_delay)
twaddle(current_fdc, current_drive); /* this clears the dcl on certain
* drive/controller combinations */
/* schedules handler, waiting for completion. May be interrupted, will then * return -EINTR, in which case the driver will automatically be unlocked.
*/ staticint wait_til_done(void (*handler)(void), bool interruptible)
{ int ret;
/* current_count_sectors can be zero if transfer failed */ if (error)
nr_sectors = blk_rq_cur_sectors(req); if (blk_update_request(req, error, nr_sectors << 9)) return;
__blk_mq_end_request(req, error);
/* We're done with the request */
floppy_off(drive);
current_req = NULL;
}
/* new request_done. Can handle physical sectors which are smaller than a
* logical buffer */ staticvoid request_done(int uptodate)
{ struct request *req = current_req; int block; char msg[sizeof("request done ") + sizeof(int) * 3];
if (!req) {
pr_info("floppy.c: no request in request_done\n"); return;
}
if (uptodate) { /* maintain values for invalidation on geometry
* change */
block = current_count_sectors + blk_rq_pos(req);
INFBOUND(drive_state[current_drive].maxblock, block); if (block > _floppy->sect)
drive_state[current_drive].maxtrack = 1;
floppy_end_request(req, 0);
} else { if (rq_data_dir(req) == WRITE) { /* record write error information */
write_errors[current_drive].write_errors++; if (write_errors[current_drive].write_errors == 1) {
write_errors[current_drive].first_error_sector = blk_rq_pos(req);
write_errors[current_drive].first_error_generation = drive_state[current_drive].generation;
}
write_errors[current_drive].last_error_sector = blk_rq_pos(req);
write_errors[current_drive].last_error_generation = drive_state[current_drive].generation;
}
floppy_end_request(req, BLK_STS_IOERR);
}
}
/* Interrupt handler evaluating the result of the r/w operation */ staticvoid rw_interrupt(void)
{ int eoc; int ssize; int heads; int nr_sectors;
if (reply_buffer[R_HEAD] >= 2) { /* some Toshiba floppy controllers occasionnally seem to * return bogus interrupts after read/write operations, which
* can be recognized by a bad head number (>= 2) */ return;
}
if (!drive_state[current_drive].first_read_date)
drive_state[current_drive].first_read_date = jiffies;
/* transfer size, beginning not aligned */
current_count_sectors = max_sector - fsector_t;
return max_sector;
}
/* * Move data from/to the track buffer to/from the buffer cache.
*/ staticvoid copy_buffer(int ssize, int max_sector, int max_sector_2)
{ int remaining; /* number of transferred 512-byte sectors */ struct bio_vec bv; char *dma_buffer; int size; struct req_iterator iter;
/* 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
*/ staticvoid virtualdmabug_workaround(void)
{ int hard_sectors; int end_sector;
if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
raw_cmd->cmd[COMMAND] &= ~0x80; /* switch off multiple track mode */
hard_sectors = raw_cmd->length >> (7 + raw_cmd->cmd[SIZECODE]);
end_sector = raw_cmd->cmd[SECTOR] + hard_sectors - 1; if (end_sector > raw_cmd->cmd[SECT_PER_TRACK]) {
pr_info("too many sectors %d > %d\n",
end_sector, raw_cmd->cmd[SECT_PER_TRACK]); return;
}
raw_cmd->cmd[SECT_PER_TRACK] = end_sector; /* make sure raw_cmd->cmd[SECT_PER_TRACK]
* points to end of transfer */
}
}
/* * 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.
*/
staticint make_raw_rw_request(void)
{ int aligned_sector_t; int max_sector; int max_size; int tracksize; int ssize;
if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n")) return 0;
/* tracksize describes the size which can be filled up with sectors * of size ssize.
*/
tracksize = _floppy->sect - _floppy->sect % ssize; if (tracksize < _floppy->sect) {
raw_cmd->cmd[SECT_PER_TRACK]++; if (tracksize <= fsector_t % _floppy->sect)
raw_cmd->cmd[SECTOR]--;
/* if we are beyond tracksize, fill up using smaller sectors */ while (tracksize <= fsector_t % _floppy->sect) { while (tracksize + ssize > _floppy->sect) {
raw_cmd->cmd[SIZECODE]--;
ssize >>= 1;
}
raw_cmd->cmd[SECTOR]++;
raw_cmd->cmd[SECT_PER_TRACK]++;
tracksize += ssize;
}
max_sector = raw_cmd->cmd[HEAD] * _floppy->sect + tracksize;
} elseif (!raw_cmd->cmd[TRACK] && !raw_cmd->cmd[HEAD] && !(_floppy->rate & FD_2M) && probing) {
max_sector = _floppy->sect;
} elseif (!raw_cmd->cmd[HEAD] && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) { /* for virtual DMA bug workaround */
max_sector = _floppy->sect;
}
if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) { /* copy write buffer to track buffer. * if we get here, we know that the write * is either aligned or the data already in the buffer
* (buffer will be overwritten) */ if (in_sector_offset && buffer_track == -1)
DPRINT("internal error offset !=0 on write\n");
buffer_track = raw_cmd->track;
buffer_drive = current_drive;
copy_buffer(ssize, max_sector,
2 * max_buffer_sectors + buffer_min);
} else
transfer_size(ssize, max_sector,
2 * max_buffer_sectors + buffer_min -
aligned_sector_t);
/* Starts or continues processing request. Will automatically unlock the * driver at end of request.
*/ staticvoid redo_fd_request(void)
{ int drive; int tmp;
lastredo = jiffies; if (current_drive < N_DRIVE)
floppy_off(current_drive);
/* schedule the request and automatically unlock the driver on completion */ staticvoid process_fd_request(void)
{
cont = &rw_cont;
schedule_bh(redo_fd_request);
}
if (test_and_set_bit(0, &fdc_busy)) { /* fdc busy, this new request will be treated when the
current one is done */
is_alive(__func__, "old request running"); return BLK_STS_RESOURCE;
}
/* * Resets the FDC connected to drive <drive>. * Both current_drive and current_fdc are changed to match the new drive.
*/ staticint user_reset_fdc(int drive, int arg, bool interruptible)
{ int ret;
if (lock_fdc(drive)) return -EINTR;
if (arg == FD_RESET_ALWAYS)
fdc_state[current_fdc].reset = 1; if (fdc_state[current_fdc].reset) { /* note: reset_fdc will take care of unlocking the driver * on completion.
*/
cont = &reset_cont;
ret = wait_til_done(reset_fdc, interruptible); if (ret == -EINTR) return -EINTR;
}
process_fd_request(); return 0;
}
staticint raw_cmd_ioctl(int cmd, void __user *param)
{ struct floppy_raw_cmd *my_raw_cmd; int drive; int ret2; int ret;
if (fdc_state[current_fdc].rawcmd <= 1)
fdc_state[current_fdc].rawcmd = 1; for (drive = 0; drive < N_DRIVE; drive++) { if (FDC(drive) != current_fdc) continue; if (drive == current_drive) { if (drive_state[drive].fd_ref > 1) {
fdc_state[current_fdc].rawcmd = 2; break;
}
} elseif (drive_state[drive].fd_ref) {
fdc_state[current_fdc].rawcmd = 2; break;
}
}
if (fdc_state[current_fdc].reset) return -EIO;
ret = raw_cmd_copyin(cmd, param, &my_raw_cmd); if (ret) {
raw_cmd_free(&my_raw_cmd); return ret;
}
raw_cmd = my_raw_cmd;
cont = &raw_cmd_cont;
ret = wait_til_done(floppy_start, true);
debug_dcl(drive_params[current_drive].flags, "calling disk change from raw_cmd ioctl\n");
if (ret != -EINTR && fdc_state[current_fdc].reset)
ret = -EIO;
drive_state[current_drive].track = NO_TRACK;
ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd); if (!ret)
ret = ret2;
raw_cmd_free(&my_raw_cmd); return ret;
}
staticint floppy_raw_cmd_ioctl(int type, int drive, int cmd, void __user *param)
{ int ret;
pr_warn_once("Note: FDRAWCMD is deprecated and will be removed from the kernel in the near future.\n");
if (type) return -EINVAL; if (lock_fdc(drive)) return -EINTR;
set_floppy(drive);
ret = raw_cmd_ioctl(cmd, param); if (ret == -EINTR) return -EINTR;
process_fd_request(); return ret;
}
#else/* CONFIG_BLK_DEV_FD_RAWCMD */
staticint floppy_raw_cmd_ioctl(int type, int drive, int cmd, void __user *param)
{ return -EOPNOTSUPP;
}
#endif
staticint invalidate_drive(struct gendisk *disk)
{ /* invalidate the buffer track to force a reread */
set_bit((long)disk->private_data, &fake_change);
process_fd_request(); if (disk_check_media_change(disk)) {
bdev_mark_dead(disk->part0, true);
floppy_revalidate(disk);
} return 0;
}
staticint set_geometry(unsignedint cmd, struct floppy_struct *g, int drive, int type, struct block_device *bdev)
{ int cnt;
/* sanity checking for parameters. */ if ((int)g->sect <= 0 ||
(int)g->head <= 0 || /* check for overflow in max_sector */
(int)(g->sect * g->head) <= 0 || /* check for zero in raw_cmd->cmd[F_SECT_PER_TRACK] */
(unsignedchar)((g->sect << 2) >> FD_SIZECODE(g)) == 0 ||
g->track <= 0 || g->track > drive_params[drive].tracks >> STRETCH(g) || /* check if reserved bits are set */
(g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0) return -EINVAL; if (type) { if (!capable(CAP_SYS_ADMIN)) return -EPERM;
mutex_lock(&open_lock); if (lock_fdc(drive)) {
mutex_unlock(&open_lock); return -EINTR;
}
floppy_type[type] = *g;
floppy_type[type].name = "user format"; for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] =
floppy_type[type].size + 1;
process_fd_request(); for (cnt = 0; cnt < N_DRIVE; cnt++) { struct gendisk *disk = opened_disk[cnt];
if (!disk || ITYPE(drive_state[cnt].fd_device) != type) continue;
disk_force_media_change(disk);
}
mutex_unlock(&open_lock);
} else { int oldStretch;
if (lock_fdc(drive)) return -EINTR; if (cmd != FDDEFPRM) { /* notice a disk change immediately, else
* we lose our settings immediately*/ if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR;
}
oldStretch = g->stretch;
user_params[drive] = *g; if (buffer_drive == drive)
SUPBOUND(buffer_max, user_params[drive].sect);
current_type[drive] = &user_params[drive];
floppy_sizes[drive] = user_params[drive].size; if (cmd == FDDEFPRM)
drive_state[current_drive].keep_data = -1; else
drive_state[current_drive].keep_data = 1; /* invalidation. Invalidate only when needed, i.e. * when there are already sectors in the buffer cache * whose number will change. This is useful, because * mtools often changes the geometry of the disk after
* looking at the boot block */ if (drive_state[current_drive].maxblock > user_params[drive].sect ||
drive_state[current_drive].maxtrack ||
((user_params[drive].sect ^ oldStretch) &
(FD_SWAPSIDES | FD_SECTBASEMASK)))
invalidate_drive(bdev->bd_disk); else
process_fd_request();
} return 0;
}
staticbool valid_floppy_drive_params(constshort autodetect[FD_AUTODETECT_SIZE], int native_format)
{
size_t floppy_type_size = ARRAY_SIZE(floppy_type);
size_t i = 0;
for (i = 0; i < FD_AUTODETECT_SIZE; ++i) { if (autodetect[i] < 0 ||
autodetect[i] >= floppy_type_size) returnfalse;
}
if (native_format < 0 || native_format >= floppy_type_size) returnfalse;
returntrue;
}
staticint fd_locked_ioctl(struct block_device *bdev, blk_mode_t mode, unsignedint cmd, unsignedlong param)
{ int drive = (long)bdev->bd_disk->private_data; int type = ITYPE(drive_state[drive].fd_device); int ret; int size; union inparam { struct floppy_struct g; /* geometry */ struct format_descr f; struct floppy_max_errors max_errors; struct floppy_drive_params dp;
} inparam; /* parameters coming from user space */ constvoid *outparam = NULL; /* parameters passed back to user space */
/* convert compatibility eject ioctls into floppy eject ioctl. * We do this in order to provide a means to eject floppy disks before
* installing the new fdutils package */ if (cmd == CDROMEJECT || /* CD-ROM eject */
cmd == 0x6470) { /* SunOS floppy eject */
DPRINT("obsolete eject ioctl\n");
DPRINT("please use floppycontrol --eject\n");
cmd = FDEJECT;
}
if (!((cmd & 0xff00) == 0x0200)) return -EINVAL;
/* convert the old style command into a new style command */
ret = normalize_ioctl(&cmd, &size); if (ret) return ret;
if (WARN_ON(size < 0 || size > sizeof(inparam))) return -EINVAL;
/* copyin */
memset(&inparam, 0, sizeof(inparam)); if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = fd_copyin((void __user *)param, &inparam, size); if (ret) return ret;
}
switch (cmd) { case FDEJECT: if (drive_state[drive].fd_ref != 1) /* somebody else has this drive open */ return -EBUSY; if (lock_fdc(drive)) return -EINTR;
/* do the actual eject. Fails on
* non-Sparc architectures */
ret = fd_eject(UNIT(drive));
set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
process_fd_request(); return ret; case FDCLRPRM: if (lock_fdc(drive)) return -EINTR;
current_type[drive] = NULL;
floppy_sizes[drive] = MAX_DISK_SIZE << 1;
drive_state[drive].keep_data = 0; return invalidate_drive(bdev->bd_disk); case FDSETPRM: case FDDEFPRM: return set_geometry(cmd, &inparam.g, drive, type, bdev); case FDGETPRM:
ret = get_floppy_geometry(drive, type,
(struct floppy_struct **)&outparam); if (ret) return ret;
memcpy(&inparam.g, outparam,
offsetof(struct floppy_struct, name));
outparam = &inparam.g; break; case FDMSGON:
drive_params[drive].flags |= FTD_MSG; return 0; case FDMSGOFF:
drive_params[drive].flags &= ~FTD_MSG; return 0; case FDFMTBEG: if (lock_fdc(drive)) return -EINTR; if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR;
ret = drive_state[drive].flags;
process_fd_request(); if (ret & FD_VERIFY) return -ENODEV; if (!(ret & FD_DISK_WRITABLE)) return -EROFS; return 0; case FDFMTTRK: if (drive_state[drive].fd_ref != 1) return -EBUSY; return do_format(drive, &inparam.f); case FDFMTEND: case FDFLUSH: if (lock_fdc(drive)) return -EINTR; return invalidate_drive(bdev->bd_disk); case FDSETEMSGTRESH:
drive_params[drive].max_errors.reporting = (unsignedshort)(param & 0x0f); return 0; case FDGETMAXERRS:
outparam = &drive_params[drive].max_errors; break; case FDSETMAXERRS:
drive_params[drive].max_errors = inparam.max_errors; break; case FDGETDRVTYP:
outparam = drive_name(type, drive);
SUPBOUND(size, strlen((constchar *)outparam) + 1); break; case FDSETDRVPRM: if (!valid_floppy_drive_params(inparam.dp.autodetect,
inparam.dp.native_format)) return -EINVAL;
drive_params[drive] = inparam.dp; break; case FDGETDRVPRM:
outparam = &drive_params[drive]; break; case FDPOLLDRVSTAT: if (lock_fdc(drive)) return -EINTR; if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR) return -EINTR;
process_fd_request();
fallthrough; case FDGETDRVSTAT:
outparam = &drive_state[drive]; break; case FDRESET: return user_reset_fdc(drive, (int)param, true); case FDGETFDCSTAT:
outparam = &fdc_state[FDC(drive)]; break; case FDWERRORCLR:
memset(&write_errors[drive], 0, sizeof(write_errors[drive])); return 0; case FDWERRORGET:
outparam = &write_errors[drive]; break; case FDRAWCMD: return floppy_raw_cmd_ioctl(type, drive, cmd, (void __user *)param); case FDTWADDLE: if (lock_fdc(drive)) return -EINTR;
twaddle(current_fdc, current_drive);
process_fd_request(); return 0; default: return -EINVAL;
}
if (_IOC_DIR(cmd) & _IOC_READ) return fd_copyout((void __user *)param, outparam, size);
case FDMSGON: case FDMSGOFF: case FDSETEMSGTRESH: case FDFLUSH: case FDWERRORCLR: case FDEJECT: case FDCLRPRM: case FDFMTBEG: case FDRESET: case FDTWADDLE: return fd_ioctl(bdev, mode, cmd, param); case FDSETMAXERRS: case FDGETMAXERRS: case FDGETDRVTYP: case FDFMTEND: case FDFMTTRK: case FDRAWCMD: return fd_ioctl(bdev, mode, cmd,
(unsignedlong)compat_ptr(param)); case FDSETPRM32: case FDDEFPRM32: return compat_set_geometry(bdev, mode, cmd, compat_ptr(param)); case FDGETPRM32: return compat_get_prm(drive, compat_ptr(param)); case FDSETDRVPRM32: return compat_setdrvprm(drive, compat_ptr(param)); case FDGETDRVPRM32: return compat_getdrvprm(drive, compat_ptr(param)); case FDPOLLDRVSTAT32: return compat_getdrvstat(drive, true, compat_ptr(param)); case FDGETDRVSTAT32: return compat_getdrvstat(drive, false, compat_ptr(param)); case FDGETFDCSTAT32: return compat_getfdcstat(drive, compat_ptr(param)); case FDWERRORGET32: return compat_werrorget(drive, compat_ptr(param));
} return -EINVAL;
} #endif
staticvoid __init config_types(void)
{ bool has_drive = false; int drive;
/* read drive info out of physical CMOS */
drive = 0; if (!drive_params[drive].cmos)
drive_params[drive].cmos = FLOPPY0_TYPE;
drive = 1; if (!drive_params[drive].cmos)
drive_params[drive].cmos = FLOPPY1_TYPE;
/* FIXME: additional physical CMOS drive detection should go here */
staticvoid floppy_release(struct gendisk *disk)
{ int drive = (long)disk->private_data;
mutex_lock(&floppy_mutex);
mutex_lock(&open_lock); if (!drive_state[drive].fd_ref--) {
DPRINT("floppy_release with fd_ref == 0");
drive_state[drive].fd_ref = 0;
} if (!drive_state[drive].fd_ref)
opened_disk[drive] = NULL;
mutex_unlock(&open_lock);
mutex_unlock(&floppy_mutex);
}
/* * 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.
*/ staticint floppy_open(struct gendisk *disk, blk_mode_t mode)
{ int drive = (long)disk->private_data; int old_dev, new_dev; inttry; int res = -EBUSY; char *tmp;
if (!floppy_track_buffer) { /* if opening an ED drive, reserve a big buffer,
* else reserve a small one */ if ((drive_params[drive].cmos == 6) || (drive_params[drive].cmos == 5)) try = 64; /* Only 48 actually useful */ else try = 32; /* Only 24 actually useful */
tmp = (char *)fd_dma_mem_alloc(1024 * try); if (!tmp && !floppy_track_buffer) { try >>= 1; /* buffer only one side */
INFBOUND(try, 16);
tmp = (char *)fd_dma_mem_alloc(1024 * try);
} if (!tmp && !floppy_track_buffer)
fallback_on_nodma_alloc(&tmp, 2048 * try); if (!tmp && !floppy_track_buffer) {
DPRINT("Unable to allocate DMA memory\n"); goto out;
} if (floppy_track_buffer) { if (tmp)
fd_dma_mem_free((unsignedlong)tmp, try * 1024);
} else {
buffer_min = buffer_max = -1;
floppy_track_buffer = tmp;
max_buffer_sectors = try;
}
}
/* * Check if the disk has been changed or if a change has been faked.
*/ staticunsignedint floppy_check_events(struct gendisk *disk, unsignedint clearing)
{ int drive = (long)disk->private_data;
if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)) return DISK_EVENT_MEDIA_CHANGE;
if (time_after(jiffies, drive_state[drive].last_checked + drive_params[drive].checkfreq)) { if (lock_fdc(drive)) return 0;
poll_drive(false, 0);
process_fd_request();
}
/* * 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 { int drive; struct completion complete;
};
staticvoid floppy_rb0_cb(struct bio *bio)
{ struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private; int drive = cbdata->drive;
if (bio->bi_status) {
pr_info("floppy: error %d while reading block 0\n",
bio->bi_status);
set_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags);
}
complete(&cbdata->complete);
}
staticint __floppy_read_block_0(struct block_device *bdev, int drive)
{ struct bio bio; struct bio_vec bio_vec; struct page *page; struct rb0_cbdata cbdata;
page = alloc_page(GFP_NOIO); if (!page) {
process_fd_request(); return -ENOMEM;
}
/* 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 */ staticint floppy_revalidate(struct gendisk *disk)
{ int drive = (long)disk->private_data; int cf; int res = 0;
if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
test_bit(drive, &fake_change) ||
drive_no_geom(drive)) { if (WARN(atomic_read(&usage_count) == 0, "VFS: revalidate called on non-open device.\n")) return -EFAULT;
res = lock_fdc(drive); if (res) return res;
cf = (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)); if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) {
process_fd_request(); /*already done by another thread */ return 0;
}
drive_state[drive].maxblock = 0;
drive_state[drive].maxtrack = 0; if (buffer_drive == drive)
buffer_track = -1;
clear_bit(drive, &fake_change);
clear_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags); if (cf)
drive_state[drive].generation++; if (drive_no_geom(drive)) { /* auto-sensing */
res = __floppy_read_block_0(opened_disk[drive]->part0,
drive);
} else { if (cf)
poll_drive(false, FD_RAW_NEED_DISK);
process_fd_request();
}
}
set_capacity(disk, floppy_sizes[drive_state[drive].fd_device]); return res;
}
/* Determine the floppy disk controller type */ /* This routine was written by David C. Niemi */ staticchar __init get_fdc_version(int fdc)
{ int r;
output_byte(fdc, FD_DUMPREGS); /* 82072 and better know DUMPREGS */ if (fdc_state[fdc].reset) return FDC_NONE;
r = result(fdc); if (r <= 0x00) return FDC_NONE; /* No FDC present ??? */ if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
pr_info("FDC %d is an 8272A\n", fdc); return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
} if (r != 10) {
pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
fdc, r); return FDC_UNKNOWN;
}
if (!fdc_configure(fdc)) {
pr_info("FDC %d is an 82072\n", fdc); return FDC_82072; /* 82072 doesn't know CONFIGURE */
}
output_byte(fdc, FD_PERPENDICULAR); if (need_more_output(fdc) == MORE_OUTPUT) {
output_byte(fdc, 0);
} else {
pr_info("FDC %d is an 82072A\n", fdc); return FDC_82072A; /* 82072A as found on Sparcs. */
}
output_byte(fdc, FD_UNLOCK);
r = result(fdc); if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
pr_info("FDC %d is a pre-1991 82077\n", fdc); return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
* LOCK/UNLOCK */
} if ((r != 1) || (reply_buffer[ST0] != 0x00)) {
pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
fdc, r); return FDC_UNKNOWN;
}
output_byte(fdc, FD_PARTID);
r = result(fdc); if (r != 1) {
pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n",
fdc, r); return FDC_UNKNOWN;
} if (reply_buffer[ST0] == 0x80) {
pr_info("FDC %d is a post-1991 82077\n", fdc); return FDC_82077; /* Revised 82077AA passes all the tests */
} switch (reply_buffer[ST0] >> 5) { case 0x0: /* Either a 82078-1 or a 82078SL running at 5Volt */
pr_info("FDC %d is an 82078.\n", fdc); return FDC_82078; case 0x1:
pr_info("FDC %d is a 44pin 82078\n", fdc); return FDC_82078; case 0x2:
pr_info("FDC %d is a S82078B\n", fdc); return FDC_S82078B; case 0x3:
pr_info("FDC %d is a National Semiconductor PC87306\n", fdc); return FDC_87306; default:
pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n",
fdc, reply_buffer[ST0] >> 5); return FDC_82078_UNKN;
}
} /* get_fdc_version */
/* lilo configuration */
staticvoid __init floppy_set_flags(int *ints, int param, int param2)
{ int i;
for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) { if (param)
default_drive_params[i].params.flags |= param2; else
default_drive_params[i].params.flags &= ~param2;
}
DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param);
}
staticvoid __init daring(int *ints, int param, int param2)
{ int i;
for (i = 0; i < N_FDC; i++) {
memset(&fdc_state[i], 0, sizeof(*fdc_state));
fdc_state[i].dtr = -1;
fdc_state[i].dor = 0x4; #ifdefined(__sparc__) || defined(__mc68000__) /*sparcs/sun3x don't have a DOR reset which we can fall back on to */ #ifdef __mc68000__ if (MACH_IS_SUN3X) #endif
fdc_state[i].version = FDC_82072A; #endif
}
current_fdc = 0; /* reset fdc in case of unexpected interrupt */
err = floppy_grab_irq_and_dma(); if (err) {
cancel_delayed_work(&fd_timeout);
err = -EBUSY; goto out_unreg_driver;
}
/* initialise drive state */ for (drive = 0; drive < N_DRIVE; drive++) {
memset(&drive_state[drive], 0, sizeof(drive_state[drive]));
memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
drive_state[drive].fd_device = -1;
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_state[i].driver_version = FD_DRIVER_VERSION; for (unit = 0; unit < 4; unit++)
fdc_state[i].track[unit] = 0; if (fdc_state[i].address == -1) continue;
fdc_state[i].rawcmd = 2; if (user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false)) { /* free ioports reserved by floppy_grab_irq_and_dma() */
floppy_release_regions(i);
fdc_state[i].address = -1;
fdc_state[i].version = FDC_NONE; continue;
} /* Try to determine the floppy controller type */
fdc_state[i].version = get_fdc_version(i); if (fdc_state[i].version == FDC_NONE) { /* free ioports reserved by floppy_grab_irq_and_dma() */
floppy_release_regions(i);
fdc_state[i].address = -1; continue;
} if (can_use_virtual_dma == 2 &&
fdc_state[i].version < FDC_82072A)
can_use_virtual_dma = 0;
have_no_fdc = 0; /* Not all FDCs seem to be able to handle the version command * properly, so force a reset for the standard FDC clones, * to avoid interrupt garbage.
*/
user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false);
}
current_fdc = 0;
cancel_delayed_work(&fd_timeout);
current_drive = 0;
initialized = true; if (have_no_fdc) {
DPRINT("no floppy controllers found\n");
err = have_no_fdc; goto out_release_dma;
}
for (drive = 0; drive < N_DRIVE; drive++) { if (!floppy_available(drive)) continue;
staticint __init floppy_init(void)
{ #ifdef MODULE return do_floppy_init(); #else /* Don't hold up the bootup by the floppy initialization */
async_schedule(floppy_async_init, NULL); return 0; #endif
}
staticconststruct io_region { int offset; int size;
} io_regions[] = {
{ 2, 1 }, /* address + 3 is sometimes reserved by pnp bios for motherboard */
{ 4, 2 }, /* address + 6 is reserved, and may be taken by IDE.
* Unfortunately, Adaptec doesn't know this :-(, */
{ 7, 1 },
};
staticint floppy_grab_irq_and_dma(void)
{ int fdc;
if (atomic_inc_return(&usage_count) > 1) return 0;
/* * We might have scheduled a free_irq(), wait it to * drain first:
*/
flush_workqueue(floppy_wq);
if (fd_request_irq()) {
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
FLOPPY_IRQ);
atomic_dec(&usage_count); return -1;
} if (fd_request_dma()) {
DPRINT("Unable to grab DMA%d for the floppy driver\n",
FLOPPY_DMA); if (can_use_virtual_dma & 2)
use_virtual_dma = can_use_virtual_dma = 1; if (!(can_use_virtual_dma & 1)) {
fd_free_irq();
atomic_dec(&usage_count); return -1;
}
}
for (fdc = 0; fdc < N_FDC; fdc++) { if (fdc_state[fdc].address != -1) { if (floppy_request_regions(fdc)) goto cleanup;
}
} for (fdc = 0; fdc < N_FDC; fdc++) { if (fdc_state[fdc].address != -1) {
reset_fdc_info(fdc, 1);
fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
}
}
for (fdc = 0; fdc < N_FDC; fdc++) if (fdc_state[fdc].address != -1)
fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR); /* * The driver will try and free resources and relies on us * to know if they were allocated or not.
*/
current_fdc = 0;
irqdma_allocated = 1; return 0;
cleanup:
fd_free_irq();
fd_free_dma(); while (--fdc >= 0)
floppy_release_regions(fdc);
current_fdc = 0;
atomic_dec(&usage_count); return -1;
}
staticvoid floppy_release_irq_and_dma(void)
{ int fdc; #ifndef __sparc__ int drive; #endif long tmpsize; unsignedlong tmpaddr;
if (delayed_work_pending(&fd_timeout))
pr_info("floppy timer still active:%s\n", timeout_message); if (delayed_work_pending(&fd_timer))
pr_info("auxiliary floppy timer still active\n"); if (work_pending(&floppy_work))
pr_info("work still pending\n"); for (fdc = 0; fdc < N_FDC; fdc++) if (fdc_state[fdc].address != -1)
floppy_release_regions(fdc);
}
for (drive = 0; drive < N_DRIVE; drive++) {
timer_delete_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]);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.125Bemerkung:
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.