blob: f53810ba486de4a11597fde2cfcd0302ef5c12c8 [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * linux/drivers/block/floppy.c
4 *
5 * Copyright (C) 1991, 1992 Linus Torvalds
6 * Copyright (C) 1993, 1994 Alain Knaff
7 * Copyright (C) 1998 Alan Cox
8 */
Jesper Juhl06f748c2007-10-16 23:30:57 -07009
Linus Torvalds1da177e2005-04-16 15:20:36 -070010/*
11 * 02.12.91 - Changed to static variables to indicate need for reset
12 * and recalibrate. This makes some things easier (output_byte reset
13 * checking etc), and means less interrupt jumping in case of errors,
14 * so the code is hopefully easier to understand.
15 */
16
17/*
18 * This file is certainly a mess. I've tried my best to get it working,
19 * but I don't like programming floppies, and I have only one anyway.
20 * Urgel. I should check for more errors, and do more graceful error
21 * recovery. Seems there are problems with several drives. I've tried to
22 * correct them. No promises.
23 */
24
25/*
26 * As with hd.c, all routines within this file can (and will) be called
27 * by interrupts, so extreme caution is needed. A hardware interrupt
28 * handler may not sleep, or a kernel panic will happen. Thus I cannot
29 * call "floppy-on" directly, but have to set a special timer interrupt
30 * etc.
31 */
32
33/*
34 * 28.02.92 - made track-buffering routines, based on the routines written
35 * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
36 */
37
38/*
39 * Automatic floppy-detection and formatting written by Werner Almesberger
40 * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
41 * the floppy-change signal detection.
42 */
43
44/*
45 * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
46 * FDC data overrun bug, added some preliminary stuff for vertical
47 * recording support.
48 *
49 * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
50 *
51 * TODO: Errors are still not counted properly.
52 */
53
54/* 1992/9/20
55 * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
56 * modeled after the freeware MS-DOS program fdformat/88 V1.8 by
57 * Christoph H. Hochst\"atter.
58 * I have fixed the shift values to the ones I always use. Maybe a new
59 * ioctl() should be created to be able to modify them.
60 * There is a bug in the driver that makes it impossible to format a
61 * floppy as the first thing after bootup.
62 */
63
64/*
65 * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
66 * this helped the floppy driver as well. Much cleaner, and still seems to
67 * work.
68 */
69
70/* 1994/6/24 --bbroad-- added the floppy table entries and made
71 * minor modifications to allow 2.88 floppies to be run.
72 */
73
74/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
75 * disk types.
76 */
77
78/*
79 * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
80 * format bug fixes, but unfortunately some new bugs too...
81 */
82
83/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
84 * errors to allow safe writing by specialized programs.
85 */
86
87/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
88 * by defining bit 1 of the "stretch" parameter to mean put sectors on the
89 * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
90 * drives are "upside-down").
91 */
92
93/*
94 * 1995/8/26 -- Andreas Busse -- added Mips support.
95 */
96
97/*
98 * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependent
99 * features to asm/floppy.h.
100 */
101
102/*
James Nelsonb88b0982005-11-08 16:52:12 +0100103 * 1998/1/21 -- Richard Gooch <rgooch@atnf.csiro.au> -- devfs support
104 */
105
106/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 * 1998/05/07 -- Russell King -- More portability cleanups; moved definition of
108 * interrupt and dma channel to asm/floppy.h. Cleaned up some formatting &
109 * use of '0' for NULL.
110 */
111
112/*
113 * 1998/06/07 -- Alan Cox -- Merged the 2.0.34 fixes for resource allocation
114 * failures.
115 */
116
117/*
118 * 1998/09/20 -- David Weinehall -- Added slow-down code for buggy PS/2-drives.
119 */
120
121/*
122 * 1999/08/13 -- Paul Slootman -- floppy stopped working on Alpha after 24
123 * days, 6 hours, 32 minutes and 32 seconds (i.e. MAXINT jiffies; ints were
124 * being used to store jiffies, which are unsigned longs).
125 */
126
127/*
128 * 2000/08/28 -- Arnaldo Carvalho de Melo <acme@conectiva.com.br>
129 * - get rid of check_region
130 * - s/suser/capable/
131 */
132
133/*
134 * 2001/08/26 -- Paul Gortmaker - fix insmod oops on machines with no
135 * floppy controller (lingering task on list after module is gone... boom.)
136 */
137
138/*
139 * 2002/02/07 -- Anton Altaparmakov - Fix io ports reservation to correct range
140 * (0x3f2-0x3f5, 0x3f7). This fix is a bit of a hack but the proper fix
141 * requires many non-obvious changes in arch dependent code.
142 */
143
144/* 2003/07/28 -- Daniele Bellucci <bellucda@tiscali.it>.
145 * Better audit of register_blkdev.
146 */
147
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148#undef FLOPPY_SILENT_DCL_CLEAR
149
150#define REALLY_SLOW_IO
151
152#define DEBUGT 2
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
Joe Perches891eda82010-03-10 15:21:05 -0800154#define DPRINT(format, args...) \
155 pr_info("floppy%d: " format, current_drive, ##args)
156
157#define DCL_DEBUG /* debug disk change line */
Joe Perches87f530d2010-03-10 15:20:54 -0800158#ifdef DCL_DEBUG
159#define debug_dcl(test, fmt, args...) \
160 do { if ((test) & FD_DEBUG) DPRINT(fmt, ##args); } while (0)
161#else
162#define debug_dcl(test, fmt, args...) \
163 do { if (0) DPRINT(fmt, ##args); } while (0)
164#endif
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166/* do print messages for unexpected interrupts */
167static int print_unex = 1;
168#include <linux/module.h>
169#include <linux/sched.h>
170#include <linux/fs.h>
171#include <linux/kernel.h>
172#include <linux/timer.h>
173#include <linux/workqueue.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174#include <linux/fdreg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175#include <linux/fd.h>
176#include <linux/hdreg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177#include <linux/errno.h>
178#include <linux/slab.h>
179#include <linux/mm.h>
180#include <linux/bio.h>
181#include <linux/string.h>
Marcelo Feitoza Parisi50297cb2006-03-28 01:56:44 -0800182#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183#include <linux/fcntl.h>
184#include <linux/delay.h>
185#include <linux/mc146818rtc.h> /* CMOS defines */
186#include <linux/ioport.h>
187#include <linux/interrupt.h>
188#include <linux/init.h>
Russell Kingd052d1b2005-10-29 19:07:23 +0100189#include <linux/platform_device.h>
Scott James Remnant83f9ef42009-04-02 16:56:47 -0700190#include <linux/mod_devicetable.h>
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800191#include <linux/mutex.h>
Joe Perchesd4937542010-03-10 15:20:44 -0800192#include <linux/io.h>
193#include <linux/uaccess.h>
Andi Kleen0cc15d032012-07-02 17:27:04 -0700194#include <linux/async.h>
Al Viro229b53c2017-06-27 15:47:56 -0400195#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197/*
198 * PS/2 floppies have much slower step rates than regular floppies.
199 * It's been recommended that take about 1/4 of the default speed
200 * in some more extreme cases.
201 */
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200202static DEFINE_MUTEX(floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203static int slow_floppy;
204
205#include <asm/dma.h>
206#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207
208static int FLOPPY_IRQ = 6;
209static int FLOPPY_DMA = 2;
210static int can_use_virtual_dma = 2;
211/* =======
212 * can use virtual DMA:
213 * 0 = use of virtual DMA disallowed by config
214 * 1 = use of virtual DMA prescribed by config
215 * 2 = no virtual DMA preference configured. By default try hard DMA,
216 * but fall back on virtual DMA when not enough memory available
217 */
218
219static int use_virtual_dma;
220/* =======
221 * use virtual DMA
222 * 0 using hard DMA
223 * 1 using virtual DMA
224 * This variable is set to virtual when a DMA mem problem arises, and
225 * reset back in floppy_grab_irq_and_dma.
226 * It is not safe to reset it in other circumstances, because the floppy
227 * driver may have several buffers in use at once, and we do currently not
228 * record each buffers capabilities
229 */
230
231static DEFINE_SPINLOCK(floppy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
233static unsigned short virtual_dma_port = 0x3f0;
David Howells7d12e782006-10-05 14:55:46 +0100234irqreturn_t floppy_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235static int set_dor(int fdc, char mask, char data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
237#define K_64 0x10000 /* 64KB */
238
239/* the following is the mask of allowed drives. By default units 2 and
240 * 3 of both floppy controllers are disabled, because switching on the
241 * motor of these drives causes system hangs on some PCI computers. drive
242 * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
243 * a drive is allowed.
244 *
245 * NOTE: This must come before we include the arch floppy header because
246 * some ports reference this variable from there. -DaveM
247 */
248
249static int allowed_drive_mask = 0x33;
250
251#include <asm/floppy.h>
252
253static int irqdma_allocated;
254
Omar Sandovala9f38e12018-10-15 09:21:34 -0600255#include <linux/blk-mq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256#include <linux/blkpg.h>
257#include <linux/cdrom.h> /* for the compatibility eject ioctl */
258#include <linux/completion.h>
259
Omar Sandovala9f38e12018-10-15 09:21:34 -0600260static LIST_HEAD(floppy_reqs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261static struct request *current_req;
Jens Axboe48821182010-09-22 09:32:36 +0200262static int set_next_request(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264#ifndef fd_get_dma_residue
265#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
266#endif
267
268/* Dma Memory related stuff */
269
270#ifndef fd_dma_mem_free
271#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
272#endif
273
274#ifndef fd_dma_mem_alloc
Joe Perches48c8cee2010-03-10 15:20:45 -0800275#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL, get_order(size))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276#endif
277
Christoph Hellwigacfef4f2017-07-13 16:12:05 +0200278#ifndef fd_cacheflush
279#define fd_cacheflush(addr, size) /* nothing... */
280#endif
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282static inline void fallback_on_nodma_alloc(char **addr, size_t l)
283{
284#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
285 if (*addr)
286 return; /* we have the memory */
287 if (can_use_virtual_dma != 2)
288 return; /* no fallback allowed */
Joe Perchesb46df352010-03-10 15:20:46 -0800289 pr_info("DMA memory shortage. Temporarily falling back on virtual DMA\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 *addr = (char *)nodma_mem_alloc(l);
291#else
292 return;
293#endif
294}
295
296/* End dma memory related stuff */
297
298static unsigned long fake_change;
Joe Perches29f1c782010-03-10 15:21:00 -0800299static bool initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
Joe Perches48c8cee2010-03-10 15:20:45 -0800301#define ITYPE(x) (((x) >> 2) & 0x1f)
302#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
303#define UNIT(x) ((x) & 0x03) /* drive on fdc */
304#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700305 /* reverse mapping from unit and fdc to drive */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Joe Perches48c8cee2010-03-10 15:20:45 -0800308#define PH_HEAD(floppy, head) (((((floppy)->stretch & 2) >> 1) ^ head) << 2)
309#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
310
Willy Tarreau76dabe72020-02-24 22:23:51 +0100311/* read/write commands */
312#define COMMAND 0
313#define DR_SELECT 1
314#define TRACK 2
315#define HEAD 3
316#define SECTOR 4
317#define SIZECODE 5
318#define SECT_PER_TRACK 6
319#define GAP 7
320#define SIZECODE2 8
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321#define NR_RW 9
322
Willy Tarreau76dabe72020-02-24 22:23:51 +0100323/* format commands */
324#define F_SIZECODE 2
325#define F_SECT_PER_TRACK 3
326#define F_GAP 4
327#define F_FILL 5
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328#define NR_F 6
329
330/*
Joe Perches48c8cee2010-03-10 15:20:45 -0800331 * Maximum disk size (in kilobytes).
332 * This default is used whenever the current disk size is unknown.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 * [Now it is rather a minimum]
334 */
335#define MAX_DISK_SIZE 4 /* 3984 */
336
337/*
338 * globals used by 'result()'
339 */
340#define MAX_REPLIES 16
341static unsigned char reply_buffer[MAX_REPLIES];
Joe Perches891eda82010-03-10 15:21:05 -0800342static int inr; /* size of reply buffer, when called from interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +0100343#define ST0 0
344#define ST1 1
345#define ST2 2
346#define ST3 0 /* result of GETSTATUS */
347#define R_TRACK 3
348#define R_HEAD 4
349#define R_SECTOR 5
350#define R_SIZECODE 6
Joe Perches48c8cee2010-03-10 15:20:45 -0800351
352#define SEL_DLY (2 * HZ / 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354/*
355 * this struct defines the different floppy drive types.
356 */
357static struct {
358 struct floppy_drive_params params;
359 const char *name; /* name printed while booting */
360} default_drive_params[] = {
361/* NOTE: the time values in jiffies should be in msec!
362 CMOS drive type
363 | Maximum data rate supported by drive type
364 | | Head load time, msec
365 | | | Head unload time, msec (not used)
366 | | | | Step rate interval, usec
367 | | | | | Time needed for spinup time (jiffies)
368 | | | | | | Timeout for spinning down (jiffies)
369 | | | | | | | Spindown offset (where disk stops)
370 | | | | | | | | Select delay
371 | | | | | | | | | RPS
372 | | | | | | | | | | Max number of tracks
373 | | | | | | | | | | | Interrupt timeout
374 | | | | | | | | | | | | Max nonintlv. sectors
375 | | | | | | | | | | | | | -Max Errors- flags */
376{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
377 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
378
379{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
380 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
381
382{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
383 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
384
385{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
386 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
387
388{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
389 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
390
391{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
392 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
393
394{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
395 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
396/* | --autodetected formats--- | | |
397 * read_track | | Name printed when booting
398 * | Native format
399 * Frequency of disk change checks */
400};
401
402static struct floppy_drive_params drive_params[N_DRIVE];
403static struct floppy_drive_struct drive_state[N_DRIVE];
404static struct floppy_write_errors write_errors[N_DRIVE];
405static struct timer_list motor_off_timer[N_DRIVE];
406static struct gendisk *disks[N_DRIVE];
Omar Sandovala9f38e12018-10-15 09:21:34 -0600407static struct blk_mq_tag_set tag_sets[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408static struct block_device *opened_bdev[N_DRIVE];
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800409static DEFINE_MUTEX(open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
411
412/*
413 * This struct defines the different floppy types.
414 *
415 * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
416 * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
417 * tells if the disk is in Commodore 1581 format, which means side 0 sectors
418 * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
419 * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
420 * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
421 * side 0 is on physical side 0 (but with the misnamed sector IDs).
422 * 'stretch' should probably be renamed to something more general, like
Keith Wansbrough9e491842008-09-22 14:57:17 -0700423 * 'options'.
424 *
425 * Bits 2 through 9 of 'stretch' tell the number of the first sector.
426 * The LSB (bit 2) is flipped. For most disks, the first sector
427 * is 1 (represented by 0x00<<2). For some CP/M and music sampler
428 * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2).
429 * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2).
430 *
431 * Other parameters should be self-explanatory (see also setfdprm(8)).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 */
433/*
434 Size
435 | Sectors per track
436 | | Head
437 | | | Tracks
438 | | | | Stretch
439 | | | | | Gap 1 size
440 | | | | | | Data rate, | 0x40 for perp
441 | | | | | | | Spec1 (stepping rate, head unload
442 | | | | | | | | /fmt gap (gap2) */
443static struct floppy_struct floppy_type[32] = {
444 { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
445 { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
446 { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
447 { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
448 { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
449 { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
450 { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
451 { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
452 { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
453 { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
454
455 { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
456 { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
457 { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
458 { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
459 { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
460 { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
461 { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
462 { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
463 { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
464 { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
465
466 { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
467 { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
468 { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
469 { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
470 { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
471 { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
472 { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
473 { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
474 { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475 { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700476
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
478 { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
479};
480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481#define SECTSIZE (_FD_SECTSIZE(*floppy))
482
483/* Auto-detection: Disk type used until the next media change occurs. */
484static struct floppy_struct *current_type[N_DRIVE];
485
486/*
487 * User-provided type information. current_type points to
488 * the respective entry of this array.
489 */
490static struct floppy_struct user_params[N_DRIVE];
491
492static sector_t floppy_sizes[256];
493
Hannes Reinecke94fd0db2005-07-15 10:09:25 +0200494static char floppy_device_name[] = "floppy";
495
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496/*
497 * The driver is trying to determine the correct media format
498 * while probing is set. rw_interrupt() clears it after a
499 * successful access.
500 */
501static int probing;
502
503/* Synchronization of FDC access. */
Joe Perches48c8cee2010-03-10 15:20:45 -0800504#define FD_COMMAND_NONE -1
505#define FD_COMMAND_ERROR 2
506#define FD_COMMAND_OKAY 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508static volatile int command_status = FD_COMMAND_NONE;
509static unsigned long fdc_busy;
510static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
511static DECLARE_WAIT_QUEUE_HEAD(command_done);
512
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513/* Errors during formatting are counted here. */
514static int format_errors;
515
516/* Format request descriptor. */
517static struct format_descr format_req;
518
519/*
520 * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
521 * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
522 * H is head unload time (1=16ms, 2=32ms, etc)
523 */
524
525/*
526 * Track buffer
527 * Because these are written to by the DMA controller, they must
528 * not contain a 64k byte boundary crossing, or data will be
529 * corrupted/lost.
530 */
531static char *floppy_track_buffer;
532static int max_buffer_sectors;
533
534static int *errors;
Jesper Juhl06f748c2007-10-16 23:30:57 -0700535typedef void (*done_f)(int);
Stephen Hemminger3b06c212010-07-20 20:09:00 -0600536static const struct cont_t {
Joe Perches48c8cee2010-03-10 15:20:45 -0800537 void (*interrupt)(void);
538 /* this is called after the interrupt of the
539 * main command */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700540 void (*redo)(void); /* this is called to retry the operation */
541 void (*error)(void); /* this is called to tally an error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 done_f done; /* this is called to say if the operation has
543 * succeeded/failed */
544} *cont;
545
546static void floppy_ready(void);
547static void floppy_start(void);
548static void process_fd_request(void);
549static void recalibrate_floppy(void);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200550static void floppy_shutdown(struct work_struct *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Philippe De Muyter5a74db02009-02-18 14:48:36 -0800552static int floppy_request_regions(int);
553static void floppy_release_regions(int);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554static int floppy_grab_irq_and_dma(void);
555static void floppy_release_irq_and_dma(void);
556
557/*
558 * The "reset" variable should be tested whenever an interrupt is scheduled,
559 * after the commands have been sent. This is to ensure that the driver doesn't
560 * get wedged when the interrupt doesn't come because of a failed command.
561 * reset doesn't need to be tested before sending commands, because
562 * output_byte is automatically disabled when reset is set.
563 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564static void reset_fdc(void);
565
566/*
567 * These are global variables, as that's the easiest way to give
568 * information to interrupts. They are the data used for the current
569 * request.
570 */
Joe Perches48c8cee2010-03-10 15:20:45 -0800571#define NO_TRACK -1
572#define NEED_1_RECAL -2
573#define NEED_2_RECAL -3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Stephen Hemminger575cfc62010-06-15 13:21:11 +0200575static atomic_t usage_count = ATOMIC_INIT(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
577/* buffer related variables */
578static int buffer_track = -1;
579static int buffer_drive = -1;
580static int buffer_min = -1;
581static int buffer_max = -1;
582
583/* fdc related variables, should end up in a struct */
584static struct floppy_fdc_state fdc_state[N_FDC];
Willy Tarreaue83995c2020-03-01 20:55:55 +0100585static int current_fdc; /* current fdc */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200587static struct workqueue_struct *floppy_wq;
588
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589static struct floppy_struct *_floppy = floppy_type;
590static unsigned char current_drive;
591static long current_count_sectors;
592static unsigned char fsector_t; /* sector in track */
593static unsigned char in_sector_offset; /* offset within physical sector,
594 * expressed in units of 512 bytes */
595
Willy Tarreaue2032462020-03-01 20:55:54 +0100596static inline unsigned char fdc_inb(int fdc, int reg)
Willy Tarreauac701862020-03-01 20:55:53 +0100597{
Willy Tarreaue72e8bf2020-03-31 11:40:32 +0200598 return fd_inb(fdc_state[fdc].address, reg);
Willy Tarreauac701862020-03-01 20:55:53 +0100599}
600
Willy Tarreaue2032462020-03-01 20:55:54 +0100601static inline void fdc_outb(unsigned char value, int fdc, int reg)
Willy Tarreauac701862020-03-01 20:55:53 +0100602{
Willy Tarreaue72e8bf2020-03-31 11:40:32 +0200603 fd_outb(value, fdc_state[fdc].address, reg);
Willy Tarreauac701862020-03-01 20:55:53 +0100604}
605
Pekka Enberg2b51dca2010-11-08 14:44:34 +0100606static inline bool drive_no_geom(int drive)
607{
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100608 return !current_type[drive] && !ITYPE(drive_state[drive].fd_device);
Pekka Enberg2b51dca2010-11-08 14:44:34 +0100609}
610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611#ifndef fd_eject
612static inline int fd_eject(int drive)
613{
614 return -EINVAL;
615}
616#endif
617
618/*
619 * Debugging
620 * =========
621 */
622#ifdef DEBUGT
623static long unsigned debugtimer;
624
625static inline void set_debugt(void)
626{
627 debugtimer = jiffies;
628}
629
Joe Perchesded28632010-03-10 15:21:09 -0800630static inline void debugt(const char *func, const char *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
Willy Tarreau031faab2020-02-24 22:23:48 +0100632 if (drive_params[current_drive].flags & DEBUGT)
Joe Perchesded28632010-03-10 15:21:09 -0800633 pr_info("%s:%s dtime=%lu\n", func, msg, jiffies - debugtimer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635#else
636static inline void set_debugt(void) { }
Joe Perchesded28632010-03-10 15:21:09 -0800637static inline void debugt(const char *func, const char *msg) { }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638#endif /* DEBUGT */
639
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200641static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642static const char *timeout_message;
643
Joe Perches275176b2010-03-10 15:21:06 -0800644static void is_alive(const char *func, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
646 /* this routine checks whether the floppy driver is "alive" */
Joe Perchesc5297302010-03-10 15:20:58 -0800647 if (test_bit(0, &fdc_busy) && command_status < 2 &&
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200648 !delayed_work_pending(&fd_timeout)) {
Joe Perches275176b2010-03-10 15:21:06 -0800649 DPRINT("%s: timeout handler died. %s\n", func, message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 }
651}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Joe Perches48c8cee2010-03-10 15:20:45 -0800653static void (*do_floppy)(void) = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655#define OLOGSIZE 20
656
Joe Perches48c8cee2010-03-10 15:20:45 -0800657static void (*lasthandler)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658static unsigned long interruptjiffies;
659static unsigned long resultjiffies;
660static int resultsize;
661static unsigned long lastredo;
662
663static struct output_log {
664 unsigned char data;
665 unsigned char status;
666 unsigned long jiffies;
667} output_log[OLOGSIZE];
668
669static int output_log_pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670
671#define current_reqD -1
672#define MAXTIMEOUT -2
673
Joe Perches73507e62010-03-10 15:21:03 -0800674static void __reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675{
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200676 unsigned long delay;
677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 if (drive == current_reqD)
679 drive = current_drive;
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200680
Eric Sesterhenn / Snakebyte4acb3e22007-05-23 13:58:15 -0700681 if (drive < 0 || drive >= N_DRIVE) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200682 delay = 20UL * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 drive = 0;
684 } else
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100685 delay = drive_params[drive].timeout;
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200686
Tejun Heoe7c2f962012-08-21 13:18:24 -0700687 mod_delayed_work(floppy_wq, &fd_timeout, delay);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100688 if (drive_params[drive].flags & FD_DEBUG)
Joe Perches73507e62010-03-10 15:21:03 -0800689 DPRINT("reschedule timeout %s\n", message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 timeout_message = message;
691}
692
Joe Perches73507e62010-03-10 15:21:03 -0800693static void reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
695 unsigned long flags;
696
697 spin_lock_irqsave(&floppy_lock, flags);
Joe Perches73507e62010-03-10 15:21:03 -0800698 __reschedule_timeout(drive, message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 spin_unlock_irqrestore(&floppy_lock, flags);
700}
701
Joe Perches48c8cee2010-03-10 15:20:45 -0800702#define INFBOUND(a, b) (a) = max_t(int, a, b)
703#define SUPBOUND(a, b) (a) = min_t(int, a, b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705/*
706 * Bottom half floppy driver.
707 * ==========================
708 *
709 * This part of the file contains the code talking directly to the hardware,
710 * and also the main service loop (seek-configure-spinup-command)
711 */
712
713/*
714 * disk change.
715 * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
716 * and the last_checked date.
717 *
718 * last_checked is the date of the last check which showed 'no disk change'
719 * FD_DISK_CHANGE is set under two conditions:
720 * 1. The floppy has been changed after some i/o to that floppy already
721 * took place.
722 * 2. No floppy disk is in the drive. This is done in order to ensure that
723 * requests are quickly flushed in case there is no disk in the drive. It
724 * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
725 * the drive.
726 *
727 * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
728 * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
729 * each seek. If a disk is present, the disk change line should also be
730 * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
731 * change line is set, this means either that no disk is in the drive, or
732 * that it has been removed since the last seek.
733 *
734 * This means that we really have a third possibility too:
735 * The floppy has been changed after the last seek.
736 */
737
738static int disk_change(int drive)
739{
740 int fdc = FDC(drive);
Jesper Juhl06f748c2007-10-16 23:30:57 -0700741
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100742 if (time_before(jiffies, drive_state[drive].select_date + drive_params[drive].select_delay))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 DPRINT("WARNING disk change called early\n");
Willy Tarreaude6048b2020-02-24 22:23:43 +0100744 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))) ||
745 (fdc_state[fdc].dor & 3) != UNIT(drive) || fdc != FDC(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 DPRINT("probing disk change on unselected drive\n");
747 DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive),
Willy Tarreaude6048b2020-02-24 22:23:43 +0100748 (unsigned int)fdc_state[fdc].dor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100751 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800752 "checking disk change line for drive %d\n", drive);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100753 debug_dcl(drive_params[drive].flags, "jiffies=%lu\n", jiffies);
754 debug_dcl(drive_params[drive].flags, "disk change line=%x\n",
Willy Tarreauac701862020-03-01 20:55:53 +0100755 fdc_inb(fdc, FD_DIR) & 0x80);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100756 debug_dcl(drive_params[drive].flags, "flags=%lx\n",
757 drive_state[drive].flags);
Joe Perches87f530d2010-03-10 15:20:54 -0800758
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100759 if (drive_params[drive].flags & FD_BROKEN_DCL)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100760 return test_bit(FD_DISK_CHANGED_BIT,
761 &drive_state[drive].flags);
Willy Tarreauac701862020-03-01 20:55:53 +0100762 if ((fdc_inb(fdc, FD_DIR) ^ drive_params[drive].flags) & 0x80) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100763 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -0800764 /* verify write protection */
765
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100766 if (drive_state[drive].maxblock) /* mark it changed */
767 set_bit(FD_DISK_CHANGED_BIT,
768 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769
770 /* invalidate its geometry */
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100771 if (drive_state[drive].keep_data >= 0) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100772 if ((drive_params[drive].flags & FTD_MSG) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 current_type[drive] != NULL)
Joe Perches891eda82010-03-10 15:21:05 -0800774 DPRINT("Disk type is undefined after disk change\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 current_type[drive] = NULL;
776 floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1;
777 }
778
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 return 1;
780 } else {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100781 drive_state[drive].last_checked = jiffies;
782 clear_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 }
784 return 0;
785}
786
787static inline int is_selected(int dor, int unit)
788{
789 return ((dor & (0x10 << unit)) && (dor & 3) == unit);
790}
791
Joe Perches57584c52010-03-10 15:21:00 -0800792static bool is_ready_state(int status)
793{
794 int state = status & (STATUS_READY | STATUS_DIR | STATUS_DMA);
795 return state == STATUS_READY;
796}
797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798static int set_dor(int fdc, char mask, char data)
799{
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700800 unsigned char unit;
801 unsigned char drive;
802 unsigned char newdor;
803 unsigned char olddor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804
Willy Tarreaude6048b2020-02-24 22:23:43 +0100805 if (fdc_state[fdc].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 return -1;
807
Willy Tarreaude6048b2020-02-24 22:23:43 +0100808 olddor = fdc_state[fdc].dor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 newdor = (olddor & mask) | data;
810 if (newdor != olddor) {
811 unit = olddor & 0x3;
812 if (is_selected(olddor, unit) && !is_selected(newdor, unit)) {
813 drive = REVDRIVE(fdc, unit);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100814 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800815 "calling disk change from set_dor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 disk_change(drive);
817 }
Willy Tarreaude6048b2020-02-24 22:23:43 +0100818 fdc_state[fdc].dor = newdor;
Willy Tarreauac701862020-03-01 20:55:53 +0100819 fdc_outb(newdor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
821 unit = newdor & 0x3;
822 if (!is_selected(olddor, unit) && is_selected(newdor, unit)) {
823 drive = REVDRIVE(fdc, unit);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100824 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825 }
826 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 return olddor;
828}
829
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200830static void twaddle(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831{
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200832 if (drive_params[drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 return;
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200834 fdc_outb(fdc_state[fdc].dor & ~(0x10 << UNIT(drive)),
835 fdc, FD_DOR);
836 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
837 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838}
839
Joe Perches57584c52010-03-10 15:21:00 -0800840/*
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200841 * Reset all driver information about the specified fdc.
Joe Perches57584c52010-03-10 15:21:00 -0800842 * This is needed after a reset, and after a raw command.
843 */
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200844static void reset_fdc_info(int fdc, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
846 int drive;
847
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200848 fdc_state[fdc].spec1 = fdc_state[fdc].spec2 = -1;
849 fdc_state[fdc].need_configure = 1;
850 fdc_state[fdc].perp_mode = 1;
851 fdc_state[fdc].rawcmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 for (drive = 0; drive < N_DRIVE; drive++)
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200853 if (FDC(drive) == fdc &&
Willy Tarreaue83995c2020-03-01 20:55:55 +0100854 (mode || drive_state[drive].track != NEED_1_RECAL))
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100855 drive_state[drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856}
857
858/* selects the fdc and drive, and enables the fdc's input/dma. */
859static void set_fdc(int drive)
860{
Willy Tarreaue83995c2020-03-01 20:55:55 +0100861 unsigned int new_fdc = current_fdc;
Linus Torvalds2e90ca62020-02-21 12:43:35 -0800862
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 if (drive >= 0 && drive < N_DRIVE) {
Linus Torvalds2e90ca62020-02-21 12:43:35 -0800864 new_fdc = FDC(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 current_drive = drive;
866 }
Linus Torvalds2e90ca62020-02-21 12:43:35 -0800867 if (new_fdc >= N_FDC) {
Joe Perchesb46df352010-03-10 15:20:46 -0800868 pr_info("bad fdc value\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 return;
870 }
Willy Tarreaue83995c2020-03-01 20:55:55 +0100871 current_fdc = new_fdc;
872 set_dor(current_fdc, ~0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873#if N_FDC > 1
Willy Tarreaue83995c2020-03-01 20:55:55 +0100874 set_dor(1 - current_fdc, ~8, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875#endif
Willy Tarreaue83995c2020-03-01 20:55:55 +0100876 if (fdc_state[current_fdc].rawcmd == 2)
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200877 reset_fdc_info(current_fdc, 1);
Willy Tarreaue83995c2020-03-01 20:55:55 +0100878 if (fdc_inb(current_fdc, FD_STATUS) != STATUS_READY)
879 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880}
881
882/* locks the driver */
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +0100883static int lock_fdc(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884{
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200885 if (WARN(atomic_read(&usage_count) == 0,
886 "Trying to lock fdc while usage count=0\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200889 if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy)))
890 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 command_status = FD_COMMAND_NONE;
893
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200894 reschedule_timeout(drive, "lock fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 set_fdc(drive);
896 return 0;
897}
898
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899/* unlocks the driver */
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +0200900static void unlock_fdc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 if (!test_bit(0, &fdc_busy))
903 DPRINT("FDC access conflict!\n");
904
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200905 raw_cmd = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 command_status = FD_COMMAND_NONE;
Tejun Heo136b5722012-08-21 13:18:24 -0700907 cancel_delayed_work(&fd_timeout);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200908 do_floppy = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 cont = NULL;
910 clear_bit(0, &fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911 wake_up(&fdc_wait);
912}
913
914/* switches the motor off after a given timeout */
Kees Cookb1bf4212017-10-04 17:49:29 -0700915static void motor_off_callback(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916{
Kees Cookb1bf4212017-10-04 17:49:29 -0700917 unsigned long nr = t - motor_off_timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 unsigned char mask = ~(0x10 << UNIT(nr));
919
Kees Cookb1bf4212017-10-04 17:49:29 -0700920 if (WARN_ON_ONCE(nr >= N_DRIVE))
921 return;
922
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 set_dor(FDC(nr), mask, 0);
924}
925
926/* schedules motor off */
927static void floppy_off(unsigned int drive)
928{
929 unsigned long volatile delta;
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700930 int fdc = FDC(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Willy Tarreaude6048b2020-02-24 22:23:43 +0100932 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 return;
934
935 del_timer(motor_off_timer + drive);
936
937 /* make spindle stop in a position which minimizes spinup time
938 * next time */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100939 if (drive_params[drive].rps) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100940 delta = jiffies - drive_state[drive].first_read_date + HZ -
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100941 drive_params[drive].spindown_offset;
942 delta = ((delta * drive_params[drive].rps) % HZ) / drive_params[drive].rps;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 motor_off_timer[drive].expires =
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100944 jiffies + drive_params[drive].spindown - delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 }
946 add_timer(motor_off_timer + drive);
947}
948
949/*
950 * cycle through all N_DRIVE floppy drives, for disk change testing.
951 * stopping at current drive. This is done before any long operation, to
952 * be sure to have up to date disk change information.
953 */
954static void scandrives(void)
955{
Jesper Juhl06f748c2007-10-16 23:30:57 -0700956 int i;
957 int drive;
958 int saved_drive;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Willy Tarreau031faab2020-02-24 22:23:48 +0100960 if (drive_params[current_drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 return;
962
963 saved_drive = current_drive;
964 for (i = 0; i < N_DRIVE; i++) {
965 drive = (saved_drive + i + 1) % N_DRIVE;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100966 if (drive_state[drive].fd_ref == 0 || drive_params[drive].select_delay != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 continue; /* skip closed drives */
968 set_fdc(drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +0100969 if (!(set_dor(current_fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 (0x10 << UNIT(drive))))
971 /* switch the motor off again, if it was off to
972 * begin with */
Willy Tarreaue83995c2020-03-01 20:55:55 +0100973 set_dor(current_fdc, ~(0x10 << UNIT(drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 }
975 set_fdc(saved_drive);
976}
977
978static void empty(void)
979{
980}
981
Tejun Heo75ddb382014-03-07 10:24:48 -0500982static void (*floppy_work_fn)(void);
983
984static void floppy_work_workfn(struct work_struct *work)
985{
986 floppy_work_fn();
987}
988
989static DECLARE_WORK(floppy_work, floppy_work_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
Joe Perches48c8cee2010-03-10 15:20:45 -0800991static void schedule_bh(void (*handler)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992{
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200993 WARN_ON(work_pending(&floppy_work));
994
Tejun Heo75ddb382014-03-07 10:24:48 -0500995 floppy_work_fn = handler;
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200996 queue_work(floppy_wq, &floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997}
998
Tejun Heo75ddb382014-03-07 10:24:48 -0500999static void (*fd_timer_fn)(void) = NULL;
1000
1001static void fd_timer_workfn(struct work_struct *work)
1002{
1003 fd_timer_fn();
1004}
1005
1006static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
1008static void cancel_activity(void)
1009{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 do_floppy = NULL;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001011 cancel_delayed_work_sync(&fd_timer);
1012 cancel_work_sync(&floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013}
1014
1015/* this function makes sure that the disk stays in the drive during the
1016 * transfer */
Tejun Heo75ddb382014-03-07 10:24:48 -05001017static void fd_watchdog(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018{
Willy Tarreau031faab2020-02-24 22:23:48 +01001019 debug_dcl(drive_params[current_drive].flags,
1020 "calling disk change from watchdog\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
1022 if (disk_change(current_drive)) {
1023 DPRINT("disk removed during i/o\n");
1024 cancel_activity();
1025 cont->done(0);
1026 reset_fdc();
1027 } else {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001028 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001029 fd_timer_fn = fd_watchdog;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001030 queue_delayed_work(floppy_wq, &fd_timer, HZ / 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
1032}
1033
1034static void main_command_interrupt(void)
1035{
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001036 cancel_delayed_work(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 cont->interrupt();
1038}
1039
1040/* waits for a delay (spinup or select) to pass */
Tejun Heo75ddb382014-03-07 10:24:48 -05001041static int fd_wait_for_completion(unsigned long expires,
1042 void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001044 if (fdc_state[current_fdc].reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 reset_fdc(); /* do the reset during sleep to win time
1046 * if we don't need to sleep, it's a good
1047 * occasion anyways */
1048 return 1;
1049 }
1050
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001051 if (time_before(jiffies, expires)) {
1052 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001053 fd_timer_fn = function;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001054 queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 return 1;
1056 }
1057 return 0;
1058}
1059
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060static void setup_DMA(void)
1061{
1062 unsigned long f;
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 if (raw_cmd->length == 0) {
1065 int i;
1066
Joe Perchesb46df352010-03-10 15:20:46 -08001067 pr_info("zero dma transfer size:");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 for (i = 0; i < raw_cmd->cmd_count; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001069 pr_cont("%x,", raw_cmd->cmd[i]);
1070 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001072 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 return;
1074 }
1075 if (((unsigned long)raw_cmd->kernel_data) % 512) {
Joe Perchesb46df352010-03-10 15:20:46 -08001076 pr_info("non aligned address: %p\n", raw_cmd->kernel_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001078 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 return;
1080 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 f = claim_dma_lock();
1082 fd_disable_dma();
1083#ifdef fd_dma_setup
1084 if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length,
1085 (raw_cmd->flags & FD_RAW_READ) ?
Willy Tarreaue83995c2020-03-01 20:55:55 +01001086 DMA_MODE_READ : DMA_MODE_WRITE,
1087 fdc_state[current_fdc].address) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 release_dma_lock(f);
1089 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001090 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 return;
1092 }
1093 release_dma_lock(f);
1094#else
1095 fd_clear_dma_ff();
1096 fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
1097 fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ) ?
1098 DMA_MODE_READ : DMA_MODE_WRITE);
1099 fd_set_dma_addr(raw_cmd->kernel_data);
1100 fd_set_dma_count(raw_cmd->length);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001101 virtual_dma_port = fdc_state[current_fdc].address;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102 fd_enable_dma();
1103 release_dma_lock(f);
1104#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105}
1106
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001107static void show_floppy(int fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108
1109/* waits until the fdc becomes ready */
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001110static int wait_til_ready(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001112 int status;
1113 int counter;
1114
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001115 if (fdc_state[fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 return -1;
1117 for (counter = 0; counter < 10000; counter++) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001118 status = fdc_inb(fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 if (status & STATUS_READY)
1120 return status;
1121 }
Joe Perches29f1c782010-03-10 15:21:00 -08001122 if (initialized) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001123 DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc);
1124 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 }
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001126 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 return -1;
1128}
1129
1130/* sends a command byte to the fdc */
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001131static int output_byte(int fdc, char byte)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132{
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001133 int status = wait_til_ready(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001135 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001137
1138 if (is_ready_state(status)) {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001139 fdc_outb(byte, fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 output_log[output_log_pos].data = byte;
1141 output_log[output_log_pos].status = status;
1142 output_log[output_log_pos].jiffies = jiffies;
1143 output_log_pos = (output_log_pos + 1) % OLOGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 return 0;
1145 }
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001146 fdc_state[fdc].reset = 1;
Joe Perches29f1c782010-03-10 15:21:00 -08001147 if (initialized) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001149 byte, fdc, status);
1150 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 }
1152 return -1;
1153}
1154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155/* gets the response from the fdc */
Willy Tarreau96dad772020-03-31 11:40:45 +02001156static int result(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001157{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001158 int i;
1159 int status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160
1161 for (i = 0; i < MAX_REPLIES; i++) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001162 status = wait_til_ready(fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001163 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165 status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA;
1166 if ((status & ~STATUS_BUSY) == STATUS_READY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 resultjiffies = jiffies;
1168 resultsize = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 return i;
1170 }
1171 if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY))
Willy Tarreau96dad772020-03-31 11:40:45 +02001172 reply_buffer[i] = fdc_inb(fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 else
1174 break;
1175 }
Joe Perches29f1c782010-03-10 15:21:00 -08001176 if (initialized) {
1177 DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
Willy Tarreau96dad772020-03-31 11:40:45 +02001178 fdc, status, i);
1179 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 }
Willy Tarreau96dad772020-03-31 11:40:45 +02001181 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 return -1;
1183}
1184
1185#define MORE_OUTPUT -2
1186/* does the fdc need more output? */
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001187static int need_more_output(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188{
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001189 int status = wait_til_ready(fdc);
Jesper Juhl06f748c2007-10-16 23:30:57 -07001190
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001191 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001193
1194 if (is_ready_state(status))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 return MORE_OUTPUT;
Joe Perches57584c52010-03-10 15:21:00 -08001196
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001197 return result(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198}
1199
1200/* Set perpendicular mode as required, based on data rate, if supported.
1201 * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
1202 */
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001203static void perpendicular_mode(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204{
1205 unsigned char perp_mode;
1206
1207 if (raw_cmd->rate & 0x40) {
1208 switch (raw_cmd->rate & 3) {
1209 case 0:
1210 perp_mode = 2;
1211 break;
1212 case 3:
1213 perp_mode = 3;
1214 break;
1215 default:
1216 DPRINT("Invalid data rate for perpendicular mode!\n");
1217 cont->done(0);
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001218 fdc_state[fdc].reset = 1;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001219 /*
1220 * convenient way to return to
1221 * redo without too much hassle
1222 * (deep stack et al.)
1223 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 return;
1225 }
1226 } else
1227 perp_mode = 0;
1228
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001229 if (fdc_state[fdc].perp_mode == perp_mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 return;
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001231 if (fdc_state[fdc].version >= FDC_82077_ORIG) {
1232 output_byte(fdc, FD_PERPENDICULAR);
1233 output_byte(fdc, perp_mode);
1234 fdc_state[fdc].perp_mode = perp_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 } else if (perp_mode) {
1236 DPRINT("perpendicular mode not supported by this FDC.\n");
1237 }
1238} /* perpendicular_mode */
1239
1240static int fifo_depth = 0xa;
1241static int no_fifo;
1242
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001243static int fdc_configure(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244{
1245 /* Turn on FIFO */
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001246 output_byte(fdc, FD_CONFIGURE);
1247 if (need_more_output(fdc) != MORE_OUTPUT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 return 0;
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001249 output_byte(fdc, 0);
1250 output_byte(fdc, 0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
1251 output_byte(fdc, 0); /* pre-compensation from track 0 upwards */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 return 1;
1253}
1254
1255#define NOMINAL_DTR 500
1256
1257/* Issue a "SPECIFY" command to set the step rate time, head unload time,
1258 * head load time, and DMA disable flag to values needed by floppy.
1259 *
1260 * The value "dtr" is the data transfer rate in Kbps. It is needed
1261 * to account for the data rate-based scaling done by the 82072 and 82077
1262 * FDC types. This parameter is ignored for other types of FDCs (i.e.
1263 * 8272a).
1264 *
1265 * Note that changing the data transfer rate has a (probably deleterious)
1266 * effect on the parameters subject to scaling for 82072/82077 FDCs, so
1267 * fdc_specify is called again after each data transfer rate
1268 * change.
1269 *
1270 * srt: 1000 to 16000 in microseconds
1271 * hut: 16 to 240 milliseconds
1272 * hlt: 2 to 254 milliseconds
1273 *
1274 * These values are rounded up to the next highest available delay time.
1275 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001276static void fdc_specify(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001278 unsigned char spec1;
1279 unsigned char spec2;
1280 unsigned long srt;
1281 unsigned long hlt;
1282 unsigned long hut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283 unsigned long dtr = NOMINAL_DTR;
1284 unsigned long scale_dtr = NOMINAL_DTR;
1285 int hlt_max_code = 0x7f;
1286 int hut_max_code = 0xf;
1287
Willy Tarreau3631a672020-03-31 11:40:49 +02001288 if (fdc_state[fdc].need_configure &&
1289 fdc_state[fdc].version >= FDC_82072A) {
1290 fdc_configure(fdc);
1291 fdc_state[fdc].need_configure = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 }
1293
1294 switch (raw_cmd->rate & 0x03) {
1295 case 3:
1296 dtr = 1000;
1297 break;
1298 case 1:
1299 dtr = 300;
Willy Tarreau3631a672020-03-31 11:40:49 +02001300 if (fdc_state[fdc].version >= FDC_82078) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 /* chose the default rate table, not the one
1302 * where 1 = 2 Mbps */
Willy Tarreau3631a672020-03-31 11:40:49 +02001303 output_byte(fdc, FD_DRIVESPEC);
1304 if (need_more_output(fdc) == MORE_OUTPUT) {
1305 output_byte(fdc, UNIT(drive));
1306 output_byte(fdc, 0xc0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 }
1308 }
1309 break;
1310 case 2:
1311 dtr = 250;
1312 break;
1313 }
1314
Willy Tarreau3631a672020-03-31 11:40:49 +02001315 if (fdc_state[fdc].version >= FDC_82072) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 scale_dtr = dtr;
1317 hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
1318 hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
1319 }
1320
1321 /* Convert step rate from microseconds to milliseconds and 4 bits */
Willy Tarreau3631a672020-03-31 11:40:49 +02001322 srt = 16 - DIV_ROUND_UP(drive_params[drive].srt * scale_dtr / 1000,
Willy Tarreau031faab2020-02-24 22:23:48 +01001323 NOMINAL_DTR);
Joe Perchesa81ee542010-03-10 15:20:46 -08001324 if (slow_floppy)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325 srt = srt / 4;
Joe Perchesa81ee542010-03-10 15:20:46 -08001326
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 SUPBOUND(srt, 0xf);
1328 INFBOUND(srt, 0);
1329
Willy Tarreau3631a672020-03-31 11:40:49 +02001330 hlt = DIV_ROUND_UP(drive_params[drive].hlt * scale_dtr / 2,
Willy Tarreau031faab2020-02-24 22:23:48 +01001331 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332 if (hlt < 0x01)
1333 hlt = 0x01;
1334 else if (hlt > 0x7f)
1335 hlt = hlt_max_code;
1336
Willy Tarreau3631a672020-03-31 11:40:49 +02001337 hut = DIV_ROUND_UP(drive_params[drive].hut * scale_dtr / 16,
Willy Tarreau031faab2020-02-24 22:23:48 +01001338 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 if (hut < 0x1)
1340 hut = 0x1;
1341 else if (hut > 0xf)
1342 hut = hut_max_code;
1343
1344 spec1 = (srt << 4) | hut;
1345 spec2 = (hlt << 1) | (use_virtual_dma & 1);
1346
1347 /* If these parameters did not change, just return with success */
Willy Tarreau3631a672020-03-31 11:40:49 +02001348 if (fdc_state[fdc].spec1 != spec1 ||
1349 fdc_state[fdc].spec2 != spec2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 /* Go ahead and set spec1 and spec2 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001351 output_byte(fdc, FD_SPECIFY);
1352 output_byte(fdc, fdc_state[fdc].spec1 = spec1);
1353 output_byte(fdc, fdc_state[fdc].spec2 = spec2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 }
1355} /* fdc_specify */
1356
1357/* Set the FDC's data transfer rate on behalf of the specified drive.
1358 * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
1359 * of the specify command (i.e. using the fdc_specify function).
1360 */
1361static int fdc_dtr(void)
1362{
1363 /* If data rate not already set to desired value, set it. */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001364 if ((raw_cmd->rate & 3) == fdc_state[current_fdc].dtr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 return 0;
1366
1367 /* Set dtr */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001368 fdc_outb(raw_cmd->rate & 3, current_fdc, FD_DCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
1370 /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
1371 * need a stabilization period of several milliseconds to be
1372 * enforced after data rate changes before R/W operations.
1373 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
1374 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001375 fdc_state[current_fdc].dtr = raw_cmd->rate & 3;
Tejun Heo75ddb382014-03-07 10:24:48 -05001376 return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377} /* fdc_dtr */
1378
1379static void tell_sector(void)
1380{
Joe Perchesb46df352010-03-10 15:20:46 -08001381 pr_cont(": track %d, head %d, sector %d, size %d",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001382 reply_buffer[R_TRACK], reply_buffer[R_HEAD],
1383 reply_buffer[R_SECTOR],
1384 reply_buffer[R_SIZECODE]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001385} /* tell_sector */
1386
Joe Perchesb46df352010-03-10 15:20:46 -08001387static void print_errors(void)
1388{
1389 DPRINT("");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001390 if (reply_buffer[ST0] & ST0_ECE) {
Joe Perchesb46df352010-03-10 15:20:46 -08001391 pr_cont("Recalibrate failed!");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001392 } else if (reply_buffer[ST2] & ST2_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001393 pr_cont("data CRC error");
1394 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001395 } else if (reply_buffer[ST1] & ST1_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001396 pr_cont("CRC error");
1397 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001398 } else if ((reply_buffer[ST1] & (ST1_MAM | ST1_ND)) ||
1399 (reply_buffer[ST2] & ST2_MAM)) {
Joe Perchesb46df352010-03-10 15:20:46 -08001400 if (!probing) {
1401 pr_cont("sector not found");
1402 tell_sector();
1403 } else
1404 pr_cont("probe failed...");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001405 } else if (reply_buffer[ST2] & ST2_WC) { /* seek error */
Joe Perchesb46df352010-03-10 15:20:46 -08001406 pr_cont("wrong cylinder");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001407 } else if (reply_buffer[ST2] & ST2_BC) { /* cylinder marked as bad */
Joe Perchesb46df352010-03-10 15:20:46 -08001408 pr_cont("bad cylinder");
1409 } else {
1410 pr_cont("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001411 reply_buffer[ST0], reply_buffer[ST1],
1412 reply_buffer[ST2]);
Joe Perchesb46df352010-03-10 15:20:46 -08001413 tell_sector();
1414 }
1415 pr_cont("\n");
1416}
1417
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418/*
1419 * OK, this error interpreting routine is called after a
1420 * DMA read/write has succeeded
1421 * or failed, so we check the results, and copy any buffers.
1422 * hhb: Added better error reporting.
1423 * ak: Made this into a separate routine.
1424 */
1425static int interpret_errors(void)
1426{
1427 char bad;
1428
1429 if (inr != 7) {
Joe Perches891eda82010-03-10 15:21:05 -08001430 DPRINT("-- FDC reply error\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001431 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432 return 1;
1433 }
1434
1435 /* check IC to find cause of interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001436 switch (reply_buffer[ST0] & ST0_INTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001437 case 0x40: /* error occurred during command execution */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001438 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439 return 0; /* occurs with pseudo-DMA */
1440 bad = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001441 if (reply_buffer[ST1] & ST1_WP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 DPRINT("Drive is write protected\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001443 clear_bit(FD_DISK_WRITABLE_BIT,
1444 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 cont->done(0);
1446 bad = 2;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001447 } else if (reply_buffer[ST1] & ST1_ND) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001448 set_bit(FD_NEED_TWADDLE_BIT,
1449 &drive_state[current_drive].flags);
Willy Tarreau8fb38452020-02-24 22:23:52 +01001450 } else if (reply_buffer[ST1] & ST1_OR) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001451 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 DPRINT("Over/Underrun - retrying\n");
1453 bad = 0;
Willy Tarreau031faab2020-02-24 22:23:48 +01001454 } else if (*errors >= drive_params[current_drive].max_errors.reporting) {
Joe Perchesb46df352010-03-10 15:20:46 -08001455 print_errors();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001457 if (reply_buffer[ST2] & ST2_WC || reply_buffer[ST2] & ST2_BC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458 /* wrong cylinder => recal */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001459 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 return bad;
1461 case 0x80: /* invalid command given */
1462 DPRINT("Invalid FDC command given!\n");
1463 cont->done(0);
1464 return 2;
1465 case 0xc0:
1466 DPRINT("Abnormal termination caused by polling\n");
1467 cont->error();
1468 return 2;
1469 default: /* (0) Normal command termination */
1470 return 0;
1471 }
1472}
1473
1474/*
1475 * This routine is called when everything should be correctly set up
1476 * for the transfer (i.e. floppy motor is on, the correct floppy is
1477 * selected, and the head is sitting on the right track).
1478 */
1479static void setup_rw_floppy(void)
1480{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001481 int i;
1482 int r;
1483 int flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484 unsigned long ready_date;
Tejun Heo75ddb382014-03-07 10:24:48 -05001485 void (*function)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 flags = raw_cmd->flags;
1488 if (flags & (FD_RAW_READ | FD_RAW_WRITE))
1489 flags |= FD_RAW_INTR;
1490
1491 if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001492 ready_date = drive_state[current_drive].spinup_date + drive_params[current_drive].spinup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 /* If spinup will take a long time, rerun scandrives
1494 * again just before spinup completion. Beware that
1495 * after scandrives, we must again wait for selection.
1496 */
Willy Tarreau031faab2020-02-24 22:23:48 +01001497 if (time_after(ready_date, jiffies + drive_params[current_drive].select_delay)) {
1498 ready_date -= drive_params[current_drive].select_delay;
Tejun Heo75ddb382014-03-07 10:24:48 -05001499 function = floppy_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 } else
Tejun Heo75ddb382014-03-07 10:24:48 -05001501 function = setup_rw_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502
1503 /* wait until the floppy is spinning fast enough */
1504 if (fd_wait_for_completion(ready_date, function))
1505 return;
1506 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
1508 setup_DMA();
1509
1510 if (flags & FD_RAW_INTR)
1511 do_floppy = main_command_interrupt;
1512
1513 r = 0;
1514 for (i = 0; i < raw_cmd->cmd_count; i++)
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001515 r |= output_byte(current_fdc, raw_cmd->cmd[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516
Joe Perchesded28632010-03-10 15:21:09 -08001517 debugt(__func__, "rw_command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
1519 if (r) {
1520 cont->error();
1521 reset_fdc();
1522 return;
1523 }
1524
1525 if (!(flags & FD_RAW_INTR)) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001526 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 cont->interrupt();
1528 } else if (flags & FD_RAW_NEED_DISK)
Tejun Heo75ddb382014-03-07 10:24:48 -05001529 fd_watchdog();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530}
1531
1532static int blind_seek;
1533
1534/*
1535 * This is the routine called after every seek (or recalibrate) interrupt
1536 * from the floppy controller.
1537 */
1538static void seek_interrupt(void)
1539{
Joe Perchesded28632010-03-10 15:21:09 -08001540 debugt(__func__, "");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001541 if (inr != 2 || (reply_buffer[ST0] & 0xF8) != 0x20) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 DPRINT("seek failed\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001543 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 cont->error();
1545 cont->redo();
1546 return;
1547 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001548 if (drive_state[current_drive].track >= 0 &&
1549 drive_state[current_drive].track != reply_buffer[ST1] &&
1550 !blind_seek) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001551 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001552 "clearing NEWCHANGE flag because of effective seek\n");
Willy Tarreau031faab2020-02-24 22:23:48 +01001553 debug_dcl(drive_params[current_drive].flags, "jiffies=%lu\n",
1554 jiffies);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001555 clear_bit(FD_DISK_NEWCHANGE_BIT,
1556 &drive_state[current_drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -08001557 /* effective seek */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001558 drive_state[current_drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001560 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 floppy_ready();
1562}
1563
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001564static void check_wp(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565{
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001566 if (test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)) {
Joe Perchese0298532010-03-10 15:20:55 -08001567 /* check write protection */
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001568 output_byte(fdc, FD_GETSTATUS);
1569 output_byte(fdc, UNIT(drive));
1570 if (result(fdc) != 1) {
1571 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 return;
1573 }
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001574 clear_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001575 clear_bit(FD_NEED_TWADDLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001576 &drive_state[drive].flags);
1577 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001578 "checking whether disk is write protected\n");
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001579 debug_dcl(drive_params[drive].flags, "wp=%x\n",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001580 reply_buffer[ST3] & 0x40);
1581 if (!(reply_buffer[ST3] & 0x40))
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001582 set_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001583 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001585 clear_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001586 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 }
1588}
1589
1590static void seek_floppy(void)
1591{
1592 int track;
1593
1594 blind_seek = 0;
1595
Willy Tarreau031faab2020-02-24 22:23:48 +01001596 debug_dcl(drive_params[current_drive].flags,
1597 "calling disk change from %s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001599 if (!test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) {
1601 /* the media changed flag should be cleared after the seek.
1602 * If it isn't, this means that there is really no disk in
1603 * the drive.
1604 */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001605 set_bit(FD_DISK_CHANGED_BIT,
1606 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 cont->done(0);
1608 cont->redo();
1609 return;
1610 }
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001611 if (drive_state[current_drive].track <= NEED_1_RECAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 recalibrate_floppy();
1613 return;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001614 } else if (test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 (raw_cmd->flags & FD_RAW_NEED_DISK) &&
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001616 (drive_state[current_drive].track <= NO_TRACK || drive_state[current_drive].track == raw_cmd->track)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 /* we seek to clear the media-changed condition. Does anybody
1618 * know a more elegant way, which works on all drives? */
1619 if (raw_cmd->track)
1620 track = raw_cmd->track - 1;
1621 else {
Willy Tarreau031faab2020-02-24 22:23:48 +01001622 if (drive_params[current_drive].flags & FD_SILENT_DCL_CLEAR) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001623 set_dor(current_fdc, ~(0x10 << UNIT(current_drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 blind_seek = 1;
1625 raw_cmd->flags |= FD_RAW_NEED_SEEK;
1626 }
1627 track = 1;
1628 }
1629 } else {
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001630 check_wp(current_fdc, current_drive);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001631 if (raw_cmd->track != drive_state[current_drive].track &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 (raw_cmd->flags & FD_RAW_NEED_SEEK))
1633 track = raw_cmd->track;
1634 else {
1635 setup_rw_floppy();
1636 return;
1637 }
1638 }
1639
1640 do_floppy = seek_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001641 output_byte(current_fdc, FD_SEEK);
1642 output_byte(current_fdc, UNIT(current_drive));
1643 if (output_byte(current_fdc, track) < 0) {
Joe Perches2300f902010-03-10 15:20:49 -08001644 reset_fdc();
1645 return;
1646 }
Joe Perchesded28632010-03-10 15:21:09 -08001647 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648}
1649
1650static void recal_interrupt(void)
1651{
Joe Perchesded28632010-03-10 15:21:09 -08001652 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 if (inr != 2)
Willy Tarreaue83995c2020-03-01 20:55:55 +01001654 fdc_state[current_fdc].reset = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001655 else if (reply_buffer[ST0] & ST0_ECE) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001656 switch (drive_state[current_drive].track) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 case NEED_1_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001658 debugt(__func__, "need 1 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001659 /* after a second recalibrate, we still haven't
1660 * reached track 0. Probably no drive. Raise an
1661 * error, as failing immediately might upset
1662 * computers possessed by the Devil :-) */
1663 cont->error();
1664 cont->redo();
1665 return;
1666 case NEED_2_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001667 debugt(__func__, "need 2 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 /* If we already did a recalibrate,
1669 * and we are not at track 0, this
1670 * means we have moved. (The only way
1671 * not to move at recalibration is to
1672 * be already at track 0.) Clear the
1673 * new change flag */
Willy Tarreau031faab2020-02-24 22:23:48 +01001674 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001675 "clearing NEWCHANGE flag because of second recalibrate\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001677 clear_bit(FD_DISK_NEWCHANGE_BIT,
1678 &drive_state[current_drive].flags);
1679 drive_state[current_drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680 /* fall through */
1681 default:
Joe Perchesded28632010-03-10 15:21:09 -08001682 debugt(__func__, "default");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 /* Recalibrate moves the head by at
1684 * most 80 steps. If after one
1685 * recalibrate we don't have reached
1686 * track 0, this might mean that we
1687 * started beyond track 80. Try
1688 * again. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001689 drive_state[current_drive].track = NEED_1_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690 break;
1691 }
1692 } else
Willy Tarreau8fb38452020-02-24 22:23:52 +01001693 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 floppy_ready();
1695}
1696
1697static void print_result(char *message, int inr)
1698{
1699 int i;
1700
1701 DPRINT("%s ", message);
1702 if (inr >= 0)
1703 for (i = 0; i < inr; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001704 pr_cont("repl[%d]=%x ", i, reply_buffer[i]);
1705 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706}
1707
1708/* interrupt handler. Note that this can be called externally on the Sparc */
David Howells7d12e782006-10-05 14:55:46 +01001709irqreturn_t floppy_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 int do_print;
1712 unsigned long f;
Jesper Juhl06f748c2007-10-16 23:30:57 -07001713 void (*handler)(void) = do_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
1715 lasthandler = handler;
1716 interruptjiffies = jiffies;
1717
1718 f = claim_dma_lock();
1719 fd_disable_dma();
1720 release_dma_lock(f);
1721
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 do_floppy = NULL;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001723 if (current_fdc >= N_FDC || fdc_state[current_fdc].address == -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001724 /* we don't even know which FDC is the culprit */
Joe Perchesb46df352010-03-10 15:20:46 -08001725 pr_info("DOR0=%x\n", fdc_state[0].dor);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001726 pr_info("floppy interrupt on bizarre fdc %d\n", current_fdc);
Sakari Ailusd75f7732019-03-25 21:32:28 +02001727 pr_info("handler=%ps\n", handler);
Joe Perches275176b2010-03-10 15:21:06 -08001728 is_alive(__func__, "bizarre fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 return IRQ_NONE;
1730 }
1731
Willy Tarreaue83995c2020-03-01 20:55:55 +01001732 fdc_state[current_fdc].reset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 /* We have to clear the reset flag here, because apparently on boxes
1734 * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
Willy Tarreaude6048b2020-02-24 22:23:43 +01001735 * emit SENSEI's to clear the interrupt line. And fdc_state[fdc].reset
1736 * blocks the emission of the SENSEI's.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 * It is OK to emit floppy commands because we are in an interrupt
1738 * handler here, and thus we have to fear no interference of other
1739 * activity.
1740 */
1741
Joe Perches29f1c782010-03-10 15:21:00 -08001742 do_print = !handler && print_unex && initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743
Willy Tarreau96dad772020-03-31 11:40:45 +02001744 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 if (do_print)
1746 print_result("unexpected interrupt", inr);
1747 if (inr == 0) {
1748 int max_sensei = 4;
1749 do {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001750 output_byte(current_fdc, FD_SENSEI);
Willy Tarreau96dad772020-03-31 11:40:45 +02001751 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001752 if (do_print)
1753 print_result("sensei", inr);
1754 max_sensei--;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001755 } while ((reply_buffer[ST0] & 0x83) != UNIT(current_drive) &&
Joe Perchesc5297302010-03-10 15:20:58 -08001756 inr == 2 && max_sensei);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757 }
1758 if (!handler) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001759 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 return IRQ_NONE;
1761 }
1762 schedule_bh(handler);
Joe Perches275176b2010-03-10 15:21:06 -08001763 is_alive(__func__, "normal interrupt end");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764
1765 /* FIXME! Was it really for us? */
1766 return IRQ_HANDLED;
1767}
1768
1769static void recalibrate_floppy(void)
1770{
Joe Perchesded28632010-03-10 15:21:09 -08001771 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 do_floppy = recal_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001773 output_byte(current_fdc, FD_RECALIBRATE);
1774 if (output_byte(current_fdc, UNIT(current_drive)) < 0)
Joe Perches2300f902010-03-10 15:20:49 -08001775 reset_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776}
1777
1778/*
1779 * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
1780 */
1781static void reset_interrupt(void)
1782{
Joe Perchesded28632010-03-10 15:21:09 -08001783 debugt(__func__, "");
Willy Tarreau96dad772020-03-31 11:40:45 +02001784 result(current_fdc); /* get the status ready for set_fdc */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001785 if (fdc_state[current_fdc].reset) {
Sakari Ailusd75f7732019-03-25 21:32:28 +02001786 pr_info("reset set in interrupt, calling %ps\n", cont->error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787 cont->error(); /* a reset just after a reset. BAD! */
1788 }
1789 cont->redo();
1790}
1791
1792/*
1793 * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
1794 * or by setting the self clearing bit 7 of STATUS (newer FDCs)
1795 */
1796static void reset_fdc(void)
1797{
1798 unsigned long flags;
1799
1800 do_floppy = reset_interrupt;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001801 fdc_state[current_fdc].reset = 0;
Willy Tarreauf3e0dc12020-03-31 11:40:41 +02001802 reset_fdc_info(current_fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
1804 /* Pseudo-DMA may intercept 'reset finished' interrupt. */
1805 /* Irrelevant for systems with true DMA (i386). */
1806
1807 flags = claim_dma_lock();
1808 fd_disable_dma();
1809 release_dma_lock(flags);
1810
Willy Tarreaue83995c2020-03-01 20:55:55 +01001811 if (fdc_state[current_fdc].version >= FDC_82072A)
1812 fdc_outb(0x80 | (fdc_state[current_fdc].dtr & 3),
1813 current_fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814 else {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001815 fdc_outb(fdc_state[current_fdc].dor & ~0x04, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 udelay(FD_RESET_DELAY);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001817 fdc_outb(fdc_state[current_fdc].dor, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 }
1819}
1820
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001821static void show_floppy(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822{
1823 int i;
1824
Joe Perchesb46df352010-03-10 15:20:46 -08001825 pr_info("\n");
1826 pr_info("floppy driver state\n");
1827 pr_info("-------------------\n");
Sakari Ailusd75f7732019-03-25 21:32:28 +02001828 pr_info("now=%lu last interrupt=%lu diff=%lu last called handler=%ps\n",
Joe Perchesb46df352010-03-10 15:20:46 -08001829 jiffies, interruptjiffies, jiffies - interruptjiffies,
1830 lasthandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Joe Perchesb46df352010-03-10 15:20:46 -08001832 pr_info("timeout_message=%s\n", timeout_message);
1833 pr_info("last output bytes:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001834 for (i = 0; i < OLOGSIZE; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001835 pr_info("%2x %2x %lu\n",
1836 output_log[(i + output_log_pos) % OLOGSIZE].data,
1837 output_log[(i + output_log_pos) % OLOGSIZE].status,
1838 output_log[(i + output_log_pos) % OLOGSIZE].jiffies);
1839 pr_info("last result at %lu\n", resultjiffies);
1840 pr_info("last redo_fd_request at %lu\n", lastredo);
1841 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
1842 reply_buffer, resultsize, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001844 pr_info("status=%x\n", fdc_inb(fdc, FD_STATUS));
Joe Perchesb46df352010-03-10 15:20:46 -08001845 pr_info("fdc_busy=%lu\n", fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 if (do_floppy)
Sakari Ailusd75f7732019-03-25 21:32:28 +02001847 pr_info("do_floppy=%ps\n", do_floppy);
David Howells365970a2006-11-22 14:54:49 +00001848 if (work_pending(&floppy_work))
Sakari Ailusd75f7732019-03-25 21:32:28 +02001849 pr_info("floppy_work.func=%ps\n", floppy_work.func);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001850 if (delayed_work_pending(&fd_timer))
1851 pr_info("delayed work.function=%p expires=%ld\n",
1852 fd_timer.work.func,
1853 fd_timer.timer.expires - jiffies);
1854 if (delayed_work_pending(&fd_timeout))
1855 pr_info("timer_function=%p expires=%ld\n",
1856 fd_timeout.work.func,
1857 fd_timeout.timer.expires - jiffies);
1858
Joe Perchesb46df352010-03-10 15:20:46 -08001859 pr_info("cont=%p\n", cont);
1860 pr_info("current_req=%p\n", current_req);
1861 pr_info("command_status=%d\n", command_status);
1862 pr_info("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863}
1864
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001865static void floppy_shutdown(struct work_struct *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001866{
1867 unsigned long flags;
1868
Joe Perches29f1c782010-03-10 15:21:00 -08001869 if (initialized)
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001870 show_floppy(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871 cancel_activity();
1872
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 flags = claim_dma_lock();
1874 fd_disable_dma();
1875 release_dma_lock(flags);
1876
1877 /* avoid dma going to a random drive after shutdown */
1878
Joe Perches29f1c782010-03-10 15:21:00 -08001879 if (initialized)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 DPRINT("floppy timeout called\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001881 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001882 if (cont) {
1883 cont->done(0);
1884 cont->redo(); /* this will recall reset when needed */
1885 } else {
Joe Perchesb46df352010-03-10 15:20:46 -08001886 pr_info("no cont in shutdown!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 process_fd_request();
1888 }
Joe Perches275176b2010-03-10 15:21:06 -08001889 is_alive(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890}
1891
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892/* start motor, check media-changed condition and write protection */
Jesper Juhl06f748c2007-10-16 23:30:57 -07001893static int start_motor(void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001895 int mask;
1896 int data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897
1898 mask = 0xfc;
1899 data = UNIT(current_drive);
1900 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001901 if (!(fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 set_debugt();
1903 /* no read since this drive is running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001904 drive_state[current_drive].first_read_date = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905 /* note motor start time if motor is not yet running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001906 drive_state[current_drive].spinup_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 data |= (0x10 << UNIT(current_drive));
1908 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01001909 } else if (fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 mask &= ~(0x10 << UNIT(current_drive));
1911
1912 /* starts motor and selects floppy */
1913 del_timer(motor_off_timer + current_drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001914 set_dor(current_fdc, mask, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915
1916 /* wait_for_completion also schedules reset if needed. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001917 return fd_wait_for_completion(drive_state[current_drive].select_date + drive_params[current_drive].select_delay,
Tejun Heo75ddb382014-03-07 10:24:48 -05001918 function);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919}
1920
1921static void floppy_ready(void)
1922{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001923 if (fdc_state[current_fdc].reset) {
Joe Perches045f9832010-03-10 15:20:47 -08001924 reset_fdc();
1925 return;
1926 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 if (start_motor(floppy_ready))
1928 return;
1929 if (fdc_dtr())
1930 return;
1931
Willy Tarreau031faab2020-02-24 22:23:48 +01001932 debug_dcl(drive_params[current_drive].flags,
1933 "calling disk change from floppy_ready\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
Willy Tarreau031faab2020-02-24 22:23:48 +01001935 disk_change(current_drive) && !drive_params[current_drive].select_delay)
Willy Tarreauc1f710b2020-03-31 11:40:40 +02001936 twaddle(current_fdc, current_drive); /* this clears the dcl on certain
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001937 * drive/controller combinations */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938
1939#ifdef fd_chose_dma_mode
1940 if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) {
1941 unsigned long flags = claim_dma_lock();
1942 fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length);
1943 release_dma_lock(flags);
1944 }
1945#endif
1946
1947 if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) {
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001948 perpendicular_mode(current_fdc);
Willy Tarreau3631a672020-03-31 11:40:49 +02001949 fdc_specify(current_fdc, current_drive); /* must be done here because of hut, hlt ... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001950 seek_floppy();
1951 } else {
1952 if ((raw_cmd->flags & FD_RAW_READ) ||
1953 (raw_cmd->flags & FD_RAW_WRITE))
Willy Tarreau3631a672020-03-31 11:40:49 +02001954 fdc_specify(current_fdc, current_drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 setup_rw_floppy();
1956 }
1957}
1958
1959static void floppy_start(void)
1960{
Joe Perches73507e62010-03-10 15:21:03 -08001961 reschedule_timeout(current_reqD, "floppy start");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963 scandrives();
Willy Tarreau031faab2020-02-24 22:23:48 +01001964 debug_dcl(drive_params[current_drive].flags,
1965 "setting NEWCHANGE in floppy_start\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001966 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 floppy_ready();
1968}
1969
1970/*
1971 * ========================================================================
1972 * here ends the bottom half. Exported routines are:
1973 * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
1974 * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
1975 * Initialization also uses output_byte, result, set_dor, floppy_interrupt
1976 * and set_dor.
1977 * ========================================================================
1978 */
1979/*
1980 * General purpose continuations.
1981 * ==============================
1982 */
1983
1984static void do_wakeup(void)
1985{
Joe Perches73507e62010-03-10 15:21:03 -08001986 reschedule_timeout(MAXTIMEOUT, "do wakeup");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987 cont = NULL;
1988 command_status += 2;
1989 wake_up(&command_done);
1990}
1991
Stephen Hemminger3b06c212010-07-20 20:09:00 -06001992static const struct cont_t wakeup_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 .interrupt = empty,
1994 .redo = do_wakeup,
1995 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07001996 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997};
1998
Stephen Hemminger3b06c212010-07-20 20:09:00 -06001999static const struct cont_t intr_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002000 .interrupt = empty,
2001 .redo = process_fd_request,
2002 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07002003 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004};
2005
Joe Perches74f63f42010-03-10 15:20:58 -08002006static int wait_til_done(void (*handler)(void), bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
2008 int ret;
2009
2010 schedule_bh(handler);
2011
Stephen Hemmingerb862f262010-06-15 13:21:11 +02002012 if (interruptible)
2013 wait_event_interruptible(command_done, command_status >= 2);
2014 else
2015 wait_event(command_done, command_status >= 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
2017 if (command_status < 2) {
2018 cancel_activity();
2019 cont = &intr_cont;
2020 reset_fdc();
2021 return -EINTR;
2022 }
2023
Willy Tarreaue83995c2020-03-01 20:55:55 +01002024 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025 command_status = FD_COMMAND_ERROR;
2026 if (command_status == FD_COMMAND_OKAY)
2027 ret = 0;
2028 else
2029 ret = -EIO;
2030 command_status = FD_COMMAND_NONE;
2031 return ret;
2032}
2033
2034static void generic_done(int result)
2035{
2036 command_status = result;
2037 cont = &wakeup_cont;
2038}
2039
2040static void generic_success(void)
2041{
2042 cont->done(1);
2043}
2044
2045static void generic_failure(void)
2046{
2047 cont->done(0);
2048}
2049
2050static void success_and_wakeup(void)
2051{
2052 generic_success();
2053 cont->redo();
2054}
2055
2056/*
2057 * formatting and rw support.
2058 * ==========================
2059 */
2060
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002061static int next_valid_format(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062{
2063 int probed_format;
2064
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002065 probed_format = drive_state[drive].probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 while (1) {
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002067 if (probed_format >= 8 || !drive_params[drive].autodetect[probed_format]) {
2068 drive_state[drive].probed_format = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 return 1;
2070 }
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002071 if (floppy_type[drive_params[drive].autodetect[probed_format]].sect) {
2072 drive_state[drive].probed_format = probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 return 0;
2074 }
2075 probed_format++;
2076 }
2077}
2078
2079static void bad_flp_intr(void)
2080{
2081 int err_count;
2082
2083 if (probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002084 drive_state[current_drive].probed_format++;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002085 if (!next_valid_format(current_drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 return;
2087 }
2088 err_count = ++(*errors);
Willy Tarreau2a348752020-02-24 22:23:50 +01002089 INFBOUND(write_errors[current_drive].badness, err_count);
Willy Tarreau031faab2020-02-24 22:23:48 +01002090 if (err_count > drive_params[current_drive].max_errors.abort)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 cont->done(0);
Willy Tarreau031faab2020-02-24 22:23:48 +01002092 if (err_count > drive_params[current_drive].max_errors.reset)
Willy Tarreaue83995c2020-03-01 20:55:55 +01002093 fdc_state[current_fdc].reset = 1;
Willy Tarreau031faab2020-02-24 22:23:48 +01002094 else if (err_count > drive_params[current_drive].max_errors.recal)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002095 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096}
2097
2098static void set_floppy(int drive)
2099{
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01002100 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07002101
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 if (type)
2103 _floppy = floppy_type + type;
2104 else
2105 _floppy = current_type[drive];
2106}
2107
2108/*
2109 * formatting support.
2110 * ===================
2111 */
2112static void format_interrupt(void)
2113{
2114 switch (interpret_errors()) {
2115 case 1:
2116 cont->error();
2117 case 2:
2118 break;
2119 case 0:
2120 cont->done(1);
2121 }
2122 cont->redo();
2123}
2124
Joe Perches48c8cee2010-03-10 15:20:45 -08002125#define FM_MODE(x, y) ((y) & ~(((x)->rate & 0x80) >> 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126#define CT(x) ((x) | 0xc0)
Joe Perches48c8cee2010-03-10 15:20:45 -08002127
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128static void setup_format_params(int track)
2129{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002130 int n;
2131 int il;
2132 int count;
2133 int head_shift;
2134 int track_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135 struct fparm {
2136 unsigned char track, head, sect, size;
2137 } *here = (struct fparm *)floppy_track_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138
2139 raw_cmd = &default_raw_cmd;
2140 raw_cmd->track = track;
2141
Joe Perches48c8cee2010-03-10 15:20:45 -08002142 raw_cmd->flags = (FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
2143 FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 raw_cmd->rate = _floppy->rate & 0x43;
2145 raw_cmd->cmd_count = NR_F;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002146 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_FORMAT);
2147 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, format_req.head);
2148 raw_cmd->cmd[F_SIZECODE] = FD_SIZECODE(_floppy);
2149 raw_cmd->cmd[F_SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[F_SIZECODE];
2150 raw_cmd->cmd[F_GAP] = _floppy->fmt_gap;
2151 raw_cmd->cmd[F_FILL] = FD_FILL_BYTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152
2153 raw_cmd->kernel_data = floppy_track_buffer;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002154 raw_cmd->length = 4 * raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155
Willy Tarreau76dabe72020-02-24 22:23:51 +01002156 if (!raw_cmd->cmd[F_SECT_PER_TRACK])
Denis Efremovf3554ae2019-07-12 21:55:20 +03002157 return;
2158
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 /* allow for about 30ms for data transport per track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002160 head_shift = (raw_cmd->cmd[F_SECT_PER_TRACK] + 5) / 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161
2162 /* a ``cylinder'' is two tracks plus a little stepping time */
2163 track_shift = 2 * head_shift + 3;
2164
2165 /* position of logical sector 1 on this track */
2166 n = (track_shift * format_req.track + head_shift * format_req.head)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002167 % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168
2169 /* determine interleave */
2170 il = 1;
2171 if (_floppy->fmt_gap < 0x22)
2172 il++;
2173
2174 /* initialize field */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002175 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 here[count].track = format_req.track;
2177 here[count].head = format_req.head;
2178 here[count].sect = 0;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002179 here[count].size = raw_cmd->cmd[F_SIZECODE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180 }
2181 /* place logical sectors */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002182 for (count = 1; count <= raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 here[n].sect = count;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002184 n = (n + il) % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 if (here[n].sect) { /* sector busy, find next free sector */
2186 ++n;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002187 if (n >= raw_cmd->cmd[F_SECT_PER_TRACK]) {
2188 n -= raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 while (here[n].sect)
2190 ++n;
2191 }
2192 }
2193 }
Keith Wansbrough9e491842008-09-22 14:57:17 -07002194 if (_floppy->stretch & FD_SECTBASEMASK) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002195 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; count++)
Keith Wansbrough9e491842008-09-22 14:57:17 -07002196 here[count].sect += FD_SECTBASE(_floppy) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 }
2198}
2199
2200static void redo_format(void)
2201{
2202 buffer_track = -1;
2203 setup_format_params(format_req.track << STRETCH(_floppy));
2204 floppy_start();
Joe Perchesded28632010-03-10 15:21:09 -08002205 debugt(__func__, "queue format request");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206}
2207
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002208static const struct cont_t format_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 .interrupt = format_interrupt,
2210 .redo = redo_format,
2211 .error = bad_flp_intr,
2212 .done = generic_done
2213};
2214
2215static int do_format(int drive, struct format_descr *tmp_format_req)
2216{
2217 int ret;
2218
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01002219 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08002220 return -EINTR;
2221
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222 set_floppy(drive);
2223 if (!_floppy ||
Willy Tarreau031faab2020-02-24 22:23:48 +01002224 _floppy->track > drive_params[current_drive].tracks ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225 tmp_format_req->track >= _floppy->track ||
2226 tmp_format_req->head >= _floppy->head ||
2227 (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
2228 !_floppy->fmt_gap) {
2229 process_fd_request();
2230 return -EINVAL;
2231 }
2232 format_req = *tmp_format_req;
2233 format_errors = 0;
2234 cont = &format_cont;
2235 errors = &format_errors;
Joe Perches74f63f42010-03-10 15:20:58 -08002236 ret = wait_til_done(redo_format, true);
Joe Perches55eee802010-03-10 15:20:57 -08002237 if (ret == -EINTR)
2238 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239 process_fd_request();
2240 return ret;
2241}
2242
2243/*
2244 * Buffer read/write and support
2245 * =============================
2246 */
2247
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002248static void floppy_end_request(struct request *req, blk_status_t error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249{
2250 unsigned int nr_sectors = current_count_sectors;
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002251 unsigned int drive = (unsigned long)req->rq_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252
2253 /* current_count_sectors can be zero if transfer failed */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002254 if (error)
Tejun Heo83096eb2009-05-07 22:24:39 +09002255 nr_sectors = blk_rq_cur_sectors(req);
Omar Sandovala9f38e12018-10-15 09:21:34 -06002256 if (blk_update_request(req, error, nr_sectors << 9))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 return;
Omar Sandovala9f38e12018-10-15 09:21:34 -06002258 __blk_mq_end_request(req, error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259
2260 /* We're done with the request */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002261 floppy_off(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 current_req = NULL;
2263}
2264
2265/* new request_done. Can handle physical sectors which are smaller than a
2266 * logical buffer */
2267static void request_done(int uptodate)
2268{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 struct request *req = current_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 int block;
Joe Perches73507e62010-03-10 15:21:03 -08002271 char msg[sizeof("request done ") + sizeof(int) * 3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
2273 probing = 0;
Joe Perches73507e62010-03-10 15:21:03 -08002274 snprintf(msg, sizeof(msg), "request done %d", uptodate);
2275 reschedule_timeout(MAXTIMEOUT, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276
2277 if (!req) {
Joe Perchesb46df352010-03-10 15:20:46 -08002278 pr_info("floppy.c: no request in request_done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 return;
2280 }
2281
2282 if (uptodate) {
2283 /* maintain values for invalidation on geometry
2284 * change */
Tejun Heo83096eb2009-05-07 22:24:39 +09002285 block = current_count_sectors + blk_rq_pos(req);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002286 INFBOUND(drive_state[current_drive].maxblock, block);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 if (block > _floppy->sect)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002288 drive_state[current_drive].maxtrack = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002290 floppy_end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 } else {
2292 if (rq_data_dir(req) == WRITE) {
2293 /* record write error information */
Willy Tarreau2a348752020-02-24 22:23:50 +01002294 write_errors[current_drive].write_errors++;
2295 if (write_errors[current_drive].write_errors == 1) {
2296 write_errors[current_drive].first_error_sector = blk_rq_pos(req);
2297 write_errors[current_drive].first_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 }
Willy Tarreau2a348752020-02-24 22:23:50 +01002299 write_errors[current_drive].last_error_sector = blk_rq_pos(req);
2300 write_errors[current_drive].last_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 }
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002302 floppy_end_request(req, BLK_STS_IOERR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 }
2304}
2305
2306/* Interrupt handler evaluating the result of the r/w operation */
2307static void rw_interrupt(void)
2308{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002309 int eoc;
2310 int ssize;
2311 int heads;
2312 int nr_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313
Willy Tarreau8fb38452020-02-24 22:23:52 +01002314 if (reply_buffer[R_HEAD] >= 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315 /* some Toshiba floppy controllers occasionnally seem to
2316 * return bogus interrupts after read/write operations, which
2317 * can be recognized by a bad head number (>= 2) */
2318 return;
2319 }
2320
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002321 if (!drive_state[current_drive].first_read_date)
2322 drive_state[current_drive].first_read_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323
2324 nr_sectors = 0;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002325 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326
Willy Tarreau8fb38452020-02-24 22:23:52 +01002327 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 eoc = 1;
2329 else
2330 eoc = 0;
2331
Willy Tarreau76dabe72020-02-24 22:23:51 +01002332 if (raw_cmd->cmd[COMMAND] & 0x80)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 heads = 2;
2334 else
2335 heads = 1;
2336
Willy Tarreau8fb38452020-02-24 22:23:52 +01002337 nr_sectors = (((reply_buffer[R_TRACK] - raw_cmd->cmd[TRACK]) * heads +
2338 reply_buffer[R_HEAD] - raw_cmd->cmd[HEAD]) * raw_cmd->cmd[SECT_PER_TRACK] +
2339 reply_buffer[R_SECTOR] - raw_cmd->cmd[SECTOR] + eoc) << raw_cmd->cmd[SIZECODE] >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341 if (nr_sectors / ssize >
Julia Lawall061837b2008-09-22 14:57:16 -07002342 DIV_ROUND_UP(in_sector_offset + current_count_sectors, ssize)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 DPRINT("long rw: %x instead of %lx\n",
2344 nr_sectors, current_count_sectors);
Willy Tarreau8fb38452020-02-24 22:23:52 +01002345 pr_info("rs=%d s=%d\n", reply_buffer[R_SECTOR],
2346 raw_cmd->cmd[SECTOR]);
2347 pr_info("rh=%d h=%d\n", reply_buffer[R_HEAD],
2348 raw_cmd->cmd[HEAD]);
2349 pr_info("rt=%d t=%d\n", reply_buffer[R_TRACK],
2350 raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002351 pr_info("heads=%d eoc=%d\n", heads, eoc);
2352 pr_info("spt=%d st=%d ss=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002353 raw_cmd->cmd[SECT_PER_TRACK], fsector_t, ssize);
Joe Perchesb46df352010-03-10 15:20:46 -08002354 pr_info("in_sector_offset=%d\n", in_sector_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 nr_sectors -= in_sector_offset;
2358 INFBOUND(nr_sectors, 0);
2359 SUPBOUND(current_count_sectors, nr_sectors);
2360
2361 switch (interpret_errors()) {
2362 case 2:
2363 cont->redo();
2364 return;
2365 case 1:
2366 if (!current_count_sectors) {
2367 cont->error();
2368 cont->redo();
2369 return;
2370 }
2371 break;
2372 case 0:
2373 if (!current_count_sectors) {
2374 cont->redo();
2375 return;
2376 }
2377 current_type[current_drive] = _floppy;
2378 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2379 break;
2380 }
2381
2382 if (probing) {
Willy Tarreau031faab2020-02-24 22:23:48 +01002383 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 DPRINT("Auto-detected floppy type %s in fd%d\n",
2385 _floppy->name, current_drive);
2386 current_type[current_drive] = _floppy;
2387 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2388 probing = 0;
2389 }
2390
Willy Tarreau76dabe72020-02-24 22:23:51 +01002391 if (CT(raw_cmd->cmd[COMMAND]) != FD_READ ||
Jens Axboeb4f42e22014-04-10 09:46:28 -06002392 raw_cmd->kernel_data == bio_data(current_req->bio)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 /* transfer directly from buffer */
2394 cont->done(1);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002395 } else if (CT(raw_cmd->cmd[COMMAND]) == FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 buffer_track = raw_cmd->track;
2397 buffer_drive = current_drive;
2398 INFBOUND(buffer_max, nr_sectors + fsector_t);
2399 }
2400 cont->redo();
2401}
2402
2403/* Compute maximal contiguous buffer size. */
2404static int buffer_chain_size(void)
2405{
Kent Overstreet79886132013-11-23 17:19:00 -08002406 struct bio_vec bv;
NeilBrown5705f702007-09-25 12:35:59 +02002407 int size;
2408 struct req_iterator iter;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002409 char *base;
2410
2411 base = bio_data(current_req->bio);
2412 size = 0;
2413
NeilBrown5705f702007-09-25 12:35:59 +02002414 rq_for_each_segment(bv, current_req, iter) {
Kent Overstreet79886132013-11-23 17:19:00 -08002415 if (page_address(bv.bv_page) + bv.bv_offset != base + size)
NeilBrown5705f702007-09-25 12:35:59 +02002416 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417
Kent Overstreet79886132013-11-23 17:19:00 -08002418 size += bv.bv_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419 }
2420
2421 return size >> 9;
2422}
2423
2424/* Compute the maximal transfer size */
2425static int transfer_size(int ssize, int max_sector, int max_size)
2426{
2427 SUPBOUND(max_sector, fsector_t + max_size);
2428
2429 /* alignment */
2430 max_sector -= (max_sector % _floppy->sect) % ssize;
2431
2432 /* transfer size, beginning not aligned */
2433 current_count_sectors = max_sector - fsector_t;
2434
2435 return max_sector;
2436}
2437
2438/*
2439 * Move data from/to the track buffer to/from the buffer cache.
2440 */
2441static void copy_buffer(int ssize, int max_sector, int max_sector_2)
2442{
2443 int remaining; /* number of transferred 512-byte sectors */
Kent Overstreet79886132013-11-23 17:19:00 -08002444 struct bio_vec bv;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002445 char *buffer;
2446 char *dma_buffer;
NeilBrown5705f702007-09-25 12:35:59 +02002447 int size;
2448 struct req_iterator iter;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449
2450 max_sector = transfer_size(ssize,
2451 min(max_sector, max_sector_2),
Tejun Heo83096eb2009-05-07 22:24:39 +09002452 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453
Willy Tarreau76dabe72020-02-24 22:23:51 +01002454 if (current_count_sectors <= 0 && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Tejun Heo83096eb2009-05-07 22:24:39 +09002455 buffer_max > fsector_t + blk_rq_sectors(current_req))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 current_count_sectors = min_t(int, buffer_max - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002457 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
2459 remaining = current_count_sectors << 9;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002460 if (remaining > blk_rq_bytes(current_req) && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461 DPRINT("in copy buffer\n");
Joe Perchesb46df352010-03-10 15:20:46 -08002462 pr_info("current_count_sectors=%ld\n", current_count_sectors);
2463 pr_info("remaining=%d\n", remaining >> 9);
2464 pr_info("current_req->nr_sectors=%u\n",
2465 blk_rq_sectors(current_req));
2466 pr_info("current_req->current_nr_sectors=%u\n",
2467 blk_rq_cur_sectors(current_req));
2468 pr_info("max_sector=%d\n", max_sector);
2469 pr_info("ssize=%d\n", ssize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471
2472 buffer_max = max(max_sector, buffer_max);
2473
2474 dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9);
2475
Tejun Heo1011c1b2009-05-07 22:24:45 +09002476 size = blk_rq_cur_bytes(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477
NeilBrown5705f702007-09-25 12:35:59 +02002478 rq_for_each_segment(bv, current_req, iter) {
2479 if (!remaining)
2480 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481
Kent Overstreet79886132013-11-23 17:19:00 -08002482 size = bv.bv_len;
NeilBrown5705f702007-09-25 12:35:59 +02002483 SUPBOUND(size, remaining);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484
Kent Overstreet79886132013-11-23 17:19:00 -08002485 buffer = page_address(bv.bv_page) + bv.bv_offset;
NeilBrown5705f702007-09-25 12:35:59 +02002486 if (dma_buffer + size >
2487 floppy_track_buffer + (max_buffer_sectors << 10) ||
2488 dma_buffer < floppy_track_buffer) {
2489 DPRINT("buffer overrun in copy buffer %d\n",
Joe Perchesb46df352010-03-10 15:20:46 -08002490 (int)((floppy_track_buffer - dma_buffer) >> 9));
2491 pr_info("fsector_t=%d buffer_min=%d\n",
2492 fsector_t, buffer_min);
2493 pr_info("current_count_sectors=%ld\n",
2494 current_count_sectors);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002495 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Joe Perchesb46df352010-03-10 15:20:46 -08002496 pr_info("read\n");
Willy Tarreau76dabe72020-02-24 22:23:51 +01002497 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
Joe Perchesb46df352010-03-10 15:20:46 -08002498 pr_info("write\n");
NeilBrown5705f702007-09-25 12:35:59 +02002499 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500 }
NeilBrown5705f702007-09-25 12:35:59 +02002501 if (((unsigned long)buffer) % 512)
2502 DPRINT("%p buffer not aligned\n", buffer);
Joe Perches1a23d132010-03-10 15:21:04 -08002503
Willy Tarreau76dabe72020-02-24 22:23:51 +01002504 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
NeilBrown5705f702007-09-25 12:35:59 +02002505 memcpy(buffer, dma_buffer, size);
2506 else
2507 memcpy(dma_buffer, buffer, size);
2508
2509 remaining -= size;
2510 dma_buffer += size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002511 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512 if (remaining) {
2513 if (remaining > 0)
2514 max_sector -= remaining >> 9;
2515 DPRINT("weirdness: remaining %d\n", remaining >> 9);
2516 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517}
2518
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519/* work around a bug in pseudo DMA
2520 * (on some FDCs) pseudo DMA does not stop when the CPU stops
2521 * sending data. Hence we need a different way to signal the
Willy Tarreau76dabe72020-02-24 22:23:51 +01002522 * transfer length: We use raw_cmd->cmd[SECT_PER_TRACK]. Unfortunately, this
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 * does not work with MT, hence we can only transfer one head at
2524 * a time
2525 */
2526static void virtualdmabug_workaround(void)
2527{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002528 int hard_sectors;
2529 int end_sector;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530
Willy Tarreau76dabe72020-02-24 22:23:51 +01002531 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
2532 raw_cmd->cmd[COMMAND] &= ~0x80; /* switch off multiple track mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533
Willy Tarreau76dabe72020-02-24 22:23:51 +01002534 hard_sectors = raw_cmd->length >> (7 + raw_cmd->cmd[SIZECODE]);
2535 end_sector = raw_cmd->cmd[SECTOR] + hard_sectors - 1;
2536 if (end_sector > raw_cmd->cmd[SECT_PER_TRACK]) {
Joe Perchesb46df352010-03-10 15:20:46 -08002537 pr_info("too many sectors %d > %d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002538 end_sector, raw_cmd->cmd[SECT_PER_TRACK]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 return;
2540 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002541 raw_cmd->cmd[SECT_PER_TRACK] = end_sector;
2542 /* make sure raw_cmd->cmd[SECT_PER_TRACK]
Joe Perches48c8cee2010-03-10 15:20:45 -08002543 * points to end of transfer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544 }
2545}
2546
2547/*
2548 * Formulate a read/write request.
2549 * this routine decides where to load the data (directly to buffer, or to
2550 * tmp floppy area), how much data to load (the size of the buffer, the whole
2551 * track, or a single sector)
2552 * All floppy_track_buffer handling goes in here. If we ever add track buffer
2553 * allocation on the fly, it should be done here. No other part should need
2554 * modification.
2555 */
2556
2557static int make_raw_rw_request(void)
2558{
2559 int aligned_sector_t;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002560 int max_sector;
2561 int max_size;
2562 int tracksize;
2563 int ssize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002565 if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567
2568 set_fdc((long)current_req->rq_disk->private_data);
2569
2570 raw_cmd = &default_raw_cmd;
Fengguang Wu2fb2ca62012-07-28 19:45:59 +08002571 raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 raw_cmd->cmd_count = NR_RW;
2573 if (rq_data_dir(current_req) == READ) {
2574 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002575 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 } else if (rq_data_dir(current_req) == WRITE) {
2577 raw_cmd->flags |= FD_RAW_WRITE;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002578 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_WRITE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 } else {
Joe Perches275176b2010-03-10 15:21:06 -08002580 DPRINT("%s: unknown command\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 return 0;
2582 }
2583
2584 max_sector = _floppy->sect * _floppy->head;
2585
Willy Tarreau76dabe72020-02-24 22:23:51 +01002586 raw_cmd->cmd[TRACK] = (int)blk_rq_pos(current_req) / max_sector;
Tejun Heo83096eb2009-05-07 22:24:39 +09002587 fsector_t = (int)blk_rq_pos(current_req) % max_sector;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002588 if (_floppy->track && raw_cmd->cmd[TRACK] >= _floppy->track) {
Tejun Heo83096eb2009-05-07 22:24:39 +09002589 if (blk_rq_cur_sectors(current_req) & 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 current_count_sectors = 1;
2591 return 1;
2592 } else
2593 return 0;
2594 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002595 raw_cmd->cmd[HEAD] = fsector_t / _floppy->sect;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596
Keith Wansbrough9e491842008-09-22 14:57:17 -07002597 if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002598 test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags)) &&
Joe Perchese0298532010-03-10 15:20:55 -08002599 fsector_t < _floppy->sect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 max_sector = _floppy->sect;
2601
2602 /* 2M disks have phantom sectors on the first track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002603 if ((_floppy->rate & FD_2M) && (!raw_cmd->cmd[TRACK]) && (!raw_cmd->cmd[HEAD])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 max_sector = 2 * _floppy->sect / 3;
2605 if (fsector_t >= max_sector) {
2606 current_count_sectors =
2607 min_t(int, _floppy->sect - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002608 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002609 return 1;
2610 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002611 raw_cmd->cmd[SIZECODE] = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 } else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002613 raw_cmd->cmd[SIZECODE] = FD_SIZECODE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 raw_cmd->rate = _floppy->rate & 0x43;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002615 if ((_floppy->rate & FD_2M) &&
2616 (raw_cmd->cmd[TRACK] || raw_cmd->cmd[HEAD]) && raw_cmd->rate == 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617 raw_cmd->rate = 1;
2618
Willy Tarreau76dabe72020-02-24 22:23:51 +01002619 if (raw_cmd->cmd[SIZECODE])
2620 raw_cmd->cmd[SIZECODE2] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002622 raw_cmd->cmd[SIZECODE2] = 0x80;
2623 raw_cmd->track = raw_cmd->cmd[TRACK] << STRETCH(_floppy);
2624 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, raw_cmd->cmd[HEAD]);
2625 raw_cmd->cmd[GAP] = _floppy->gap;
2626 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
2627 raw_cmd->cmd[SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[SIZECODE];
2628 raw_cmd->cmd[SECTOR] = ((fsector_t % _floppy->sect) << 2 >> raw_cmd->cmd[SIZECODE]) +
Keith Wansbrough9e491842008-09-22 14:57:17 -07002629 FD_SECTBASE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002630
2631 /* tracksize describes the size which can be filled up with sectors
2632 * of size ssize.
2633 */
2634 tracksize = _floppy->sect - _floppy->sect % ssize;
2635 if (tracksize < _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002636 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 if (tracksize <= fsector_t % _floppy->sect)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002638 raw_cmd->cmd[SECTOR]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639
2640 /* if we are beyond tracksize, fill up using smaller sectors */
2641 while (tracksize <= fsector_t % _floppy->sect) {
2642 while (tracksize + ssize > _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002643 raw_cmd->cmd[SIZECODE]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 ssize >>= 1;
2645 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002646 raw_cmd->cmd[SECTOR]++;
2647 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648 tracksize += ssize;
2649 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002650 max_sector = raw_cmd->cmd[HEAD] * _floppy->sect + tracksize;
2651 } else if (!raw_cmd->cmd[TRACK] && !raw_cmd->cmd[HEAD] && !(_floppy->rate & FD_2M) && probing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652 max_sector = _floppy->sect;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002653 } else if (!raw_cmd->cmd[HEAD] && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654 /* for virtual DMA bug workaround */
2655 max_sector = _floppy->sect;
2656 }
2657
2658 in_sector_offset = (fsector_t % _floppy->sect) % ssize;
2659 aligned_sector_t = fsector_t - in_sector_offset;
Tejun Heo83096eb2009-05-07 22:24:39 +09002660 max_size = blk_rq_sectors(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661 if ((raw_cmd->track == buffer_track) &&
2662 (current_drive == buffer_drive) &&
2663 (fsector_t >= buffer_min) && (fsector_t < buffer_max)) {
2664 /* data already in track buffer */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002665 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 copy_buffer(1, max_sector, buffer_max);
2667 return 1;
2668 }
Tejun Heo83096eb2009-05-07 22:24:39 +09002669 } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002670 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002671 unsigned int sectors;
2672
2673 sectors = fsector_t + blk_rq_sectors(current_req);
2674 if (sectors > ssize && sectors < ssize + ssize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675 max_size = ssize + ssize;
2676 else
2677 max_size = ssize;
2678 }
2679 raw_cmd->flags &= ~FD_RAW_WRITE;
2680 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002681 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Jens Axboeb4f42e22014-04-10 09:46:28 -06002682 } else if ((unsigned long)bio_data(current_req->bio) < MAX_DMA_ADDRESS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 unsigned long dma_limit;
2684 int direct, indirect;
2685
2686 indirect =
2687 transfer_size(ssize, max_sector,
2688 max_buffer_sectors * 2) - fsector_t;
2689
2690 /*
2691 * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
2692 * on a 64 bit machine!
2693 */
2694 max_size = buffer_chain_size();
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002695 dma_limit = (MAX_DMA_ADDRESS -
Jens Axboeb4f42e22014-04-10 09:46:28 -06002696 ((unsigned long)bio_data(current_req->bio))) >> 9;
Joe Perchesa81ee542010-03-10 15:20:46 -08002697 if ((unsigned long)max_size > dma_limit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002698 max_size = dma_limit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002699 /* 64 kb boundaries */
Jens Axboeb4f42e22014-04-10 09:46:28 -06002700 if (CROSS_64KB(bio_data(current_req->bio), max_size << 9))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002701 max_size = (K_64 -
Jens Axboeb4f42e22014-04-10 09:46:28 -06002702 ((unsigned long)bio_data(current_req->bio)) %
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 K_64) >> 9;
2704 direct = transfer_size(ssize, max_sector, max_size) - fsector_t;
2705 /*
2706 * We try to read tracks, but if we get too many errors, we
2707 * go back to reading just one sector at a time.
2708 *
2709 * This means we should be able to read a sector even if there
2710 * are other bad sectors on this track.
2711 */
2712 if (!direct ||
2713 (indirect * 2 > direct * 3 &&
Willy Tarreau031faab2020-02-24 22:23:48 +01002714 *errors < drive_params[current_drive].max_errors.read_track &&
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002715 ((!probing ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002716 (drive_params[current_drive].read_track & (1 << drive_state[current_drive].probed_format)))))) {
Tejun Heo83096eb2009-05-07 22:24:39 +09002717 max_size = blk_rq_sectors(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 } else {
Jens Axboeb4f42e22014-04-10 09:46:28 -06002719 raw_cmd->kernel_data = bio_data(current_req->bio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720 raw_cmd->length = current_count_sectors << 9;
2721 if (raw_cmd->length == 0) {
Joe Perches275176b2010-03-10 15:21:06 -08002722 DPRINT("%s: zero dma transfer attempted\n", __func__);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002723 DPRINT("indirect=%d direct=%d fsector_t=%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002724 indirect, direct, fsector_t);
2725 return 0;
2726 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 virtualdmabug_workaround();
2728 return 2;
2729 }
2730 }
2731
Willy Tarreau76dabe72020-02-24 22:23:51 +01002732 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733 max_size = max_sector; /* unbounded */
2734
2735 /* claim buffer track if needed */
2736 if (buffer_track != raw_cmd->track || /* bad track */
2737 buffer_drive != current_drive || /* bad drive */
2738 fsector_t > buffer_max ||
2739 fsector_t < buffer_min ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002740 ((CT(raw_cmd->cmd[COMMAND]) == FD_READ ||
Tejun Heo83096eb2009-05-07 22:24:39 +09002741 (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742 max_sector > 2 * max_buffer_sectors + buffer_min &&
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002743 max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)) {
2744 /* not enough space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 buffer_track = -1;
2746 buffer_drive = current_drive;
2747 buffer_max = buffer_min = aligned_sector_t;
2748 }
2749 raw_cmd->kernel_data = floppy_track_buffer +
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002750 ((aligned_sector_t - buffer_min) << 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
Willy Tarreau76dabe72020-02-24 22:23:51 +01002752 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753 /* copy write buffer to track buffer.
2754 * if we get here, we know that the write
2755 * is either aligned or the data already in the buffer
2756 * (buffer will be overwritten) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 if (in_sector_offset && buffer_track == -1)
2758 DPRINT("internal error offset !=0 on write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759 buffer_track = raw_cmd->track;
2760 buffer_drive = current_drive;
2761 copy_buffer(ssize, max_sector,
2762 2 * max_buffer_sectors + buffer_min);
2763 } else
2764 transfer_size(ssize, max_sector,
2765 2 * max_buffer_sectors + buffer_min -
2766 aligned_sector_t);
2767
2768 /* round up current_count_sectors to get dma xfer size */
2769 raw_cmd->length = in_sector_offset + current_count_sectors;
2770 raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1;
2771 raw_cmd->length <<= 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772 if ((raw_cmd->length < current_count_sectors << 9) ||
Jens Axboeb4f42e22014-04-10 09:46:28 -06002773 (raw_cmd->kernel_data != bio_data(current_req->bio) &&
Willy Tarreau76dabe72020-02-24 22:23:51 +01002774 CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
2776 aligned_sector_t < buffer_min)) ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002777 raw_cmd->length % (128 << raw_cmd->cmd[SIZECODE]) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 raw_cmd->length <= 0 || current_count_sectors <= 0) {
2779 DPRINT("fractionary current count b=%lx s=%lx\n",
2780 raw_cmd->length, current_count_sectors);
Jens Axboeb4f42e22014-04-10 09:46:28 -06002781 if (raw_cmd->kernel_data != bio_data(current_req->bio))
Joe Perchesb46df352010-03-10 15:20:46 -08002782 pr_info("addr=%d, length=%ld\n",
2783 (int)((raw_cmd->kernel_data -
2784 floppy_track_buffer) >> 9),
2785 current_count_sectors);
2786 pr_info("st=%d ast=%d mse=%d msi=%d\n",
2787 fsector_t, aligned_sector_t, max_sector, max_size);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002788 pr_info("ssize=%x SIZECODE=%d\n", ssize, raw_cmd->cmd[SIZECODE]);
Joe Perchesb46df352010-03-10 15:20:46 -08002789 pr_info("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002790 raw_cmd->cmd[COMMAND], raw_cmd->cmd[SECTOR],
2791 raw_cmd->cmd[HEAD], raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002792 pr_info("buffer drive=%d\n", buffer_drive);
2793 pr_info("buffer track=%d\n", buffer_track);
2794 pr_info("buffer_min=%d\n", buffer_min);
2795 pr_info("buffer_max=%d\n", buffer_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002796 return 0;
2797 }
2798
Jens Axboeb4f42e22014-04-10 09:46:28 -06002799 if (raw_cmd->kernel_data != bio_data(current_req->bio)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800 if (raw_cmd->kernel_data < floppy_track_buffer ||
2801 current_count_sectors < 0 ||
2802 raw_cmd->length < 0 ||
2803 raw_cmd->kernel_data + raw_cmd->length >
2804 floppy_track_buffer + (max_buffer_sectors << 10)) {
2805 DPRINT("buffer overrun in schedule dma\n");
Joe Perchesb46df352010-03-10 15:20:46 -08002806 pr_info("fsector_t=%d buffer_min=%d current_count=%ld\n",
2807 fsector_t, buffer_min, raw_cmd->length >> 9);
2808 pr_info("current_count_sectors=%ld\n",
2809 current_count_sectors);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002810 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Joe Perchesb46df352010-03-10 15:20:46 -08002811 pr_info("read\n");
Willy Tarreau76dabe72020-02-24 22:23:51 +01002812 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
Joe Perchesb46df352010-03-10 15:20:46 -08002813 pr_info("write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 return 0;
2815 }
Tejun Heo1011c1b2009-05-07 22:24:45 +09002816 } else if (raw_cmd->length > blk_rq_bytes(current_req) ||
Tejun Heo83096eb2009-05-07 22:24:39 +09002817 current_count_sectors > blk_rq_sectors(current_req)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818 DPRINT("buffer overrun in direct transfer\n");
2819 return 0;
2820 } else if (raw_cmd->length < current_count_sectors << 9) {
2821 DPRINT("more sectors than bytes\n");
Joe Perchesb46df352010-03-10 15:20:46 -08002822 pr_info("bytes=%ld\n", raw_cmd->length >> 9);
2823 pr_info("sectors=%ld\n", current_count_sectors);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824 }
2825 if (raw_cmd->length == 0) {
2826 DPRINT("zero dma transfer attempted from make_raw_request\n");
2827 return 0;
2828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829
2830 virtualdmabug_workaround();
2831 return 2;
2832}
2833
Jens Axboe48821182010-09-22 09:32:36 +02002834static int set_next_request(void)
2835{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002836 current_req = list_first_entry_or_null(&floppy_reqs, struct request,
2837 queuelist);
2838 if (current_req) {
2839 current_req->error_count = 0;
2840 list_del_init(&current_req->queuelist);
2841 }
Jens Axboe48821182010-09-22 09:32:36 +02002842 return current_req != NULL;
2843}
2844
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845static void redo_fd_request(void)
2846{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 int drive;
2848 int tmp;
2849
2850 lastredo = jiffies;
2851 if (current_drive < N_DRIVE)
2852 floppy_off(current_drive);
2853
Joe Perches0da31322010-03-10 15:21:03 -08002854do_request:
2855 if (!current_req) {
Jens Axboe48821182010-09-22 09:32:36 +02002856 int pending;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857
Jens Axboe48821182010-09-22 09:32:36 +02002858 spin_lock_irq(&floppy_lock);
2859 pending = set_next_request();
2860 spin_unlock_irq(&floppy_lock);
Jens Axboe48821182010-09-22 09:32:36 +02002861 if (!pending) {
Joe Perches0da31322010-03-10 15:21:03 -08002862 do_floppy = NULL;
2863 unlock_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 }
Joe Perches0da31322010-03-10 15:21:03 -08002867 drive = (long)current_req->rq_disk->private_data;
2868 set_fdc(drive);
Joe Perches73507e62010-03-10 15:21:03 -08002869 reschedule_timeout(current_reqD, "redo fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002870
2871 set_floppy(drive);
2872 raw_cmd = &default_raw_cmd;
2873 raw_cmd->flags = 0;
2874 if (start_motor(redo_fd_request))
2875 return;
2876
2877 disk_change(current_drive);
2878 if (test_bit(current_drive, &fake_change) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002879 test_bit(FD_DISK_CHANGED_BIT, &drive_state[current_drive].flags)) {
Joe Perches0da31322010-03-10 15:21:03 -08002880 DPRINT("disk absent or changed during operation\n");
2881 request_done(0);
2882 goto do_request;
2883 }
2884 if (!_floppy) { /* Autodetection */
2885 if (!probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002886 drive_state[current_drive].probed_format = 0;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002887 if (next_valid_format(current_drive)) {
Joe Perches0da31322010-03-10 15:21:03 -08002888 DPRINT("no autodetectable formats\n");
2889 _floppy = NULL;
2890 request_done(0);
2891 goto do_request;
2892 }
2893 }
2894 probing = 1;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002895 _floppy = floppy_type + drive_params[current_drive].autodetect[drive_state[current_drive].probed_format];
Joe Perches0da31322010-03-10 15:21:03 -08002896 } else
2897 probing = 0;
Christoph Hellwig45908792017-04-20 16:03:12 +02002898 errors = &(current_req->error_count);
Joe Perches0da31322010-03-10 15:21:03 -08002899 tmp = make_raw_rw_request();
2900 if (tmp < 2) {
2901 request_done(tmp);
2902 goto do_request;
2903 }
2904
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002905 if (test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags))
Willy Tarreauc1f710b2020-03-31 11:40:40 +02002906 twaddle(current_fdc, current_drive);
Joe Perches0da31322010-03-10 15:21:03 -08002907 schedule_bh(floppy_start);
Joe Perchesded28632010-03-10 15:21:09 -08002908 debugt(__func__, "queue fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002909 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910}
2911
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002912static const struct cont_t rw_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913 .interrupt = rw_interrupt,
2914 .redo = redo_fd_request,
2915 .error = bad_flp_intr,
2916 .done = request_done
2917};
2918
2919static void process_fd_request(void)
2920{
2921 cont = &rw_cont;
2922 schedule_bh(redo_fd_request);
2923}
2924
Omar Sandovala9f38e12018-10-15 09:21:34 -06002925static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx,
2926 const struct blk_mq_queue_data *bd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002928 blk_mq_start_request(bd->rq);
2929
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002930 if (WARN(max_buffer_sectors == 0,
2931 "VFS: %s called on non-open device\n", __func__))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002932 return BLK_STS_IOERR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002934 if (WARN(atomic_read(&usage_count) == 0,
Christoph Hellwigaebf5262017-01-31 16:57:31 +01002935 "warning: usage count=0, current_req=%p sect=%ld flags=%llx\n",
2936 current_req, (long)blk_rq_pos(current_req),
Jens Axboe59533162013-05-23 12:25:08 +02002937 (unsigned long long) current_req->cmd_flags))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002938 return BLK_STS_IOERR;
2939
2940 spin_lock_irq(&floppy_lock);
2941 list_add_tail(&bd->rq->queuelist, &floppy_reqs);
2942 spin_unlock_irq(&floppy_lock);
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002943
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002944 if (test_and_set_bit(0, &fdc_busy)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 /* fdc busy, this new request will be treated when the
2946 current one is done */
Joe Perches275176b2010-03-10 15:21:06 -08002947 is_alive(__func__, "old request running");
Omar Sandovala9f38e12018-10-15 09:21:34 -06002948 return BLK_STS_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 }
Omar Sandovala9f38e12018-10-15 09:21:34 -06002950
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002951 command_status = FD_COMMAND_NONE;
2952 __reschedule_timeout(MAXTIMEOUT, "fd_request");
2953 set_fdc(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954 process_fd_request();
Joe Perches275176b2010-03-10 15:21:06 -08002955 is_alive(__func__, "");
Omar Sandovala9f38e12018-10-15 09:21:34 -06002956 return BLK_STS_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957}
2958
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002959static const struct cont_t poll_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960 .interrupt = success_and_wakeup,
2961 .redo = floppy_ready,
2962 .error = generic_failure,
2963 .done = generic_done
2964};
2965
Joe Perches74f63f42010-03-10 15:20:58 -08002966static int poll_drive(bool interruptible, int flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002967{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 /* no auto-sense, just clear dcl */
2969 raw_cmd = &default_raw_cmd;
2970 raw_cmd->flags = flag;
2971 raw_cmd->track = 0;
2972 raw_cmd->cmd_count = 0;
2973 cont = &poll_cont;
Willy Tarreau031faab2020-02-24 22:23:48 +01002974 debug_dcl(drive_params[current_drive].flags,
2975 "setting NEWCHANGE in poll_drive\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002976 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Joe Perches55eee802010-03-10 15:20:57 -08002977
2978 return wait_til_done(floppy_ready, interruptible);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979}
2980
2981/*
2982 * User triggered reset
2983 * ====================
2984 */
2985
2986static void reset_intr(void)
2987{
Joe Perchesb46df352010-03-10 15:20:46 -08002988 pr_info("weird, reset interrupt called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989}
2990
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002991static const struct cont_t reset_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992 .interrupt = reset_intr,
2993 .redo = success_and_wakeup,
2994 .error = generic_failure,
2995 .done = generic_done
2996};
2997
Joe Perches74f63f42010-03-10 15:20:58 -08002998static int user_reset_fdc(int drive, int arg, bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002999{
3000 int ret;
3001
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003002 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003003 return -EINTR;
3004
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005 if (arg == FD_RESET_ALWAYS)
Willy Tarreaue83995c2020-03-01 20:55:55 +01003006 fdc_state[current_fdc].reset = 1;
3007 if (fdc_state[current_fdc].reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008 cont = &reset_cont;
Joe Perches55eee802010-03-10 15:20:57 -08003009 ret = wait_til_done(reset_fdc, interruptible);
3010 if (ret == -EINTR)
3011 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003012 }
3013 process_fd_request();
Joe Perches52a0d612010-03-10 15:20:53 -08003014 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015}
3016
3017/*
3018 * Misc Ioctl's and support
3019 * ========================
3020 */
3021static inline int fd_copyout(void __user *param, const void *address,
3022 unsigned long size)
3023{
3024 return copy_to_user(param, address, size) ? -EFAULT : 0;
3025}
3026
Joe Perches48c8cee2010-03-10 15:20:45 -08003027static inline int fd_copyin(void __user *param, void *address,
3028 unsigned long size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029{
3030 return copy_from_user(address, param, size) ? -EFAULT : 0;
3031}
3032
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003033static const char *drive_name(int type, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034{
3035 struct floppy_struct *floppy;
3036
3037 if (type)
3038 floppy = floppy_type + type;
3039 else {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003040 if (drive_params[drive].native_format)
3041 floppy = floppy_type + drive_params[drive].native_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003042 else
3043 return "(null)";
3044 }
3045 if (floppy->name)
3046 return floppy->name;
3047 else
3048 return "(null)";
3049}
3050
3051/* raw commands */
3052static void raw_cmd_done(int flag)
3053{
3054 int i;
3055
3056 if (!flag) {
3057 raw_cmd->flags |= FD_RAW_FAILURE;
3058 raw_cmd->flags |= FD_RAW_HARDFAILURE;
3059 } else {
3060 raw_cmd->reply_count = inr;
3061 if (raw_cmd->reply_count > MAX_REPLIES)
3062 raw_cmd->reply_count = 0;
3063 for (i = 0; i < raw_cmd->reply_count; i++)
3064 raw_cmd->reply[i] = reply_buffer[i];
3065
3066 if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
3067 unsigned long flags;
3068 flags = claim_dma_lock();
3069 raw_cmd->length = fd_get_dma_residue();
3070 release_dma_lock(flags);
3071 }
3072
3073 if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
3074 (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
3075 raw_cmd->flags |= FD_RAW_FAILURE;
3076
3077 if (disk_change(current_drive))
3078 raw_cmd->flags |= FD_RAW_DISK_CHANGE;
3079 else
3080 raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
3081 if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
Kees Cookb1bf4212017-10-04 17:49:29 -07003082 motor_off_callback(&motor_off_timer[current_drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003083
3084 if (raw_cmd->next &&
3085 (!(raw_cmd->flags & FD_RAW_FAILURE) ||
3086 !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
3087 ((raw_cmd->flags & FD_RAW_FAILURE) ||
3088 !(raw_cmd->flags & FD_RAW_STOP_IF_SUCCESS))) {
3089 raw_cmd = raw_cmd->next;
3090 return;
3091 }
3092 }
3093 generic_done(flag);
3094}
3095
Stephen Hemminger3b06c212010-07-20 20:09:00 -06003096static const struct cont_t raw_cmd_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003097 .interrupt = success_and_wakeup,
3098 .redo = floppy_start,
3099 .error = generic_failure,
3100 .done = raw_cmd_done
3101};
3102
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003103static int raw_cmd_copyout(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003104 struct floppy_raw_cmd *ptr)
3105{
3106 int ret;
3107
3108 while (ptr) {
Matthew Daley2145e152014-04-28 19:05:21 +12003109 struct floppy_raw_cmd cmd = *ptr;
3110 cmd.next = NULL;
3111 cmd.kernel_data = NULL;
3112 ret = copy_to_user(param, &cmd, sizeof(cmd));
Joe Perches86b12b42010-03-10 15:20:56 -08003113 if (ret)
3114 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003115 param += sizeof(struct floppy_raw_cmd);
3116 if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003117 if (ptr->length >= 0 &&
3118 ptr->length <= ptr->buffer_length) {
3119 long length = ptr->buffer_length - ptr->length;
Joe Perches4575b552010-03-10 15:20:55 -08003120 ret = fd_copyout(ptr->data, ptr->kernel_data,
3121 length);
3122 if (ret)
3123 return ret;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003124 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125 }
3126 ptr = ptr->next;
3127 }
Joe Perches7f252712010-03-10 15:21:08 -08003128
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129 return 0;
3130}
3131
3132static void raw_cmd_free(struct floppy_raw_cmd **ptr)
3133{
Jesper Juhl06f748c2007-10-16 23:30:57 -07003134 struct floppy_raw_cmd *next;
3135 struct floppy_raw_cmd *this;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136
3137 this = *ptr;
3138 *ptr = NULL;
3139 while (this) {
3140 if (this->buffer_length) {
3141 fd_dma_mem_free((unsigned long)this->kernel_data,
3142 this->buffer_length);
3143 this->buffer_length = 0;
3144 }
3145 next = this->next;
3146 kfree(this);
3147 this = next;
3148 }
3149}
3150
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003151static int raw_cmd_copyin(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152 struct floppy_raw_cmd **rcmd)
3153{
3154 struct floppy_raw_cmd *ptr;
3155 int ret;
3156 int i;
3157
3158 *rcmd = NULL;
Joe Perches7f252712010-03-10 15:21:08 -08003159
3160loop:
Vlastimil Babka1661f2e2017-01-04 11:19:31 +01003161 ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL);
Joe Perches7f252712010-03-10 15:21:08 -08003162 if (!ptr)
3163 return -ENOMEM;
3164 *rcmd = ptr;
3165 ret = copy_from_user(ptr, param, sizeof(*ptr));
Joe Perches7f252712010-03-10 15:21:08 -08003166 ptr->next = NULL;
3167 ptr->buffer_length = 0;
Matthew Daleyef87dbe2014-04-28 19:05:20 +12003168 ptr->kernel_data = NULL;
3169 if (ret)
3170 return -EFAULT;
Joe Perches7f252712010-03-10 15:21:08 -08003171 param += sizeof(struct floppy_raw_cmd);
3172 if (ptr->cmd_count > 33)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 /* the command may now also take up the space
3174 * initially intended for the reply & the
3175 * reply count. Needed for long 82078 commands
3176 * such as RESTORE, which takes ... 17 command
3177 * bytes. Murphy's law #137: When you reserve
3178 * 16 bytes for a structure, you'll one day
3179 * discover that you really need 17...
3180 */
Joe Perches7f252712010-03-10 15:21:08 -08003181 return -EINVAL;
3182
3183 for (i = 0; i < 16; i++)
3184 ptr->reply[i] = 0;
3185 ptr->resultcode = 0;
Joe Perches7f252712010-03-10 15:21:08 -08003186
3187 if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
3188 if (ptr->length <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189 return -EINVAL;
Joe Perches7f252712010-03-10 15:21:08 -08003190 ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length);
3191 fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length);
3192 if (!ptr->kernel_data)
3193 return -ENOMEM;
3194 ptr->buffer_length = ptr->length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195 }
Joe Perches7f252712010-03-10 15:21:08 -08003196 if (ptr->flags & FD_RAW_WRITE) {
3197 ret = fd_copyin(ptr->data, ptr->kernel_data, ptr->length);
3198 if (ret)
3199 return ret;
3200 }
3201
3202 if (ptr->flags & FD_RAW_MORE) {
3203 rcmd = &(ptr->next);
3204 ptr->rate &= 0x43;
3205 goto loop;
3206 }
3207
3208 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209}
3210
3211static int raw_cmd_ioctl(int cmd, void __user *param)
3212{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003213 struct floppy_raw_cmd *my_raw_cmd;
Jesper Juhl06f748c2007-10-16 23:30:57 -07003214 int drive;
3215 int ret2;
3216 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217
Willy Tarreaue83995c2020-03-01 20:55:55 +01003218 if (fdc_state[current_fdc].rawcmd <= 1)
3219 fdc_state[current_fdc].rawcmd = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003221 if (FDC(drive) != current_fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003222 continue;
3223 if (drive == current_drive) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003224 if (drive_state[drive].fd_ref > 1) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003225 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 break;
3227 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003228 } else if (drive_state[drive].fd_ref) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003229 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230 break;
3231 }
3232 }
3233
Willy Tarreaue83995c2020-03-01 20:55:55 +01003234 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003235 return -EIO;
3236
3237 ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
3238 if (ret) {
3239 raw_cmd_free(&my_raw_cmd);
3240 return ret;
3241 }
3242
3243 raw_cmd = my_raw_cmd;
3244 cont = &raw_cmd_cont;
Joe Perches74f63f42010-03-10 15:20:58 -08003245 ret = wait_til_done(floppy_start, true);
Willy Tarreau031faab2020-02-24 22:23:48 +01003246 debug_dcl(drive_params[current_drive].flags,
3247 "calling disk change from raw_cmd ioctl\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003248
Willy Tarreaue83995c2020-03-01 20:55:55 +01003249 if (ret != -EINTR && fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003250 ret = -EIO;
3251
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003252 drive_state[current_drive].track = NO_TRACK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003253
3254 ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
3255 if (!ret)
3256 ret = ret2;
3257 raw_cmd_free(&my_raw_cmd);
3258 return ret;
3259}
3260
3261static int invalidate_drive(struct block_device *bdev)
3262{
3263 /* invalidate the buffer track to force a reread */
3264 set_bit((long)bdev->bd_disk->private_data, &fake_change);
3265 process_fd_request();
3266 check_disk_change(bdev);
3267 return 0;
3268}
3269
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003270static int set_geometry(unsigned int cmd, struct floppy_struct *g,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 int drive, int type, struct block_device *bdev)
3272{
3273 int cnt;
3274
3275 /* sanity checking for parameters. */
Denis Efremovda994662019-07-12 21:55:23 +03003276 if ((int)g->sect <= 0 ||
3277 (int)g->head <= 0 ||
3278 /* check for overflow in max_sector */
3279 (int)(g->sect * g->head) <= 0 ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01003280 /* check for zero in raw_cmd->cmd[F_SECT_PER_TRACK] */
Denis Efremovf3554ae2019-07-12 21:55:20 +03003281 (unsigned char)((g->sect << 2) >> FD_SIZECODE(g)) == 0 ||
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003282 g->track <= 0 || g->track > drive_params[drive].tracks >> STRETCH(g) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003283 /* check if reserved bits are set */
Keith Wansbrough9e491842008-09-22 14:57:17 -07003284 (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003285 return -EINVAL;
3286 if (type) {
3287 if (!capable(CAP_SYS_ADMIN))
3288 return -EPERM;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003289 mutex_lock(&open_lock);
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003290 if (lock_fdc(drive)) {
Jiri Slaby8516a502009-06-30 11:41:44 -07003291 mutex_unlock(&open_lock);
3292 return -EINTR;
3293 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 floppy_type[type] = *g;
3295 floppy_type[type].name = "user format";
3296 for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
3297 floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] =
3298 floppy_type[type].size + 1;
3299 process_fd_request();
3300 for (cnt = 0; cnt < N_DRIVE; cnt++) {
3301 struct block_device *bdev = opened_bdev[cnt];
3302 if (!bdev || ITYPE(drive_state[cnt].fd_device) != type)
3303 continue;
NeilBrown93b270f2011-02-24 17:25:47 +11003304 __invalidate_device(bdev, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003305 }
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003306 mutex_unlock(&open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307 } else {
3308 int oldStretch;
Joe Perches52a0d612010-03-10 15:20:53 -08003309
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003310 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003311 return -EINTR;
Joe Perches4575b552010-03-10 15:20:55 -08003312 if (cmd != FDDEFPRM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 /* notice a disk change immediately, else
3314 * we lose our settings immediately*/
Joe Perches74f63f42010-03-10 15:20:58 -08003315 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003316 return -EINTR;
3317 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003318 oldStretch = g->stretch;
3319 user_params[drive] = *g;
3320 if (buffer_drive == drive)
3321 SUPBOUND(buffer_max, user_params[drive].sect);
3322 current_type[drive] = &user_params[drive];
3323 floppy_sizes[drive] = user_params[drive].size;
3324 if (cmd == FDDEFPRM)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003325 drive_state[current_drive].keep_data = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003327 drive_state[current_drive].keep_data = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003328 /* invalidation. Invalidate only when needed, i.e.
3329 * when there are already sectors in the buffer cache
3330 * whose number will change. This is useful, because
3331 * mtools often changes the geometry of the disk after
3332 * looking at the boot block */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003333 if (drive_state[current_drive].maxblock > user_params[drive].sect ||
3334 drive_state[current_drive].maxtrack ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003335 ((user_params[drive].sect ^ oldStretch) &
Keith Wansbrough9e491842008-09-22 14:57:17 -07003336 (FD_SWAPSIDES | FD_SECTBASEMASK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003337 invalidate_drive(bdev);
3338 else
3339 process_fd_request();
3340 }
3341 return 0;
3342}
3343
3344/* handle obsolete ioctl's */
Stephen Hemminger21af5442010-06-15 13:21:11 +02003345static unsigned int ioctl_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003346 FDCLRPRM,
3347 FDSETPRM,
3348 FDDEFPRM,
3349 FDGETPRM,
3350 FDMSGON,
3351 FDMSGOFF,
3352 FDFMTBEG,
3353 FDFMTTRK,
3354 FDFMTEND,
3355 FDSETEMSGTRESH,
3356 FDFLUSH,
3357 FDSETMAXERRS,
3358 FDGETMAXERRS,
3359 FDGETDRVTYP,
3360 FDSETDRVPRM,
3361 FDGETDRVPRM,
3362 FDGETDRVSTAT,
3363 FDPOLLDRVSTAT,
3364 FDRESET,
3365 FDGETFDCSTAT,
3366 FDWERRORCLR,
3367 FDWERRORGET,
3368 FDRAWCMD,
3369 FDEJECT,
3370 FDTWADDLE
3371};
3372
Stephen Hemminger21af5442010-06-15 13:21:11 +02003373static int normalize_ioctl(unsigned int *cmd, int *size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003374{
3375 int i;
3376
3377 for (i = 0; i < ARRAY_SIZE(ioctl_table); i++) {
3378 if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)) {
3379 *size = _IOC_SIZE(*cmd);
3380 *cmd = ioctl_table[i];
3381 if (*size > _IOC_SIZE(*cmd)) {
Joe Perchesb46df352010-03-10 15:20:46 -08003382 pr_info("ioctl not yet supported\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383 return -EFAULT;
3384 }
3385 return 0;
3386 }
3387 }
3388 return -EINVAL;
3389}
3390
3391static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
3392{
3393 if (type)
3394 *g = &floppy_type[type];
3395 else {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003396 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003397 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003398 if (poll_drive(false, 0) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003399 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 process_fd_request();
3401 *g = current_type[drive];
3402 }
3403 if (!*g)
3404 return -ENODEV;
3405 return 0;
3406}
3407
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08003408static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
3409{
3410 int drive = (long)bdev->bd_disk->private_data;
3411 int type = ITYPE(drive_state[drive].fd_device);
3412 struct floppy_struct *g;
3413 int ret;
3414
3415 ret = get_floppy_geometry(drive, type, &g);
3416 if (ret)
3417 return ret;
3418
3419 geo->heads = g->head;
3420 geo->sectors = g->sect;
3421 geo->cylinders = g->track;
3422 return 0;
3423}
3424
Denis Efremov9b046092019-07-12 21:55:22 +03003425static bool valid_floppy_drive_params(const short autodetect[8],
3426 int native_format)
Denis Efremov5635f892019-07-12 21:55:21 +03003427{
3428 size_t floppy_type_size = ARRAY_SIZE(floppy_type);
3429 size_t i = 0;
3430
3431 for (i = 0; i < 8; ++i) {
3432 if (autodetect[i] < 0 ||
3433 autodetect[i] >= floppy_type_size)
3434 return false;
3435 }
3436
Denis Efremov9b046092019-07-12 21:55:22 +03003437 if (native_format < 0 || native_format >= floppy_type_size)
3438 return false;
3439
Denis Efremov5635f892019-07-12 21:55:21 +03003440 return true;
3441}
3442
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003443static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003444 unsigned long param)
3445{
Al Viroa4af9b42008-03-02 09:27:55 -05003446 int drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003447 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07003448 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449 int ret;
3450 int size;
3451 union inparam {
3452 struct floppy_struct g; /* geometry */
3453 struct format_descr f;
3454 struct floppy_max_errors max_errors;
3455 struct floppy_drive_params dp;
3456 } inparam; /* parameters coming from user space */
Joe Perches724ee622010-03-10 15:21:11 -08003457 const void *outparam; /* parameters passed back to user space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003458
3459 /* convert compatibility eject ioctls into floppy eject ioctl.
3460 * We do this in order to provide a means to eject floppy disks before
3461 * installing the new fdutils package */
3462 if (cmd == CDROMEJECT || /* CD-ROM eject */
Joe Perchesa81ee542010-03-10 15:20:46 -08003463 cmd == 0x6470) { /* SunOS floppy eject */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003464 DPRINT("obsolete eject ioctl\n");
3465 DPRINT("please use floppycontrol --eject\n");
3466 cmd = FDEJECT;
3467 }
3468
Joe Perchesa81ee542010-03-10 15:20:46 -08003469 if (!((cmd & 0xff00) == 0x0200))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003470 return -EINVAL;
3471
Joe Perchesa81ee542010-03-10 15:20:46 -08003472 /* convert the old style command into a new style command */
Joe Perches4575b552010-03-10 15:20:55 -08003473 ret = normalize_ioctl(&cmd, &size);
3474 if (ret)
3475 return ret;
Joe Perchesa81ee542010-03-10 15:20:46 -08003476
Linus Torvalds1da177e2005-04-16 15:20:36 -07003477 /* permission checks */
Joe Perches0aad92c2010-03-10 15:21:10 -08003478 if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003479 ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
3480 return -EPERM;
3481
Arjan van de Ven2886a8b2009-12-14 18:00:11 -08003482 if (WARN_ON(size < 0 || size > sizeof(inparam)))
3483 return -EINVAL;
3484
Linus Torvalds1da177e2005-04-16 15:20:36 -07003485 /* copyin */
Joe Perchesb87c9e02010-03-10 15:20:50 -08003486 memset(&inparam, 0, sizeof(inparam));
Joe Perches4575b552010-03-10 15:20:55 -08003487 if (_IOC_DIR(cmd) & _IOC_WRITE) {
3488 ret = fd_copyin((void __user *)param, &inparam, size);
3489 if (ret)
3490 return ret;
3491 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003492
Joe Perchesda273652010-03-10 15:20:52 -08003493 switch (cmd) {
3494 case FDEJECT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003495 if (drive_state[drive].fd_ref != 1)
Joe Perchesda273652010-03-10 15:20:52 -08003496 /* somebody else has this drive open */
3497 return -EBUSY;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003498 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003499 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500
Joe Perchesda273652010-03-10 15:20:52 -08003501 /* do the actual eject. Fails on
3502 * non-Sparc architectures */
3503 ret = fd_eject(UNIT(drive));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003505 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
3506 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchesda273652010-03-10 15:20:52 -08003507 process_fd_request();
3508 return ret;
3509 case FDCLRPRM:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003510 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003511 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003512 current_type[drive] = NULL;
3513 floppy_sizes[drive] = MAX_DISK_SIZE << 1;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003514 drive_state[drive].keep_data = 0;
Joe Perchesda273652010-03-10 15:20:52 -08003515 return invalidate_drive(bdev);
3516 case FDSETPRM:
3517 case FDDEFPRM:
3518 return set_geometry(cmd, &inparam.g, drive, type, bdev);
3519 case FDGETPRM:
Joe Perches4575b552010-03-10 15:20:55 -08003520 ret = get_floppy_geometry(drive, type,
Joe Perches724ee622010-03-10 15:21:11 -08003521 (struct floppy_struct **)&outparam);
Joe Perches4575b552010-03-10 15:20:55 -08003522 if (ret)
3523 return ret;
Andy Whitcroft65eea8e2018-09-20 09:09:48 -06003524 memcpy(&inparam.g, outparam,
3525 offsetof(struct floppy_struct, name));
3526 outparam = &inparam.g;
Joe Perchesda273652010-03-10 15:20:52 -08003527 break;
3528 case FDMSGON:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003529 drive_params[drive].flags |= FTD_MSG;
Joe Perchesda273652010-03-10 15:20:52 -08003530 return 0;
3531 case FDMSGOFF:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003532 drive_params[drive].flags &= ~FTD_MSG;
Joe Perchesda273652010-03-10 15:20:52 -08003533 return 0;
3534 case FDFMTBEG:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003535 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003536 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003537 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003538 return -EINTR;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003539 ret = drive_state[drive].flags;
Joe Perchesda273652010-03-10 15:20:52 -08003540 process_fd_request();
3541 if (ret & FD_VERIFY)
3542 return -ENODEV;
3543 if (!(ret & FD_DISK_WRITABLE))
3544 return -EROFS;
3545 return 0;
3546 case FDFMTTRK:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003547 if (drive_state[drive].fd_ref != 1)
Joe Perchesda273652010-03-10 15:20:52 -08003548 return -EBUSY;
3549 return do_format(drive, &inparam.f);
3550 case FDFMTEND:
3551 case FDFLUSH:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003552 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003553 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003554 return invalidate_drive(bdev);
3555 case FDSETEMSGTRESH:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003556 drive_params[drive].max_errors.reporting = (unsigned short)(param & 0x0f);
Joe Perchesda273652010-03-10 15:20:52 -08003557 return 0;
3558 case FDGETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003559 outparam = &drive_params[drive].max_errors;
Joe Perchesda273652010-03-10 15:20:52 -08003560 break;
3561 case FDSETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003562 drive_params[drive].max_errors = inparam.max_errors;
Joe Perchesda273652010-03-10 15:20:52 -08003563 break;
3564 case FDGETDRVTYP:
3565 outparam = drive_name(type, drive);
Joe Perches724ee622010-03-10 15:21:11 -08003566 SUPBOUND(size, strlen((const char *)outparam) + 1);
Joe Perchesda273652010-03-10 15:20:52 -08003567 break;
3568 case FDSETDRVPRM:
Denis Efremov9b046092019-07-12 21:55:22 +03003569 if (!valid_floppy_drive_params(inparam.dp.autodetect,
3570 inparam.dp.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003571 return -EINVAL;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003572 drive_params[drive] = inparam.dp;
Joe Perchesda273652010-03-10 15:20:52 -08003573 break;
3574 case FDGETDRVPRM:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003575 outparam = &drive_params[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003576 break;
3577 case FDPOLLDRVSTAT:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003578 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003579 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003580 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003581 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003582 process_fd_request();
3583 /* fall through */
3584 case FDGETDRVSTAT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003585 outparam = &drive_state[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003586 break;
3587 case FDRESET:
Joe Perches74f63f42010-03-10 15:20:58 -08003588 return user_reset_fdc(drive, (int)param, true);
Joe Perchesda273652010-03-10 15:20:52 -08003589 case FDGETFDCSTAT:
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003590 outparam = &fdc_state[FDC(drive)];
Joe Perchesda273652010-03-10 15:20:52 -08003591 break;
3592 case FDWERRORCLR:
Willy Tarreau121e2972020-02-24 22:23:47 +01003593 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Joe Perchesda273652010-03-10 15:20:52 -08003594 return 0;
3595 case FDWERRORGET:
Willy Tarreau121e2972020-02-24 22:23:47 +01003596 outparam = &write_errors[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003597 break;
3598 case FDRAWCMD:
3599 if (type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003600 return -EINVAL;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003601 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003602 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003603 set_floppy(drive);
Joe Perches4575b552010-03-10 15:20:55 -08003604 i = raw_cmd_ioctl(cmd, (void __user *)param);
3605 if (i == -EINTR)
3606 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003607 process_fd_request();
3608 return i;
3609 case FDTWADDLE:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003610 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003611 return -EINTR;
Willy Tarreauc1f710b2020-03-31 11:40:40 +02003612 twaddle(current_fdc, current_drive);
Joe Perchesda273652010-03-10 15:20:52 -08003613 process_fd_request();
3614 return 0;
3615 default:
3616 return -EINVAL;
3617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618
3619 if (_IOC_DIR(cmd) & _IOC_READ)
3620 return fd_copyout((void __user *)param, outparam, size);
Joe Perchesda273652010-03-10 15:20:52 -08003621
3622 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623}
3624
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003625static int fd_ioctl(struct block_device *bdev, fmode_t mode,
3626 unsigned int cmd, unsigned long param)
3627{
3628 int ret;
3629
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003630 mutex_lock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003631 ret = fd_locked_ioctl(bdev, mode, cmd, param);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003632 mutex_unlock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003633
3634 return ret;
3635}
3636
Al Viro229b53c2017-06-27 15:47:56 -04003637#ifdef CONFIG_COMPAT
3638
3639struct compat_floppy_drive_params {
3640 char cmos;
3641 compat_ulong_t max_dtr;
3642 compat_ulong_t hlt;
3643 compat_ulong_t hut;
3644 compat_ulong_t srt;
3645 compat_ulong_t spinup;
3646 compat_ulong_t spindown;
3647 unsigned char spindown_offset;
3648 unsigned char select_delay;
3649 unsigned char rps;
3650 unsigned char tracks;
3651 compat_ulong_t timeout;
3652 unsigned char interleave_sect;
3653 struct floppy_max_errors max_errors;
3654 char flags;
3655 char read_track;
3656 short autodetect[8];
3657 compat_int_t checkfreq;
3658 compat_int_t native_format;
3659};
3660
3661struct compat_floppy_drive_struct {
3662 signed char flags;
3663 compat_ulong_t spinup_date;
3664 compat_ulong_t select_date;
3665 compat_ulong_t first_read_date;
3666 short probed_format;
3667 short track;
3668 short maxblock;
3669 short maxtrack;
3670 compat_int_t generation;
3671 compat_int_t keep_data;
3672 compat_int_t fd_ref;
3673 compat_int_t fd_device;
3674 compat_int_t last_checked;
3675 compat_caddr_t dmabuf;
3676 compat_int_t bufblocks;
3677};
3678
3679struct compat_floppy_fdc_state {
3680 compat_int_t spec1;
3681 compat_int_t spec2;
3682 compat_int_t dtr;
3683 unsigned char version;
3684 unsigned char dor;
3685 compat_ulong_t address;
3686 unsigned int rawcmd:2;
3687 unsigned int reset:1;
3688 unsigned int need_configure:1;
3689 unsigned int perp_mode:2;
3690 unsigned int has_fifo:1;
3691 unsigned int driver_version;
3692 unsigned char track[4];
3693};
3694
3695struct compat_floppy_write_errors {
3696 unsigned int write_errors;
3697 compat_ulong_t first_error_sector;
3698 compat_int_t first_error_generation;
3699 compat_ulong_t last_error_sector;
3700 compat_int_t last_error_generation;
3701 compat_uint_t badness;
3702};
3703
3704#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct)
3705#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct)
3706#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params)
3707#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params)
3708#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct)
3709#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct)
3710#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
3711#define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors)
3712
3713static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3714 struct compat_floppy_struct __user *arg)
3715{
3716 struct floppy_struct v;
3717 int drive, type;
3718 int err;
3719
3720 BUILD_BUG_ON(offsetof(struct floppy_struct, name) !=
3721 offsetof(struct compat_floppy_struct, name));
3722
3723 if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL)))
3724 return -EPERM;
3725
3726 memset(&v, 0, sizeof(struct floppy_struct));
3727 if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name)))
3728 return -EFAULT;
3729
3730 mutex_lock(&floppy_mutex);
3731 drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003732 type = ITYPE(drive_state[drive].fd_device);
Al Viro229b53c2017-06-27 15:47:56 -04003733 err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM,
3734 &v, drive, type, bdev);
3735 mutex_unlock(&floppy_mutex);
3736 return err;
3737}
3738
3739static int compat_get_prm(int drive,
3740 struct compat_floppy_struct __user *arg)
3741{
3742 struct compat_floppy_struct v;
3743 struct floppy_struct *p;
3744 int err;
3745
3746 memset(&v, 0, sizeof(v));
3747 mutex_lock(&floppy_mutex);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003748 err = get_floppy_geometry(drive, ITYPE(drive_state[drive].fd_device),
3749 &p);
Al Viro229b53c2017-06-27 15:47:56 -04003750 if (err) {
3751 mutex_unlock(&floppy_mutex);
3752 return err;
3753 }
3754 memcpy(&v, p, offsetof(struct floppy_struct, name));
3755 mutex_unlock(&floppy_mutex);
3756 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct)))
3757 return -EFAULT;
3758 return 0;
3759}
3760
3761static int compat_setdrvprm(int drive,
3762 struct compat_floppy_drive_params __user *arg)
3763{
3764 struct compat_floppy_drive_params v;
3765
3766 if (!capable(CAP_SYS_ADMIN))
3767 return -EPERM;
3768 if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params)))
3769 return -EFAULT;
Denis Efremov9b046092019-07-12 21:55:22 +03003770 if (!valid_floppy_drive_params(v.autodetect, v.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003771 return -EINVAL;
Al Viro229b53c2017-06-27 15:47:56 -04003772 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003773 drive_params[drive].cmos = v.cmos;
3774 drive_params[drive].max_dtr = v.max_dtr;
3775 drive_params[drive].hlt = v.hlt;
3776 drive_params[drive].hut = v.hut;
3777 drive_params[drive].srt = v.srt;
3778 drive_params[drive].spinup = v.spinup;
3779 drive_params[drive].spindown = v.spindown;
3780 drive_params[drive].spindown_offset = v.spindown_offset;
3781 drive_params[drive].select_delay = v.select_delay;
3782 drive_params[drive].rps = v.rps;
3783 drive_params[drive].tracks = v.tracks;
3784 drive_params[drive].timeout = v.timeout;
3785 drive_params[drive].interleave_sect = v.interleave_sect;
3786 drive_params[drive].max_errors = v.max_errors;
3787 drive_params[drive].flags = v.flags;
3788 drive_params[drive].read_track = v.read_track;
3789 memcpy(drive_params[drive].autodetect, v.autodetect,
3790 sizeof(v.autodetect));
3791 drive_params[drive].checkfreq = v.checkfreq;
3792 drive_params[drive].native_format = v.native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003793 mutex_unlock(&floppy_mutex);
3794 return 0;
3795}
3796
3797static int compat_getdrvprm(int drive,
3798 struct compat_floppy_drive_params __user *arg)
3799{
3800 struct compat_floppy_drive_params v;
3801
3802 memset(&v, 0, sizeof(struct compat_floppy_drive_params));
3803 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003804 v.cmos = drive_params[drive].cmos;
3805 v.max_dtr = drive_params[drive].max_dtr;
3806 v.hlt = drive_params[drive].hlt;
3807 v.hut = drive_params[drive].hut;
3808 v.srt = drive_params[drive].srt;
3809 v.spinup = drive_params[drive].spinup;
3810 v.spindown = drive_params[drive].spindown;
3811 v.spindown_offset = drive_params[drive].spindown_offset;
3812 v.select_delay = drive_params[drive].select_delay;
3813 v.rps = drive_params[drive].rps;
3814 v.tracks = drive_params[drive].tracks;
3815 v.timeout = drive_params[drive].timeout;
3816 v.interleave_sect = drive_params[drive].interleave_sect;
3817 v.max_errors = drive_params[drive].max_errors;
3818 v.flags = drive_params[drive].flags;
3819 v.read_track = drive_params[drive].read_track;
3820 memcpy(v.autodetect, drive_params[drive].autodetect,
3821 sizeof(v.autodetect));
3822 v.checkfreq = drive_params[drive].checkfreq;
3823 v.native_format = drive_params[drive].native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003824 mutex_unlock(&floppy_mutex);
3825
Jann Horn52f6f9d2019-03-26 23:03:48 +01003826 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_params)))
Al Viro229b53c2017-06-27 15:47:56 -04003827 return -EFAULT;
3828 return 0;
3829}
3830
3831static int compat_getdrvstat(int drive, bool poll,
3832 struct compat_floppy_drive_struct __user *arg)
3833{
3834 struct compat_floppy_drive_struct v;
3835
3836 memset(&v, 0, sizeof(struct compat_floppy_drive_struct));
3837 mutex_lock(&floppy_mutex);
3838
3839 if (poll) {
3840 if (lock_fdc(drive))
3841 goto Eintr;
3842 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
3843 goto Eintr;
3844 process_fd_request();
3845 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003846 v.spinup_date = drive_state[drive].spinup_date;
3847 v.select_date = drive_state[drive].select_date;
3848 v.first_read_date = drive_state[drive].first_read_date;
3849 v.probed_format = drive_state[drive].probed_format;
3850 v.track = drive_state[drive].track;
3851 v.maxblock = drive_state[drive].maxblock;
3852 v.maxtrack = drive_state[drive].maxtrack;
3853 v.generation = drive_state[drive].generation;
3854 v.keep_data = drive_state[drive].keep_data;
3855 v.fd_ref = drive_state[drive].fd_ref;
3856 v.fd_device = drive_state[drive].fd_device;
3857 v.last_checked = drive_state[drive].last_checked;
3858 v.dmabuf = (uintptr_t) drive_state[drive].dmabuf;
3859 v.bufblocks = drive_state[drive].bufblocks;
Al Viro229b53c2017-06-27 15:47:56 -04003860 mutex_unlock(&floppy_mutex);
3861
Jann Horn52f6f9d2019-03-26 23:03:48 +01003862 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_struct)))
Al Viro229b53c2017-06-27 15:47:56 -04003863 return -EFAULT;
3864 return 0;
3865Eintr:
3866 mutex_unlock(&floppy_mutex);
3867 return -EINTR;
3868}
3869
3870static int compat_getfdcstat(int drive,
3871 struct compat_floppy_fdc_state __user *arg)
3872{
3873 struct compat_floppy_fdc_state v32;
3874 struct floppy_fdc_state v;
3875
3876 mutex_lock(&floppy_mutex);
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003877 v = fdc_state[FDC(drive)];
Al Viro229b53c2017-06-27 15:47:56 -04003878 mutex_unlock(&floppy_mutex);
3879
3880 memset(&v32, 0, sizeof(struct compat_floppy_fdc_state));
3881 v32.spec1 = v.spec1;
3882 v32.spec2 = v.spec2;
3883 v32.dtr = v.dtr;
3884 v32.version = v.version;
3885 v32.dor = v.dor;
3886 v32.address = v.address;
3887 v32.rawcmd = v.rawcmd;
3888 v32.reset = v.reset;
3889 v32.need_configure = v.need_configure;
3890 v32.perp_mode = v.perp_mode;
3891 v32.has_fifo = v.has_fifo;
3892 v32.driver_version = v.driver_version;
3893 memcpy(v32.track, v.track, 4);
3894 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state)))
3895 return -EFAULT;
3896 return 0;
3897}
3898
3899static int compat_werrorget(int drive,
3900 struct compat_floppy_write_errors __user *arg)
3901{
3902 struct compat_floppy_write_errors v32;
3903 struct floppy_write_errors v;
3904
3905 memset(&v32, 0, sizeof(struct compat_floppy_write_errors));
3906 mutex_lock(&floppy_mutex);
Willy Tarreau121e2972020-02-24 22:23:47 +01003907 v = write_errors[drive];
Al Viro229b53c2017-06-27 15:47:56 -04003908 mutex_unlock(&floppy_mutex);
3909 v32.write_errors = v.write_errors;
3910 v32.first_error_sector = v.first_error_sector;
3911 v32.first_error_generation = v.first_error_generation;
3912 v32.last_error_sector = v.last_error_sector;
3913 v32.last_error_generation = v.last_error_generation;
3914 v32.badness = v.badness;
3915 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors)))
3916 return -EFAULT;
3917 return 0;
3918}
3919
3920static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3921 unsigned long param)
3922{
3923 int drive = (long)bdev->bd_disk->private_data;
3924 switch (cmd) {
Arnd Bergmann9452b1a2019-11-28 15:48:10 +01003925 case CDROMEJECT: /* CD-ROM eject */
3926 case 0x6470: /* SunOS floppy eject */
3927
Al Viro229b53c2017-06-27 15:47:56 -04003928 case FDMSGON:
3929 case FDMSGOFF:
3930 case FDSETEMSGTRESH:
3931 case FDFLUSH:
3932 case FDWERRORCLR:
3933 case FDEJECT:
3934 case FDCLRPRM:
3935 case FDFMTBEG:
3936 case FDRESET:
3937 case FDTWADDLE:
3938 return fd_ioctl(bdev, mode, cmd, param);
3939 case FDSETMAXERRS:
3940 case FDGETMAXERRS:
3941 case FDGETDRVTYP:
3942 case FDFMTEND:
3943 case FDFMTTRK:
3944 case FDRAWCMD:
3945 return fd_ioctl(bdev, mode, cmd,
3946 (unsigned long)compat_ptr(param));
3947 case FDSETPRM32:
3948 case FDDEFPRM32:
3949 return compat_set_geometry(bdev, mode, cmd, compat_ptr(param));
3950 case FDGETPRM32:
3951 return compat_get_prm(drive, compat_ptr(param));
3952 case FDSETDRVPRM32:
3953 return compat_setdrvprm(drive, compat_ptr(param));
3954 case FDGETDRVPRM32:
3955 return compat_getdrvprm(drive, compat_ptr(param));
3956 case FDPOLLDRVSTAT32:
3957 return compat_getdrvstat(drive, true, compat_ptr(param));
3958 case FDGETDRVSTAT32:
3959 return compat_getdrvstat(drive, false, compat_ptr(param));
3960 case FDGETFDCSTAT32:
3961 return compat_getfdcstat(drive, compat_ptr(param));
3962 case FDWERRORGET32:
3963 return compat_werrorget(drive, compat_ptr(param));
3964 }
3965 return -EINVAL;
3966}
3967#endif
3968
Linus Torvalds1da177e2005-04-16 15:20:36 -07003969static void __init config_types(void)
3970{
Joe Perchesb46df352010-03-10 15:20:46 -08003971 bool has_drive = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003972 int drive;
3973
3974 /* read drive info out of physical CMOS */
3975 drive = 0;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003976 if (!drive_params[drive].cmos)
3977 drive_params[drive].cmos = FLOPPY0_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003978 drive = 1;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003979 if (!drive_params[drive].cmos)
3980 drive_params[drive].cmos = FLOPPY1_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003981
Jesper Juhl06f748c2007-10-16 23:30:57 -07003982 /* FIXME: additional physical CMOS drive detection should go here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003983
3984 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003985 unsigned int type = drive_params[drive].cmos;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986 struct floppy_drive_params *params;
3987 const char *name = NULL;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003988 char temparea[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003989
Tobias Klauser945f3902006-01-08 01:05:11 -08003990 if (type < ARRAY_SIZE(default_drive_params)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003991 params = &default_drive_params[type].params;
3992 if (type) {
3993 name = default_drive_params[type].name;
3994 allowed_drive_mask |= 1 << drive;
3995 } else
3996 allowed_drive_mask &= ~(1 << drive);
3997 } else {
3998 params = &default_drive_params[0].params;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003999 snprintf(temparea, sizeof(temparea),
4000 "unknown type %d (usb?)", type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004001 name = temparea;
4002 }
4003 if (name) {
Joe Perchesb46df352010-03-10 15:20:46 -08004004 const char *prepend;
4005 if (!has_drive) {
4006 prepend = "";
4007 has_drive = true;
4008 pr_info("Floppy drive(s):");
4009 } else {
4010 prepend = ",";
Linus Torvalds1da177e2005-04-16 15:20:36 -07004011 }
Joe Perchesb46df352010-03-10 15:20:46 -08004012
4013 pr_cont("%s fd%d is %s", prepend, drive, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004014 }
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01004015 drive_params[drive] = *params;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004016 }
Joe Perchesb46df352010-03-10 15:20:46 -08004017
4018 if (has_drive)
4019 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004020}
4021
Al Virodb2a1442013-05-05 21:52:57 -04004022static void floppy_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004023{
Al Viroa4af9b42008-03-02 09:27:55 -05004024 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004025
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004026 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004027 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004028 if (!drive_state[drive].fd_ref--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004029 DPRINT("floppy_release with fd_ref == 0");
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004030 drive_state[drive].fd_ref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004032 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004033 opened_bdev[drive] = NULL;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004034 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004035 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004036}
4037
4038/*
4039 * floppy_open check for aliasing (/dev/fd0 can be the same as
4040 * /dev/PS0 etc), and disallows simultaneous access to the same
4041 * drive with different device numbers.
4042 */
Al Viroa4af9b42008-03-02 09:27:55 -05004043static int floppy_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004044{
Al Viroa4af9b42008-03-02 09:27:55 -05004045 int drive = (long)bdev->bd_disk->private_data;
4046 int old_dev, new_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004047 int try;
4048 int res = -EBUSY;
4049 char *tmp;
4050
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004051 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004052 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004053 old_dev = drive_state[drive].fd_device;
Al Viroa4af9b42008-03-02 09:27:55 -05004054 if (opened_bdev[drive] && opened_bdev[drive] != bdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004055 goto out2;
4056
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004057 if (!drive_state[drive].fd_ref && (drive_params[drive].flags & FD_BROKEN_DCL)) {
4058 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
4059 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004060 }
4061
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004062 drive_state[drive].fd_ref++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004063
Al Viroa4af9b42008-03-02 09:27:55 -05004064 opened_bdev[drive] = bdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004065
4066 res = -ENXIO;
4067
4068 if (!floppy_track_buffer) {
4069 /* if opening an ED drive, reserve a big buffer,
4070 * else reserve a small one */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01004071 if ((drive_params[drive].cmos == 6) || (drive_params[drive].cmos == 5))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004072 try = 64; /* Only 48 actually useful */
4073 else
4074 try = 32; /* Only 24 actually useful */
4075
4076 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4077 if (!tmp && !floppy_track_buffer) {
4078 try >>= 1; /* buffer only one side */
4079 INFBOUND(try, 16);
4080 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4081 }
Joe Perchesa81ee542010-03-10 15:20:46 -08004082 if (!tmp && !floppy_track_buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004083 fallback_on_nodma_alloc(&tmp, 2048 * try);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004084 if (!tmp && !floppy_track_buffer) {
4085 DPRINT("Unable to allocate DMA memory\n");
4086 goto out;
4087 }
4088 if (floppy_track_buffer) {
4089 if (tmp)
4090 fd_dma_mem_free((unsigned long)tmp, try * 1024);
4091 } else {
4092 buffer_min = buffer_max = -1;
4093 floppy_track_buffer = tmp;
4094 max_buffer_sectors = try;
4095 }
4096 }
4097
Al Viroa4af9b42008-03-02 09:27:55 -05004098 new_dev = MINOR(bdev->bd_dev);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004099 drive_state[drive].fd_device = new_dev;
Al Viroa4af9b42008-03-02 09:27:55 -05004100 set_capacity(disks[drive], floppy_sizes[new_dev]);
4101 if (old_dev != -1 && old_dev != new_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004102 if (buffer_drive == drive)
4103 buffer_track = -1;
4104 }
4105
Willy Tarreauf9d322b2020-02-24 22:23:44 +01004106 if (fdc_state[FDC(drive)].rawcmd == 1)
4107 fdc_state[FDC(drive)].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004108
Jens Axboef2791e72016-08-25 08:56:51 -06004109 if (!(mode & FMODE_NDELAY)) {
4110 if (mode & (FMODE_READ|FMODE_WRITE)) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004111 drive_state[drive].last_checked = 0;
4112 clear_bit(FD_OPEN_SHOULD_FAIL_BIT,
4113 &drive_state[drive].flags);
Jens Axboef2791e72016-08-25 08:56:51 -06004114 check_disk_change(bdev);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004115 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags))
Jens Axboef2791e72016-08-25 08:56:51 -06004116 goto out;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004117 if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags))
Jens Axboef2791e72016-08-25 08:56:51 -06004118 goto out;
4119 }
4120 res = -EROFS;
4121 if ((mode & FMODE_WRITE) &&
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004122 !test_bit(FD_DISK_WRITABLE_BIT, &drive_state[drive].flags))
Jens Axboef2791e72016-08-25 08:56:51 -06004123 goto out;
4124 }
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004125 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004126 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004127 return 0;
4128out:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004129 drive_state[drive].fd_ref--;
Jiri Kosinabfa10b82012-05-18 13:50:28 +02004130
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004131 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004132 opened_bdev[drive] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004133out2:
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004134 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004135 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004136 return res;
4137}
4138
4139/*
4140 * Check if the disk has been changed or if a change has been faked.
4141 */
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004142static unsigned int floppy_check_events(struct gendisk *disk,
4143 unsigned int clearing)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004144{
4145 int drive = (long)disk->private_data;
4146
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004147 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4148 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004149 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004150
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004151 if (time_after(jiffies, drive_state[drive].last_checked + drive_params[drive].checkfreq)) {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004152 if (lock_fdc(drive))
Yufen Yu96d7cb92019-01-29 16:34:04 +08004153 return 0;
Joe Perches74f63f42010-03-10 15:20:58 -08004154 poll_drive(false, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004155 process_fd_request();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004156 }
4157
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004158 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4159 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004160 test_bit(drive, &fake_change) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004161 drive_no_geom(drive))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004162 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004163 return 0;
4164}
4165
4166/*
4167 * This implements "read block 0" for floppy_revalidate().
4168 * Needed for format autodetection, checking whether there is
4169 * a disk in the drive, and whether that disk is writable.
4170 */
4171
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004172struct rb0_cbdata {
4173 int drive;
4174 struct completion complete;
4175};
4176
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004177static void floppy_rb0_cb(struct bio *bio)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004178{
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004179 struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private;
4180 int drive = cbdata->drive;
4181
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004182 if (bio->bi_status) {
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004183 pr_info("floppy: error %d while reading block 0\n",
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004184 bio->bi_status);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004185 set_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004186 }
4187 complete(&cbdata->complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004188}
4189
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004190static int __floppy_read_block_0(struct block_device *bdev, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004191{
4192 struct bio bio;
4193 struct bio_vec bio_vec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004194 struct page *page;
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004195 struct rb0_cbdata cbdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004196 size_t size;
4197
4198 page = alloc_page(GFP_NOIO);
4199 if (!page) {
4200 process_fd_request();
4201 return -ENOMEM;
4202 }
4203
4204 size = bdev->bd_block_size;
4205 if (!size)
4206 size = 1024;
4207
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004208 cbdata.drive = drive;
4209
Ming Lei3a83f462016-11-22 08:57:21 -07004210 bio_init(&bio, &bio_vec, 1);
Christoph Hellwig74d46992017-08-23 19:10:32 +02004211 bio_set_dev(&bio, bdev);
Ming Lei2c73a602016-11-11 20:05:31 +08004212 bio_add_page(&bio, page, size, 0);
4213
Kent Overstreet4f024f32013-10-11 15:44:27 -07004214 bio.bi_iter.bi_sector = 0;
Jiri Kosina6314a102014-05-28 11:55:23 +02004215 bio.bi_flags |= (1 << BIO_QUIET);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004216 bio.bi_private = &cbdata;
4217 bio.bi_end_io = floppy_rb0_cb;
Mike Christie95fe6c12016-06-05 14:31:48 -05004218 bio_set_op_attrs(&bio, REQ_OP_READ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004219
Jens Axboede7b75d2018-11-09 15:58:40 -07004220 init_completion(&cbdata.complete);
4221
Mike Christie4e49ea42016-06-05 14:31:41 -05004222 submit_bio(&bio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004223 process_fd_request();
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004224
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004225 wait_for_completion(&cbdata.complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004226
4227 __free_page(page);
4228
4229 return 0;
4230}
4231
4232/* revalidate the floppy disk, i.e. trigger format autodetection by reading
4233 * the bootblock (block 0). "Autodetection" is also needed to check whether
4234 * there is a disk in the drive at all... Thus we also do it for fixed
4235 * geometry formats */
4236static int floppy_revalidate(struct gendisk *disk)
4237{
4238 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004239 int cf;
4240 int res = 0;
4241
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004242 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4243 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004244 test_bit(drive, &fake_change) ||
4245 drive_no_geom(drive)) {
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004246 if (WARN(atomic_read(&usage_count) == 0,
4247 "VFS: revalidate called on non-open device.\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004248 return -EFAULT;
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004249
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004250 res = lock_fdc(drive);
4251 if (res)
4252 return res;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004253 cf = (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4254 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags));
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004255 if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004256 process_fd_request(); /*already done by another thread */
4257 return 0;
4258 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004259 drive_state[drive].maxblock = 0;
4260 drive_state[drive].maxtrack = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004261 if (buffer_drive == drive)
4262 buffer_track = -1;
4263 clear_bit(drive, &fake_change);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004264 clear_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004265 if (cf)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004266 drive_state[drive].generation++;
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004267 if (drive_no_geom(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268 /* auto-sensing */
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004269 res = __floppy_read_block_0(opened_bdev[drive], drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004270 } else {
4271 if (cf)
Joe Perches74f63f42010-03-10 15:20:58 -08004272 poll_drive(false, FD_RAW_NEED_DISK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004273 process_fd_request();
4274 }
4275 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004276 set_capacity(disk, floppy_sizes[drive_state[drive].fd_device]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004277 return res;
4278}
4279
Alexey Dobriyan83d5cde2009-09-21 17:01:13 -07004280static const struct block_device_operations floppy_fops = {
Jesper Juhl06f748c2007-10-16 23:30:57 -07004281 .owner = THIS_MODULE,
Al Viroa4af9b42008-03-02 09:27:55 -05004282 .open = floppy_open,
4283 .release = floppy_release,
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02004284 .ioctl = fd_ioctl,
Jesper Juhl06f748c2007-10-16 23:30:57 -07004285 .getgeo = fd_getgeo,
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004286 .check_events = floppy_check_events,
Jesper Juhl06f748c2007-10-16 23:30:57 -07004287 .revalidate_disk = floppy_revalidate,
Al Viro229b53c2017-06-27 15:47:56 -04004288#ifdef CONFIG_COMPAT
4289 .compat_ioctl = fd_compat_ioctl,
4290#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004291};
Linus Torvalds1da177e2005-04-16 15:20:36 -07004292
Linus Torvalds1da177e2005-04-16 15:20:36 -07004293/*
4294 * Floppy Driver initialization
4295 * =============================
4296 */
4297
4298/* Determine the floppy disk controller type */
4299/* This routine was written by David C. Niemi */
4300static char __init get_fdc_version(void)
4301{
4302 int r;
4303
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02004304 output_byte(current_fdc, FD_DUMPREGS); /* 82072 and better know DUMPREGS */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004305 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004306 return FDC_NONE;
Willy Tarreau96dad772020-03-31 11:40:45 +02004307 r = result(current_fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004308 if (r <= 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004309 return FDC_NONE; /* No FDC present ??? */
4310 if ((r == 1) && (reply_buffer[0] == 0x80)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004311 pr_info("FDC %d is an 8272A\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004312 return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
4313 }
4314 if (r != 10) {
Joe Perchesb46df352010-03-10 15:20:46 -08004315 pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
Willy Tarreaue83995c2020-03-01 20:55:55 +01004316 current_fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004317 return FDC_UNKNOWN;
4318 }
4319
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02004320 if (!fdc_configure(current_fdc)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004321 pr_info("FDC %d is an 82072\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004322 return FDC_82072; /* 82072 doesn't know CONFIGURE */
4323 }
4324
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02004325 output_byte(current_fdc, FD_PERPENDICULAR);
Willy Tarreau3ab12a12020-03-31 11:40:46 +02004326 if (need_more_output(current_fdc) == MORE_OUTPUT) {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02004327 output_byte(current_fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004328 } else {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004329 pr_info("FDC %d is an 82072A\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004330 return FDC_82072A; /* 82072A as found on Sparcs. */
4331 }
4332
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02004333 output_byte(current_fdc, FD_UNLOCK);
Willy Tarreau96dad772020-03-31 11:40:45 +02004334 r = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004335 if ((r == 1) && (reply_buffer[0] == 0x80)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004336 pr_info("FDC %d is a pre-1991 82077\n", current_fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004337 return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
Linus Torvalds1da177e2005-04-16 15:20:36 -07004338 * LOCK/UNLOCK */
4339 }
4340 if ((r != 1) || (reply_buffer[0] != 0x00)) {
Joe Perchesb46df352010-03-10 15:20:46 -08004341 pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
Willy Tarreaue83995c2020-03-01 20:55:55 +01004342 current_fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004343 return FDC_UNKNOWN;
4344 }
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02004345 output_byte(current_fdc, FD_PARTID);
Willy Tarreau96dad772020-03-31 11:40:45 +02004346 r = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 if (r != 1) {
Joe Perchesb46df352010-03-10 15:20:46 -08004348 pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n",
Willy Tarreaue83995c2020-03-01 20:55:55 +01004349 current_fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004350 return FDC_UNKNOWN;
4351 }
4352 if (reply_buffer[0] == 0x80) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004353 pr_info("FDC %d is a post-1991 82077\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004354 return FDC_82077; /* Revised 82077AA passes all the tests */
4355 }
4356 switch (reply_buffer[0] >> 5) {
4357 case 0x0:
4358 /* Either a 82078-1 or a 82078SL running at 5Volt */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004359 pr_info("FDC %d is an 82078.\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004360 return FDC_82078;
4361 case 0x1:
Willy Tarreaue83995c2020-03-01 20:55:55 +01004362 pr_info("FDC %d is a 44pin 82078\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004363 return FDC_82078;
4364 case 0x2:
Willy Tarreaue83995c2020-03-01 20:55:55 +01004365 pr_info("FDC %d is a S82078B\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004366 return FDC_S82078B;
4367 case 0x3:
Willy Tarreaue83995c2020-03-01 20:55:55 +01004368 pr_info("FDC %d is a National Semiconductor PC87306\n", current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004369 return FDC_87306;
4370 default:
Joe Perchesb46df352010-03-10 15:20:46 -08004371 pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n",
Willy Tarreaue83995c2020-03-01 20:55:55 +01004372 current_fdc, reply_buffer[0] >> 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004373 return FDC_82078_UNKN;
4374 }
4375} /* get_fdc_version */
4376
4377/* lilo configuration */
4378
4379static void __init floppy_set_flags(int *ints, int param, int param2)
4380{
4381 int i;
4382
4383 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4384 if (param)
4385 default_drive_params[i].params.flags |= param2;
4386 else
4387 default_drive_params[i].params.flags &= ~param2;
4388 }
4389 DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param);
4390}
4391
4392static void __init daring(int *ints, int param, int param2)
4393{
4394 int i;
4395
4396 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4397 if (param) {
4398 default_drive_params[i].params.select_delay = 0;
4399 default_drive_params[i].params.flags |=
4400 FD_SILENT_DCL_CLEAR;
4401 } else {
4402 default_drive_params[i].params.select_delay =
4403 2 * HZ / 100;
4404 default_drive_params[i].params.flags &=
4405 ~FD_SILENT_DCL_CLEAR;
4406 }
4407 }
4408 DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
4409}
4410
4411static void __init set_cmos(int *ints, int dummy, int dummy2)
4412{
4413 int current_drive = 0;
4414
4415 if (ints[0] != 2) {
4416 DPRINT("wrong number of parameters for CMOS\n");
4417 return;
4418 }
4419 current_drive = ints[1];
4420 if (current_drive < 0 || current_drive >= 8) {
4421 DPRINT("bad drive for set_cmos\n");
4422 return;
4423 }
4424#if N_FDC > 1
4425 if (current_drive >= 4 && !FDC2)
4426 FDC2 = 0x370;
4427#endif
Willy Tarreau031faab2020-02-24 22:23:48 +01004428 drive_params[current_drive].cmos = ints[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004429 DPRINT("setting CMOS code to %d\n", ints[2]);
4430}
4431
4432static struct param_table {
4433 const char *name;
4434 void (*fn) (int *ints, int param, int param2);
4435 int *var;
4436 int def_param;
4437 int param2;
4438} config_params[] __initdata = {
4439 {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4440 {"all_drives", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4441 {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0},
4442 {"irq", NULL, &FLOPPY_IRQ, 6, 0},
4443 {"dma", NULL, &FLOPPY_DMA, 2, 0},
4444 {"daring", daring, NULL, 1, 0},
4445#if N_FDC > 1
4446 {"two_fdc", NULL, &FDC2, 0x370, 0},
4447 {"one_fdc", NULL, &FDC2, 0, 0},
4448#endif
4449 {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL},
4450 {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL},
4451 {"messages", floppy_set_flags, NULL, 1, FTD_MSG},
4452 {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR},
4453 {"debug", floppy_set_flags, NULL, 1, FD_DEBUG},
4454 {"nodma", NULL, &can_use_virtual_dma, 1, 0},
4455 {"omnibook", NULL, &can_use_virtual_dma, 1, 0},
4456 {"yesdma", NULL, &can_use_virtual_dma, 0, 0},
4457 {"fifo_depth", NULL, &fifo_depth, 0xa, 0},
4458 {"nofifo", NULL, &no_fifo, 0x20, 0},
4459 {"usefifo", NULL, &no_fifo, 0, 0},
4460 {"cmos", set_cmos, NULL, 0, 0},
4461 {"slow", NULL, &slow_floppy, 1, 0},
4462 {"unexpected_interrupts", NULL, &print_unex, 1, 0},
4463 {"no_unexpected_interrupts", NULL, &print_unex, 0, 0},
4464 {"L40SX", NULL, &print_unex, 0, 0}
4465
4466 EXTRA_FLOPPY_PARAMS
4467};
4468
4469static int __init floppy_setup(char *str)
4470{
4471 int i;
4472 int param;
4473 int ints[11];
4474
4475 str = get_options(str, ARRAY_SIZE(ints), ints);
4476 if (str) {
4477 for (i = 0; i < ARRAY_SIZE(config_params); i++) {
4478 if (strcmp(str, config_params[i].name) == 0) {
4479 if (ints[0])
4480 param = ints[1];
4481 else
4482 param = config_params[i].def_param;
4483 if (config_params[i].fn)
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004484 config_params[i].fn(ints, param,
4485 config_params[i].
4486 param2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004487 if (config_params[i].var) {
4488 DPRINT("%s=%d\n", str, param);
4489 *config_params[i].var = param;
4490 }
4491 return 1;
4492 }
4493 }
4494 }
4495 if (str) {
4496 DPRINT("unknown floppy option [%s]\n", str);
4497
4498 DPRINT("allowed options are:");
4499 for (i = 0; i < ARRAY_SIZE(config_params); i++)
Joe Perchesb46df352010-03-10 15:20:46 -08004500 pr_cont(" %s", config_params[i].name);
4501 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004502 } else
4503 DPRINT("botched floppy option\n");
Mauro Carvalho Chehabe7751612019-06-18 11:47:10 -03004504 DPRINT("Read Documentation/admin-guide/blockdev/floppy.rst\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004505 return 0;
4506}
4507
4508static int have_no_fdc = -ENODEV;
4509
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004510static ssize_t floppy_cmos_show(struct device *dev,
4511 struct device_attribute *attr, char *buf)
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004512{
Eric Miao71b3e0c2009-01-31 22:47:44 +08004513 struct platform_device *p = to_platform_device(dev);
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004514 int drive;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004515
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004516 drive = p->id;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01004517 return sprintf(buf, "%X\n", drive_params[drive].cmos);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004518}
Joe Perches48c8cee2010-03-10 15:20:45 -08004519
Joe Perches5657a812018-05-24 13:38:59 -06004520static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004521
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004522static struct attribute *floppy_dev_attrs[] = {
4523 &dev_attr_cmos.attr,
4524 NULL
4525};
4526
4527ATTRIBUTE_GROUPS(floppy_dev);
4528
Linus Torvalds1da177e2005-04-16 15:20:36 -07004529static void floppy_device_release(struct device *dev)
4530{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004531}
4532
Frans Popc90cd332009-07-25 22:24:54 +02004533static int floppy_resume(struct device *dev)
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004534{
4535 int fdc;
4536
4537 for (fdc = 0; fdc < N_FDC; fdc++)
Willy Tarreaude6048b2020-02-24 22:23:43 +01004538 if (fdc_state[fdc].address != -1)
Joe Perches74f63f42010-03-10 15:20:58 -08004539 user_reset_fdc(-1, FD_RESET_ALWAYS, false);
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004540
4541 return 0;
4542}
4543
Alexey Dobriyan47145212009-12-14 18:00:08 -08004544static const struct dev_pm_ops floppy_pm_ops = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004545 .resume = floppy_resume,
Frans Popc90cd332009-07-25 22:24:54 +02004546 .restore = floppy_resume,
4547};
4548
4549static struct platform_driver floppy_driver = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004550 .driver = {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004551 .name = "floppy",
4552 .pm = &floppy_pm_ops,
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004553 },
4554};
4555
Omar Sandovala9f38e12018-10-15 09:21:34 -06004556static const struct blk_mq_ops floppy_mq_ops = {
4557 .queue_rq = floppy_queue_rq,
4558};
4559
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004560static struct platform_device floppy_device[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004561
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004562static bool floppy_available(int drive)
4563{
4564 if (!(allowed_drive_mask & (1 << drive)))
4565 return false;
4566 if (fdc_state[FDC(drive)].version == FDC_NONE)
4567 return false;
4568 return true;
4569}
4570
Linus Torvalds1da177e2005-04-16 15:20:36 -07004571static struct kobject *floppy_find(dev_t dev, int *part, void *data)
4572{
4573 int drive = (*part & 3) | ((*part & 0x80) >> 5);
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004574 if (drive >= N_DRIVE || !floppy_available(drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004575 return NULL;
Tobias Klauser945f3902006-01-08 01:05:11 -08004576 if (((*part >> 2) & 0x1f) >= ARRAY_SIZE(floppy_type))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004577 return NULL;
4578 *part = 0;
Jan Kara3079c222018-02-26 13:01:38 +01004579 return get_disk_and_module(disks[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004580}
4581
Andi Kleen0cc15d032012-07-02 17:27:04 -07004582static int __init do_floppy_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004583{
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004584 int i, unit, drive, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004585
Stephen Hemminger285203c2010-06-15 13:21:11 +02004586 set_debugt();
4587 interruptjiffies = resultjiffies = jiffies;
4588
Kumar Gala68e1ee62008-09-22 14:41:31 -07004589#if defined(CONFIG_PPC)
Olaf Heringef16b512006-08-31 21:27:41 -07004590 if (check_legacy_ioport(FDC1))
4591 return -ENODEV;
4592#endif
4593
Linus Torvalds1da177e2005-04-16 15:20:36 -07004594 raw_cmd = NULL;
4595
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004596 floppy_wq = alloc_ordered_workqueue("floppy", 0);
4597 if (!floppy_wq)
4598 return -ENOMEM;
4599
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004600 for (drive = 0; drive < N_DRIVE; drive++) {
4601 disks[drive] = alloc_disk(1);
4602 if (!disks[drive]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004603 err = -ENOMEM;
4604 goto out_put_disk;
4605 }
4606
Omar Sandovala9f38e12018-10-15 09:21:34 -06004607 disks[drive]->queue = blk_mq_init_sq_queue(&tag_sets[drive],
4608 &floppy_mq_ops, 2,
4609 BLK_MQ_F_SHOULD_MERGE);
4610 if (IS_ERR(disks[drive]->queue)) {
4611 err = PTR_ERR(disks[drive]->queue);
4612 disks[drive]->queue = NULL;
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004613 goto out_put_disk;
Jens Axboe48821182010-09-22 09:32:36 +02004614 }
4615
Christoph Hellwig8fc45042017-06-19 09:26:26 +02004616 blk_queue_bounce_limit(disks[drive]->queue, BLK_BOUNCE_HIGH);
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004617 blk_queue_max_hw_sectors(disks[drive]->queue, 64);
4618 disks[drive]->major = FLOPPY_MAJOR;
4619 disks[drive]->first_minor = TOMINOR(drive);
4620 disks[drive]->fops = &floppy_fops;
Martin Wilck773008f2019-03-27 14:51:04 +01004621 disks[drive]->events = DISK_EVENT_MEDIA_CHANGE;
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004622 sprintf(disks[drive]->disk_name, "fd%d", drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004623
Kees Cookb1bf4212017-10-04 17:49:29 -07004624 timer_setup(&motor_off_timer[drive], motor_off_callback, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004625 }
4626
Linus Torvalds1da177e2005-04-16 15:20:36 -07004627 err = register_blkdev(FLOPPY_MAJOR, "fd");
4628 if (err)
Greg Kroah-Hartman8ab5e4c2005-06-20 21:15:16 -07004629 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004630
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004631 err = platform_driver_register(&floppy_driver);
4632 if (err)
4633 goto out_unreg_blkdev;
4634
Linus Torvalds1da177e2005-04-16 15:20:36 -07004635 blk_register_region(MKDEV(FLOPPY_MAJOR, 0), 256, THIS_MODULE,
4636 floppy_find, NULL, NULL);
4637
4638 for (i = 0; i < 256; i++)
4639 if (ITYPE(i))
4640 floppy_sizes[i] = floppy_type[ITYPE(i)].size;
4641 else
4642 floppy_sizes[i] = MAX_DISK_SIZE << 1;
4643
Joe Perches73507e62010-03-10 15:21:03 -08004644 reschedule_timeout(MAXTIMEOUT, "floppy init");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004645 config_types();
4646
4647 for (i = 0; i < N_FDC; i++) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004648 current_fdc = i;
4649 memset(&fdc_state[current_fdc], 0, sizeof(*fdc_state));
4650 fdc_state[current_fdc].dtr = -1;
4651 fdc_state[current_fdc].dor = 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004652#if defined(__sparc__) || defined(__mc68000__)
Joe Perches96534f12010-03-10 15:20:51 -08004653 /*sparcs/sun3x don't have a DOR reset which we can fall back on to */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004654#ifdef __mc68000__
4655 if (MACH_IS_SUN3X)
4656#endif
Willy Tarreaue83995c2020-03-01 20:55:55 +01004657 fdc_state[current_fdc].version = FDC_82072A;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004658#endif
4659 }
4660
4661 use_virtual_dma = can_use_virtual_dma & 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662 fdc_state[0].address = FDC1;
4663 if (fdc_state[0].address == -1) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004664 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665 err = -ENODEV;
4666 goto out_unreg_region;
4667 }
4668#if N_FDC > 1
4669 fdc_state[1].address = FDC2;
4670#endif
4671
Willy Tarreaue83995c2020-03-01 20:55:55 +01004672 current_fdc = 0; /* reset fdc in case of unexpected interrupt */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004673 err = floppy_grab_irq_and_dma();
4674 if (err) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004675 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004676 err = -EBUSY;
4677 goto out_unreg_region;
4678 }
4679
4680 /* initialise drive state */
4681 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004682 memset(&drive_state[drive], 0, sizeof(drive_state[drive]));
Willy Tarreau121e2972020-02-24 22:23:47 +01004683 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004684 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
4685 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
4686 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
4687 drive_state[drive].fd_device = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004688 floppy_track_buffer = NULL;
4689 max_buffer_sectors = 0;
4690 }
4691 /*
4692 * Small 10 msec delay to let through any interrupt that
4693 * initialization might have triggered, to not
4694 * confuse detection:
4695 */
4696 msleep(10);
4697
4698 for (i = 0; i < N_FDC; i++) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01004699 current_fdc = i;
4700 fdc_state[current_fdc].driver_version = FD_DRIVER_VERSION;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004701 for (unit = 0; unit < 4; unit++)
Willy Tarreaue83995c2020-03-01 20:55:55 +01004702 fdc_state[current_fdc].track[unit] = 0;
4703 if (fdc_state[current_fdc].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004704 continue;
Willy Tarreaue83995c2020-03-01 20:55:55 +01004705 fdc_state[current_fdc].rawcmd = 2;
Joe Perches74f63f42010-03-10 15:20:58 -08004706 if (user_reset_fdc(-1, FD_RESET_ALWAYS, false)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004707 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004708 floppy_release_regions(current_fdc);
4709 fdc_state[current_fdc].address = -1;
4710 fdc_state[current_fdc].version = FDC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004711 continue;
4712 }
4713 /* Try to determine the floppy controller type */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004714 fdc_state[current_fdc].version = get_fdc_version();
4715 if (fdc_state[current_fdc].version == FDC_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004716 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004717 floppy_release_regions(current_fdc);
4718 fdc_state[current_fdc].address = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719 continue;
4720 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004721 if (can_use_virtual_dma == 2 &&
4722 fdc_state[current_fdc].version < FDC_82072A)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004723 can_use_virtual_dma = 0;
4724
4725 have_no_fdc = 0;
4726 /* Not all FDCs seem to be able to handle the version command
4727 * properly, so force a reset for the standard FDC clones,
4728 * to avoid interrupt garbage.
4729 */
Joe Perches74f63f42010-03-10 15:20:58 -08004730 user_reset_fdc(-1, FD_RESET_ALWAYS, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004731 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004732 current_fdc = 0;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004733 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 current_drive = 0;
Joe Perches29f1c782010-03-10 15:21:00 -08004735 initialized = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004736 if (have_no_fdc) {
4737 DPRINT("no floppy controllers found\n");
4738 err = have_no_fdc;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004739 goto out_release_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004740 }
4741
Linus Torvalds1da177e2005-04-16 15:20:36 -07004742 for (drive = 0; drive < N_DRIVE; drive++) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004743 if (!floppy_available(drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004744 continue;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004745
4746 floppy_device[drive].name = floppy_device_name;
4747 floppy_device[drive].id = drive;
4748 floppy_device[drive].dev.release = floppy_device_release;
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004749 floppy_device[drive].dev.groups = floppy_dev_groups;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004750
4751 err = platform_device_register(&floppy_device[drive]);
4752 if (err)
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004753 goto out_remove_drives;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004754
Linus Torvalds1da177e2005-04-16 15:20:36 -07004755 /* to be cleaned up... */
4756 disks[drive]->private_data = (void *)(long)drive;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004757 disks[drive]->flags |= GENHD_FL_REMOVABLE;
Hannes Reineckefef912b2018-09-28 08:17:19 +02004758 device_add_disk(&floppy_device[drive].dev, disks[drive], NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004759 }
4760
4761 return 0;
4762
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004763out_remove_drives:
4764 while (drive--) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004765 if (floppy_available(drive)) {
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004766 del_gendisk(disks[drive]);
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004767 platform_device_unregister(&floppy_device[drive]);
4768 }
4769 }
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004770out_release_dma:
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004771 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004772 floppy_release_irq_and_dma();
4773out_unreg_region:
4774 blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004775 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004776out_unreg_blkdev:
4777 unregister_blkdev(FLOPPY_MAJOR, "fd");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004778out_put_disk:
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01004779 destroy_workqueue(floppy_wq);
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004780 for (drive = 0; drive < N_DRIVE; drive++) {
4781 if (!disks[drive])
4782 break;
4783 if (disks[drive]->queue) {
4784 del_timer_sync(&motor_off_timer[drive]);
4785 blk_cleanup_queue(disks[drive]->queue);
4786 disks[drive]->queue = NULL;
Omar Sandovala9f38e12018-10-15 09:21:34 -06004787 blk_mq_free_tag_set(&tag_sets[drive]);
Vivek Goyal3f9a5aa2012-02-08 20:03:38 +01004788 }
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004789 put_disk(disks[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004790 }
4791 return err;
4792}
4793
Andi Kleen0cc15d032012-07-02 17:27:04 -07004794#ifndef MODULE
4795static __init void floppy_async_init(void *data, async_cookie_t cookie)
4796{
4797 do_floppy_init();
4798}
4799#endif
4800
4801static int __init floppy_init(void)
4802{
4803#ifdef MODULE
4804 return do_floppy_init();
4805#else
4806 /* Don't hold up the bootup by the floppy initialization */
4807 async_schedule(floppy_async_init, NULL);
4808 return 0;
4809#endif
4810}
4811
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004812static const struct io_region {
4813 int offset;
4814 int size;
4815} io_regions[] = {
4816 { 2, 1 },
4817 /* address + 3 is sometimes reserved by pnp bios for motherboard */
4818 { 4, 2 },
4819 /* address + 6 is reserved, and may be taken by IDE.
4820 * Unfortunately, Adaptec doesn't know this :-(, */
4821 { 7, 1 },
4822};
4823
4824static void floppy_release_allocated_regions(int fdc, const struct io_region *p)
4825{
4826 while (p != io_regions) {
4827 p--;
Willy Tarreaude6048b2020-02-24 22:23:43 +01004828 release_region(fdc_state[fdc].address + p->offset, p->size);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004829 }
4830}
4831
4832#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)]))
4833
4834static int floppy_request_regions(int fdc)
4835{
4836 const struct io_region *p;
4837
4838 for (p = io_regions; p < ARRAY_END(io_regions); p++) {
Willy Tarreaude6048b2020-02-24 22:23:43 +01004839 if (!request_region(fdc_state[fdc].address + p->offset,
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004840 p->size, "floppy")) {
4841 DPRINT("Floppy io-port 0x%04lx in use\n",
Willy Tarreaude6048b2020-02-24 22:23:43 +01004842 fdc_state[fdc].address + p->offset);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004843 floppy_release_allocated_regions(fdc, p);
4844 return -EBUSY;
4845 }
4846 }
4847 return 0;
4848}
4849
4850static void floppy_release_regions(int fdc)
4851{
4852 floppy_release_allocated_regions(fdc, ARRAY_END(io_regions));
4853}
4854
Linus Torvalds1da177e2005-04-16 15:20:36 -07004855static int floppy_grab_irq_and_dma(void)
4856{
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004857 if (atomic_inc_return(&usage_count) > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004858 return 0;
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004859
4860 /*
4861 * We might have scheduled a free_irq(), wait it to
4862 * drain first:
4863 */
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004864 flush_workqueue(floppy_wq);
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004865
Linus Torvalds1da177e2005-04-16 15:20:36 -07004866 if (fd_request_irq()) {
4867 DPRINT("Unable to grab IRQ%d for the floppy driver\n",
4868 FLOPPY_IRQ);
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004869 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004870 return -1;
4871 }
4872 if (fd_request_dma()) {
4873 DPRINT("Unable to grab DMA%d for the floppy driver\n",
4874 FLOPPY_DMA);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004875 if (can_use_virtual_dma & 2)
4876 use_virtual_dma = can_use_virtual_dma = 1;
4877 if (!(can_use_virtual_dma & 1)) {
4878 fd_free_irq();
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004879 atomic_dec(&usage_count);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004880 return -1;
4881 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004882 }
4883
Willy Tarreaue83995c2020-03-01 20:55:55 +01004884 for (current_fdc = 0; current_fdc < N_FDC; current_fdc++) {
4885 if (fdc_state[current_fdc].address != -1) {
4886 if (floppy_request_regions(current_fdc))
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004887 goto cleanup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004888 }
4889 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004890 for (current_fdc = 0; current_fdc < N_FDC; current_fdc++) {
4891 if (fdc_state[current_fdc].address != -1) {
Willy Tarreauf3e0dc12020-03-31 11:40:41 +02004892 reset_fdc_info(current_fdc, 1);
Willy Tarreaue83995c2020-03-01 20:55:55 +01004893 fdc_outb(fdc_state[current_fdc].dor, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004894 }
4895 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004896 current_fdc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004897 set_dor(0, ~0, 8); /* avoid immediate interrupt */
4898
Willy Tarreaue83995c2020-03-01 20:55:55 +01004899 for (current_fdc = 0; current_fdc < N_FDC; current_fdc++)
4900 if (fdc_state[current_fdc].address != -1)
4901 fdc_outb(fdc_state[current_fdc].dor, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004902 /*
Jesper Juhl06f748c2007-10-16 23:30:57 -07004903 * The driver will try and free resources and relies on us
4904 * to know if they were allocated or not.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004905 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004906 current_fdc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004907 irqdma_allocated = 1;
4908 return 0;
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004909cleanup:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004910 fd_free_irq();
4911 fd_free_dma();
Willy Tarreaue83995c2020-03-01 20:55:55 +01004912 while (--current_fdc >= 0)
4913 floppy_release_regions(current_fdc);
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004914 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004915 return -1;
4916}
4917
4918static void floppy_release_irq_and_dma(void)
4919{
4920 int old_fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004921#ifndef __sparc__
4922 int drive;
4923#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004924 long tmpsize;
4925 unsigned long tmpaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004926
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004927 if (!atomic_dec_and_test(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004928 return;
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004929
Linus Torvalds1da177e2005-04-16 15:20:36 -07004930 if (irqdma_allocated) {
4931 fd_disable_dma();
4932 fd_free_dma();
Ingo Molnar3e541a42006-07-03 00:24:23 -07004933 fd_free_irq();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004934 irqdma_allocated = 0;
4935 }
4936 set_dor(0, ~0, 8);
4937#if N_FDC > 1
4938 set_dor(1, ~8, 0);
4939#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004940
4941 if (floppy_track_buffer && max_buffer_sectors) {
4942 tmpsize = max_buffer_sectors * 1024;
4943 tmpaddr = (unsigned long)floppy_track_buffer;
4944 floppy_track_buffer = NULL;
4945 max_buffer_sectors = 0;
4946 buffer_min = buffer_max = -1;
4947 fd_dma_mem_free(tmpaddr, tmpsize);
4948 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004949#ifndef __sparc__
4950 for (drive = 0; drive < N_FDC * 4; drive++)
4951 if (timer_pending(motor_off_timer + drive))
Joe Perchesb46df352010-03-10 15:20:46 -08004952 pr_info("motor off timer %d still active\n", drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004953#endif
4954
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004955 if (delayed_work_pending(&fd_timeout))
Joe Perchesb46df352010-03-10 15:20:46 -08004956 pr_info("floppy timer still active:%s\n", timeout_message);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004957 if (delayed_work_pending(&fd_timer))
Joe Perchesb46df352010-03-10 15:20:46 -08004958 pr_info("auxiliary floppy timer still active\n");
David Howells365970a2006-11-22 14:54:49 +00004959 if (work_pending(&floppy_work))
Joe Perchesb46df352010-03-10 15:20:46 -08004960 pr_info("work still pending\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01004961 old_fdc = current_fdc;
4962 for (current_fdc = 0; current_fdc < N_FDC; current_fdc++)
4963 if (fdc_state[current_fdc].address != -1)
4964 floppy_release_regions(current_fdc);
4965 current_fdc = old_fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004966}
4967
4968#ifdef MODULE
4969
4970static char *floppy;
4971
Linus Torvalds1da177e2005-04-16 15:20:36 -07004972static void __init parse_floppy_cfg_string(char *cfg)
4973{
4974 char *ptr;
4975
4976 while (*cfg) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004977 ptr = cfg;
4978 while (*cfg && *cfg != ' ' && *cfg != '\t')
4979 cfg++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980 if (*cfg) {
4981 *cfg = '\0';
4982 cfg++;
4983 }
4984 if (*ptr)
4985 floppy_setup(ptr);
4986 }
4987}
4988
Jon Schindler7afea3b2008-04-29 00:59:21 -07004989static int __init floppy_module_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004990{
4991 if (floppy)
4992 parse_floppy_cfg_string(floppy);
4993 return floppy_init();
4994}
Jon Schindler7afea3b2008-04-29 00:59:21 -07004995module_init(floppy_module_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996
Jon Schindler7afea3b2008-04-29 00:59:21 -07004997static void __exit floppy_module_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004998{
4999 int drive;
5000
Linus Torvalds1da177e2005-04-16 15:20:36 -07005001 blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
5002 unregister_blkdev(FLOPPY_MAJOR, "fd");
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07005003 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005004
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01005005 destroy_workqueue(floppy_wq);
5006
Linus Torvalds1da177e2005-04-16 15:20:36 -07005007 for (drive = 0; drive < N_DRIVE; drive++) {
5008 del_timer_sync(&motor_off_timer[drive]);
5009
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03005010 if (floppy_available(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07005011 del_gendisk(disks[drive]);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02005012 platform_device_unregister(&floppy_device[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005013 }
Jens Axboe48821182010-09-22 09:32:36 +02005014 blk_cleanup_queue(disks[drive]->queue);
Omar Sandovala9f38e12018-10-15 09:21:34 -06005015 blk_mq_free_tag_set(&tag_sets[drive]);
Vivek Goyal4609dff2012-02-08 20:03:39 +01005016
5017 /*
5018 * These disks have not called add_disk(). Don't put down
5019 * queue reference in put_disk().
5020 */
5021 if (!(allowed_drive_mask & (1 << drive)) ||
5022 fdc_state[FDC(drive)].version == FDC_NONE)
5023 disks[drive]->queue = NULL;
5024
Vivek Goyald017bf62010-11-06 08:16:05 -04005025 put_disk(disks[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005026 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07005027
Jiri Kosina070ad7e2012-05-18 13:50:25 +02005028 cancel_delayed_work_sync(&fd_timeout);
5029 cancel_delayed_work_sync(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005030
Stephen Hemminger575cfc62010-06-15 13:21:11 +02005031 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07005032 floppy_release_irq_and_dma();
5033
5034 /* eject disk, if any */
5035 fd_eject(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005036}
Joe Perches48c8cee2010-03-10 15:20:45 -08005037
Jon Schindler7afea3b2008-04-29 00:59:21 -07005038module_exit(floppy_module_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07005039
5040module_param(floppy, charp, 0);
5041module_param(FLOPPY_IRQ, int, 0);
5042module_param(FLOPPY_DMA, int, 0);
5043MODULE_AUTHOR("Alain L. Knaff");
5044MODULE_SUPPORTED_DEVICE("fd");
5045MODULE_LICENSE("GPL");
5046
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005047/* This doesn't actually get used other than for module information */
5048static const struct pnp_device_id floppy_pnpids[] = {
Joe Perches48c8cee2010-03-10 15:20:45 -08005049 {"PNP0700", 0},
5050 {}
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005051};
Joe Perches48c8cee2010-03-10 15:20:45 -08005052
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005053MODULE_DEVICE_TABLE(pnp, floppy_pnpids);
5054
Linus Torvalds1da177e2005-04-16 15:20:36 -07005055#else
5056
5057__setup("floppy=", floppy_setup);
5058module_init(floppy_init)
5059#endif
5060
5061MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);