blob: e611411a934ce793282112be7293da500c9f98e3 [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#define REALLY_SLOW_IO
149
150#define DEBUGT 2
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Joe Perches891eda82010-03-10 15:21:05 -0800152#define DPRINT(format, args...) \
153 pr_info("floppy%d: " format, current_drive, ##args)
154
155#define DCL_DEBUG /* debug disk change line */
Joe Perches87f530d2010-03-10 15:20:54 -0800156#ifdef DCL_DEBUG
157#define debug_dcl(test, fmt, args...) \
158 do { if ((test) & FD_DEBUG) DPRINT(fmt, ##args); } while (0)
159#else
160#define debug_dcl(test, fmt, args...) \
161 do { if (0) DPRINT(fmt, ##args); } while (0)
162#endif
163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164/* do print messages for unexpected interrupts */
165static int print_unex = 1;
166#include <linux/module.h>
167#include <linux/sched.h>
168#include <linux/fs.h>
169#include <linux/kernel.h>
170#include <linux/timer.h>
171#include <linux/workqueue.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172#include <linux/fdreg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173#include <linux/fd.h>
174#include <linux/hdreg.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175#include <linux/errno.h>
176#include <linux/slab.h>
177#include <linux/mm.h>
178#include <linux/bio.h>
179#include <linux/string.h>
Marcelo Feitoza Parisi50297cb2006-03-28 01:56:44 -0800180#include <linux/jiffies.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181#include <linux/fcntl.h>
182#include <linux/delay.h>
183#include <linux/mc146818rtc.h> /* CMOS defines */
184#include <linux/ioport.h>
185#include <linux/interrupt.h>
186#include <linux/init.h>
Christoph Hellwigb81e0c22021-09-20 14:33:25 +0200187#include <linux/major.h>
Russell Kingd052d1b2005-10-29 19:07:23 +0100188#include <linux/platform_device.h>
Scott James Remnant83f9ef42009-04-02 16:56:47 -0700189#include <linux/mod_devicetable.h>
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800190#include <linux/mutex.h>
Joe Perchesd4937542010-03-10 15:20:44 -0800191#include <linux/io.h>
192#include <linux/uaccess.h>
Andi Kleen0cc15d032012-07-02 17:27:04 -0700193#include <linux/async.h>
Al Viro229b53c2017-06-27 15:47:56 -0400194#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
196/*
197 * PS/2 floppies have much slower step rates than regular floppies.
198 * It's been recommended that take about 1/4 of the default speed
199 * in some more extreme cases.
200 */
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200201static DEFINE_MUTEX(floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202static int slow_floppy;
203
204#include <asm/dma.h>
205#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206
207static int FLOPPY_IRQ = 6;
208static int FLOPPY_DMA = 2;
209static int can_use_virtual_dma = 2;
210/* =======
211 * can use virtual DMA:
212 * 0 = use of virtual DMA disallowed by config
213 * 1 = use of virtual DMA prescribed by config
214 * 2 = no virtual DMA preference configured. By default try hard DMA,
215 * but fall back on virtual DMA when not enough memory available
216 */
217
218static int use_virtual_dma;
219/* =======
220 * use virtual DMA
221 * 0 using hard DMA
222 * 1 using virtual DMA
223 * This variable is set to virtual when a DMA mem problem arises, and
224 * reset back in floppy_grab_irq_and_dma.
225 * It is not safe to reset it in other circumstances, because the floppy
226 * driver may have several buffers in use at once, and we do currently not
227 * record each buffers capabilities
228 */
229
230static DEFINE_SPINLOCK(floppy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
232static unsigned short virtual_dma_port = 0x3f0;
David Howells7d12e782006-10-05 14:55:46 +0100233irqreturn_t floppy_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234static int set_dor(int fdc, char mask, char data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236#define K_64 0x10000 /* 64KB */
237
238/* the following is the mask of allowed drives. By default units 2 and
239 * 3 of both floppy controllers are disabled, because switching on the
240 * motor of these drives causes system hangs on some PCI computers. drive
241 * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
242 * a drive is allowed.
243 *
244 * NOTE: This must come before we include the arch floppy header because
245 * some ports reference this variable from there. -DaveM
246 */
247
248static int allowed_drive_mask = 0x33;
249
250#include <asm/floppy.h>
251
252static int irqdma_allocated;
253
Omar Sandovala9f38e12018-10-15 09:21:34 -0600254#include <linux/blk-mq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255#include <linux/blkpg.h>
256#include <linux/cdrom.h> /* for the compatibility eject ioctl */
257#include <linux/completion.h>
258
Omar Sandovala9f38e12018-10-15 09:21:34 -0600259static LIST_HEAD(floppy_reqs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260static struct request *current_req;
Jens Axboe48821182010-09-22 09:32:36 +0200261static int set_next_request(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263#ifndef fd_get_dma_residue
264#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
265#endif
266
267/* Dma Memory related stuff */
268
269#ifndef fd_dma_mem_free
270#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
271#endif
272
273#ifndef fd_dma_mem_alloc
Joe Perches48c8cee2010-03-10 15:20:45 -0800274#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL, get_order(size))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275#endif
276
Christoph Hellwigacfef4f2017-07-13 16:12:05 +0200277#ifndef fd_cacheflush
278#define fd_cacheflush(addr, size) /* nothing... */
279#endif
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281static inline void fallback_on_nodma_alloc(char **addr, size_t l)
282{
283#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
284 if (*addr)
285 return; /* we have the memory */
286 if (can_use_virtual_dma != 2)
287 return; /* no fallback allowed */
Joe Perchesb46df352010-03-10 15:20:46 -0800288 pr_info("DMA memory shortage. Temporarily falling back on virtual DMA\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 *addr = (char *)nodma_mem_alloc(l);
290#else
291 return;
292#endif
293}
294
295/* End dma memory related stuff */
296
297static unsigned long fake_change;
Joe Perches29f1c782010-03-10 15:21:00 -0800298static bool initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
Joe Perches48c8cee2010-03-10 15:20:45 -0800300#define ITYPE(x) (((x) >> 2) & 0x1f)
301#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
302#define UNIT(x) ((x) & 0x03) /* drive on fdc */
303#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700304 /* reverse mapping from unit and fdc to drive */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Joe Perches48c8cee2010-03-10 15:20:45 -0800307#define PH_HEAD(floppy, head) (((((floppy)->stretch & 2) >> 1) ^ head) << 2)
308#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
309
Willy Tarreau76dabe72020-02-24 22:23:51 +0100310/* read/write commands */
311#define COMMAND 0
312#define DR_SELECT 1
313#define TRACK 2
314#define HEAD 3
315#define SECTOR 4
316#define SIZECODE 5
317#define SECT_PER_TRACK 6
318#define GAP 7
319#define SIZECODE2 8
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320#define NR_RW 9
321
Willy Tarreau76dabe72020-02-24 22:23:51 +0100322/* format commands */
323#define F_SIZECODE 2
324#define F_SECT_PER_TRACK 3
325#define F_GAP 4
326#define F_FILL 5
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327#define NR_F 6
328
329/*
Joe Perches48c8cee2010-03-10 15:20:45 -0800330 * Maximum disk size (in kilobytes).
331 * This default is used whenever the current disk size is unknown.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 * [Now it is rather a minimum]
333 */
334#define MAX_DISK_SIZE 4 /* 3984 */
335
336/*
337 * globals used by 'result()'
338 */
Denis Efremovbd10a5f2020-05-01 16:44:15 +0300339static unsigned char reply_buffer[FD_RAW_REPLY_SIZE];
Joe Perches891eda82010-03-10 15:21:05 -0800340static int inr; /* size of reply buffer, when called from interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +0100341#define ST0 0
342#define ST1 1
343#define ST2 2
344#define ST3 0 /* result of GETSTATUS */
345#define R_TRACK 3
346#define R_HEAD 4
347#define R_SECTOR 5
348#define R_SIZECODE 6
Joe Perches48c8cee2010-03-10 15:20:45 -0800349
350#define SEL_DLY (2 * HZ / 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
352/*
353 * this struct defines the different floppy drive types.
354 */
355static struct {
356 struct floppy_drive_params params;
357 const char *name; /* name printed while booting */
358} default_drive_params[] = {
359/* NOTE: the time values in jiffies should be in msec!
360 CMOS drive type
361 | Maximum data rate supported by drive type
362 | | Head load time, msec
363 | | | Head unload time, msec (not used)
364 | | | | Step rate interval, usec
365 | | | | | Time needed for spinup time (jiffies)
366 | | | | | | Timeout for spinning down (jiffies)
367 | | | | | | | Spindown offset (where disk stops)
368 | | | | | | | | Select delay
369 | | | | | | | | | RPS
370 | | | | | | | | | | Max number of tracks
371 | | | | | | | | | | | Interrupt timeout
372 | | | | | | | | | | | | Max nonintlv. sectors
373 | | | | | | | | | | | | | -Max Errors- flags */
374{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
375 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
376
377{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
378 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
379
380{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
381 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
382
383{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
384 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
385
386{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
387 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
388
389{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
390 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
391
392{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
393 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
394/* | --autodetected formats--- | | |
395 * read_track | | Name printed when booting
396 * | Native format
397 * Frequency of disk change checks */
398};
399
400static struct floppy_drive_params drive_params[N_DRIVE];
401static struct floppy_drive_struct drive_state[N_DRIVE];
402static struct floppy_write_errors write_errors[N_DRIVE];
403static struct timer_list motor_off_timer[N_DRIVE];
Omar Sandovala9f38e12018-10-15 09:21:34 -0600404static struct blk_mq_tag_set tag_sets[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405static struct block_device *opened_bdev[N_DRIVE];
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800406static DEFINE_MUTEX(open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
408
409/*
410 * This struct defines the different floppy types.
411 *
412 * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
413 * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
414 * tells if the disk is in Commodore 1581 format, which means side 0 sectors
415 * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
416 * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
417 * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
418 * side 0 is on physical side 0 (but with the misnamed sector IDs).
419 * 'stretch' should probably be renamed to something more general, like
Keith Wansbrough9e491842008-09-22 14:57:17 -0700420 * 'options'.
421 *
422 * Bits 2 through 9 of 'stretch' tell the number of the first sector.
423 * The LSB (bit 2) is flipped. For most disks, the first sector
424 * is 1 (represented by 0x00<<2). For some CP/M and music sampler
425 * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2).
426 * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2).
427 *
428 * Other parameters should be self-explanatory (see also setfdprm(8)).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 */
430/*
431 Size
432 | Sectors per track
433 | | Head
434 | | | Tracks
435 | | | | Stretch
436 | | | | | Gap 1 size
437 | | | | | | Data rate, | 0x40 for perp
438 | | | | | | | Spec1 (stepping rate, head unload
439 | | | | | | | | /fmt gap (gap2) */
440static struct floppy_struct floppy_type[32] = {
441 { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
442 { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
443 { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
444 { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
445 { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
446 { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
447 { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
448 { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
449 { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
450 { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
451
452 { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
453 { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
454 { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
455 { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
456 { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
457 { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
458 { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
459 { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
460 { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
461 { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
462
463 { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
464 { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
465 { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
466 { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
467 { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
468 { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
469 { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
470 { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
471 { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
475 { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
476};
477
Christoph Hellwig302cfee2020-10-29 15:58:36 +0100478static struct gendisk *disks[N_DRIVE][ARRAY_SIZE(floppy_type)];
479
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480#define SECTSIZE (_FD_SECTSIZE(*floppy))
481
482/* Auto-detection: Disk type used until the next media change occurs. */
483static struct floppy_struct *current_type[N_DRIVE];
484
485/*
486 * User-provided type information. current_type points to
487 * the respective entry of this array.
488 */
489static struct floppy_struct user_params[N_DRIVE];
490
491static sector_t floppy_sizes[256];
492
Hannes Reinecke94fd0db2005-07-15 10:09:25 +0200493static char floppy_device_name[] = "floppy";
494
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495/*
496 * The driver is trying to determine the correct media format
497 * while probing is set. rw_interrupt() clears it after a
498 * successful access.
499 */
500static int probing;
501
502/* Synchronization of FDC access. */
Joe Perches48c8cee2010-03-10 15:20:45 -0800503#define FD_COMMAND_NONE -1
504#define FD_COMMAND_ERROR 2
505#define FD_COMMAND_OKAY 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
507static volatile int command_status = FD_COMMAND_NONE;
508static unsigned long fdc_busy;
509static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
510static DECLARE_WAIT_QUEUE_HEAD(command_done);
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512/* Errors during formatting are counted here. */
513static int format_errors;
514
515/* Format request descriptor. */
516static struct format_descr format_req;
517
518/*
519 * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
520 * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
521 * H is head unload time (1=16ms, 2=32ms, etc)
522 */
523
524/*
525 * Track buffer
526 * Because these are written to by the DMA controller, they must
527 * not contain a 64k byte boundary crossing, or data will be
528 * corrupted/lost.
529 */
530static char *floppy_track_buffer;
531static int max_buffer_sectors;
532
533static int *errors;
Jesper Juhl06f748c2007-10-16 23:30:57 -0700534typedef void (*done_f)(int);
Stephen Hemminger3b06c212010-07-20 20:09:00 -0600535static const struct cont_t {
Joe Perches48c8cee2010-03-10 15:20:45 -0800536 void (*interrupt)(void);
537 /* this is called after the interrupt of the
538 * main command */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700539 void (*redo)(void); /* this is called to retry the operation */
540 void (*error)(void); /* this is called to tally an error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 done_f done; /* this is called to say if the operation has
542 * succeeded/failed */
543} *cont;
544
545static void floppy_ready(void);
546static void floppy_start(void);
547static void process_fd_request(void);
548static void recalibrate_floppy(void);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200549static void floppy_shutdown(struct work_struct *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
Philippe De Muyter5a74db02009-02-18 14:48:36 -0800551static int floppy_request_regions(int);
552static void floppy_release_regions(int);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553static int floppy_grab_irq_and_dma(void);
554static void floppy_release_irq_and_dma(void);
555
556/*
557 * The "reset" variable should be tested whenever an interrupt is scheduled,
558 * after the commands have been sent. This is to ensure that the driver doesn't
559 * get wedged when the interrupt doesn't come because of a failed command.
560 * reset doesn't need to be tested before sending commands, because
561 * output_byte is automatically disabled when reset is set.
562 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563static void reset_fdc(void);
Christoph Hellwig4a6f3d42020-09-08 16:53:32 +0200564static int floppy_revalidate(struct gendisk *disk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565
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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671#define MAXTIMEOUT -2
672
Joe Perches73507e62010-03-10 15:21:03 -0800673static void __reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674{
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200675 unsigned long delay;
676
Eric Sesterhenn / Snakebyte4acb3e22007-05-23 13:58:15 -0700677 if (drive < 0 || drive >= N_DRIVE) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200678 delay = 20UL * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 drive = 0;
680 } else
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100681 delay = drive_params[drive].timeout;
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200682
Tejun Heoe7c2f962012-08-21 13:18:24 -0700683 mod_delayed_work(floppy_wq, &fd_timeout, delay);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100684 if (drive_params[drive].flags & FD_DEBUG)
Joe Perches73507e62010-03-10 15:21:03 -0800685 DPRINT("reschedule timeout %s\n", message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 timeout_message = message;
687}
688
Joe Perches73507e62010-03-10 15:21:03 -0800689static void reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690{
691 unsigned long flags;
692
693 spin_lock_irqsave(&floppy_lock, flags);
Joe Perches73507e62010-03-10 15:21:03 -0800694 __reschedule_timeout(drive, message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 spin_unlock_irqrestore(&floppy_lock, flags);
696}
697
Joe Perches48c8cee2010-03-10 15:20:45 -0800698#define INFBOUND(a, b) (a) = max_t(int, a, b)
699#define SUPBOUND(a, b) (a) = min_t(int, a, b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
701/*
702 * Bottom half floppy driver.
703 * ==========================
704 *
705 * This part of the file contains the code talking directly to the hardware,
706 * and also the main service loop (seek-configure-spinup-command)
707 */
708
709/*
710 * disk change.
711 * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
712 * and the last_checked date.
713 *
714 * last_checked is the date of the last check which showed 'no disk change'
715 * FD_DISK_CHANGE is set under two conditions:
716 * 1. The floppy has been changed after some i/o to that floppy already
717 * took place.
718 * 2. No floppy disk is in the drive. This is done in order to ensure that
719 * requests are quickly flushed in case there is no disk in the drive. It
720 * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
721 * the drive.
722 *
723 * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
724 * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
725 * each seek. If a disk is present, the disk change line should also be
726 * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
727 * change line is set, this means either that no disk is in the drive, or
728 * that it has been removed since the last seek.
729 *
730 * This means that we really have a third possibility too:
731 * The floppy has been changed after the last seek.
732 */
733
734static int disk_change(int drive)
735{
736 int fdc = FDC(drive);
Jesper Juhl06f748c2007-10-16 23:30:57 -0700737
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100738 if (time_before(jiffies, drive_state[drive].select_date + drive_params[drive].select_delay))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 DPRINT("WARNING disk change called early\n");
Willy Tarreaude6048b2020-02-24 22:23:43 +0100740 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))) ||
741 (fdc_state[fdc].dor & 3) != UNIT(drive) || fdc != FDC(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 DPRINT("probing disk change on unselected drive\n");
743 DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive),
Willy Tarreaude6048b2020-02-24 22:23:43 +0100744 (unsigned int)fdc_state[fdc].dor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100747 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800748 "checking disk change line for drive %d\n", drive);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100749 debug_dcl(drive_params[drive].flags, "jiffies=%lu\n", jiffies);
750 debug_dcl(drive_params[drive].flags, "disk change line=%x\n",
Willy Tarreauac701862020-03-01 20:55:53 +0100751 fdc_inb(fdc, FD_DIR) & 0x80);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100752 debug_dcl(drive_params[drive].flags, "flags=%lx\n",
753 drive_state[drive].flags);
Joe Perches87f530d2010-03-10 15:20:54 -0800754
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100755 if (drive_params[drive].flags & FD_BROKEN_DCL)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100756 return test_bit(FD_DISK_CHANGED_BIT,
757 &drive_state[drive].flags);
Willy Tarreauac701862020-03-01 20:55:53 +0100758 if ((fdc_inb(fdc, FD_DIR) ^ drive_params[drive].flags) & 0x80) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100759 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -0800760 /* verify write protection */
761
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100762 if (drive_state[drive].maxblock) /* mark it changed */
763 set_bit(FD_DISK_CHANGED_BIT,
764 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
766 /* invalidate its geometry */
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100767 if (drive_state[drive].keep_data >= 0) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100768 if ((drive_params[drive].flags & FTD_MSG) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 current_type[drive] != NULL)
Joe Perches891eda82010-03-10 15:21:05 -0800770 DPRINT("Disk type is undefined after disk change\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 current_type[drive] = NULL;
772 floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1;
773 }
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 return 1;
776 } else {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100777 drive_state[drive].last_checked = jiffies;
778 clear_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779 }
780 return 0;
781}
782
783static inline int is_selected(int dor, int unit)
784{
785 return ((dor & (0x10 << unit)) && (dor & 3) == unit);
786}
787
Joe Perches57584c52010-03-10 15:21:00 -0800788static bool is_ready_state(int status)
789{
790 int state = status & (STATUS_READY | STATUS_DIR | STATUS_DMA);
791 return state == STATUS_READY;
792}
793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794static int set_dor(int fdc, char mask, char data)
795{
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700796 unsigned char unit;
797 unsigned char drive;
798 unsigned char newdor;
799 unsigned char olddor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Willy Tarreaude6048b2020-02-24 22:23:43 +0100801 if (fdc_state[fdc].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 return -1;
803
Willy Tarreaude6048b2020-02-24 22:23:43 +0100804 olddor = fdc_state[fdc].dor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 newdor = (olddor & mask) | data;
806 if (newdor != olddor) {
807 unit = olddor & 0x3;
808 if (is_selected(olddor, unit) && !is_selected(newdor, unit)) {
809 drive = REVDRIVE(fdc, unit);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100810 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800811 "calling disk change from set_dor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 disk_change(drive);
813 }
Willy Tarreaude6048b2020-02-24 22:23:43 +0100814 fdc_state[fdc].dor = newdor;
Willy Tarreauac701862020-03-01 20:55:53 +0100815 fdc_outb(newdor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 unit = newdor & 0x3;
818 if (!is_selected(olddor, unit) && is_selected(newdor, unit)) {
819 drive = REVDRIVE(fdc, unit);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100820 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 }
822 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 return olddor;
824}
825
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200826static void twaddle(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827{
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200828 if (drive_params[drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 return;
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200830 fdc_outb(fdc_state[fdc].dor & ~(0x10 << UNIT(drive)),
831 fdc, FD_DOR);
832 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
833 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834}
835
Joe Perches57584c52010-03-10 15:21:00 -0800836/*
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200837 * Reset all driver information about the specified fdc.
Joe Perches57584c52010-03-10 15:21:00 -0800838 * This is needed after a reset, and after a raw command.
839 */
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200840static void reset_fdc_info(int fdc, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841{
842 int drive;
843
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200844 fdc_state[fdc].spec1 = fdc_state[fdc].spec2 = -1;
845 fdc_state[fdc].need_configure = 1;
846 fdc_state[fdc].perp_mode = 1;
847 fdc_state[fdc].rawcmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 for (drive = 0; drive < N_DRIVE; drive++)
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200849 if (FDC(drive) == fdc &&
Willy Tarreaue83995c2020-03-01 20:55:55 +0100850 (mode || drive_state[drive].track != NEED_1_RECAL))
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100851 drive_state[drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852}
853
Willy Tarreauca1b4092020-04-10 12:19:04 +0200854/*
855 * selects the fdc and drive, and enables the fdc's input/dma.
856 * Both current_drive and current_fdc are changed to match the new drive.
857 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858static void set_fdc(int drive)
859{
Willy Tarreauca1b4092020-04-10 12:19:04 +0200860 unsigned int fdc;
Linus Torvalds2e90ca62020-02-21 12:43:35 -0800861
Willy Tarreauca1b4092020-04-10 12:19:04 +0200862 if (drive < 0 || drive >= N_DRIVE) {
863 pr_info("bad drive value %d\n", drive);
864 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 }
Willy Tarreauca1b4092020-04-10 12:19:04 +0200866
867 fdc = FDC(drive);
868 if (fdc >= N_FDC) {
Joe Perchesb46df352010-03-10 15:20:46 -0800869 pr_info("bad fdc value\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 return;
871 }
Willy Tarreauca1b4092020-04-10 12:19:04 +0200872
873 set_dor(fdc, ~0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874#if N_FDC > 1
Willy Tarreauca1b4092020-04-10 12:19:04 +0200875 set_dor(1 - fdc, ~8, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876#endif
Willy Tarreauca1b4092020-04-10 12:19:04 +0200877 if (fdc_state[fdc].rawcmd == 2)
878 reset_fdc_info(fdc, 1);
879 if (fdc_inb(fdc, FD_STATUS) != STATUS_READY)
880 fdc_state[fdc].reset = 1;
881
882 current_drive = drive;
883 current_fdc = fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884}
885
Willy Tarreauca1b4092020-04-10 12:19:04 +0200886/*
887 * locks the driver.
888 * Both current_drive and current_fdc are changed to match the new drive.
889 */
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +0100890static int lock_fdc(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891{
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200892 if (WARN(atomic_read(&usage_count) == 0,
893 "Trying to lock fdc while usage count=0\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200896 if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy)))
897 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899 command_status = FD_COMMAND_NONE;
900
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200901 reschedule_timeout(drive, "lock fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 set_fdc(drive);
903 return 0;
904}
905
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906/* unlocks the driver */
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +0200907static void unlock_fdc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 if (!test_bit(0, &fdc_busy))
910 DPRINT("FDC access conflict!\n");
911
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200912 raw_cmd = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 command_status = FD_COMMAND_NONE;
Tejun Heo136b5722012-08-21 13:18:24 -0700914 cancel_delayed_work(&fd_timeout);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200915 do_floppy = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 cont = NULL;
917 clear_bit(0, &fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 wake_up(&fdc_wait);
919}
920
921/* switches the motor off after a given timeout */
Kees Cookb1bf4212017-10-04 17:49:29 -0700922static void motor_off_callback(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923{
Kees Cookb1bf4212017-10-04 17:49:29 -0700924 unsigned long nr = t - motor_off_timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 unsigned char mask = ~(0x10 << UNIT(nr));
926
Kees Cookb1bf4212017-10-04 17:49:29 -0700927 if (WARN_ON_ONCE(nr >= N_DRIVE))
928 return;
929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 set_dor(FDC(nr), mask, 0);
931}
932
933/* schedules motor off */
934static void floppy_off(unsigned int drive)
935{
936 unsigned long volatile delta;
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700937 int fdc = FDC(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Willy Tarreaude6048b2020-02-24 22:23:43 +0100939 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 return;
941
942 del_timer(motor_off_timer + drive);
943
944 /* make spindle stop in a position which minimizes spinup time
945 * next time */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100946 if (drive_params[drive].rps) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100947 delta = jiffies - drive_state[drive].first_read_date + HZ -
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100948 drive_params[drive].spindown_offset;
949 delta = ((delta * drive_params[drive].rps) % HZ) / drive_params[drive].rps;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 motor_off_timer[drive].expires =
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100951 jiffies + drive_params[drive].spindown - delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
953 add_timer(motor_off_timer + drive);
954}
955
956/*
957 * cycle through all N_DRIVE floppy drives, for disk change testing.
958 * stopping at current drive. This is done before any long operation, to
959 * be sure to have up to date disk change information.
960 */
961static void scandrives(void)
962{
Jesper Juhl06f748c2007-10-16 23:30:57 -0700963 int i;
964 int drive;
965 int saved_drive;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966
Willy Tarreau031faab2020-02-24 22:23:48 +0100967 if (drive_params[current_drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 return;
969
970 saved_drive = current_drive;
971 for (i = 0; i < N_DRIVE; i++) {
972 drive = (saved_drive + i + 1) % N_DRIVE;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100973 if (drive_state[drive].fd_ref == 0 || drive_params[drive].select_delay != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 continue; /* skip closed drives */
975 set_fdc(drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +0100976 if (!(set_dor(current_fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 (0x10 << UNIT(drive))))
978 /* switch the motor off again, if it was off to
979 * begin with */
Willy Tarreaue83995c2020-03-01 20:55:55 +0100980 set_dor(current_fdc, ~(0x10 << UNIT(drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 }
982 set_fdc(saved_drive);
983}
984
985static void empty(void)
986{
987}
988
Tejun Heo75ddb382014-03-07 10:24:48 -0500989static void (*floppy_work_fn)(void);
990
991static void floppy_work_workfn(struct work_struct *work)
992{
993 floppy_work_fn();
994}
995
996static DECLARE_WORK(floppy_work, floppy_work_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Joe Perches48c8cee2010-03-10 15:20:45 -0800998static void schedule_bh(void (*handler)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999{
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001000 WARN_ON(work_pending(&floppy_work));
1001
Tejun Heo75ddb382014-03-07 10:24:48 -05001002 floppy_work_fn = handler;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001003 queue_work(floppy_wq, &floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004}
1005
Tejun Heo75ddb382014-03-07 10:24:48 -05001006static void (*fd_timer_fn)(void) = NULL;
1007
1008static void fd_timer_workfn(struct work_struct *work)
1009{
1010 fd_timer_fn();
1011}
1012
1013static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
1015static void cancel_activity(void)
1016{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 do_floppy = NULL;
Tasos Sahanidisfb48feb2021-09-03 09:47:58 +03001018 cancel_delayed_work(&fd_timer);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001019 cancel_work_sync(&floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020}
1021
1022/* this function makes sure that the disk stays in the drive during the
1023 * transfer */
Tejun Heo75ddb382014-03-07 10:24:48 -05001024static void fd_watchdog(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025{
Willy Tarreau031faab2020-02-24 22:23:48 +01001026 debug_dcl(drive_params[current_drive].flags,
1027 "calling disk change from watchdog\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
1029 if (disk_change(current_drive)) {
1030 DPRINT("disk removed during i/o\n");
1031 cancel_activity();
1032 cont->done(0);
1033 reset_fdc();
1034 } else {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001035 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001036 fd_timer_fn = fd_watchdog;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001037 queue_delayed_work(floppy_wq, &fd_timer, HZ / 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 }
1039}
1040
1041static void main_command_interrupt(void)
1042{
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001043 cancel_delayed_work(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 cont->interrupt();
1045}
1046
1047/* waits for a delay (spinup or select) to pass */
Tejun Heo75ddb382014-03-07 10:24:48 -05001048static int fd_wait_for_completion(unsigned long expires,
1049 void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001051 if (fdc_state[current_fdc].reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 reset_fdc(); /* do the reset during sleep to win time
1053 * if we don't need to sleep, it's a good
1054 * occasion anyways */
1055 return 1;
1056 }
1057
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001058 if (time_before(jiffies, expires)) {
1059 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001060 fd_timer_fn = function;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001061 queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 return 1;
1063 }
1064 return 0;
1065}
1066
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067static void setup_DMA(void)
1068{
1069 unsigned long f;
1070
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 if (raw_cmd->length == 0) {
Denis Efremov29ac6762020-05-01 16:44:13 +03001072 print_hex_dump(KERN_INFO, "zero dma transfer size: ",
1073 DUMP_PREFIX_NONE, 16, 1,
Denis Efremov08362752020-05-01 16:44:16 +03001074 raw_cmd->fullcmd, raw_cmd->cmd_count, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001076 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 return;
1078 }
1079 if (((unsigned long)raw_cmd->kernel_data) % 512) {
Joe Perchesb46df352010-03-10 15:20:46 -08001080 pr_info("non aligned address: %p\n", raw_cmd->kernel_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001082 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 return;
1084 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 f = claim_dma_lock();
1086 fd_disable_dma();
1087#ifdef fd_dma_setup
1088 if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length,
1089 (raw_cmd->flags & FD_RAW_READ) ?
Willy Tarreaue83995c2020-03-01 20:55:55 +01001090 DMA_MODE_READ : DMA_MODE_WRITE,
1091 fdc_state[current_fdc].address) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 release_dma_lock(f);
1093 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001094 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 return;
1096 }
1097 release_dma_lock(f);
1098#else
1099 fd_clear_dma_ff();
1100 fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
1101 fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ) ?
1102 DMA_MODE_READ : DMA_MODE_WRITE);
1103 fd_set_dma_addr(raw_cmd->kernel_data);
1104 fd_set_dma_count(raw_cmd->length);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001105 virtual_dma_port = fdc_state[current_fdc].address;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 fd_enable_dma();
1107 release_dma_lock(f);
1108#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001109}
1110
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001111static void show_floppy(int fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001112
1113/* waits until the fdc becomes ready */
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001114static int wait_til_ready(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001116 int status;
1117 int counter;
1118
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001119 if (fdc_state[fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 return -1;
1121 for (counter = 0; counter < 10000; counter++) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001122 status = fdc_inb(fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 if (status & STATUS_READY)
1124 return status;
1125 }
Joe Perches29f1c782010-03-10 15:21:00 -08001126 if (initialized) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001127 DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc);
1128 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 }
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001130 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 return -1;
1132}
1133
1134/* sends a command byte to the fdc */
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001135static int output_byte(int fdc, char byte)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136{
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001137 int status = wait_til_ready(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001139 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001141
1142 if (is_ready_state(status)) {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001143 fdc_outb(byte, fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 output_log[output_log_pos].data = byte;
1145 output_log[output_log_pos].status = status;
1146 output_log[output_log_pos].jiffies = jiffies;
1147 output_log_pos = (output_log_pos + 1) % OLOGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001148 return 0;
1149 }
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001150 fdc_state[fdc].reset = 1;
Joe Perches29f1c782010-03-10 15:21:00 -08001151 if (initialized) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001153 byte, fdc, status);
1154 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 }
1156 return -1;
1157}
1158
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159/* gets the response from the fdc */
Willy Tarreau96dad772020-03-31 11:40:45 +02001160static int result(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001162 int i;
1163 int status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
Denis Efremovbd10a5f2020-05-01 16:44:15 +03001165 for (i = 0; i < FD_RAW_REPLY_SIZE; i++) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001166 status = wait_til_ready(fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001167 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169 status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA;
1170 if ((status & ~STATUS_BUSY) == STATUS_READY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 resultjiffies = jiffies;
1172 resultsize = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 return i;
1174 }
1175 if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY))
Willy Tarreau96dad772020-03-31 11:40:45 +02001176 reply_buffer[i] = fdc_inb(fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 else
1178 break;
1179 }
Joe Perches29f1c782010-03-10 15:21:00 -08001180 if (initialized) {
1181 DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
Willy Tarreau96dad772020-03-31 11:40:45 +02001182 fdc, status, i);
1183 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 }
Willy Tarreau96dad772020-03-31 11:40:45 +02001185 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 return -1;
1187}
1188
1189#define MORE_OUTPUT -2
1190/* does the fdc need more output? */
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001191static int need_more_output(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001193 int status = wait_til_ready(fdc);
Jesper Juhl06f748c2007-10-16 23:30:57 -07001194
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001195 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001197
1198 if (is_ready_state(status))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 return MORE_OUTPUT;
Joe Perches57584c52010-03-10 15:21:00 -08001200
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001201 return result(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202}
1203
1204/* Set perpendicular mode as required, based on data rate, if supported.
1205 * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
1206 */
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001207static void perpendicular_mode(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208{
1209 unsigned char perp_mode;
1210
1211 if (raw_cmd->rate & 0x40) {
1212 switch (raw_cmd->rate & 3) {
1213 case 0:
1214 perp_mode = 2;
1215 break;
1216 case 3:
1217 perp_mode = 3;
1218 break;
1219 default:
1220 DPRINT("Invalid data rate for perpendicular mode!\n");
1221 cont->done(0);
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001222 fdc_state[fdc].reset = 1;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001223 /*
1224 * convenient way to return to
1225 * redo without too much hassle
1226 * (deep stack et al.)
1227 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 return;
1229 }
1230 } else
1231 perp_mode = 0;
1232
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001233 if (fdc_state[fdc].perp_mode == perp_mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001234 return;
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001235 if (fdc_state[fdc].version >= FDC_82077_ORIG) {
1236 output_byte(fdc, FD_PERPENDICULAR);
1237 output_byte(fdc, perp_mode);
1238 fdc_state[fdc].perp_mode = perp_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 } else if (perp_mode) {
1240 DPRINT("perpendicular mode not supported by this FDC.\n");
1241 }
1242} /* perpendicular_mode */
1243
1244static int fifo_depth = 0xa;
1245static int no_fifo;
1246
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001247static int fdc_configure(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248{
1249 /* Turn on FIFO */
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001250 output_byte(fdc, FD_CONFIGURE);
1251 if (need_more_output(fdc) != MORE_OUTPUT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 return 0;
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001253 output_byte(fdc, 0);
1254 output_byte(fdc, 0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
1255 output_byte(fdc, 0); /* pre-compensation from track 0 upwards */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 return 1;
1257}
1258
1259#define NOMINAL_DTR 500
1260
1261/* Issue a "SPECIFY" command to set the step rate time, head unload time,
1262 * head load time, and DMA disable flag to values needed by floppy.
1263 *
1264 * The value "dtr" is the data transfer rate in Kbps. It is needed
1265 * to account for the data rate-based scaling done by the 82072 and 82077
1266 * FDC types. This parameter is ignored for other types of FDCs (i.e.
1267 * 8272a).
1268 *
1269 * Note that changing the data transfer rate has a (probably deleterious)
1270 * effect on the parameters subject to scaling for 82072/82077 FDCs, so
1271 * fdc_specify is called again after each data transfer rate
1272 * change.
1273 *
1274 * srt: 1000 to 16000 in microseconds
1275 * hut: 16 to 240 milliseconds
1276 * hlt: 2 to 254 milliseconds
1277 *
1278 * These values are rounded up to the next highest available delay time.
1279 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001280static void fdc_specify(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001282 unsigned char spec1;
1283 unsigned char spec2;
1284 unsigned long srt;
1285 unsigned long hlt;
1286 unsigned long hut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 unsigned long dtr = NOMINAL_DTR;
1288 unsigned long scale_dtr = NOMINAL_DTR;
1289 int hlt_max_code = 0x7f;
1290 int hut_max_code = 0xf;
1291
Willy Tarreau3631a672020-03-31 11:40:49 +02001292 if (fdc_state[fdc].need_configure &&
1293 fdc_state[fdc].version >= FDC_82072A) {
1294 fdc_configure(fdc);
1295 fdc_state[fdc].need_configure = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 }
1297
1298 switch (raw_cmd->rate & 0x03) {
1299 case 3:
1300 dtr = 1000;
1301 break;
1302 case 1:
1303 dtr = 300;
Willy Tarreau3631a672020-03-31 11:40:49 +02001304 if (fdc_state[fdc].version >= FDC_82078) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 /* chose the default rate table, not the one
1306 * where 1 = 2 Mbps */
Willy Tarreau3631a672020-03-31 11:40:49 +02001307 output_byte(fdc, FD_DRIVESPEC);
1308 if (need_more_output(fdc) == MORE_OUTPUT) {
1309 output_byte(fdc, UNIT(drive));
1310 output_byte(fdc, 0xc0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 }
1312 }
1313 break;
1314 case 2:
1315 dtr = 250;
1316 break;
1317 }
1318
Willy Tarreau3631a672020-03-31 11:40:49 +02001319 if (fdc_state[fdc].version >= FDC_82072) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 scale_dtr = dtr;
1321 hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
1322 hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
1323 }
1324
1325 /* Convert step rate from microseconds to milliseconds and 4 bits */
Willy Tarreau3631a672020-03-31 11:40:49 +02001326 srt = 16 - DIV_ROUND_UP(drive_params[drive].srt * scale_dtr / 1000,
Willy Tarreau031faab2020-02-24 22:23:48 +01001327 NOMINAL_DTR);
Joe Perchesa81ee542010-03-10 15:20:46 -08001328 if (slow_floppy)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 srt = srt / 4;
Joe Perchesa81ee542010-03-10 15:20:46 -08001330
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 SUPBOUND(srt, 0xf);
1332 INFBOUND(srt, 0);
1333
Willy Tarreau3631a672020-03-31 11:40:49 +02001334 hlt = DIV_ROUND_UP(drive_params[drive].hlt * scale_dtr / 2,
Willy Tarreau031faab2020-02-24 22:23:48 +01001335 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 if (hlt < 0x01)
1337 hlt = 0x01;
1338 else if (hlt > 0x7f)
1339 hlt = hlt_max_code;
1340
Willy Tarreau3631a672020-03-31 11:40:49 +02001341 hut = DIV_ROUND_UP(drive_params[drive].hut * scale_dtr / 16,
Willy Tarreau031faab2020-02-24 22:23:48 +01001342 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 if (hut < 0x1)
1344 hut = 0x1;
1345 else if (hut > 0xf)
1346 hut = hut_max_code;
1347
1348 spec1 = (srt << 4) | hut;
1349 spec2 = (hlt << 1) | (use_virtual_dma & 1);
1350
1351 /* If these parameters did not change, just return with success */
Willy Tarreau3631a672020-03-31 11:40:49 +02001352 if (fdc_state[fdc].spec1 != spec1 ||
1353 fdc_state[fdc].spec2 != spec2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354 /* Go ahead and set spec1 and spec2 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001355 output_byte(fdc, FD_SPECIFY);
1356 output_byte(fdc, fdc_state[fdc].spec1 = spec1);
1357 output_byte(fdc, fdc_state[fdc].spec2 = spec2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 }
1359} /* fdc_specify */
1360
1361/* Set the FDC's data transfer rate on behalf of the specified drive.
1362 * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
1363 * of the specify command (i.e. using the fdc_specify function).
1364 */
1365static int fdc_dtr(void)
1366{
1367 /* If data rate not already set to desired value, set it. */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001368 if ((raw_cmd->rate & 3) == fdc_state[current_fdc].dtr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 return 0;
1370
1371 /* Set dtr */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001372 fdc_outb(raw_cmd->rate & 3, current_fdc, FD_DCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
1375 * need a stabilization period of several milliseconds to be
1376 * enforced after data rate changes before R/W operations.
1377 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
1378 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001379 fdc_state[current_fdc].dtr = raw_cmd->rate & 3;
Tejun Heo75ddb382014-03-07 10:24:48 -05001380 return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381} /* fdc_dtr */
1382
1383static void tell_sector(void)
1384{
Joe Perchesb46df352010-03-10 15:20:46 -08001385 pr_cont(": track %d, head %d, sector %d, size %d",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001386 reply_buffer[R_TRACK], reply_buffer[R_HEAD],
1387 reply_buffer[R_SECTOR],
1388 reply_buffer[R_SIZECODE]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389} /* tell_sector */
1390
Joe Perchesb46df352010-03-10 15:20:46 -08001391static void print_errors(void)
1392{
1393 DPRINT("");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001394 if (reply_buffer[ST0] & ST0_ECE) {
Joe Perchesb46df352010-03-10 15:20:46 -08001395 pr_cont("Recalibrate failed!");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001396 } else if (reply_buffer[ST2] & ST2_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001397 pr_cont("data CRC error");
1398 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001399 } else if (reply_buffer[ST1] & ST1_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001400 pr_cont("CRC error");
1401 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001402 } else if ((reply_buffer[ST1] & (ST1_MAM | ST1_ND)) ||
1403 (reply_buffer[ST2] & ST2_MAM)) {
Joe Perchesb46df352010-03-10 15:20:46 -08001404 if (!probing) {
1405 pr_cont("sector not found");
1406 tell_sector();
1407 } else
1408 pr_cont("probe failed...");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001409 } else if (reply_buffer[ST2] & ST2_WC) { /* seek error */
Joe Perchesb46df352010-03-10 15:20:46 -08001410 pr_cont("wrong cylinder");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001411 } else if (reply_buffer[ST2] & ST2_BC) { /* cylinder marked as bad */
Joe Perchesb46df352010-03-10 15:20:46 -08001412 pr_cont("bad cylinder");
1413 } else {
1414 pr_cont("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001415 reply_buffer[ST0], reply_buffer[ST1],
1416 reply_buffer[ST2]);
Joe Perchesb46df352010-03-10 15:20:46 -08001417 tell_sector();
1418 }
1419 pr_cont("\n");
1420}
1421
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422/*
1423 * OK, this error interpreting routine is called after a
1424 * DMA read/write has succeeded
1425 * or failed, so we check the results, and copy any buffers.
1426 * hhb: Added better error reporting.
1427 * ak: Made this into a separate routine.
1428 */
1429static int interpret_errors(void)
1430{
1431 char bad;
1432
1433 if (inr != 7) {
Joe Perches891eda82010-03-10 15:21:05 -08001434 DPRINT("-- FDC reply error\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001435 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 return 1;
1437 }
1438
1439 /* check IC to find cause of interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001440 switch (reply_buffer[ST0] & ST0_INTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 case 0x40: /* error occurred during command execution */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001442 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 return 0; /* occurs with pseudo-DMA */
1444 bad = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001445 if (reply_buffer[ST1] & ST1_WP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 DPRINT("Drive is write protected\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001447 clear_bit(FD_DISK_WRITABLE_BIT,
1448 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 cont->done(0);
1450 bad = 2;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001451 } else if (reply_buffer[ST1] & ST1_ND) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001452 set_bit(FD_NEED_TWADDLE_BIT,
1453 &drive_state[current_drive].flags);
Willy Tarreau8fb38452020-02-24 22:23:52 +01001454 } else if (reply_buffer[ST1] & ST1_OR) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001455 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 DPRINT("Over/Underrun - retrying\n");
1457 bad = 0;
Willy Tarreau031faab2020-02-24 22:23:48 +01001458 } else if (*errors >= drive_params[current_drive].max_errors.reporting) {
Joe Perchesb46df352010-03-10 15:20:46 -08001459 print_errors();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001461 if (reply_buffer[ST2] & ST2_WC || reply_buffer[ST2] & ST2_BC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462 /* wrong cylinder => recal */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001463 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 return bad;
1465 case 0x80: /* invalid command given */
1466 DPRINT("Invalid FDC command given!\n");
1467 cont->done(0);
1468 return 2;
1469 case 0xc0:
1470 DPRINT("Abnormal termination caused by polling\n");
1471 cont->error();
1472 return 2;
1473 default: /* (0) Normal command termination */
1474 return 0;
1475 }
1476}
1477
1478/*
1479 * This routine is called when everything should be correctly set up
1480 * for the transfer (i.e. floppy motor is on, the correct floppy is
1481 * selected, and the head is sitting on the right track).
1482 */
1483static void setup_rw_floppy(void)
1484{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001485 int i;
1486 int r;
1487 int flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 unsigned long ready_date;
Tejun Heo75ddb382014-03-07 10:24:48 -05001489 void (*function)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
1491 flags = raw_cmd->flags;
1492 if (flags & (FD_RAW_READ | FD_RAW_WRITE))
1493 flags |= FD_RAW_INTR;
1494
1495 if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001496 ready_date = drive_state[current_drive].spinup_date + drive_params[current_drive].spinup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 /* If spinup will take a long time, rerun scandrives
1498 * again just before spinup completion. Beware that
1499 * after scandrives, we must again wait for selection.
1500 */
Willy Tarreau031faab2020-02-24 22:23:48 +01001501 if (time_after(ready_date, jiffies + drive_params[current_drive].select_delay)) {
1502 ready_date -= drive_params[current_drive].select_delay;
Tejun Heo75ddb382014-03-07 10:24:48 -05001503 function = floppy_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001504 } else
Tejun Heo75ddb382014-03-07 10:24:48 -05001505 function = setup_rw_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 /* wait until the floppy is spinning fast enough */
1508 if (fd_wait_for_completion(ready_date, function))
1509 return;
1510 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
1512 setup_DMA();
1513
1514 if (flags & FD_RAW_INTR)
1515 do_floppy = main_command_interrupt;
1516
1517 r = 0;
1518 for (i = 0; i < raw_cmd->cmd_count; i++)
Denis Efremov08362752020-05-01 16:44:16 +03001519 r |= output_byte(current_fdc, raw_cmd->fullcmd[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520
Joe Perchesded28632010-03-10 15:21:09 -08001521 debugt(__func__, "rw_command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
1523 if (r) {
1524 cont->error();
1525 reset_fdc();
1526 return;
1527 }
1528
1529 if (!(flags & FD_RAW_INTR)) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001530 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 cont->interrupt();
1532 } else if (flags & FD_RAW_NEED_DISK)
Tejun Heo75ddb382014-03-07 10:24:48 -05001533 fd_watchdog();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534}
1535
1536static int blind_seek;
1537
1538/*
1539 * This is the routine called after every seek (or recalibrate) interrupt
1540 * from the floppy controller.
1541 */
1542static void seek_interrupt(void)
1543{
Joe Perchesded28632010-03-10 15:21:09 -08001544 debugt(__func__, "");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001545 if (inr != 2 || (reply_buffer[ST0] & 0xF8) != 0x20) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 DPRINT("seek failed\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001547 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 cont->error();
1549 cont->redo();
1550 return;
1551 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001552 if (drive_state[current_drive].track >= 0 &&
1553 drive_state[current_drive].track != reply_buffer[ST1] &&
1554 !blind_seek) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001555 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001556 "clearing NEWCHANGE flag because of effective seek\n");
Willy Tarreau031faab2020-02-24 22:23:48 +01001557 debug_dcl(drive_params[current_drive].flags, "jiffies=%lu\n",
1558 jiffies);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001559 clear_bit(FD_DISK_NEWCHANGE_BIT,
1560 &drive_state[current_drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -08001561 /* effective seek */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001562 drive_state[current_drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001564 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565 floppy_ready();
1566}
1567
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001568static void check_wp(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569{
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001570 if (test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)) {
Joe Perchese0298532010-03-10 15:20:55 -08001571 /* check write protection */
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001572 output_byte(fdc, FD_GETSTATUS);
1573 output_byte(fdc, UNIT(drive));
1574 if (result(fdc) != 1) {
1575 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 return;
1577 }
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001578 clear_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001579 clear_bit(FD_NEED_TWADDLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001580 &drive_state[drive].flags);
1581 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001582 "checking whether disk is write protected\n");
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001583 debug_dcl(drive_params[drive].flags, "wp=%x\n",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001584 reply_buffer[ST3] & 0x40);
1585 if (!(reply_buffer[ST3] & 0x40))
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001586 set_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001587 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001589 clear_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001590 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 }
1592}
1593
1594static void seek_floppy(void)
1595{
1596 int track;
1597
1598 blind_seek = 0;
1599
Willy Tarreau031faab2020-02-24 22:23:48 +01001600 debug_dcl(drive_params[current_drive].flags,
1601 "calling disk change from %s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001603 if (!test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) {
1605 /* the media changed flag should be cleared after the seek.
1606 * If it isn't, this means that there is really no disk in
1607 * the drive.
1608 */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001609 set_bit(FD_DISK_CHANGED_BIT,
1610 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 cont->done(0);
1612 cont->redo();
1613 return;
1614 }
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001615 if (drive_state[current_drive].track <= NEED_1_RECAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 recalibrate_floppy();
1617 return;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001618 } else if (test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 (raw_cmd->flags & FD_RAW_NEED_DISK) &&
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001620 (drive_state[current_drive].track <= NO_TRACK || drive_state[current_drive].track == raw_cmd->track)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 /* we seek to clear the media-changed condition. Does anybody
1622 * know a more elegant way, which works on all drives? */
1623 if (raw_cmd->track)
1624 track = raw_cmd->track - 1;
1625 else {
Willy Tarreau031faab2020-02-24 22:23:48 +01001626 if (drive_params[current_drive].flags & FD_SILENT_DCL_CLEAR) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001627 set_dor(current_fdc, ~(0x10 << UNIT(current_drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628 blind_seek = 1;
1629 raw_cmd->flags |= FD_RAW_NEED_SEEK;
1630 }
1631 track = 1;
1632 }
1633 } else {
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001634 check_wp(current_fdc, current_drive);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001635 if (raw_cmd->track != drive_state[current_drive].track &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001636 (raw_cmd->flags & FD_RAW_NEED_SEEK))
1637 track = raw_cmd->track;
1638 else {
1639 setup_rw_floppy();
1640 return;
1641 }
1642 }
1643
1644 do_floppy = seek_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001645 output_byte(current_fdc, FD_SEEK);
1646 output_byte(current_fdc, UNIT(current_drive));
1647 if (output_byte(current_fdc, track) < 0) {
Joe Perches2300f902010-03-10 15:20:49 -08001648 reset_fdc();
1649 return;
1650 }
Joe Perchesded28632010-03-10 15:21:09 -08001651 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652}
1653
1654static void recal_interrupt(void)
1655{
Joe Perchesded28632010-03-10 15:21:09 -08001656 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 if (inr != 2)
Willy Tarreaue83995c2020-03-01 20:55:55 +01001658 fdc_state[current_fdc].reset = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001659 else if (reply_buffer[ST0] & ST0_ECE) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001660 switch (drive_state[current_drive].track) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 case NEED_1_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001662 debugt(__func__, "need 1 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 /* after a second recalibrate, we still haven't
1664 * reached track 0. Probably no drive. Raise an
1665 * error, as failing immediately might upset
1666 * computers possessed by the Devil :-) */
1667 cont->error();
1668 cont->redo();
1669 return;
1670 case NEED_2_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001671 debugt(__func__, "need 2 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672 /* If we already did a recalibrate,
1673 * and we are not at track 0, this
1674 * means we have moved. (The only way
1675 * not to move at recalibration is to
1676 * be already at track 0.) Clear the
1677 * new change flag */
Willy Tarreau031faab2020-02-24 22:23:48 +01001678 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001679 "clearing NEWCHANGE flag because of second recalibrate\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001681 clear_bit(FD_DISK_NEWCHANGE_BIT,
1682 &drive_state[current_drive].flags);
1683 drive_state[current_drive].select_date = jiffies;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05001684 fallthrough;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 default:
Joe Perchesded28632010-03-10 15:21:09 -08001686 debugt(__func__, "default");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687 /* Recalibrate moves the head by at
1688 * most 80 steps. If after one
1689 * recalibrate we don't have reached
1690 * track 0, this might mean that we
1691 * started beyond track 80. Try
1692 * again. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001693 drive_state[current_drive].track = NEED_1_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 break;
1695 }
1696 } else
Willy Tarreau8fb38452020-02-24 22:23:52 +01001697 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 floppy_ready();
1699}
1700
1701static void print_result(char *message, int inr)
1702{
1703 int i;
1704
1705 DPRINT("%s ", message);
1706 if (inr >= 0)
1707 for (i = 0; i < inr; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001708 pr_cont("repl[%d]=%x ", i, reply_buffer[i]);
1709 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710}
1711
1712/* interrupt handler. Note that this can be called externally on the Sparc */
David Howells7d12e782006-10-05 14:55:46 +01001713irqreturn_t floppy_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001715 int do_print;
1716 unsigned long f;
Jesper Juhl06f748c2007-10-16 23:30:57 -07001717 void (*handler)(void) = do_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718
1719 lasthandler = handler;
1720 interruptjiffies = jiffies;
1721
1722 f = claim_dma_lock();
1723 fd_disable_dma();
1724 release_dma_lock(f);
1725
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 do_floppy = NULL;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001727 if (current_fdc >= N_FDC || fdc_state[current_fdc].address == -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728 /* we don't even know which FDC is the culprit */
Joe Perchesb46df352010-03-10 15:20:46 -08001729 pr_info("DOR0=%x\n", fdc_state[0].dor);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001730 pr_info("floppy interrupt on bizarre fdc %d\n", current_fdc);
Sakari Ailusd75f7732019-03-25 21:32:28 +02001731 pr_info("handler=%ps\n", handler);
Joe Perches275176b2010-03-10 15:21:06 -08001732 is_alive(__func__, "bizarre fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 return IRQ_NONE;
1734 }
1735
Willy Tarreaue83995c2020-03-01 20:55:55 +01001736 fdc_state[current_fdc].reset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001737 /* We have to clear the reset flag here, because apparently on boxes
1738 * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
Willy Tarreaude6048b2020-02-24 22:23:43 +01001739 * emit SENSEI's to clear the interrupt line. And fdc_state[fdc].reset
1740 * blocks the emission of the SENSEI's.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741 * It is OK to emit floppy commands because we are in an interrupt
1742 * handler here, and thus we have to fear no interference of other
1743 * activity.
1744 */
1745
Joe Perches29f1c782010-03-10 15:21:00 -08001746 do_print = !handler && print_unex && initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001747
Willy Tarreau96dad772020-03-31 11:40:45 +02001748 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 if (do_print)
1750 print_result("unexpected interrupt", inr);
1751 if (inr == 0) {
1752 int max_sensei = 4;
1753 do {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001754 output_byte(current_fdc, FD_SENSEI);
Willy Tarreau96dad772020-03-31 11:40:45 +02001755 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001756 if (do_print)
1757 print_result("sensei", inr);
1758 max_sensei--;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001759 } while ((reply_buffer[ST0] & 0x83) != UNIT(current_drive) &&
Joe Perchesc5297302010-03-10 15:20:58 -08001760 inr == 2 && max_sensei);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 }
1762 if (!handler) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001763 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764 return IRQ_NONE;
1765 }
1766 schedule_bh(handler);
Joe Perches275176b2010-03-10 15:21:06 -08001767 is_alive(__func__, "normal interrupt end");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
1769 /* FIXME! Was it really for us? */
1770 return IRQ_HANDLED;
1771}
1772
1773static void recalibrate_floppy(void)
1774{
Joe Perchesded28632010-03-10 15:21:09 -08001775 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776 do_floppy = recal_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001777 output_byte(current_fdc, FD_RECALIBRATE);
1778 if (output_byte(current_fdc, UNIT(current_drive)) < 0)
Joe Perches2300f902010-03-10 15:20:49 -08001779 reset_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780}
1781
1782/*
1783 * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
1784 */
1785static void reset_interrupt(void)
1786{
Joe Perchesded28632010-03-10 15:21:09 -08001787 debugt(__func__, "");
Willy Tarreau96dad772020-03-31 11:40:45 +02001788 result(current_fdc); /* get the status ready for set_fdc */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001789 if (fdc_state[current_fdc].reset) {
Sakari Ailusd75f7732019-03-25 21:32:28 +02001790 pr_info("reset set in interrupt, calling %ps\n", cont->error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 cont->error(); /* a reset just after a reset. BAD! */
1792 }
1793 cont->redo();
1794}
1795
1796/*
1797 * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
Willy Tarreau12aebfa2020-03-31 11:40:54 +02001798 * or by setting the self clearing bit 7 of STATUS (newer FDCs).
1799 * This WILL trigger an interrupt, causing the handlers in the current
1800 * cont's ->redo() to be called via reset_interrupt().
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 */
1802static void reset_fdc(void)
1803{
1804 unsigned long flags;
1805
1806 do_floppy = reset_interrupt;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001807 fdc_state[current_fdc].reset = 0;
Willy Tarreauf3e0dc12020-03-31 11:40:41 +02001808 reset_fdc_info(current_fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
1810 /* Pseudo-DMA may intercept 'reset finished' interrupt. */
1811 /* Irrelevant for systems with true DMA (i386). */
1812
1813 flags = claim_dma_lock();
1814 fd_disable_dma();
1815 release_dma_lock(flags);
1816
Willy Tarreaue83995c2020-03-01 20:55:55 +01001817 if (fdc_state[current_fdc].version >= FDC_82072A)
1818 fdc_outb(0x80 | (fdc_state[current_fdc].dtr & 3),
1819 current_fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820 else {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001821 fdc_outb(fdc_state[current_fdc].dor & ~0x04, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822 udelay(FD_RESET_DELAY);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001823 fdc_outb(fdc_state[current_fdc].dor, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 }
1825}
1826
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001827static void show_floppy(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828{
1829 int i;
1830
Joe Perchesb46df352010-03-10 15:20:46 -08001831 pr_info("\n");
1832 pr_info("floppy driver state\n");
1833 pr_info("-------------------\n");
Sakari Ailusd75f7732019-03-25 21:32:28 +02001834 pr_info("now=%lu last interrupt=%lu diff=%lu last called handler=%ps\n",
Joe Perchesb46df352010-03-10 15:20:46 -08001835 jiffies, interruptjiffies, jiffies - interruptjiffies,
1836 lasthandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837
Joe Perchesb46df352010-03-10 15:20:46 -08001838 pr_info("timeout_message=%s\n", timeout_message);
1839 pr_info("last output bytes:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 for (i = 0; i < OLOGSIZE; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001841 pr_info("%2x %2x %lu\n",
1842 output_log[(i + output_log_pos) % OLOGSIZE].data,
1843 output_log[(i + output_log_pos) % OLOGSIZE].status,
1844 output_log[(i + output_log_pos) % OLOGSIZE].jiffies);
1845 pr_info("last result at %lu\n", resultjiffies);
1846 pr_info("last redo_fd_request at %lu\n", lastredo);
1847 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
1848 reply_buffer, resultsize, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001850 pr_info("status=%x\n", fdc_inb(fdc, FD_STATUS));
Joe Perchesb46df352010-03-10 15:20:46 -08001851 pr_info("fdc_busy=%lu\n", fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 if (do_floppy)
Sakari Ailusd75f7732019-03-25 21:32:28 +02001853 pr_info("do_floppy=%ps\n", do_floppy);
David Howells365970a2006-11-22 14:54:49 +00001854 if (work_pending(&floppy_work))
Sakari Ailusd75f7732019-03-25 21:32:28 +02001855 pr_info("floppy_work.func=%ps\n", floppy_work.func);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001856 if (delayed_work_pending(&fd_timer))
1857 pr_info("delayed work.function=%p expires=%ld\n",
1858 fd_timer.work.func,
1859 fd_timer.timer.expires - jiffies);
1860 if (delayed_work_pending(&fd_timeout))
1861 pr_info("timer_function=%p expires=%ld\n",
1862 fd_timeout.work.func,
1863 fd_timeout.timer.expires - jiffies);
1864
Joe Perchesb46df352010-03-10 15:20:46 -08001865 pr_info("cont=%p\n", cont);
1866 pr_info("current_req=%p\n", current_req);
1867 pr_info("command_status=%d\n", command_status);
1868 pr_info("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001869}
1870
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001871static void floppy_shutdown(struct work_struct *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001872{
1873 unsigned long flags;
1874
Joe Perches29f1c782010-03-10 15:21:00 -08001875 if (initialized)
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001876 show_floppy(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877 cancel_activity();
1878
Linus Torvalds1da177e2005-04-16 15:20:36 -07001879 flags = claim_dma_lock();
1880 fd_disable_dma();
1881 release_dma_lock(flags);
1882
1883 /* avoid dma going to a random drive after shutdown */
1884
Joe Perches29f1c782010-03-10 15:21:00 -08001885 if (initialized)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 DPRINT("floppy timeout called\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001887 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888 if (cont) {
1889 cont->done(0);
1890 cont->redo(); /* this will recall reset when needed */
1891 } else {
Joe Perchesb46df352010-03-10 15:20:46 -08001892 pr_info("no cont in shutdown!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893 process_fd_request();
1894 }
Joe Perches275176b2010-03-10 15:21:06 -08001895 is_alive(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896}
1897
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898/* start motor, check media-changed condition and write protection */
Jesper Juhl06f748c2007-10-16 23:30:57 -07001899static int start_motor(void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001901 int mask;
1902 int data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903
1904 mask = 0xfc;
1905 data = UNIT(current_drive);
1906 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001907 if (!(fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 set_debugt();
1909 /* no read since this drive is running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001910 drive_state[current_drive].first_read_date = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 /* note motor start time if motor is not yet running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001912 drive_state[current_drive].spinup_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 data |= (0x10 << UNIT(current_drive));
1914 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01001915 } else if (fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916 mask &= ~(0x10 << UNIT(current_drive));
1917
1918 /* starts motor and selects floppy */
1919 del_timer(motor_off_timer + current_drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001920 set_dor(current_fdc, mask, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
1922 /* wait_for_completion also schedules reset if needed. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001923 return fd_wait_for_completion(drive_state[current_drive].select_date + drive_params[current_drive].select_delay,
Tejun Heo75ddb382014-03-07 10:24:48 -05001924 function);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925}
1926
1927static void floppy_ready(void)
1928{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001929 if (fdc_state[current_fdc].reset) {
Joe Perches045f9832010-03-10 15:20:47 -08001930 reset_fdc();
1931 return;
1932 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 if (start_motor(floppy_ready))
1934 return;
1935 if (fdc_dtr())
1936 return;
1937
Willy Tarreau031faab2020-02-24 22:23:48 +01001938 debug_dcl(drive_params[current_drive].flags,
1939 "calling disk change from floppy_ready\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
Willy Tarreau031faab2020-02-24 22:23:48 +01001941 disk_change(current_drive) && !drive_params[current_drive].select_delay)
Willy Tarreauc1f710b2020-03-31 11:40:40 +02001942 twaddle(current_fdc, current_drive); /* this clears the dcl on certain
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001943 * drive/controller combinations */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944
1945#ifdef fd_chose_dma_mode
1946 if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) {
1947 unsigned long flags = claim_dma_lock();
1948 fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length);
1949 release_dma_lock(flags);
1950 }
1951#endif
1952
1953 if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) {
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001954 perpendicular_mode(current_fdc);
Willy Tarreau3631a672020-03-31 11:40:49 +02001955 fdc_specify(current_fdc, current_drive); /* must be done here because of hut, hlt ... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 seek_floppy();
1957 } else {
1958 if ((raw_cmd->flags & FD_RAW_READ) ||
1959 (raw_cmd->flags & FD_RAW_WRITE))
Willy Tarreau3631a672020-03-31 11:40:49 +02001960 fdc_specify(current_fdc, current_drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 setup_rw_floppy();
1962 }
1963}
1964
1965static void floppy_start(void)
1966{
Willy Tarreau99ba6cc2020-04-10 12:19:03 +02001967 reschedule_timeout(current_drive, "floppy start");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968
1969 scandrives();
Willy Tarreau031faab2020-02-24 22:23:48 +01001970 debug_dcl(drive_params[current_drive].flags,
1971 "setting NEWCHANGE in floppy_start\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001972 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 floppy_ready();
1974}
1975
1976/*
1977 * ========================================================================
1978 * here ends the bottom half. Exported routines are:
1979 * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
1980 * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
1981 * Initialization also uses output_byte, result, set_dor, floppy_interrupt
1982 * and set_dor.
1983 * ========================================================================
1984 */
1985/*
1986 * General purpose continuations.
1987 * ==============================
1988 */
1989
1990static void do_wakeup(void)
1991{
Joe Perches73507e62010-03-10 15:21:03 -08001992 reschedule_timeout(MAXTIMEOUT, "do wakeup");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 cont = NULL;
1994 command_status += 2;
1995 wake_up(&command_done);
1996}
1997
Stephen Hemminger3b06c212010-07-20 20:09:00 -06001998static const struct cont_t wakeup_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 .interrupt = empty,
2000 .redo = do_wakeup,
2001 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07002002 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003};
2004
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002005static const struct cont_t intr_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 .interrupt = empty,
2007 .redo = process_fd_request,
2008 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07002009 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010};
2011
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002012/* schedules handler, waiting for completion. May be interrupted, will then
2013 * return -EINTR, in which case the driver will automatically be unlocked.
2014 */
Joe Perches74f63f42010-03-10 15:20:58 -08002015static int wait_til_done(void (*handler)(void), bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016{
2017 int ret;
2018
2019 schedule_bh(handler);
2020
Stephen Hemmingerb862f262010-06-15 13:21:11 +02002021 if (interruptible)
2022 wait_event_interruptible(command_done, command_status >= 2);
2023 else
2024 wait_event(command_done, command_status >= 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025
2026 if (command_status < 2) {
2027 cancel_activity();
2028 cont = &intr_cont;
2029 reset_fdc();
2030 return -EINTR;
2031 }
2032
Willy Tarreaue83995c2020-03-01 20:55:55 +01002033 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034 command_status = FD_COMMAND_ERROR;
2035 if (command_status == FD_COMMAND_OKAY)
2036 ret = 0;
2037 else
2038 ret = -EIO;
2039 command_status = FD_COMMAND_NONE;
2040 return ret;
2041}
2042
2043static void generic_done(int result)
2044{
2045 command_status = result;
2046 cont = &wakeup_cont;
2047}
2048
2049static void generic_success(void)
2050{
2051 cont->done(1);
2052}
2053
2054static void generic_failure(void)
2055{
2056 cont->done(0);
2057}
2058
2059static void success_and_wakeup(void)
2060{
2061 generic_success();
2062 cont->redo();
2063}
2064
2065/*
2066 * formatting and rw support.
2067 * ==========================
2068 */
2069
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002070static int next_valid_format(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071{
2072 int probed_format;
2073
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002074 probed_format = drive_state[drive].probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 while (1) {
Denis Efremov9c4c5a22020-05-01 16:44:14 +03002076 if (probed_format >= FD_AUTODETECT_SIZE ||
2077 !drive_params[drive].autodetect[probed_format]) {
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002078 drive_state[drive].probed_format = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 return 1;
2080 }
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002081 if (floppy_type[drive_params[drive].autodetect[probed_format]].sect) {
2082 drive_state[drive].probed_format = probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 return 0;
2084 }
2085 probed_format++;
2086 }
2087}
2088
2089static void bad_flp_intr(void)
2090{
2091 int err_count;
2092
2093 if (probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002094 drive_state[current_drive].probed_format++;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002095 if (!next_valid_format(current_drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096 return;
2097 }
2098 err_count = ++(*errors);
Willy Tarreau2a348752020-02-24 22:23:50 +01002099 INFBOUND(write_errors[current_drive].badness, err_count);
Willy Tarreau031faab2020-02-24 22:23:48 +01002100 if (err_count > drive_params[current_drive].max_errors.abort)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 cont->done(0);
Willy Tarreau031faab2020-02-24 22:23:48 +01002102 if (err_count > drive_params[current_drive].max_errors.reset)
Willy Tarreaue83995c2020-03-01 20:55:55 +01002103 fdc_state[current_fdc].reset = 1;
Willy Tarreau031faab2020-02-24 22:23:48 +01002104 else if (err_count > drive_params[current_drive].max_errors.recal)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002105 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106}
2107
2108static void set_floppy(int drive)
2109{
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01002110 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07002111
Linus Torvalds1da177e2005-04-16 15:20:36 -07002112 if (type)
2113 _floppy = floppy_type + type;
2114 else
2115 _floppy = current_type[drive];
2116}
2117
2118/*
2119 * formatting support.
2120 * ===================
2121 */
2122static void format_interrupt(void)
2123{
2124 switch (interpret_errors()) {
2125 case 1:
2126 cont->error();
Gustavo A. R. Silva2c9bdf62021-05-28 15:03:35 -05002127 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 case 2:
2129 break;
2130 case 0:
2131 cont->done(1);
2132 }
2133 cont->redo();
2134}
2135
Joe Perches48c8cee2010-03-10 15:20:45 -08002136#define FM_MODE(x, y) ((y) & ~(((x)->rate & 0x80) >> 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137#define CT(x) ((x) | 0xc0)
Joe Perches48c8cee2010-03-10 15:20:45 -08002138
Linus Torvalds1da177e2005-04-16 15:20:36 -07002139static void setup_format_params(int track)
2140{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002141 int n;
2142 int il;
2143 int count;
2144 int head_shift;
2145 int track_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 struct fparm {
2147 unsigned char track, head, sect, size;
2148 } *here = (struct fparm *)floppy_track_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149
2150 raw_cmd = &default_raw_cmd;
2151 raw_cmd->track = track;
2152
Joe Perches48c8cee2010-03-10 15:20:45 -08002153 raw_cmd->flags = (FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
2154 FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 raw_cmd->rate = _floppy->rate & 0x43;
2156 raw_cmd->cmd_count = NR_F;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002157 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_FORMAT);
2158 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, format_req.head);
2159 raw_cmd->cmd[F_SIZECODE] = FD_SIZECODE(_floppy);
2160 raw_cmd->cmd[F_SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[F_SIZECODE];
2161 raw_cmd->cmd[F_GAP] = _floppy->fmt_gap;
2162 raw_cmd->cmd[F_FILL] = FD_FILL_BYTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163
2164 raw_cmd->kernel_data = floppy_track_buffer;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002165 raw_cmd->length = 4 * raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166
Willy Tarreau76dabe72020-02-24 22:23:51 +01002167 if (!raw_cmd->cmd[F_SECT_PER_TRACK])
Denis Efremovf3554ae2019-07-12 21:55:20 +03002168 return;
2169
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 /* allow for about 30ms for data transport per track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002171 head_shift = (raw_cmd->cmd[F_SECT_PER_TRACK] + 5) / 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
2173 /* a ``cylinder'' is two tracks plus a little stepping time */
2174 track_shift = 2 * head_shift + 3;
2175
2176 /* position of logical sector 1 on this track */
2177 n = (track_shift * format_req.track + head_shift * format_req.head)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002178 % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179
2180 /* determine interleave */
2181 il = 1;
2182 if (_floppy->fmt_gap < 0x22)
2183 il++;
2184
2185 /* initialize field */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002186 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 here[count].track = format_req.track;
2188 here[count].head = format_req.head;
2189 here[count].sect = 0;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002190 here[count].size = raw_cmd->cmd[F_SIZECODE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 }
2192 /* place logical sectors */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002193 for (count = 1; count <= raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 here[n].sect = count;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002195 n = (n + il) % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196 if (here[n].sect) { /* sector busy, find next free sector */
2197 ++n;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002198 if (n >= raw_cmd->cmd[F_SECT_PER_TRACK]) {
2199 n -= raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200 while (here[n].sect)
2201 ++n;
2202 }
2203 }
2204 }
Keith Wansbrough9e491842008-09-22 14:57:17 -07002205 if (_floppy->stretch & FD_SECTBASEMASK) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002206 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; count++)
Keith Wansbrough9e491842008-09-22 14:57:17 -07002207 here[count].sect += FD_SECTBASE(_floppy) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208 }
2209}
2210
2211static void redo_format(void)
2212{
2213 buffer_track = -1;
2214 setup_format_params(format_req.track << STRETCH(_floppy));
2215 floppy_start();
Joe Perchesded28632010-03-10 15:21:09 -08002216 debugt(__func__, "queue format request");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217}
2218
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002219static const struct cont_t format_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220 .interrupt = format_interrupt,
2221 .redo = redo_format,
2222 .error = bad_flp_intr,
2223 .done = generic_done
2224};
2225
2226static int do_format(int drive, struct format_descr *tmp_format_req)
2227{
2228 int ret;
2229
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01002230 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08002231 return -EINTR;
2232
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233 set_floppy(drive);
2234 if (!_floppy ||
Willy Tarreau031faab2020-02-24 22:23:48 +01002235 _floppy->track > drive_params[current_drive].tracks ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236 tmp_format_req->track >= _floppy->track ||
2237 tmp_format_req->head >= _floppy->head ||
2238 (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
2239 !_floppy->fmt_gap) {
2240 process_fd_request();
2241 return -EINVAL;
2242 }
2243 format_req = *tmp_format_req;
2244 format_errors = 0;
2245 cont = &format_cont;
2246 errors = &format_errors;
Joe Perches74f63f42010-03-10 15:20:58 -08002247 ret = wait_til_done(redo_format, true);
Joe Perches55eee802010-03-10 15:20:57 -08002248 if (ret == -EINTR)
2249 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 process_fd_request();
2251 return ret;
2252}
2253
2254/*
2255 * Buffer read/write and support
2256 * =============================
2257 */
2258
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002259static void floppy_end_request(struct request *req, blk_status_t error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260{
2261 unsigned int nr_sectors = current_count_sectors;
Christoph Hellwigf3fa33a2021-11-26 13:18:00 +01002262 unsigned int drive = (unsigned long)req->q->disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002263
2264 /* current_count_sectors can be zero if transfer failed */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002265 if (error)
Tejun Heo83096eb2009-05-07 22:24:39 +09002266 nr_sectors = blk_rq_cur_sectors(req);
Omar Sandovala9f38e12018-10-15 09:21:34 -06002267 if (blk_update_request(req, error, nr_sectors << 9))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 return;
Omar Sandovala9f38e12018-10-15 09:21:34 -06002269 __blk_mq_end_request(req, error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270
2271 /* We're done with the request */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002272 floppy_off(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273 current_req = NULL;
2274}
2275
2276/* new request_done. Can handle physical sectors which are smaller than a
2277 * logical buffer */
2278static void request_done(int uptodate)
2279{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 struct request *req = current_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 int block;
Joe Perches73507e62010-03-10 15:21:03 -08002282 char msg[sizeof("request done ") + sizeof(int) * 3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
2284 probing = 0;
Joe Perches73507e62010-03-10 15:21:03 -08002285 snprintf(msg, sizeof(msg), "request done %d", uptodate);
2286 reschedule_timeout(MAXTIMEOUT, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
2288 if (!req) {
Joe Perchesb46df352010-03-10 15:20:46 -08002289 pr_info("floppy.c: no request in request_done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 return;
2291 }
2292
2293 if (uptodate) {
2294 /* maintain values for invalidation on geometry
2295 * change */
Tejun Heo83096eb2009-05-07 22:24:39 +09002296 block = current_count_sectors + blk_rq_pos(req);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002297 INFBOUND(drive_state[current_drive].maxblock, block);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 if (block > _floppy->sect)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002299 drive_state[current_drive].maxtrack = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002301 floppy_end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 } else {
2303 if (rq_data_dir(req) == WRITE) {
2304 /* record write error information */
Willy Tarreau2a348752020-02-24 22:23:50 +01002305 write_errors[current_drive].write_errors++;
2306 if (write_errors[current_drive].write_errors == 1) {
2307 write_errors[current_drive].first_error_sector = blk_rq_pos(req);
2308 write_errors[current_drive].first_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309 }
Willy Tarreau2a348752020-02-24 22:23:50 +01002310 write_errors[current_drive].last_error_sector = blk_rq_pos(req);
2311 write_errors[current_drive].last_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 }
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002313 floppy_end_request(req, BLK_STS_IOERR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002314 }
2315}
2316
2317/* Interrupt handler evaluating the result of the r/w operation */
2318static void rw_interrupt(void)
2319{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002320 int eoc;
2321 int ssize;
2322 int heads;
2323 int nr_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324
Willy Tarreau8fb38452020-02-24 22:23:52 +01002325 if (reply_buffer[R_HEAD] >= 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 /* some Toshiba floppy controllers occasionnally seem to
2327 * return bogus interrupts after read/write operations, which
2328 * can be recognized by a bad head number (>= 2) */
2329 return;
2330 }
2331
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002332 if (!drive_state[current_drive].first_read_date)
2333 drive_state[current_drive].first_read_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002334
Willy Tarreau76dabe72020-02-24 22:23:51 +01002335 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336
Willy Tarreau8fb38452020-02-24 22:23:52 +01002337 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 eoc = 1;
2339 else
2340 eoc = 0;
2341
Willy Tarreau76dabe72020-02-24 22:23:51 +01002342 if (raw_cmd->cmd[COMMAND] & 0x80)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 heads = 2;
2344 else
2345 heads = 1;
2346
Willy Tarreau8fb38452020-02-24 22:23:52 +01002347 nr_sectors = (((reply_buffer[R_TRACK] - raw_cmd->cmd[TRACK]) * heads +
2348 reply_buffer[R_HEAD] - raw_cmd->cmd[HEAD]) * raw_cmd->cmd[SECT_PER_TRACK] +
2349 reply_buffer[R_SECTOR] - raw_cmd->cmd[SECTOR] + eoc) << raw_cmd->cmd[SIZECODE] >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 if (nr_sectors / ssize >
Julia Lawall061837b2008-09-22 14:57:16 -07002352 DIV_ROUND_UP(in_sector_offset + current_count_sectors, ssize)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353 DPRINT("long rw: %x instead of %lx\n",
2354 nr_sectors, current_count_sectors);
Willy Tarreau8fb38452020-02-24 22:23:52 +01002355 pr_info("rs=%d s=%d\n", reply_buffer[R_SECTOR],
2356 raw_cmd->cmd[SECTOR]);
2357 pr_info("rh=%d h=%d\n", reply_buffer[R_HEAD],
2358 raw_cmd->cmd[HEAD]);
2359 pr_info("rt=%d t=%d\n", reply_buffer[R_TRACK],
2360 raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002361 pr_info("heads=%d eoc=%d\n", heads, eoc);
2362 pr_info("spt=%d st=%d ss=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002363 raw_cmd->cmd[SECT_PER_TRACK], fsector_t, ssize);
Joe Perchesb46df352010-03-10 15:20:46 -08002364 pr_info("in_sector_offset=%d\n", in_sector_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366
2367 nr_sectors -= in_sector_offset;
2368 INFBOUND(nr_sectors, 0);
2369 SUPBOUND(current_count_sectors, nr_sectors);
2370
2371 switch (interpret_errors()) {
2372 case 2:
2373 cont->redo();
2374 return;
2375 case 1:
2376 if (!current_count_sectors) {
2377 cont->error();
2378 cont->redo();
2379 return;
2380 }
2381 break;
2382 case 0:
2383 if (!current_count_sectors) {
2384 cont->redo();
2385 return;
2386 }
2387 current_type[current_drive] = _floppy;
2388 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2389 break;
2390 }
2391
2392 if (probing) {
Willy Tarreau031faab2020-02-24 22:23:48 +01002393 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 DPRINT("Auto-detected floppy type %s in fd%d\n",
2395 _floppy->name, current_drive);
2396 current_type[current_drive] = _floppy;
2397 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2398 probing = 0;
2399 }
2400
Christoph Hellwig3d867392021-04-06 08:17:55 +02002401 if (CT(raw_cmd->cmd[COMMAND]) != FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 /* transfer directly from buffer */
2403 cont->done(1);
Christoph Hellwig3d867392021-04-06 08:17:55 +02002404 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 buffer_track = raw_cmd->track;
2406 buffer_drive = current_drive;
2407 INFBOUND(buffer_max, nr_sectors + fsector_t);
2408 }
2409 cont->redo();
2410}
2411
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412/* Compute the maximal transfer size */
2413static int transfer_size(int ssize, int max_sector, int max_size)
2414{
2415 SUPBOUND(max_sector, fsector_t + max_size);
2416
2417 /* alignment */
2418 max_sector -= (max_sector % _floppy->sect) % ssize;
2419
2420 /* transfer size, beginning not aligned */
2421 current_count_sectors = max_sector - fsector_t;
2422
2423 return max_sector;
2424}
2425
2426/*
2427 * Move data from/to the track buffer to/from the buffer cache.
2428 */
2429static void copy_buffer(int ssize, int max_sector, int max_sector_2)
2430{
2431 int remaining; /* number of transferred 512-byte sectors */
Kent Overstreet79886132013-11-23 17:19:00 -08002432 struct bio_vec bv;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002433 char *dma_buffer;
NeilBrown5705f702007-09-25 12:35:59 +02002434 int size;
2435 struct req_iterator iter;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436
2437 max_sector = transfer_size(ssize,
2438 min(max_sector, max_sector_2),
Tejun Heo83096eb2009-05-07 22:24:39 +09002439 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440
Willy Tarreau76dabe72020-02-24 22:23:51 +01002441 if (current_count_sectors <= 0 && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Tejun Heo83096eb2009-05-07 22:24:39 +09002442 buffer_max > fsector_t + blk_rq_sectors(current_req))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 current_count_sectors = min_t(int, buffer_max - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002444 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445
2446 remaining = current_count_sectors << 9;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002447 if (remaining > blk_rq_bytes(current_req) && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 DPRINT("in copy buffer\n");
Joe Perchesb46df352010-03-10 15:20:46 -08002449 pr_info("current_count_sectors=%ld\n", current_count_sectors);
2450 pr_info("remaining=%d\n", remaining >> 9);
2451 pr_info("current_req->nr_sectors=%u\n",
2452 blk_rq_sectors(current_req));
2453 pr_info("current_req->current_nr_sectors=%u\n",
2454 blk_rq_cur_sectors(current_req));
2455 pr_info("max_sector=%d\n", max_sector);
2456 pr_info("ssize=%d\n", ssize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458
2459 buffer_max = max(max_sector, buffer_max);
2460
2461 dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9);
2462
Tejun Heo1011c1b2009-05-07 22:24:45 +09002463 size = blk_rq_cur_bytes(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464
NeilBrown5705f702007-09-25 12:35:59 +02002465 rq_for_each_segment(bv, current_req, iter) {
2466 if (!remaining)
2467 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468
Kent Overstreet79886132013-11-23 17:19:00 -08002469 size = bv.bv_len;
NeilBrown5705f702007-09-25 12:35:59 +02002470 SUPBOUND(size, remaining);
NeilBrown5705f702007-09-25 12:35:59 +02002471 if (dma_buffer + size >
2472 floppy_track_buffer + (max_buffer_sectors << 10) ||
2473 dma_buffer < floppy_track_buffer) {
2474 DPRINT("buffer overrun in copy buffer %d\n",
Joe Perchesb46df352010-03-10 15:20:46 -08002475 (int)((floppy_track_buffer - dma_buffer) >> 9));
2476 pr_info("fsector_t=%d buffer_min=%d\n",
2477 fsector_t, buffer_min);
2478 pr_info("current_count_sectors=%ld\n",
2479 current_count_sectors);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002480 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Joe Perchesb46df352010-03-10 15:20:46 -08002481 pr_info("read\n");
Willy Tarreau76dabe72020-02-24 22:23:51 +01002482 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
Joe Perchesb46df352010-03-10 15:20:46 -08002483 pr_info("write\n");
NeilBrown5705f702007-09-25 12:35:59 +02002484 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 }
Joe Perches1a23d132010-03-10 15:21:04 -08002486
Willy Tarreau76dabe72020-02-24 22:23:51 +01002487 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Christoph Hellwig3d867392021-04-06 08:17:55 +02002488 memcpy_to_page(bv.bv_page, bv.bv_offset, dma_buffer,
2489 size);
NeilBrown5705f702007-09-25 12:35:59 +02002490 else
Christoph Hellwig3d867392021-04-06 08:17:55 +02002491 memcpy_from_page(dma_buffer, bv.bv_page, bv.bv_offset,
2492 size);
NeilBrown5705f702007-09-25 12:35:59 +02002493
2494 remaining -= size;
2495 dma_buffer += size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 if (remaining) {
2498 if (remaining > 0)
2499 max_sector -= remaining >> 9;
2500 DPRINT("weirdness: remaining %d\n", remaining >> 9);
2501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502}
2503
Linus Torvalds1da177e2005-04-16 15:20:36 -07002504/* work around a bug in pseudo DMA
2505 * (on some FDCs) pseudo DMA does not stop when the CPU stops
2506 * sending data. Hence we need a different way to signal the
Willy Tarreau76dabe72020-02-24 22:23:51 +01002507 * transfer length: We use raw_cmd->cmd[SECT_PER_TRACK]. Unfortunately, this
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 * does not work with MT, hence we can only transfer one head at
2509 * a time
2510 */
2511static void virtualdmabug_workaround(void)
2512{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002513 int hard_sectors;
2514 int end_sector;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515
Willy Tarreau76dabe72020-02-24 22:23:51 +01002516 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
2517 raw_cmd->cmd[COMMAND] &= ~0x80; /* switch off multiple track mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518
Willy Tarreau76dabe72020-02-24 22:23:51 +01002519 hard_sectors = raw_cmd->length >> (7 + raw_cmd->cmd[SIZECODE]);
2520 end_sector = raw_cmd->cmd[SECTOR] + hard_sectors - 1;
2521 if (end_sector > raw_cmd->cmd[SECT_PER_TRACK]) {
Joe Perchesb46df352010-03-10 15:20:46 -08002522 pr_info("too many sectors %d > %d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002523 end_sector, raw_cmd->cmd[SECT_PER_TRACK]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524 return;
2525 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002526 raw_cmd->cmd[SECT_PER_TRACK] = end_sector;
2527 /* make sure raw_cmd->cmd[SECT_PER_TRACK]
Joe Perches48c8cee2010-03-10 15:20:45 -08002528 * points to end of transfer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 }
2530}
2531
2532/*
2533 * Formulate a read/write request.
2534 * this routine decides where to load the data (directly to buffer, or to
2535 * tmp floppy area), how much data to load (the size of the buffer, the whole
2536 * track, or a single sector)
2537 * All floppy_track_buffer handling goes in here. If we ever add track buffer
2538 * allocation on the fly, it should be done here. No other part should need
2539 * modification.
2540 */
2541
2542static int make_raw_rw_request(void)
2543{
2544 int aligned_sector_t;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002545 int max_sector;
2546 int max_size;
2547 int tracksize;
2548 int ssize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002550 if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552
Christoph Hellwigf3fa33a2021-11-26 13:18:00 +01002553 set_fdc((long)current_req->q->disk->private_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554
2555 raw_cmd = &default_raw_cmd;
Fengguang Wu2fb2ca62012-07-28 19:45:59 +08002556 raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557 raw_cmd->cmd_count = NR_RW;
2558 if (rq_data_dir(current_req) == READ) {
2559 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002560 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 } else if (rq_data_dir(current_req) == WRITE) {
2562 raw_cmd->flags |= FD_RAW_WRITE;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002563 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_WRITE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564 } else {
Joe Perches275176b2010-03-10 15:21:06 -08002565 DPRINT("%s: unknown command\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 return 0;
2567 }
2568
2569 max_sector = _floppy->sect * _floppy->head;
2570
Willy Tarreau76dabe72020-02-24 22:23:51 +01002571 raw_cmd->cmd[TRACK] = (int)blk_rq_pos(current_req) / max_sector;
Tejun Heo83096eb2009-05-07 22:24:39 +09002572 fsector_t = (int)blk_rq_pos(current_req) % max_sector;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002573 if (_floppy->track && raw_cmd->cmd[TRACK] >= _floppy->track) {
Tejun Heo83096eb2009-05-07 22:24:39 +09002574 if (blk_rq_cur_sectors(current_req) & 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575 current_count_sectors = 1;
2576 return 1;
2577 } else
2578 return 0;
2579 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002580 raw_cmd->cmd[HEAD] = fsector_t / _floppy->sect;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581
Keith Wansbrough9e491842008-09-22 14:57:17 -07002582 if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002583 test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags)) &&
Joe Perchese0298532010-03-10 15:20:55 -08002584 fsector_t < _floppy->sect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 max_sector = _floppy->sect;
2586
2587 /* 2M disks have phantom sectors on the first track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002588 if ((_floppy->rate & FD_2M) && (!raw_cmd->cmd[TRACK]) && (!raw_cmd->cmd[HEAD])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 max_sector = 2 * _floppy->sect / 3;
2590 if (fsector_t >= max_sector) {
2591 current_count_sectors =
2592 min_t(int, _floppy->sect - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002593 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594 return 1;
2595 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002596 raw_cmd->cmd[SIZECODE] = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597 } else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002598 raw_cmd->cmd[SIZECODE] = FD_SIZECODE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599 raw_cmd->rate = _floppy->rate & 0x43;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002600 if ((_floppy->rate & FD_2M) &&
2601 (raw_cmd->cmd[TRACK] || raw_cmd->cmd[HEAD]) && raw_cmd->rate == 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602 raw_cmd->rate = 1;
2603
Willy Tarreau76dabe72020-02-24 22:23:51 +01002604 if (raw_cmd->cmd[SIZECODE])
2605 raw_cmd->cmd[SIZECODE2] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002606 else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002607 raw_cmd->cmd[SIZECODE2] = 0x80;
2608 raw_cmd->track = raw_cmd->cmd[TRACK] << STRETCH(_floppy);
2609 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, raw_cmd->cmd[HEAD]);
2610 raw_cmd->cmd[GAP] = _floppy->gap;
2611 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
2612 raw_cmd->cmd[SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[SIZECODE];
2613 raw_cmd->cmd[SECTOR] = ((fsector_t % _floppy->sect) << 2 >> raw_cmd->cmd[SIZECODE]) +
Keith Wansbrough9e491842008-09-22 14:57:17 -07002614 FD_SECTBASE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615
2616 /* tracksize describes the size which can be filled up with sectors
2617 * of size ssize.
2618 */
2619 tracksize = _floppy->sect - _floppy->sect % ssize;
2620 if (tracksize < _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002621 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 if (tracksize <= fsector_t % _floppy->sect)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002623 raw_cmd->cmd[SECTOR]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624
2625 /* if we are beyond tracksize, fill up using smaller sectors */
2626 while (tracksize <= fsector_t % _floppy->sect) {
2627 while (tracksize + ssize > _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002628 raw_cmd->cmd[SIZECODE]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 ssize >>= 1;
2630 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002631 raw_cmd->cmd[SECTOR]++;
2632 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 tracksize += ssize;
2634 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002635 max_sector = raw_cmd->cmd[HEAD] * _floppy->sect + tracksize;
2636 } else if (!raw_cmd->cmd[TRACK] && !raw_cmd->cmd[HEAD] && !(_floppy->rate & FD_2M) && probing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 max_sector = _floppy->sect;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002638 } else if (!raw_cmd->cmd[HEAD] && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639 /* for virtual DMA bug workaround */
2640 max_sector = _floppy->sect;
2641 }
2642
2643 in_sector_offset = (fsector_t % _floppy->sect) % ssize;
2644 aligned_sector_t = fsector_t - in_sector_offset;
Tejun Heo83096eb2009-05-07 22:24:39 +09002645 max_size = blk_rq_sectors(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646 if ((raw_cmd->track == buffer_track) &&
2647 (current_drive == buffer_drive) &&
2648 (fsector_t >= buffer_min) && (fsector_t < buffer_max)) {
2649 /* data already in track buffer */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002650 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 copy_buffer(1, max_sector, buffer_max);
2652 return 1;
2653 }
Tejun Heo83096eb2009-05-07 22:24:39 +09002654 } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002655 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002656 unsigned int sectors;
2657
2658 sectors = fsector_t + blk_rq_sectors(current_req);
2659 if (sectors > ssize && sectors < ssize + ssize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660 max_size = ssize + ssize;
2661 else
2662 max_size = ssize;
2663 }
2664 raw_cmd->flags &= ~FD_RAW_WRITE;
2665 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002666 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 }
2668
Willy Tarreau76dabe72020-02-24 22:23:51 +01002669 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670 max_size = max_sector; /* unbounded */
2671
2672 /* claim buffer track if needed */
2673 if (buffer_track != raw_cmd->track || /* bad track */
2674 buffer_drive != current_drive || /* bad drive */
2675 fsector_t > buffer_max ||
2676 fsector_t < buffer_min ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002677 ((CT(raw_cmd->cmd[COMMAND]) == FD_READ ||
Tejun Heo83096eb2009-05-07 22:24:39 +09002678 (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 max_sector > 2 * max_buffer_sectors + buffer_min &&
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002680 max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)) {
2681 /* not enough space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 buffer_track = -1;
2683 buffer_drive = current_drive;
2684 buffer_max = buffer_min = aligned_sector_t;
2685 }
2686 raw_cmd->kernel_data = floppy_track_buffer +
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002687 ((aligned_sector_t - buffer_min) << 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688
Willy Tarreau76dabe72020-02-24 22:23:51 +01002689 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 /* copy write buffer to track buffer.
2691 * if we get here, we know that the write
2692 * is either aligned or the data already in the buffer
2693 * (buffer will be overwritten) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694 if (in_sector_offset && buffer_track == -1)
2695 DPRINT("internal error offset !=0 on write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 buffer_track = raw_cmd->track;
2697 buffer_drive = current_drive;
2698 copy_buffer(ssize, max_sector,
2699 2 * max_buffer_sectors + buffer_min);
2700 } else
2701 transfer_size(ssize, max_sector,
2702 2 * max_buffer_sectors + buffer_min -
2703 aligned_sector_t);
2704
2705 /* round up current_count_sectors to get dma xfer size */
2706 raw_cmd->length = in_sector_offset + current_count_sectors;
2707 raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1;
2708 raw_cmd->length <<= 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002709 if ((raw_cmd->length < current_count_sectors << 9) ||
Christoph Hellwig3d867392021-04-06 08:17:55 +02002710 (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711 (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
2712 aligned_sector_t < buffer_min)) ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002713 raw_cmd->length % (128 << raw_cmd->cmd[SIZECODE]) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002714 raw_cmd->length <= 0 || current_count_sectors <= 0) {
2715 DPRINT("fractionary current count b=%lx s=%lx\n",
2716 raw_cmd->length, current_count_sectors);
Christoph Hellwig3d867392021-04-06 08:17:55 +02002717 pr_info("addr=%d, length=%ld\n",
2718 (int)((raw_cmd->kernel_data -
2719 floppy_track_buffer) >> 9),
2720 current_count_sectors);
Joe Perchesb46df352010-03-10 15:20:46 -08002721 pr_info("st=%d ast=%d mse=%d msi=%d\n",
2722 fsector_t, aligned_sector_t, max_sector, max_size);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002723 pr_info("ssize=%x SIZECODE=%d\n", ssize, raw_cmd->cmd[SIZECODE]);
Joe Perchesb46df352010-03-10 15:20:46 -08002724 pr_info("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002725 raw_cmd->cmd[COMMAND], raw_cmd->cmd[SECTOR],
2726 raw_cmd->cmd[HEAD], raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002727 pr_info("buffer drive=%d\n", buffer_drive);
2728 pr_info("buffer track=%d\n", buffer_track);
2729 pr_info("buffer_min=%d\n", buffer_min);
2730 pr_info("buffer_max=%d\n", buffer_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731 return 0;
2732 }
2733
Christoph Hellwig3d867392021-04-06 08:17:55 +02002734 if (raw_cmd->kernel_data < floppy_track_buffer ||
2735 current_count_sectors < 0 ||
2736 raw_cmd->length < 0 ||
2737 raw_cmd->kernel_data + raw_cmd->length >
2738 floppy_track_buffer + (max_buffer_sectors << 10)) {
2739 DPRINT("buffer overrun in schedule dma\n");
2740 pr_info("fsector_t=%d buffer_min=%d current_count=%ld\n",
2741 fsector_t, buffer_min, raw_cmd->length >> 9);
2742 pr_info("current_count_sectors=%ld\n",
2743 current_count_sectors);
2744 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
2745 pr_info("read\n");
2746 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
2747 pr_info("write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 }
2750 if (raw_cmd->length == 0) {
2751 DPRINT("zero dma transfer attempted from make_raw_request\n");
2752 return 0;
2753 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754
2755 virtualdmabug_workaround();
2756 return 2;
2757}
2758
Jens Axboe48821182010-09-22 09:32:36 +02002759static int set_next_request(void)
2760{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002761 current_req = list_first_entry_or_null(&floppy_reqs, struct request,
2762 queuelist);
2763 if (current_req) {
2764 current_req->error_count = 0;
2765 list_del_init(&current_req->queuelist);
2766 }
Jens Axboe48821182010-09-22 09:32:36 +02002767 return current_req != NULL;
2768}
2769
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002770/* Starts or continues processing request. Will automatically unlock the
2771 * driver at end of request.
2772 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773static void redo_fd_request(void)
2774{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 int drive;
2776 int tmp;
2777
2778 lastredo = jiffies;
2779 if (current_drive < N_DRIVE)
2780 floppy_off(current_drive);
2781
Joe Perches0da31322010-03-10 15:21:03 -08002782do_request:
2783 if (!current_req) {
Jens Axboe48821182010-09-22 09:32:36 +02002784 int pending;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785
Jens Axboe48821182010-09-22 09:32:36 +02002786 spin_lock_irq(&floppy_lock);
2787 pending = set_next_request();
2788 spin_unlock_irq(&floppy_lock);
Jens Axboe48821182010-09-22 09:32:36 +02002789 if (!pending) {
Joe Perches0da31322010-03-10 15:21:03 -08002790 do_floppy = NULL;
2791 unlock_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794 }
Christoph Hellwigf3fa33a2021-11-26 13:18:00 +01002795 drive = (long)current_req->q->disk->private_data;
Joe Perches0da31322010-03-10 15:21:03 -08002796 set_fdc(drive);
Willy Tarreau99ba6cc2020-04-10 12:19:03 +02002797 reschedule_timeout(current_drive, "redo fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002798
2799 set_floppy(drive);
2800 raw_cmd = &default_raw_cmd;
2801 raw_cmd->flags = 0;
2802 if (start_motor(redo_fd_request))
2803 return;
2804
2805 disk_change(current_drive);
2806 if (test_bit(current_drive, &fake_change) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002807 test_bit(FD_DISK_CHANGED_BIT, &drive_state[current_drive].flags)) {
Joe Perches0da31322010-03-10 15:21:03 -08002808 DPRINT("disk absent or changed during operation\n");
2809 request_done(0);
2810 goto do_request;
2811 }
2812 if (!_floppy) { /* Autodetection */
2813 if (!probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002814 drive_state[current_drive].probed_format = 0;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002815 if (next_valid_format(current_drive)) {
Joe Perches0da31322010-03-10 15:21:03 -08002816 DPRINT("no autodetectable formats\n");
2817 _floppy = NULL;
2818 request_done(0);
2819 goto do_request;
2820 }
2821 }
2822 probing = 1;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002823 _floppy = floppy_type + drive_params[current_drive].autodetect[drive_state[current_drive].probed_format];
Joe Perches0da31322010-03-10 15:21:03 -08002824 } else
2825 probing = 0;
Christoph Hellwig45908792017-04-20 16:03:12 +02002826 errors = &(current_req->error_count);
Joe Perches0da31322010-03-10 15:21:03 -08002827 tmp = make_raw_rw_request();
2828 if (tmp < 2) {
2829 request_done(tmp);
2830 goto do_request;
2831 }
2832
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002833 if (test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags))
Willy Tarreauc1f710b2020-03-31 11:40:40 +02002834 twaddle(current_fdc, current_drive);
Joe Perches0da31322010-03-10 15:21:03 -08002835 schedule_bh(floppy_start);
Joe Perchesded28632010-03-10 15:21:09 -08002836 debugt(__func__, "queue fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002837 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838}
2839
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002840static const struct cont_t rw_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 .interrupt = rw_interrupt,
2842 .redo = redo_fd_request,
2843 .error = bad_flp_intr,
2844 .done = request_done
2845};
2846
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002847/* schedule the request and automatically unlock the driver on completion */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848static void process_fd_request(void)
2849{
2850 cont = &rw_cont;
2851 schedule_bh(redo_fd_request);
2852}
2853
Omar Sandovala9f38e12018-10-15 09:21:34 -06002854static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx,
2855 const struct blk_mq_queue_data *bd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002857 blk_mq_start_request(bd->rq);
2858
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002859 if (WARN(max_buffer_sectors == 0,
2860 "VFS: %s called on non-open device\n", __func__))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002861 return BLK_STS_IOERR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002863 if (WARN(atomic_read(&usage_count) == 0,
Christoph Hellwigaebf5262017-01-31 16:57:31 +01002864 "warning: usage count=0, current_req=%p sect=%ld flags=%llx\n",
2865 current_req, (long)blk_rq_pos(current_req),
Jens Axboe59533162013-05-23 12:25:08 +02002866 (unsigned long long) current_req->cmd_flags))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002867 return BLK_STS_IOERR;
2868
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002869 if (test_and_set_bit(0, &fdc_busy)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870 /* fdc busy, this new request will be treated when the
2871 current one is done */
Joe Perches275176b2010-03-10 15:21:06 -08002872 is_alive(__func__, "old request running");
Jiri Kosina263c6152020-05-26 11:49:18 +02002873 return BLK_STS_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 }
Omar Sandovala9f38e12018-10-15 09:21:34 -06002875
Jiri Kosina263c6152020-05-26 11:49:18 +02002876 spin_lock_irq(&floppy_lock);
2877 list_add_tail(&bd->rq->queuelist, &floppy_reqs);
2878 spin_unlock_irq(&floppy_lock);
2879
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002880 command_status = FD_COMMAND_NONE;
2881 __reschedule_timeout(MAXTIMEOUT, "fd_request");
2882 set_fdc(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 process_fd_request();
Joe Perches275176b2010-03-10 15:21:06 -08002884 is_alive(__func__, "");
Omar Sandovala9f38e12018-10-15 09:21:34 -06002885 return BLK_STS_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002886}
2887
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002888static const struct cont_t poll_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 .interrupt = success_and_wakeup,
2890 .redo = floppy_ready,
2891 .error = generic_failure,
2892 .done = generic_done
2893};
2894
Joe Perches74f63f42010-03-10 15:20:58 -08002895static int poll_drive(bool interruptible, int flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 /* no auto-sense, just clear dcl */
2898 raw_cmd = &default_raw_cmd;
2899 raw_cmd->flags = flag;
2900 raw_cmd->track = 0;
2901 raw_cmd->cmd_count = 0;
2902 cont = &poll_cont;
Willy Tarreau031faab2020-02-24 22:23:48 +01002903 debug_dcl(drive_params[current_drive].flags,
2904 "setting NEWCHANGE in poll_drive\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002905 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Joe Perches55eee802010-03-10 15:20:57 -08002906
2907 return wait_til_done(floppy_ready, interruptible);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908}
2909
2910/*
2911 * User triggered reset
2912 * ====================
2913 */
2914
2915static void reset_intr(void)
2916{
Joe Perchesb46df352010-03-10 15:20:46 -08002917 pr_info("weird, reset interrupt called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918}
2919
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002920static const struct cont_t reset_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 .interrupt = reset_intr,
2922 .redo = success_and_wakeup,
2923 .error = generic_failure,
2924 .done = generic_done
2925};
2926
Willy Tarreauca1b4092020-04-10 12:19:04 +02002927/*
2928 * Resets the FDC connected to drive <drive>.
2929 * Both current_drive and current_fdc are changed to match the new drive.
2930 */
Joe Perches74f63f42010-03-10 15:20:58 -08002931static int user_reset_fdc(int drive, int arg, bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932{
2933 int ret;
2934
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01002935 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08002936 return -EINTR;
2937
Linus Torvalds1da177e2005-04-16 15:20:36 -07002938 if (arg == FD_RESET_ALWAYS)
Willy Tarreaue83995c2020-03-01 20:55:55 +01002939 fdc_state[current_fdc].reset = 1;
2940 if (fdc_state[current_fdc].reset) {
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002941 /* note: reset_fdc will take care of unlocking the driver
2942 * on completion.
2943 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944 cont = &reset_cont;
Joe Perches55eee802010-03-10 15:20:57 -08002945 ret = wait_til_done(reset_fdc, interruptible);
2946 if (ret == -EINTR)
2947 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002948 }
2949 process_fd_request();
Joe Perches52a0d612010-03-10 15:20:53 -08002950 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951}
2952
2953/*
2954 * Misc Ioctl's and support
2955 * ========================
2956 */
2957static inline int fd_copyout(void __user *param, const void *address,
2958 unsigned long size)
2959{
2960 return copy_to_user(param, address, size) ? -EFAULT : 0;
2961}
2962
Joe Perches48c8cee2010-03-10 15:20:45 -08002963static inline int fd_copyin(void __user *param, void *address,
2964 unsigned long size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002965{
2966 return copy_from_user(address, param, size) ? -EFAULT : 0;
2967}
2968
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02002969static const char *drive_name(int type, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970{
2971 struct floppy_struct *floppy;
2972
2973 if (type)
2974 floppy = floppy_type + type;
2975 else {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01002976 if (drive_params[drive].native_format)
2977 floppy = floppy_type + drive_params[drive].native_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002978 else
2979 return "(null)";
2980 }
2981 if (floppy->name)
2982 return floppy->name;
2983 else
2984 return "(null)";
2985}
2986
2987/* raw commands */
2988static void raw_cmd_done(int flag)
2989{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002990 if (!flag) {
2991 raw_cmd->flags |= FD_RAW_FAILURE;
2992 raw_cmd->flags |= FD_RAW_HARDFAILURE;
2993 } else {
2994 raw_cmd->reply_count = inr;
Denis Efremovbd10a5f2020-05-01 16:44:15 +03002995 if (raw_cmd->reply_count > FD_RAW_REPLY_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 raw_cmd->reply_count = 0;
Denis Efremovfa6b8852021-04-16 11:34:48 +03002997 memcpy(raw_cmd->reply, reply_buffer, raw_cmd->reply_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002998
2999 if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
3000 unsigned long flags;
3001 flags = claim_dma_lock();
3002 raw_cmd->length = fd_get_dma_residue();
3003 release_dma_lock(flags);
3004 }
3005
3006 if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
3007 (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
3008 raw_cmd->flags |= FD_RAW_FAILURE;
3009
3010 if (disk_change(current_drive))
3011 raw_cmd->flags |= FD_RAW_DISK_CHANGE;
3012 else
3013 raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
3014 if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
Kees Cookb1bf4212017-10-04 17:49:29 -07003015 motor_off_callback(&motor_off_timer[current_drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003016
3017 if (raw_cmd->next &&
3018 (!(raw_cmd->flags & FD_RAW_FAILURE) ||
3019 !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
3020 ((raw_cmd->flags & FD_RAW_FAILURE) ||
3021 !(raw_cmd->flags & FD_RAW_STOP_IF_SUCCESS))) {
3022 raw_cmd = raw_cmd->next;
3023 return;
3024 }
3025 }
3026 generic_done(flag);
3027}
3028
Stephen Hemminger3b06c212010-07-20 20:09:00 -06003029static const struct cont_t raw_cmd_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003030 .interrupt = success_and_wakeup,
3031 .redo = floppy_start,
3032 .error = generic_failure,
3033 .done = raw_cmd_done
3034};
3035
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003036static int raw_cmd_copyout(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037 struct floppy_raw_cmd *ptr)
3038{
3039 int ret;
3040
3041 while (ptr) {
Matthew Daley2145e152014-04-28 19:05:21 +12003042 struct floppy_raw_cmd cmd = *ptr;
3043 cmd.next = NULL;
3044 cmd.kernel_data = NULL;
3045 ret = copy_to_user(param, &cmd, sizeof(cmd));
Joe Perches86b12b42010-03-10 15:20:56 -08003046 if (ret)
3047 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003048 param += sizeof(struct floppy_raw_cmd);
3049 if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003050 if (ptr->length >= 0 &&
3051 ptr->length <= ptr->buffer_length) {
3052 long length = ptr->buffer_length - ptr->length;
Joe Perches4575b552010-03-10 15:20:55 -08003053 ret = fd_copyout(ptr->data, ptr->kernel_data,
3054 length);
3055 if (ret)
3056 return ret;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003057 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058 }
3059 ptr = ptr->next;
3060 }
Joe Perches7f252712010-03-10 15:21:08 -08003061
Linus Torvalds1da177e2005-04-16 15:20:36 -07003062 return 0;
3063}
3064
3065static void raw_cmd_free(struct floppy_raw_cmd **ptr)
3066{
Jesper Juhl06f748c2007-10-16 23:30:57 -07003067 struct floppy_raw_cmd *next;
3068 struct floppy_raw_cmd *this;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003069
3070 this = *ptr;
3071 *ptr = NULL;
3072 while (this) {
3073 if (this->buffer_length) {
3074 fd_dma_mem_free((unsigned long)this->kernel_data,
3075 this->buffer_length);
3076 this->buffer_length = 0;
3077 }
3078 next = this->next;
3079 kfree(this);
3080 this = next;
3081 }
3082}
3083
Xiongwei Song545a3242021-11-16 21:10:33 +08003084#define MAX_LEN (1UL << MAX_ORDER << PAGE_SHIFT)
3085
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003086static int raw_cmd_copyin(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003087 struct floppy_raw_cmd **rcmd)
3088{
3089 struct floppy_raw_cmd *ptr;
3090 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091
3092 *rcmd = NULL;
Joe Perches7f252712010-03-10 15:21:08 -08003093
3094loop:
Vlastimil Babka1661f2e2017-01-04 11:19:31 +01003095 ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL);
Joe Perches7f252712010-03-10 15:21:08 -08003096 if (!ptr)
3097 return -ENOMEM;
3098 *rcmd = ptr;
3099 ret = copy_from_user(ptr, param, sizeof(*ptr));
Joe Perches7f252712010-03-10 15:21:08 -08003100 ptr->next = NULL;
3101 ptr->buffer_length = 0;
Matthew Daleyef87dbe2014-04-28 19:05:20 +12003102 ptr->kernel_data = NULL;
3103 if (ret)
3104 return -EFAULT;
Joe Perches7f252712010-03-10 15:21:08 -08003105 param += sizeof(struct floppy_raw_cmd);
Denis Efremovbd10a5f2020-05-01 16:44:15 +03003106 if (ptr->cmd_count > FD_RAW_CMD_FULLSIZE)
Joe Perches7f252712010-03-10 15:21:08 -08003107 return -EINVAL;
3108
Denis Efremovf6df18f2021-04-16 11:34:47 +03003109 memset(ptr->reply, 0, FD_RAW_REPLY_SIZE);
Joe Perches7f252712010-03-10 15:21:08 -08003110 ptr->resultcode = 0;
Joe Perches7f252712010-03-10 15:21:08 -08003111
3112 if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
Xiongwei Song545a3242021-11-16 21:10:33 +08003113 if (ptr->length <= 0 || ptr->length >= MAX_LEN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003114 return -EINVAL;
Joe Perches7f252712010-03-10 15:21:08 -08003115 ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length);
3116 fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length);
3117 if (!ptr->kernel_data)
3118 return -ENOMEM;
3119 ptr->buffer_length = ptr->length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003120 }
Joe Perches7f252712010-03-10 15:21:08 -08003121 if (ptr->flags & FD_RAW_WRITE) {
3122 ret = fd_copyin(ptr->data, ptr->kernel_data, ptr->length);
3123 if (ret)
3124 return ret;
3125 }
3126
3127 if (ptr->flags & FD_RAW_MORE) {
3128 rcmd = &(ptr->next);
3129 ptr->rate &= 0x43;
3130 goto loop;
3131 }
3132
3133 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134}
3135
3136static int raw_cmd_ioctl(int cmd, void __user *param)
3137{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003138 struct floppy_raw_cmd *my_raw_cmd;
Jesper Juhl06f748c2007-10-16 23:30:57 -07003139 int drive;
3140 int ret2;
3141 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142
Willy Tarreaue83995c2020-03-01 20:55:55 +01003143 if (fdc_state[current_fdc].rawcmd <= 1)
3144 fdc_state[current_fdc].rawcmd = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003146 if (FDC(drive) != current_fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147 continue;
3148 if (drive == current_drive) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003149 if (drive_state[drive].fd_ref > 1) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003150 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003151 break;
3152 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003153 } else if (drive_state[drive].fd_ref) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003154 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 break;
3156 }
3157 }
3158
Willy Tarreaue83995c2020-03-01 20:55:55 +01003159 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 return -EIO;
3161
3162 ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
3163 if (ret) {
3164 raw_cmd_free(&my_raw_cmd);
3165 return ret;
3166 }
3167
3168 raw_cmd = my_raw_cmd;
3169 cont = &raw_cmd_cont;
Joe Perches74f63f42010-03-10 15:20:58 -08003170 ret = wait_til_done(floppy_start, true);
Willy Tarreau031faab2020-02-24 22:23:48 +01003171 debug_dcl(drive_params[current_drive].flags,
3172 "calling disk change from raw_cmd ioctl\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173
Willy Tarreaue83995c2020-03-01 20:55:55 +01003174 if (ret != -EINTR && fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175 ret = -EIO;
3176
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003177 drive_state[current_drive].track = NO_TRACK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178
3179 ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
3180 if (!ret)
3181 ret = ret2;
3182 raw_cmd_free(&my_raw_cmd);
3183 return ret;
3184}
3185
3186static int invalidate_drive(struct block_device *bdev)
3187{
3188 /* invalidate the buffer track to force a reread */
3189 set_bit((long)bdev->bd_disk->private_data, &fake_change);
3190 process_fd_request();
Christoph Hellwig4a6f3d42020-09-08 16:53:32 +02003191 if (bdev_check_media_change(bdev))
3192 floppy_revalidate(bdev->bd_disk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003193 return 0;
3194}
3195
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003196static int set_geometry(unsigned int cmd, struct floppy_struct *g,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197 int drive, int type, struct block_device *bdev)
3198{
3199 int cnt;
3200
3201 /* sanity checking for parameters. */
Denis Efremovda994662019-07-12 21:55:23 +03003202 if ((int)g->sect <= 0 ||
3203 (int)g->head <= 0 ||
3204 /* check for overflow in max_sector */
3205 (int)(g->sect * g->head) <= 0 ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01003206 /* check for zero in raw_cmd->cmd[F_SECT_PER_TRACK] */
Denis Efremovf3554ae2019-07-12 21:55:20 +03003207 (unsigned char)((g->sect << 2) >> FD_SIZECODE(g)) == 0 ||
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003208 g->track <= 0 || g->track > drive_params[drive].tracks >> STRETCH(g) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 /* check if reserved bits are set */
Keith Wansbrough9e491842008-09-22 14:57:17 -07003210 (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211 return -EINVAL;
3212 if (type) {
3213 if (!capable(CAP_SYS_ADMIN))
3214 return -EPERM;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003215 mutex_lock(&open_lock);
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003216 if (lock_fdc(drive)) {
Jiri Slaby8516a502009-06-30 11:41:44 -07003217 mutex_unlock(&open_lock);
3218 return -EINTR;
3219 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003220 floppy_type[type] = *g;
3221 floppy_type[type].name = "user format";
3222 for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
3223 floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] =
3224 floppy_type[type].size + 1;
3225 process_fd_request();
3226 for (cnt = 0; cnt < N_DRIVE; cnt++) {
3227 struct block_device *bdev = opened_bdev[cnt];
3228 if (!bdev || ITYPE(drive_state[cnt].fd_device) != type)
3229 continue;
NeilBrown93b270f2011-02-24 17:25:47 +11003230 __invalidate_device(bdev, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003231 }
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003232 mutex_unlock(&open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003233 } else {
3234 int oldStretch;
Joe Perches52a0d612010-03-10 15:20:53 -08003235
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003236 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003237 return -EINTR;
Joe Perches4575b552010-03-10 15:20:55 -08003238 if (cmd != FDDEFPRM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 /* notice a disk change immediately, else
3240 * we lose our settings immediately*/
Joe Perches74f63f42010-03-10 15:20:58 -08003241 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003242 return -EINTR;
3243 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003244 oldStretch = g->stretch;
3245 user_params[drive] = *g;
3246 if (buffer_drive == drive)
3247 SUPBOUND(buffer_max, user_params[drive].sect);
3248 current_type[drive] = &user_params[drive];
3249 floppy_sizes[drive] = user_params[drive].size;
3250 if (cmd == FDDEFPRM)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003251 drive_state[current_drive].keep_data = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003253 drive_state[current_drive].keep_data = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003254 /* invalidation. Invalidate only when needed, i.e.
3255 * when there are already sectors in the buffer cache
3256 * whose number will change. This is useful, because
3257 * mtools often changes the geometry of the disk after
3258 * looking at the boot block */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003259 if (drive_state[current_drive].maxblock > user_params[drive].sect ||
3260 drive_state[current_drive].maxtrack ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003261 ((user_params[drive].sect ^ oldStretch) &
Keith Wansbrough9e491842008-09-22 14:57:17 -07003262 (FD_SWAPSIDES | FD_SECTBASEMASK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003263 invalidate_drive(bdev);
3264 else
3265 process_fd_request();
3266 }
3267 return 0;
3268}
3269
3270/* handle obsolete ioctl's */
Stephen Hemminger21af5442010-06-15 13:21:11 +02003271static unsigned int ioctl_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003272 FDCLRPRM,
3273 FDSETPRM,
3274 FDDEFPRM,
3275 FDGETPRM,
3276 FDMSGON,
3277 FDMSGOFF,
3278 FDFMTBEG,
3279 FDFMTTRK,
3280 FDFMTEND,
3281 FDSETEMSGTRESH,
3282 FDFLUSH,
3283 FDSETMAXERRS,
3284 FDGETMAXERRS,
3285 FDGETDRVTYP,
3286 FDSETDRVPRM,
3287 FDGETDRVPRM,
3288 FDGETDRVSTAT,
3289 FDPOLLDRVSTAT,
3290 FDRESET,
3291 FDGETFDCSTAT,
3292 FDWERRORCLR,
3293 FDWERRORGET,
3294 FDRAWCMD,
3295 FDEJECT,
3296 FDTWADDLE
3297};
3298
Stephen Hemminger21af5442010-06-15 13:21:11 +02003299static int normalize_ioctl(unsigned int *cmd, int *size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003300{
3301 int i;
3302
3303 for (i = 0; i < ARRAY_SIZE(ioctl_table); i++) {
3304 if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)) {
3305 *size = _IOC_SIZE(*cmd);
3306 *cmd = ioctl_table[i];
3307 if (*size > _IOC_SIZE(*cmd)) {
Joe Perchesb46df352010-03-10 15:20:46 -08003308 pr_info("ioctl not yet supported\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 return -EFAULT;
3310 }
3311 return 0;
3312 }
3313 }
3314 return -EINVAL;
3315}
3316
3317static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
3318{
3319 if (type)
3320 *g = &floppy_type[type];
3321 else {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003322 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003323 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003324 if (poll_drive(false, 0) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003325 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003326 process_fd_request();
3327 *g = current_type[drive];
3328 }
3329 if (!*g)
3330 return -ENODEV;
3331 return 0;
3332}
3333
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08003334static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
3335{
3336 int drive = (long)bdev->bd_disk->private_data;
3337 int type = ITYPE(drive_state[drive].fd_device);
3338 struct floppy_struct *g;
3339 int ret;
3340
3341 ret = get_floppy_geometry(drive, type, &g);
3342 if (ret)
3343 return ret;
3344
3345 geo->heads = g->head;
3346 geo->sectors = g->sect;
3347 geo->cylinders = g->track;
3348 return 0;
3349}
3350
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003351static bool valid_floppy_drive_params(const short autodetect[FD_AUTODETECT_SIZE],
Denis Efremov9b046092019-07-12 21:55:22 +03003352 int native_format)
Denis Efremov5635f892019-07-12 21:55:21 +03003353{
3354 size_t floppy_type_size = ARRAY_SIZE(floppy_type);
3355 size_t i = 0;
3356
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003357 for (i = 0; i < FD_AUTODETECT_SIZE; ++i) {
Denis Efremov5635f892019-07-12 21:55:21 +03003358 if (autodetect[i] < 0 ||
3359 autodetect[i] >= floppy_type_size)
3360 return false;
3361 }
3362
Denis Efremov9b046092019-07-12 21:55:22 +03003363 if (native_format < 0 || native_format >= floppy_type_size)
3364 return false;
3365
Denis Efremov5635f892019-07-12 21:55:21 +03003366 return true;
3367}
3368
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003369static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003370 unsigned long param)
3371{
Al Viroa4af9b42008-03-02 09:27:55 -05003372 int drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003373 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07003374 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003375 int ret;
3376 int size;
3377 union inparam {
3378 struct floppy_struct g; /* geometry */
3379 struct format_descr f;
3380 struct floppy_max_errors max_errors;
3381 struct floppy_drive_params dp;
3382 } inparam; /* parameters coming from user space */
Joe Perches724ee622010-03-10 15:21:11 -08003383 const void *outparam; /* parameters passed back to user space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003384
3385 /* convert compatibility eject ioctls into floppy eject ioctl.
3386 * We do this in order to provide a means to eject floppy disks before
3387 * installing the new fdutils package */
3388 if (cmd == CDROMEJECT || /* CD-ROM eject */
Joe Perchesa81ee542010-03-10 15:20:46 -08003389 cmd == 0x6470) { /* SunOS floppy eject */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003390 DPRINT("obsolete eject ioctl\n");
3391 DPRINT("please use floppycontrol --eject\n");
3392 cmd = FDEJECT;
3393 }
3394
Joe Perchesa81ee542010-03-10 15:20:46 -08003395 if (!((cmd & 0xff00) == 0x0200))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003396 return -EINVAL;
3397
Joe Perchesa81ee542010-03-10 15:20:46 -08003398 /* convert the old style command into a new style command */
Joe Perches4575b552010-03-10 15:20:55 -08003399 ret = normalize_ioctl(&cmd, &size);
3400 if (ret)
3401 return ret;
Joe Perchesa81ee542010-03-10 15:20:46 -08003402
Linus Torvalds1da177e2005-04-16 15:20:36 -07003403 /* permission checks */
Joe Perches0aad92c2010-03-10 15:21:10 -08003404 if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003405 ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
3406 return -EPERM;
3407
Arjan van de Ven2886a8b2009-12-14 18:00:11 -08003408 if (WARN_ON(size < 0 || size > sizeof(inparam)))
3409 return -EINVAL;
3410
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 /* copyin */
Joe Perchesb87c9e02010-03-10 15:20:50 -08003412 memset(&inparam, 0, sizeof(inparam));
Joe Perches4575b552010-03-10 15:20:55 -08003413 if (_IOC_DIR(cmd) & _IOC_WRITE) {
3414 ret = fd_copyin((void __user *)param, &inparam, size);
3415 if (ret)
3416 return ret;
3417 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418
Joe Perchesda273652010-03-10 15:20:52 -08003419 switch (cmd) {
3420 case FDEJECT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003421 if (drive_state[drive].fd_ref != 1)
Joe Perchesda273652010-03-10 15:20:52 -08003422 /* somebody else has this drive open */
3423 return -EBUSY;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003424 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003425 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003426
Joe Perchesda273652010-03-10 15:20:52 -08003427 /* do the actual eject. Fails on
3428 * non-Sparc architectures */
3429 ret = fd_eject(UNIT(drive));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003430
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003431 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
3432 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchesda273652010-03-10 15:20:52 -08003433 process_fd_request();
3434 return ret;
3435 case FDCLRPRM:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003436 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003437 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003438 current_type[drive] = NULL;
3439 floppy_sizes[drive] = MAX_DISK_SIZE << 1;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003440 drive_state[drive].keep_data = 0;
Joe Perchesda273652010-03-10 15:20:52 -08003441 return invalidate_drive(bdev);
3442 case FDSETPRM:
3443 case FDDEFPRM:
3444 return set_geometry(cmd, &inparam.g, drive, type, bdev);
3445 case FDGETPRM:
Joe Perches4575b552010-03-10 15:20:55 -08003446 ret = get_floppy_geometry(drive, type,
Joe Perches724ee622010-03-10 15:21:11 -08003447 (struct floppy_struct **)&outparam);
Joe Perches4575b552010-03-10 15:20:55 -08003448 if (ret)
3449 return ret;
Andy Whitcroft65eea8e2018-09-20 09:09:48 -06003450 memcpy(&inparam.g, outparam,
3451 offsetof(struct floppy_struct, name));
3452 outparam = &inparam.g;
Joe Perchesda273652010-03-10 15:20:52 -08003453 break;
3454 case FDMSGON:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003455 drive_params[drive].flags |= FTD_MSG;
Joe Perchesda273652010-03-10 15:20:52 -08003456 return 0;
3457 case FDMSGOFF:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003458 drive_params[drive].flags &= ~FTD_MSG;
Joe Perchesda273652010-03-10 15:20:52 -08003459 return 0;
3460 case FDFMTBEG:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003461 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003462 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003463 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003464 return -EINTR;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003465 ret = drive_state[drive].flags;
Joe Perchesda273652010-03-10 15:20:52 -08003466 process_fd_request();
3467 if (ret & FD_VERIFY)
3468 return -ENODEV;
3469 if (!(ret & FD_DISK_WRITABLE))
3470 return -EROFS;
3471 return 0;
3472 case FDFMTTRK:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003473 if (drive_state[drive].fd_ref != 1)
Joe Perchesda273652010-03-10 15:20:52 -08003474 return -EBUSY;
3475 return do_format(drive, &inparam.f);
3476 case FDFMTEND:
3477 case FDFLUSH:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003478 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003479 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003480 return invalidate_drive(bdev);
3481 case FDSETEMSGTRESH:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003482 drive_params[drive].max_errors.reporting = (unsigned short)(param & 0x0f);
Joe Perchesda273652010-03-10 15:20:52 -08003483 return 0;
3484 case FDGETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003485 outparam = &drive_params[drive].max_errors;
Joe Perchesda273652010-03-10 15:20:52 -08003486 break;
3487 case FDSETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003488 drive_params[drive].max_errors = inparam.max_errors;
Joe Perchesda273652010-03-10 15:20:52 -08003489 break;
3490 case FDGETDRVTYP:
3491 outparam = drive_name(type, drive);
Joe Perches724ee622010-03-10 15:21:11 -08003492 SUPBOUND(size, strlen((const char *)outparam) + 1);
Joe Perchesda273652010-03-10 15:20:52 -08003493 break;
3494 case FDSETDRVPRM:
Denis Efremov9b046092019-07-12 21:55:22 +03003495 if (!valid_floppy_drive_params(inparam.dp.autodetect,
3496 inparam.dp.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003497 return -EINVAL;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003498 drive_params[drive] = inparam.dp;
Joe Perchesda273652010-03-10 15:20:52 -08003499 break;
3500 case FDGETDRVPRM:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003501 outparam = &drive_params[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003502 break;
3503 case FDPOLLDRVSTAT:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003504 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003505 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003506 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003507 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003508 process_fd_request();
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05003509 fallthrough;
Joe Perchesda273652010-03-10 15:20:52 -08003510 case FDGETDRVSTAT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003511 outparam = &drive_state[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003512 break;
3513 case FDRESET:
Joe Perches74f63f42010-03-10 15:20:58 -08003514 return user_reset_fdc(drive, (int)param, true);
Joe Perchesda273652010-03-10 15:20:52 -08003515 case FDGETFDCSTAT:
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003516 outparam = &fdc_state[FDC(drive)];
Joe Perchesda273652010-03-10 15:20:52 -08003517 break;
3518 case FDWERRORCLR:
Willy Tarreau121e2972020-02-24 22:23:47 +01003519 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Joe Perchesda273652010-03-10 15:20:52 -08003520 return 0;
3521 case FDWERRORGET:
Willy Tarreau121e2972020-02-24 22:23:47 +01003522 outparam = &write_errors[drive];
Joe Perchesda273652010-03-10 15:20:52 -08003523 break;
3524 case FDRAWCMD:
3525 if (type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526 return -EINVAL;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003527 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003528 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003529 set_floppy(drive);
Joe Perches4575b552010-03-10 15:20:55 -08003530 i = raw_cmd_ioctl(cmd, (void __user *)param);
3531 if (i == -EINTR)
3532 return -EINTR;
Joe Perchesda273652010-03-10 15:20:52 -08003533 process_fd_request();
3534 return i;
3535 case FDTWADDLE:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003536 if (lock_fdc(drive))
Joe Perches52a0d612010-03-10 15:20:53 -08003537 return -EINTR;
Willy Tarreauc1f710b2020-03-31 11:40:40 +02003538 twaddle(current_fdc, current_drive);
Joe Perchesda273652010-03-10 15:20:52 -08003539 process_fd_request();
3540 return 0;
3541 default:
3542 return -EINVAL;
3543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003544
3545 if (_IOC_DIR(cmd) & _IOC_READ)
3546 return fd_copyout((void __user *)param, outparam, size);
Joe Perchesda273652010-03-10 15:20:52 -08003547
3548 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003549}
3550
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003551static int fd_ioctl(struct block_device *bdev, fmode_t mode,
3552 unsigned int cmd, unsigned long param)
3553{
3554 int ret;
3555
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003556 mutex_lock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003557 ret = fd_locked_ioctl(bdev, mode, cmd, param);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003558 mutex_unlock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003559
3560 return ret;
3561}
3562
Al Viro229b53c2017-06-27 15:47:56 -04003563#ifdef CONFIG_COMPAT
3564
3565struct compat_floppy_drive_params {
3566 char cmos;
3567 compat_ulong_t max_dtr;
3568 compat_ulong_t hlt;
3569 compat_ulong_t hut;
3570 compat_ulong_t srt;
3571 compat_ulong_t spinup;
3572 compat_ulong_t spindown;
3573 unsigned char spindown_offset;
3574 unsigned char select_delay;
3575 unsigned char rps;
3576 unsigned char tracks;
3577 compat_ulong_t timeout;
3578 unsigned char interleave_sect;
3579 struct floppy_max_errors max_errors;
3580 char flags;
3581 char read_track;
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003582 short autodetect[FD_AUTODETECT_SIZE];
Al Viro229b53c2017-06-27 15:47:56 -04003583 compat_int_t checkfreq;
3584 compat_int_t native_format;
3585};
3586
3587struct compat_floppy_drive_struct {
3588 signed char flags;
3589 compat_ulong_t spinup_date;
3590 compat_ulong_t select_date;
3591 compat_ulong_t first_read_date;
3592 short probed_format;
3593 short track;
3594 short maxblock;
3595 short maxtrack;
3596 compat_int_t generation;
3597 compat_int_t keep_data;
3598 compat_int_t fd_ref;
3599 compat_int_t fd_device;
3600 compat_int_t last_checked;
3601 compat_caddr_t dmabuf;
3602 compat_int_t bufblocks;
3603};
3604
3605struct compat_floppy_fdc_state {
3606 compat_int_t spec1;
3607 compat_int_t spec2;
3608 compat_int_t dtr;
3609 unsigned char version;
3610 unsigned char dor;
3611 compat_ulong_t address;
3612 unsigned int rawcmd:2;
3613 unsigned int reset:1;
3614 unsigned int need_configure:1;
3615 unsigned int perp_mode:2;
3616 unsigned int has_fifo:1;
3617 unsigned int driver_version;
3618 unsigned char track[4];
3619};
3620
3621struct compat_floppy_write_errors {
3622 unsigned int write_errors;
3623 compat_ulong_t first_error_sector;
3624 compat_int_t first_error_generation;
3625 compat_ulong_t last_error_sector;
3626 compat_int_t last_error_generation;
3627 compat_uint_t badness;
3628};
3629
3630#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct)
3631#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct)
3632#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params)
3633#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params)
3634#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct)
3635#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct)
3636#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
3637#define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors)
3638
3639static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3640 struct compat_floppy_struct __user *arg)
3641{
3642 struct floppy_struct v;
3643 int drive, type;
3644 int err;
3645
3646 BUILD_BUG_ON(offsetof(struct floppy_struct, name) !=
3647 offsetof(struct compat_floppy_struct, name));
3648
3649 if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL)))
3650 return -EPERM;
3651
3652 memset(&v, 0, sizeof(struct floppy_struct));
3653 if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name)))
3654 return -EFAULT;
3655
3656 mutex_lock(&floppy_mutex);
3657 drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003658 type = ITYPE(drive_state[drive].fd_device);
Al Viro229b53c2017-06-27 15:47:56 -04003659 err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM,
3660 &v, drive, type, bdev);
3661 mutex_unlock(&floppy_mutex);
3662 return err;
3663}
3664
3665static int compat_get_prm(int drive,
3666 struct compat_floppy_struct __user *arg)
3667{
3668 struct compat_floppy_struct v;
3669 struct floppy_struct *p;
3670 int err;
3671
3672 memset(&v, 0, sizeof(v));
3673 mutex_lock(&floppy_mutex);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003674 err = get_floppy_geometry(drive, ITYPE(drive_state[drive].fd_device),
3675 &p);
Al Viro229b53c2017-06-27 15:47:56 -04003676 if (err) {
3677 mutex_unlock(&floppy_mutex);
3678 return err;
3679 }
3680 memcpy(&v, p, offsetof(struct floppy_struct, name));
3681 mutex_unlock(&floppy_mutex);
3682 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct)))
3683 return -EFAULT;
3684 return 0;
3685}
3686
3687static int compat_setdrvprm(int drive,
3688 struct compat_floppy_drive_params __user *arg)
3689{
3690 struct compat_floppy_drive_params v;
3691
3692 if (!capable(CAP_SYS_ADMIN))
3693 return -EPERM;
3694 if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params)))
3695 return -EFAULT;
Denis Efremov9b046092019-07-12 21:55:22 +03003696 if (!valid_floppy_drive_params(v.autodetect, v.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003697 return -EINVAL;
Al Viro229b53c2017-06-27 15:47:56 -04003698 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003699 drive_params[drive].cmos = v.cmos;
3700 drive_params[drive].max_dtr = v.max_dtr;
3701 drive_params[drive].hlt = v.hlt;
3702 drive_params[drive].hut = v.hut;
3703 drive_params[drive].srt = v.srt;
3704 drive_params[drive].spinup = v.spinup;
3705 drive_params[drive].spindown = v.spindown;
3706 drive_params[drive].spindown_offset = v.spindown_offset;
3707 drive_params[drive].select_delay = v.select_delay;
3708 drive_params[drive].rps = v.rps;
3709 drive_params[drive].tracks = v.tracks;
3710 drive_params[drive].timeout = v.timeout;
3711 drive_params[drive].interleave_sect = v.interleave_sect;
3712 drive_params[drive].max_errors = v.max_errors;
3713 drive_params[drive].flags = v.flags;
3714 drive_params[drive].read_track = v.read_track;
3715 memcpy(drive_params[drive].autodetect, v.autodetect,
3716 sizeof(v.autodetect));
3717 drive_params[drive].checkfreq = v.checkfreq;
3718 drive_params[drive].native_format = v.native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003719 mutex_unlock(&floppy_mutex);
3720 return 0;
3721}
3722
3723static int compat_getdrvprm(int drive,
3724 struct compat_floppy_drive_params __user *arg)
3725{
3726 struct compat_floppy_drive_params v;
3727
3728 memset(&v, 0, sizeof(struct compat_floppy_drive_params));
3729 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003730 v.cmos = drive_params[drive].cmos;
3731 v.max_dtr = drive_params[drive].max_dtr;
3732 v.hlt = drive_params[drive].hlt;
3733 v.hut = drive_params[drive].hut;
3734 v.srt = drive_params[drive].srt;
3735 v.spinup = drive_params[drive].spinup;
3736 v.spindown = drive_params[drive].spindown;
3737 v.spindown_offset = drive_params[drive].spindown_offset;
3738 v.select_delay = drive_params[drive].select_delay;
3739 v.rps = drive_params[drive].rps;
3740 v.tracks = drive_params[drive].tracks;
3741 v.timeout = drive_params[drive].timeout;
3742 v.interleave_sect = drive_params[drive].interleave_sect;
3743 v.max_errors = drive_params[drive].max_errors;
3744 v.flags = drive_params[drive].flags;
3745 v.read_track = drive_params[drive].read_track;
3746 memcpy(v.autodetect, drive_params[drive].autodetect,
3747 sizeof(v.autodetect));
3748 v.checkfreq = drive_params[drive].checkfreq;
3749 v.native_format = drive_params[drive].native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003750 mutex_unlock(&floppy_mutex);
3751
Jann Horn52f6f9d2019-03-26 23:03:48 +01003752 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_params)))
Al Viro229b53c2017-06-27 15:47:56 -04003753 return -EFAULT;
3754 return 0;
3755}
3756
3757static int compat_getdrvstat(int drive, bool poll,
3758 struct compat_floppy_drive_struct __user *arg)
3759{
3760 struct compat_floppy_drive_struct v;
3761
3762 memset(&v, 0, sizeof(struct compat_floppy_drive_struct));
3763 mutex_lock(&floppy_mutex);
3764
3765 if (poll) {
3766 if (lock_fdc(drive))
3767 goto Eintr;
3768 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
3769 goto Eintr;
3770 process_fd_request();
3771 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003772 v.spinup_date = drive_state[drive].spinup_date;
3773 v.select_date = drive_state[drive].select_date;
3774 v.first_read_date = drive_state[drive].first_read_date;
3775 v.probed_format = drive_state[drive].probed_format;
3776 v.track = drive_state[drive].track;
3777 v.maxblock = drive_state[drive].maxblock;
3778 v.maxtrack = drive_state[drive].maxtrack;
3779 v.generation = drive_state[drive].generation;
3780 v.keep_data = drive_state[drive].keep_data;
3781 v.fd_ref = drive_state[drive].fd_ref;
3782 v.fd_device = drive_state[drive].fd_device;
3783 v.last_checked = drive_state[drive].last_checked;
3784 v.dmabuf = (uintptr_t) drive_state[drive].dmabuf;
3785 v.bufblocks = drive_state[drive].bufblocks;
Al Viro229b53c2017-06-27 15:47:56 -04003786 mutex_unlock(&floppy_mutex);
3787
Jann Horn52f6f9d2019-03-26 23:03:48 +01003788 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_struct)))
Al Viro229b53c2017-06-27 15:47:56 -04003789 return -EFAULT;
3790 return 0;
3791Eintr:
3792 mutex_unlock(&floppy_mutex);
3793 return -EINTR;
3794}
3795
3796static int compat_getfdcstat(int drive,
3797 struct compat_floppy_fdc_state __user *arg)
3798{
3799 struct compat_floppy_fdc_state v32;
3800 struct floppy_fdc_state v;
3801
3802 mutex_lock(&floppy_mutex);
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003803 v = fdc_state[FDC(drive)];
Al Viro229b53c2017-06-27 15:47:56 -04003804 mutex_unlock(&floppy_mutex);
3805
3806 memset(&v32, 0, sizeof(struct compat_floppy_fdc_state));
3807 v32.spec1 = v.spec1;
3808 v32.spec2 = v.spec2;
3809 v32.dtr = v.dtr;
3810 v32.version = v.version;
3811 v32.dor = v.dor;
3812 v32.address = v.address;
3813 v32.rawcmd = v.rawcmd;
3814 v32.reset = v.reset;
3815 v32.need_configure = v.need_configure;
3816 v32.perp_mode = v.perp_mode;
3817 v32.has_fifo = v.has_fifo;
3818 v32.driver_version = v.driver_version;
3819 memcpy(v32.track, v.track, 4);
3820 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state)))
3821 return -EFAULT;
3822 return 0;
3823}
3824
3825static int compat_werrorget(int drive,
3826 struct compat_floppy_write_errors __user *arg)
3827{
3828 struct compat_floppy_write_errors v32;
3829 struct floppy_write_errors v;
3830
3831 memset(&v32, 0, sizeof(struct compat_floppy_write_errors));
3832 mutex_lock(&floppy_mutex);
Willy Tarreau121e2972020-02-24 22:23:47 +01003833 v = write_errors[drive];
Al Viro229b53c2017-06-27 15:47:56 -04003834 mutex_unlock(&floppy_mutex);
3835 v32.write_errors = v.write_errors;
3836 v32.first_error_sector = v.first_error_sector;
3837 v32.first_error_generation = v.first_error_generation;
3838 v32.last_error_sector = v.last_error_sector;
3839 v32.last_error_generation = v.last_error_generation;
3840 v32.badness = v.badness;
3841 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors)))
3842 return -EFAULT;
3843 return 0;
3844}
3845
3846static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3847 unsigned long param)
3848{
3849 int drive = (long)bdev->bd_disk->private_data;
3850 switch (cmd) {
Arnd Bergmann9452b1a2019-11-28 15:48:10 +01003851 case CDROMEJECT: /* CD-ROM eject */
3852 case 0x6470: /* SunOS floppy eject */
3853
Al Viro229b53c2017-06-27 15:47:56 -04003854 case FDMSGON:
3855 case FDMSGOFF:
3856 case FDSETEMSGTRESH:
3857 case FDFLUSH:
3858 case FDWERRORCLR:
3859 case FDEJECT:
3860 case FDCLRPRM:
3861 case FDFMTBEG:
3862 case FDRESET:
3863 case FDTWADDLE:
3864 return fd_ioctl(bdev, mode, cmd, param);
3865 case FDSETMAXERRS:
3866 case FDGETMAXERRS:
3867 case FDGETDRVTYP:
3868 case FDFMTEND:
3869 case FDFMTTRK:
3870 case FDRAWCMD:
3871 return fd_ioctl(bdev, mode, cmd,
3872 (unsigned long)compat_ptr(param));
3873 case FDSETPRM32:
3874 case FDDEFPRM32:
3875 return compat_set_geometry(bdev, mode, cmd, compat_ptr(param));
3876 case FDGETPRM32:
3877 return compat_get_prm(drive, compat_ptr(param));
3878 case FDSETDRVPRM32:
3879 return compat_setdrvprm(drive, compat_ptr(param));
3880 case FDGETDRVPRM32:
3881 return compat_getdrvprm(drive, compat_ptr(param));
3882 case FDPOLLDRVSTAT32:
3883 return compat_getdrvstat(drive, true, compat_ptr(param));
3884 case FDGETDRVSTAT32:
3885 return compat_getdrvstat(drive, false, compat_ptr(param));
3886 case FDGETFDCSTAT32:
3887 return compat_getfdcstat(drive, compat_ptr(param));
3888 case FDWERRORGET32:
3889 return compat_werrorget(drive, compat_ptr(param));
3890 }
3891 return -EINVAL;
3892}
3893#endif
3894
Linus Torvalds1da177e2005-04-16 15:20:36 -07003895static void __init config_types(void)
3896{
Joe Perchesb46df352010-03-10 15:20:46 -08003897 bool has_drive = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003898 int drive;
3899
3900 /* read drive info out of physical CMOS */
3901 drive = 0;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003902 if (!drive_params[drive].cmos)
3903 drive_params[drive].cmos = FLOPPY0_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904 drive = 1;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003905 if (!drive_params[drive].cmos)
3906 drive_params[drive].cmos = FLOPPY1_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003907
Jesper Juhl06f748c2007-10-16 23:30:57 -07003908 /* FIXME: additional physical CMOS drive detection should go here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909
3910 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003911 unsigned int type = drive_params[drive].cmos;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003912 struct floppy_drive_params *params;
3913 const char *name = NULL;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003914 char temparea[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003915
Tobias Klauser945f3902006-01-08 01:05:11 -08003916 if (type < ARRAY_SIZE(default_drive_params)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003917 params = &default_drive_params[type].params;
3918 if (type) {
3919 name = default_drive_params[type].name;
3920 allowed_drive_mask |= 1 << drive;
3921 } else
3922 allowed_drive_mask &= ~(1 << drive);
3923 } else {
3924 params = &default_drive_params[0].params;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003925 snprintf(temparea, sizeof(temparea),
3926 "unknown type %d (usb?)", type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003927 name = temparea;
3928 }
3929 if (name) {
Joe Perchesb46df352010-03-10 15:20:46 -08003930 const char *prepend;
3931 if (!has_drive) {
3932 prepend = "";
3933 has_drive = true;
3934 pr_info("Floppy drive(s):");
3935 } else {
3936 prepend = ",";
Linus Torvalds1da177e2005-04-16 15:20:36 -07003937 }
Joe Perchesb46df352010-03-10 15:20:46 -08003938
3939 pr_cont("%s fd%d is %s", prepend, drive, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003940 }
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003941 drive_params[drive] = *params;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003942 }
Joe Perchesb46df352010-03-10 15:20:46 -08003943
3944 if (has_drive)
3945 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946}
3947
Al Virodb2a1442013-05-05 21:52:57 -04003948static void floppy_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003949{
Al Viroa4af9b42008-03-02 09:27:55 -05003950 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003951
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003952 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003953 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003954 if (!drive_state[drive].fd_ref--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003955 DPRINT("floppy_release with fd_ref == 0");
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003956 drive_state[drive].fd_ref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003957 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003958 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959 opened_bdev[drive] = NULL;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003960 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003961 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003962}
3963
3964/*
3965 * floppy_open check for aliasing (/dev/fd0 can be the same as
3966 * /dev/PS0 etc), and disallows simultaneous access to the same
3967 * drive with different device numbers.
3968 */
Al Viroa4af9b42008-03-02 09:27:55 -05003969static int floppy_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970{
Al Viroa4af9b42008-03-02 09:27:55 -05003971 int drive = (long)bdev->bd_disk->private_data;
3972 int old_dev, new_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003973 int try;
3974 int res = -EBUSY;
3975 char *tmp;
3976
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003977 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003978 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003979 old_dev = drive_state[drive].fd_device;
Al Viroa4af9b42008-03-02 09:27:55 -05003980 if (opened_bdev[drive] && opened_bdev[drive] != bdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003981 goto out2;
3982
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003983 if (!drive_state[drive].fd_ref && (drive_params[drive].flags & FD_BROKEN_DCL)) {
3984 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
3985 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986 }
3987
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003988 drive_state[drive].fd_ref++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003989
Al Viroa4af9b42008-03-02 09:27:55 -05003990 opened_bdev[drive] = bdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003991
3992 res = -ENXIO;
3993
3994 if (!floppy_track_buffer) {
3995 /* if opening an ED drive, reserve a big buffer,
3996 * else reserve a small one */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003997 if ((drive_params[drive].cmos == 6) || (drive_params[drive].cmos == 5))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003998 try = 64; /* Only 48 actually useful */
3999 else
4000 try = 32; /* Only 24 actually useful */
4001
4002 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4003 if (!tmp && !floppy_track_buffer) {
4004 try >>= 1; /* buffer only one side */
4005 INFBOUND(try, 16);
4006 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4007 }
Joe Perchesa81ee542010-03-10 15:20:46 -08004008 if (!tmp && !floppy_track_buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004009 fallback_on_nodma_alloc(&tmp, 2048 * try);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004010 if (!tmp && !floppy_track_buffer) {
4011 DPRINT("Unable to allocate DMA memory\n");
4012 goto out;
4013 }
4014 if (floppy_track_buffer) {
4015 if (tmp)
4016 fd_dma_mem_free((unsigned long)tmp, try * 1024);
4017 } else {
4018 buffer_min = buffer_max = -1;
4019 floppy_track_buffer = tmp;
4020 max_buffer_sectors = try;
4021 }
4022 }
4023
Al Viroa4af9b42008-03-02 09:27:55 -05004024 new_dev = MINOR(bdev->bd_dev);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004025 drive_state[drive].fd_device = new_dev;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004026 set_capacity(disks[drive][ITYPE(new_dev)], floppy_sizes[new_dev]);
Al Viroa4af9b42008-03-02 09:27:55 -05004027 if (old_dev != -1 && old_dev != new_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004028 if (buffer_drive == drive)
4029 buffer_track = -1;
4030 }
4031
Willy Tarreauf9d322b2020-02-24 22:23:44 +01004032 if (fdc_state[FDC(drive)].rawcmd == 1)
4033 fdc_state[FDC(drive)].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004034
Denis Efremovc7e9d0022021-08-07 10:37:02 +03004035 if (!(mode & FMODE_NDELAY)) {
4036 if (mode & (FMODE_READ|FMODE_WRITE)) {
4037 drive_state[drive].last_checked = 0;
4038 clear_bit(FD_OPEN_SHOULD_FAIL_BIT,
4039 &drive_state[drive].flags);
4040 if (bdev_check_media_change(bdev))
4041 floppy_revalidate(bdev->bd_disk);
4042 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags))
4043 goto out;
4044 if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags))
4045 goto out;
4046 }
4047 res = -EROFS;
4048 if ((mode & FMODE_WRITE) &&
4049 !test_bit(FD_DISK_WRITABLE_BIT, &drive_state[drive].flags))
Jens Axboef2791e72016-08-25 08:56:51 -06004050 goto out;
4051 }
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004052 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004053 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004054 return 0;
4055out:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004056 drive_state[drive].fd_ref--;
Jiri Kosinabfa10b82012-05-18 13:50:28 +02004057
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004058 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004059 opened_bdev[drive] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004060out2:
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004061 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004062 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004063 return res;
4064}
4065
4066/*
4067 * Check if the disk has been changed or if a change has been faked.
4068 */
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004069static unsigned int floppy_check_events(struct gendisk *disk,
4070 unsigned int clearing)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004071{
4072 int drive = (long)disk->private_data;
4073
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004074 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4075 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004076 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004077
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004078 if (time_after(jiffies, drive_state[drive].last_checked + drive_params[drive].checkfreq)) {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004079 if (lock_fdc(drive))
Yufen Yu96d7cb92019-01-29 16:34:04 +08004080 return 0;
Joe Perches74f63f42010-03-10 15:20:58 -08004081 poll_drive(false, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004082 process_fd_request();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004083 }
4084
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004085 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4086 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004087 test_bit(drive, &fake_change) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004088 drive_no_geom(drive))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004089 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004090 return 0;
4091}
4092
4093/*
4094 * This implements "read block 0" for floppy_revalidate().
4095 * Needed for format autodetection, checking whether there is
4096 * a disk in the drive, and whether that disk is writable.
4097 */
4098
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004099struct rb0_cbdata {
4100 int drive;
4101 struct completion complete;
4102};
4103
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004104static void floppy_rb0_cb(struct bio *bio)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004105{
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004106 struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private;
4107 int drive = cbdata->drive;
4108
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004109 if (bio->bi_status) {
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004110 pr_info("floppy: error %d while reading block 0\n",
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004111 bio->bi_status);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004112 set_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004113 }
4114 complete(&cbdata->complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004115}
4116
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004117static int __floppy_read_block_0(struct block_device *bdev, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004118{
4119 struct bio bio;
4120 struct bio_vec bio_vec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004121 struct page *page;
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004122 struct rb0_cbdata cbdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004123
4124 page = alloc_page(GFP_NOIO);
4125 if (!page) {
4126 process_fd_request();
4127 return -ENOMEM;
4128 }
4129
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004130 cbdata.drive = drive;
4131
Ming Lei3a83f462016-11-22 08:57:21 -07004132 bio_init(&bio, &bio_vec, 1);
Christoph Hellwig74d46992017-08-23 19:10:32 +02004133 bio_set_dev(&bio, bdev);
Christoph Hellwigfe4ec122020-06-26 10:01:52 +02004134 bio_add_page(&bio, page, block_size(bdev), 0);
Ming Lei2c73a602016-11-11 20:05:31 +08004135
Kent Overstreet4f024f32013-10-11 15:44:27 -07004136 bio.bi_iter.bi_sector = 0;
Jiri Kosina6314a102014-05-28 11:55:23 +02004137 bio.bi_flags |= (1 << BIO_QUIET);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004138 bio.bi_private = &cbdata;
4139 bio.bi_end_io = floppy_rb0_cb;
Mike Christie95fe6c12016-06-05 14:31:48 -05004140 bio_set_op_attrs(&bio, REQ_OP_READ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004141
Jens Axboede7b75d2018-11-09 15:58:40 -07004142 init_completion(&cbdata.complete);
4143
Mike Christie4e49ea42016-06-05 14:31:41 -05004144 submit_bio(&bio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004145 process_fd_request();
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004146
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004147 wait_for_completion(&cbdata.complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004148
4149 __free_page(page);
4150
4151 return 0;
4152}
4153
4154/* revalidate the floppy disk, i.e. trigger format autodetection by reading
4155 * the bootblock (block 0). "Autodetection" is also needed to check whether
4156 * there is a disk in the drive at all... Thus we also do it for fixed
4157 * geometry formats */
4158static int floppy_revalidate(struct gendisk *disk)
4159{
4160 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004161 int cf;
4162 int res = 0;
4163
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004164 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4165 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004166 test_bit(drive, &fake_change) ||
4167 drive_no_geom(drive)) {
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004168 if (WARN(atomic_read(&usage_count) == 0,
4169 "VFS: revalidate called on non-open device.\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004170 return -EFAULT;
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004171
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004172 res = lock_fdc(drive);
4173 if (res)
4174 return res;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004175 cf = (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4176 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags));
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004177 if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004178 process_fd_request(); /*already done by another thread */
4179 return 0;
4180 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004181 drive_state[drive].maxblock = 0;
4182 drive_state[drive].maxtrack = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004183 if (buffer_drive == drive)
4184 buffer_track = -1;
4185 clear_bit(drive, &fake_change);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004186 clear_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 if (cf)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004188 drive_state[drive].generation++;
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004189 if (drive_no_geom(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004190 /* auto-sensing */
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004191 res = __floppy_read_block_0(opened_bdev[drive], drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192 } else {
4193 if (cf)
Joe Perches74f63f42010-03-10 15:20:58 -08004194 poll_drive(false, FD_RAW_NEED_DISK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004195 process_fd_request();
4196 }
4197 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004198 set_capacity(disk, floppy_sizes[drive_state[drive].fd_device]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004199 return res;
4200}
4201
Alexey Dobriyan83d5cde2009-09-21 17:01:13 -07004202static const struct block_device_operations floppy_fops = {
Jesper Juhl06f748c2007-10-16 23:30:57 -07004203 .owner = THIS_MODULE,
Al Viroa4af9b42008-03-02 09:27:55 -05004204 .open = floppy_open,
4205 .release = floppy_release,
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02004206 .ioctl = fd_ioctl,
Jesper Juhl06f748c2007-10-16 23:30:57 -07004207 .getgeo = fd_getgeo,
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004208 .check_events = floppy_check_events,
Al Viro229b53c2017-06-27 15:47:56 -04004209#ifdef CONFIG_COMPAT
4210 .compat_ioctl = fd_compat_ioctl,
4211#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004212};
Linus Torvalds1da177e2005-04-16 15:20:36 -07004213
Linus Torvalds1da177e2005-04-16 15:20:36 -07004214/*
4215 * Floppy Driver initialization
4216 * =============================
4217 */
4218
4219/* Determine the floppy disk controller type */
4220/* This routine was written by David C. Niemi */
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004221static char __init get_fdc_version(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004222{
4223 int r;
4224
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004225 output_byte(fdc, FD_DUMPREGS); /* 82072 and better know DUMPREGS */
4226 if (fdc_state[fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004227 return FDC_NONE;
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004228 r = result(fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004229 if (r <= 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004230 return FDC_NONE; /* No FDC present ??? */
Denis Efremov67c07162021-04-16 11:34:46 +03004231 if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004232 pr_info("FDC %d is an 8272A\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004233 return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
4234 }
4235 if (r != 10) {
Joe Perchesb46df352010-03-10 15:20:46 -08004236 pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004237 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004238 return FDC_UNKNOWN;
4239 }
4240
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004241 if (!fdc_configure(fdc)) {
4242 pr_info("FDC %d is an 82072\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004243 return FDC_82072; /* 82072 doesn't know CONFIGURE */
4244 }
4245
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004246 output_byte(fdc, FD_PERPENDICULAR);
4247 if (need_more_output(fdc) == MORE_OUTPUT) {
4248 output_byte(fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004249 } else {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004250 pr_info("FDC %d is an 82072A\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004251 return FDC_82072A; /* 82072A as found on Sparcs. */
4252 }
4253
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004254 output_byte(fdc, FD_UNLOCK);
4255 r = result(fdc);
Denis Efremov67c07162021-04-16 11:34:46 +03004256 if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004257 pr_info("FDC %d is a pre-1991 82077\n", fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004258 return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
Linus Torvalds1da177e2005-04-16 15:20:36 -07004259 * LOCK/UNLOCK */
4260 }
Denis Efremov67c07162021-04-16 11:34:46 +03004261 if ((r != 1) || (reply_buffer[ST0] != 0x00)) {
Joe Perchesb46df352010-03-10 15:20:46 -08004262 pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004263 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004264 return FDC_UNKNOWN;
4265 }
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004266 output_byte(fdc, FD_PARTID);
4267 r = result(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268 if (r != 1) {
Joe Perchesb46df352010-03-10 15:20:46 -08004269 pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004270 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004271 return FDC_UNKNOWN;
4272 }
Denis Efremov67c07162021-04-16 11:34:46 +03004273 if (reply_buffer[ST0] == 0x80) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004274 pr_info("FDC %d is a post-1991 82077\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004275 return FDC_82077; /* Revised 82077AA passes all the tests */
4276 }
Denis Efremov67c07162021-04-16 11:34:46 +03004277 switch (reply_buffer[ST0] >> 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004278 case 0x0:
4279 /* Either a 82078-1 or a 82078SL running at 5Volt */
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004280 pr_info("FDC %d is an 82078.\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004281 return FDC_82078;
4282 case 0x1:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004283 pr_info("FDC %d is a 44pin 82078\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004284 return FDC_82078;
4285 case 0x2:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004286 pr_info("FDC %d is a S82078B\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004287 return FDC_S82078B;
4288 case 0x3:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004289 pr_info("FDC %d is a National Semiconductor PC87306\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004290 return FDC_87306;
4291 default:
Joe Perchesb46df352010-03-10 15:20:46 -08004292 pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n",
Denis Efremov67c07162021-04-16 11:34:46 +03004293 fdc, reply_buffer[ST0] >> 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004294 return FDC_82078_UNKN;
4295 }
4296} /* get_fdc_version */
4297
4298/* lilo configuration */
4299
4300static void __init floppy_set_flags(int *ints, int param, int param2)
4301{
4302 int i;
4303
4304 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4305 if (param)
4306 default_drive_params[i].params.flags |= param2;
4307 else
4308 default_drive_params[i].params.flags &= ~param2;
4309 }
4310 DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param);
4311}
4312
4313static void __init daring(int *ints, int param, int param2)
4314{
4315 int i;
4316
4317 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4318 if (param) {
4319 default_drive_params[i].params.select_delay = 0;
4320 default_drive_params[i].params.flags |=
4321 FD_SILENT_DCL_CLEAR;
4322 } else {
4323 default_drive_params[i].params.select_delay =
4324 2 * HZ / 100;
4325 default_drive_params[i].params.flags &=
4326 ~FD_SILENT_DCL_CLEAR;
4327 }
4328 }
4329 DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
4330}
4331
4332static void __init set_cmos(int *ints, int dummy, int dummy2)
4333{
4334 int current_drive = 0;
4335
4336 if (ints[0] != 2) {
4337 DPRINT("wrong number of parameters for CMOS\n");
4338 return;
4339 }
4340 current_drive = ints[1];
4341 if (current_drive < 0 || current_drive >= 8) {
4342 DPRINT("bad drive for set_cmos\n");
4343 return;
4344 }
4345#if N_FDC > 1
4346 if (current_drive >= 4 && !FDC2)
4347 FDC2 = 0x370;
4348#endif
Willy Tarreau031faab2020-02-24 22:23:48 +01004349 drive_params[current_drive].cmos = ints[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004350 DPRINT("setting CMOS code to %d\n", ints[2]);
4351}
4352
4353static struct param_table {
4354 const char *name;
4355 void (*fn) (int *ints, int param, int param2);
4356 int *var;
4357 int def_param;
4358 int param2;
4359} config_params[] __initdata = {
4360 {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4361 {"all_drives", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4362 {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0},
4363 {"irq", NULL, &FLOPPY_IRQ, 6, 0},
4364 {"dma", NULL, &FLOPPY_DMA, 2, 0},
4365 {"daring", daring, NULL, 1, 0},
4366#if N_FDC > 1
4367 {"two_fdc", NULL, &FDC2, 0x370, 0},
4368 {"one_fdc", NULL, &FDC2, 0, 0},
4369#endif
4370 {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL},
4371 {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL},
4372 {"messages", floppy_set_flags, NULL, 1, FTD_MSG},
4373 {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR},
4374 {"debug", floppy_set_flags, NULL, 1, FD_DEBUG},
4375 {"nodma", NULL, &can_use_virtual_dma, 1, 0},
4376 {"omnibook", NULL, &can_use_virtual_dma, 1, 0},
4377 {"yesdma", NULL, &can_use_virtual_dma, 0, 0},
4378 {"fifo_depth", NULL, &fifo_depth, 0xa, 0},
4379 {"nofifo", NULL, &no_fifo, 0x20, 0},
4380 {"usefifo", NULL, &no_fifo, 0, 0},
4381 {"cmos", set_cmos, NULL, 0, 0},
4382 {"slow", NULL, &slow_floppy, 1, 0},
4383 {"unexpected_interrupts", NULL, &print_unex, 1, 0},
4384 {"no_unexpected_interrupts", NULL, &print_unex, 0, 0},
4385 {"L40SX", NULL, &print_unex, 0, 0}
4386
4387 EXTRA_FLOPPY_PARAMS
4388};
4389
4390static int __init floppy_setup(char *str)
4391{
4392 int i;
4393 int param;
4394 int ints[11];
4395
4396 str = get_options(str, ARRAY_SIZE(ints), ints);
4397 if (str) {
4398 for (i = 0; i < ARRAY_SIZE(config_params); i++) {
4399 if (strcmp(str, config_params[i].name) == 0) {
4400 if (ints[0])
4401 param = ints[1];
4402 else
4403 param = config_params[i].def_param;
4404 if (config_params[i].fn)
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004405 config_params[i].fn(ints, param,
4406 config_params[i].
4407 param2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004408 if (config_params[i].var) {
4409 DPRINT("%s=%d\n", str, param);
4410 *config_params[i].var = param;
4411 }
4412 return 1;
4413 }
4414 }
4415 }
4416 if (str) {
4417 DPRINT("unknown floppy option [%s]\n", str);
4418
4419 DPRINT("allowed options are:");
4420 for (i = 0; i < ARRAY_SIZE(config_params); i++)
Joe Perchesb46df352010-03-10 15:20:46 -08004421 pr_cont(" %s", config_params[i].name);
4422 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004423 } else
4424 DPRINT("botched floppy option\n");
Mauro Carvalho Chehabe7751612019-06-18 11:47:10 -03004425 DPRINT("Read Documentation/admin-guide/blockdev/floppy.rst\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004426 return 0;
4427}
4428
4429static int have_no_fdc = -ENODEV;
4430
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004431static ssize_t floppy_cmos_show(struct device *dev,
4432 struct device_attribute *attr, char *buf)
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004433{
Eric Miao71b3e0c2009-01-31 22:47:44 +08004434 struct platform_device *p = to_platform_device(dev);
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004435 int drive;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004436
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004437 drive = p->id;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01004438 return sprintf(buf, "%X\n", drive_params[drive].cmos);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004439}
Joe Perches48c8cee2010-03-10 15:20:45 -08004440
Joe Perches5657a812018-05-24 13:38:59 -06004441static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004442
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004443static struct attribute *floppy_dev_attrs[] = {
4444 &dev_attr_cmos.attr,
4445 NULL
4446};
4447
4448ATTRIBUTE_GROUPS(floppy_dev);
4449
Linus Torvalds1da177e2005-04-16 15:20:36 -07004450static void floppy_device_release(struct device *dev)
4451{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004452}
4453
Frans Popc90cd332009-07-25 22:24:54 +02004454static int floppy_resume(struct device *dev)
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004455{
4456 int fdc;
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004457 int saved_drive;
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004458
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004459 saved_drive = current_drive;
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004460 for (fdc = 0; fdc < N_FDC; fdc++)
Willy Tarreaude6048b2020-02-24 22:23:43 +01004461 if (fdc_state[fdc].address != -1)
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004462 user_reset_fdc(REVDRIVE(fdc, 0), FD_RESET_ALWAYS, false);
4463 set_fdc(saved_drive);
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004464 return 0;
4465}
4466
Alexey Dobriyan47145212009-12-14 18:00:08 -08004467static const struct dev_pm_ops floppy_pm_ops = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004468 .resume = floppy_resume,
Frans Popc90cd332009-07-25 22:24:54 +02004469 .restore = floppy_resume,
4470};
4471
4472static struct platform_driver floppy_driver = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004473 .driver = {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004474 .name = "floppy",
4475 .pm = &floppy_pm_ops,
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004476 },
4477};
4478
Omar Sandovala9f38e12018-10-15 09:21:34 -06004479static const struct blk_mq_ops floppy_mq_ops = {
4480 .queue_rq = floppy_queue_rq,
4481};
4482
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004483static struct platform_device floppy_device[N_DRIVE];
Luis Chamberlain662167e2021-09-27 15:02:52 -07004484static bool registered[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004485
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004486static bool floppy_available(int drive)
4487{
4488 if (!(allowed_drive_mask & (1 << drive)))
4489 return false;
4490 if (fdc_state[FDC(drive)].version == FDC_NONE)
4491 return false;
4492 return true;
4493}
4494
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004495static int floppy_alloc_disk(unsigned int drive, unsigned int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004496{
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004497 struct gendisk *disk;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004498
Christoph Hellwig34f84ae2021-06-02 09:53:32 +03004499 disk = blk_mq_alloc_disk(&tag_sets[drive], NULL);
4500 if (IS_ERR(disk))
4501 return PTR_ERR(disk);
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004502
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004503 blk_queue_max_hw_sectors(disk->queue, 64);
4504 disk->major = FLOPPY_MAJOR;
4505 disk->first_minor = TOMINOR(drive) | (type << 2);
Christoph Hellwig34f84ae2021-06-02 09:53:32 +03004506 disk->minors = 1;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004507 disk->fops = &floppy_fops;
Christoph Hellwig1ebe2e52021-11-22 14:06:22 +01004508 disk->flags |= GENHD_FL_NO_PART;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004509 disk->events = DISK_EVENT_MEDIA_CHANGE;
4510 if (type)
4511 sprintf(disk->disk_name, "fd%d_type%d", drive, type);
4512 else
4513 sprintf(disk->disk_name, "fd%d", drive);
4514 /* to be cleaned up... */
4515 disk->private_data = (void *)(long)drive;
4516 disk->flags |= GENHD_FL_REMOVABLE;
4517
4518 disks[drive][type] = disk;
4519 return 0;
4520}
4521
4522static DEFINE_MUTEX(floppy_probe_lock);
4523
4524static void floppy_probe(dev_t dev)
4525{
4526 unsigned int drive = (MINOR(dev) & 3) | ((MINOR(dev) & 0x80) >> 5);
4527 unsigned int type = (MINOR(dev) >> 2) & 0x1f;
4528
4529 if (drive >= N_DRIVE || !floppy_available(drive) ||
4530 type >= ARRAY_SIZE(floppy_type))
4531 return;
4532
4533 mutex_lock(&floppy_probe_lock);
Luis Chamberlainec28fcc2021-11-03 16:04:36 -07004534 if (disks[drive][type])
4535 goto out;
4536 if (floppy_alloc_disk(drive, type))
4537 goto out;
4538 if (add_disk(disks[drive][type]))
4539 goto cleanup_disk;
4540out:
4541 mutex_unlock(&floppy_probe_lock);
4542 return;
4543
4544cleanup_disk:
4545 blk_cleanup_disk(disks[drive][type]);
4546 disks[drive][type] = NULL;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004547 mutex_unlock(&floppy_probe_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004548}
4549
Andi Kleen0cc15d032012-07-02 17:27:04 -07004550static int __init do_floppy_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004551{
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004552 int i, unit, drive, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004553
Stephen Hemminger285203c2010-06-15 13:21:11 +02004554 set_debugt();
4555 interruptjiffies = resultjiffies = jiffies;
4556
Kumar Gala68e1ee62008-09-22 14:41:31 -07004557#if defined(CONFIG_PPC)
Olaf Heringef16b512006-08-31 21:27:41 -07004558 if (check_legacy_ioport(FDC1))
4559 return -ENODEV;
4560#endif
4561
Linus Torvalds1da177e2005-04-16 15:20:36 -07004562 raw_cmd = NULL;
4563
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004564 floppy_wq = alloc_ordered_workqueue("floppy", 0);
4565 if (!floppy_wq)
4566 return -ENOMEM;
4567
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004568 for (drive = 0; drive < N_DRIVE; drive++) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004569 memset(&tag_sets[drive], 0, sizeof(tag_sets[drive]));
4570 tag_sets[drive].ops = &floppy_mq_ops;
4571 tag_sets[drive].nr_hw_queues = 1;
4572 tag_sets[drive].nr_maps = 1;
4573 tag_sets[drive].queue_depth = 2;
4574 tag_sets[drive].numa_node = NUMA_NO_NODE;
4575 tag_sets[drive].flags = BLK_MQ_F_SHOULD_MERGE;
4576 err = blk_mq_alloc_tag_set(&tag_sets[drive]);
4577 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004578 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004579
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004580 err = floppy_alloc_disk(drive, 0);
4581 if (err)
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004582 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004583
Kees Cookb1bf4212017-10-04 17:49:29 -07004584 timer_setup(&motor_off_timer[drive], motor_off_callback, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004585 }
4586
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004587 err = __register_blkdev(FLOPPY_MAJOR, "fd", floppy_probe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004588 if (err)
Greg Kroah-Hartman8ab5e4c2005-06-20 21:15:16 -07004589 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004590
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004591 err = platform_driver_register(&floppy_driver);
4592 if (err)
4593 goto out_unreg_blkdev;
4594
Linus Torvalds1da177e2005-04-16 15:20:36 -07004595 for (i = 0; i < 256; i++)
4596 if (ITYPE(i))
4597 floppy_sizes[i] = floppy_type[ITYPE(i)].size;
4598 else
4599 floppy_sizes[i] = MAX_DISK_SIZE << 1;
4600
Joe Perches73507e62010-03-10 15:21:03 -08004601 reschedule_timeout(MAXTIMEOUT, "floppy init");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004602 config_types();
4603
4604 for (i = 0; i < N_FDC; i++) {
Willy Tarreau05f5e312020-04-10 11:30:23 +02004605 memset(&fdc_state[i], 0, sizeof(*fdc_state));
4606 fdc_state[i].dtr = -1;
4607 fdc_state[i].dor = 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608#if defined(__sparc__) || defined(__mc68000__)
Joe Perches96534f12010-03-10 15:20:51 -08004609 /*sparcs/sun3x don't have a DOR reset which we can fall back on to */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004610#ifdef __mc68000__
4611 if (MACH_IS_SUN3X)
4612#endif
Willy Tarreau05f5e312020-04-10 11:30:23 +02004613 fdc_state[i].version = FDC_82072A;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004614#endif
4615 }
4616
4617 use_virtual_dma = can_use_virtual_dma & 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004618 fdc_state[0].address = FDC1;
4619 if (fdc_state[0].address == -1) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004620 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004621 err = -ENODEV;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004622 goto out_unreg_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004623 }
4624#if N_FDC > 1
4625 fdc_state[1].address = FDC2;
4626#endif
4627
Willy Tarreaue83995c2020-03-01 20:55:55 +01004628 current_fdc = 0; /* reset fdc in case of unexpected interrupt */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004629 err = floppy_grab_irq_and_dma();
4630 if (err) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004631 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004632 err = -EBUSY;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004633 goto out_unreg_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004634 }
4635
4636 /* initialise drive state */
4637 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004638 memset(&drive_state[drive], 0, sizeof(drive_state[drive]));
Willy Tarreau121e2972020-02-24 22:23:47 +01004639 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004640 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
4641 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
4642 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
4643 drive_state[drive].fd_device = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004644 floppy_track_buffer = NULL;
4645 max_buffer_sectors = 0;
4646 }
4647 /*
4648 * Small 10 msec delay to let through any interrupt that
4649 * initialization might have triggered, to not
4650 * confuse detection:
4651 */
4652 msleep(10);
4653
4654 for (i = 0; i < N_FDC; i++) {
Willy Tarreau05f5e312020-04-10 11:30:23 +02004655 fdc_state[i].driver_version = FD_DRIVER_VERSION;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 for (unit = 0; unit < 4; unit++)
Willy Tarreau05f5e312020-04-10 11:30:23 +02004657 fdc_state[i].track[unit] = 0;
4658 if (fdc_state[i].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004659 continue;
Willy Tarreau05f5e312020-04-10 11:30:23 +02004660 fdc_state[i].rawcmd = 2;
4661 if (user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004662 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004663 floppy_release_regions(i);
4664 fdc_state[i].address = -1;
4665 fdc_state[i].version = FDC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004666 continue;
4667 }
4668 /* Try to determine the floppy controller type */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004669 fdc_state[i].version = get_fdc_version(i);
4670 if (fdc_state[i].version == FDC_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004671 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004672 floppy_release_regions(i);
4673 fdc_state[i].address = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004674 continue;
4675 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004676 if (can_use_virtual_dma == 2 &&
Willy Tarreau05f5e312020-04-10 11:30:23 +02004677 fdc_state[i].version < FDC_82072A)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004678 can_use_virtual_dma = 0;
4679
4680 have_no_fdc = 0;
4681 /* Not all FDCs seem to be able to handle the version command
4682 * properly, so force a reset for the standard FDC clones,
4683 * to avoid interrupt garbage.
4684 */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004685 user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004686 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004687 current_fdc = 0;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004688 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004689 current_drive = 0;
Joe Perches29f1c782010-03-10 15:21:00 -08004690 initialized = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691 if (have_no_fdc) {
4692 DPRINT("no floppy controllers found\n");
4693 err = have_no_fdc;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004694 goto out_release_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004695 }
4696
Linus Torvalds1da177e2005-04-16 15:20:36 -07004697 for (drive = 0; drive < N_DRIVE; drive++) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004698 if (!floppy_available(drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004699 continue;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004700
4701 floppy_device[drive].name = floppy_device_name;
4702 floppy_device[drive].id = drive;
4703 floppy_device[drive].dev.release = floppy_device_release;
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004704 floppy_device[drive].dev.groups = floppy_dev_groups;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004705
4706 err = platform_device_register(&floppy_device[drive]);
4707 if (err)
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004708 goto out_remove_drives;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004709
Luis Chamberlain662167e2021-09-27 15:02:52 -07004710 registered[drive] = true;
4711
Luis Chamberlain47d34aa2021-09-27 15:02:53 -07004712 err = device_add_disk(&floppy_device[drive].dev,
4713 disks[drive][0], NULL);
4714 if (err)
4715 goto out_remove_drives;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004716 }
4717
4718 return 0;
4719
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004720out_remove_drives:
4721 while (drive--) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004722 if (floppy_available(drive)) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004723 del_gendisk(disks[drive][0]);
Luis Chamberlain662167e2021-09-27 15:02:52 -07004724 if (registered[drive])
4725 platform_device_unregister(&floppy_device[drive]);
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004726 }
4727 }
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004728out_release_dma:
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004729 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004730 floppy_release_irq_and_dma();
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004731out_unreg_driver:
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004732 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004733out_unreg_blkdev:
4734 unregister_blkdev(FLOPPY_MAJOR, "fd");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004735out_put_disk:
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01004736 destroy_workqueue(floppy_wq);
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004737 for (drive = 0; drive < N_DRIVE; drive++) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004738 if (!disks[drive][0])
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004739 break;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004740 del_timer_sync(&motor_off_timer[drive]);
Christoph Hellwig34f84ae2021-06-02 09:53:32 +03004741 blk_cleanup_disk(disks[drive][0]);
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004742 blk_mq_free_tag_set(&tag_sets[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004743 }
4744 return err;
4745}
4746
Andi Kleen0cc15d032012-07-02 17:27:04 -07004747#ifndef MODULE
4748static __init void floppy_async_init(void *data, async_cookie_t cookie)
4749{
4750 do_floppy_init();
4751}
4752#endif
4753
4754static int __init floppy_init(void)
4755{
4756#ifdef MODULE
4757 return do_floppy_init();
4758#else
4759 /* Don't hold up the bootup by the floppy initialization */
4760 async_schedule(floppy_async_init, NULL);
4761 return 0;
4762#endif
4763}
4764
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004765static const struct io_region {
4766 int offset;
4767 int size;
4768} io_regions[] = {
4769 { 2, 1 },
4770 /* address + 3 is sometimes reserved by pnp bios for motherboard */
4771 { 4, 2 },
4772 /* address + 6 is reserved, and may be taken by IDE.
4773 * Unfortunately, Adaptec doesn't know this :-(, */
4774 { 7, 1 },
4775};
4776
4777static void floppy_release_allocated_regions(int fdc, const struct io_region *p)
4778{
4779 while (p != io_regions) {
4780 p--;
Willy Tarreaude6048b2020-02-24 22:23:43 +01004781 release_region(fdc_state[fdc].address + p->offset, p->size);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004782 }
4783}
4784
4785#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)]))
4786
4787static int floppy_request_regions(int fdc)
4788{
4789 const struct io_region *p;
4790
4791 for (p = io_regions; p < ARRAY_END(io_regions); p++) {
Willy Tarreaude6048b2020-02-24 22:23:43 +01004792 if (!request_region(fdc_state[fdc].address + p->offset,
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004793 p->size, "floppy")) {
4794 DPRINT("Floppy io-port 0x%04lx in use\n",
Willy Tarreaude6048b2020-02-24 22:23:43 +01004795 fdc_state[fdc].address + p->offset);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004796 floppy_release_allocated_regions(fdc, p);
4797 return -EBUSY;
4798 }
4799 }
4800 return 0;
4801}
4802
4803static void floppy_release_regions(int fdc)
4804{
4805 floppy_release_allocated_regions(fdc, ARRAY_END(io_regions));
4806}
4807
Linus Torvalds1da177e2005-04-16 15:20:36 -07004808static int floppy_grab_irq_and_dma(void)
4809{
Willy Tarreau82a63012020-03-31 11:40:53 +02004810 int fdc;
4811
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004812 if (atomic_inc_return(&usage_count) > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004813 return 0;
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004814
4815 /*
4816 * We might have scheduled a free_irq(), wait it to
4817 * drain first:
4818 */
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004819 flush_workqueue(floppy_wq);
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004820
Linus Torvalds1da177e2005-04-16 15:20:36 -07004821 if (fd_request_irq()) {
4822 DPRINT("Unable to grab IRQ%d for the floppy driver\n",
4823 FLOPPY_IRQ);
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004824 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004825 return -1;
4826 }
4827 if (fd_request_dma()) {
4828 DPRINT("Unable to grab DMA%d for the floppy driver\n",
4829 FLOPPY_DMA);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004830 if (can_use_virtual_dma & 2)
4831 use_virtual_dma = can_use_virtual_dma = 1;
4832 if (!(can_use_virtual_dma & 1)) {
4833 fd_free_irq();
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004834 atomic_dec(&usage_count);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004835 return -1;
4836 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004837 }
4838
Willy Tarreau82a63012020-03-31 11:40:53 +02004839 for (fdc = 0; fdc < N_FDC; fdc++) {
4840 if (fdc_state[fdc].address != -1) {
4841 if (floppy_request_regions(fdc))
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004842 goto cleanup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004843 }
4844 }
Willy Tarreau82a63012020-03-31 11:40:53 +02004845 for (fdc = 0; fdc < N_FDC; fdc++) {
4846 if (fdc_state[fdc].address != -1) {
4847 reset_fdc_info(fdc, 1);
4848 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004849 }
4850 }
Willy Tarreau82a63012020-03-31 11:40:53 +02004851
Linus Torvalds1da177e2005-04-16 15:20:36 -07004852 set_dor(0, ~0, 8); /* avoid immediate interrupt */
4853
Willy Tarreau82a63012020-03-31 11:40:53 +02004854 for (fdc = 0; fdc < N_FDC; fdc++)
4855 if (fdc_state[fdc].address != -1)
4856 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004857 /*
Jesper Juhl06f748c2007-10-16 23:30:57 -07004858 * The driver will try and free resources and relies on us
4859 * to know if they were allocated or not.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004860 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004861 current_fdc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004862 irqdma_allocated = 1;
4863 return 0;
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004864cleanup:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004865 fd_free_irq();
4866 fd_free_dma();
Willy Tarreau82a63012020-03-31 11:40:53 +02004867 while (--fdc >= 0)
4868 floppy_release_regions(fdc);
4869 current_fdc = 0;
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004870 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004871 return -1;
4872}
4873
4874static void floppy_release_irq_and_dma(void)
4875{
Willy Tarreau82a63012020-03-31 11:40:53 +02004876 int fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877#ifndef __sparc__
4878 int drive;
4879#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004880 long tmpsize;
4881 unsigned long tmpaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004882
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004883 if (!atomic_dec_and_test(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004884 return;
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004885
Linus Torvalds1da177e2005-04-16 15:20:36 -07004886 if (irqdma_allocated) {
4887 fd_disable_dma();
4888 fd_free_dma();
Ingo Molnar3e541a42006-07-03 00:24:23 -07004889 fd_free_irq();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004890 irqdma_allocated = 0;
4891 }
4892 set_dor(0, ~0, 8);
4893#if N_FDC > 1
4894 set_dor(1, ~8, 0);
4895#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004896
4897 if (floppy_track_buffer && max_buffer_sectors) {
4898 tmpsize = max_buffer_sectors * 1024;
4899 tmpaddr = (unsigned long)floppy_track_buffer;
4900 floppy_track_buffer = NULL;
4901 max_buffer_sectors = 0;
4902 buffer_min = buffer_max = -1;
4903 fd_dma_mem_free(tmpaddr, tmpsize);
4904 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004905#ifndef __sparc__
4906 for (drive = 0; drive < N_FDC * 4; drive++)
4907 if (timer_pending(motor_off_timer + drive))
Joe Perchesb46df352010-03-10 15:20:46 -08004908 pr_info("motor off timer %d still active\n", drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004909#endif
4910
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004911 if (delayed_work_pending(&fd_timeout))
Joe Perchesb46df352010-03-10 15:20:46 -08004912 pr_info("floppy timer still active:%s\n", timeout_message);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004913 if (delayed_work_pending(&fd_timer))
Joe Perchesb46df352010-03-10 15:20:46 -08004914 pr_info("auxiliary floppy timer still active\n");
David Howells365970a2006-11-22 14:54:49 +00004915 if (work_pending(&floppy_work))
Joe Perchesb46df352010-03-10 15:20:46 -08004916 pr_info("work still pending\n");
Willy Tarreau82a63012020-03-31 11:40:53 +02004917 for (fdc = 0; fdc < N_FDC; fdc++)
4918 if (fdc_state[fdc].address != -1)
4919 floppy_release_regions(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004920}
4921
4922#ifdef MODULE
4923
4924static char *floppy;
4925
Linus Torvalds1da177e2005-04-16 15:20:36 -07004926static void __init parse_floppy_cfg_string(char *cfg)
4927{
4928 char *ptr;
4929
4930 while (*cfg) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004931 ptr = cfg;
4932 while (*cfg && *cfg != ' ' && *cfg != '\t')
4933 cfg++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004934 if (*cfg) {
4935 *cfg = '\0';
4936 cfg++;
4937 }
4938 if (*ptr)
4939 floppy_setup(ptr);
4940 }
4941}
4942
Jon Schindler7afea3b2008-04-29 00:59:21 -07004943static int __init floppy_module_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004944{
4945 if (floppy)
4946 parse_floppy_cfg_string(floppy);
4947 return floppy_init();
4948}
Jon Schindler7afea3b2008-04-29 00:59:21 -07004949module_init(floppy_module_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004950
Jon Schindler7afea3b2008-04-29 00:59:21 -07004951static void __exit floppy_module_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004952{
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004953 int drive, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004954
Linus Torvalds1da177e2005-04-16 15:20:36 -07004955 unregister_blkdev(FLOPPY_MAJOR, "fd");
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004956 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004957
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01004958 destroy_workqueue(floppy_wq);
4959
Linus Torvalds1da177e2005-04-16 15:20:36 -07004960 for (drive = 0; drive < N_DRIVE; drive++) {
4961 del_timer_sync(&motor_off_timer[drive]);
4962
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004963 if (floppy_available(drive)) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004964 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4965 if (disks[drive][i])
4966 del_gendisk(disks[drive][i]);
4967 }
Luis Chamberlain662167e2021-09-27 15:02:52 -07004968 if (registered[drive])
4969 platform_device_unregister(&floppy_device[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004970 }
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004971 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4972 if (disks[drive][i])
Luis Chamberlain37763392021-09-27 15:02:51 -07004973 blk_cleanup_disk(disks[drive][i]);
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004974 }
Omar Sandovala9f38e12018-10-15 09:21:34 -06004975 blk_mq_free_tag_set(&tag_sets[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004976 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004977
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004978 cancel_delayed_work_sync(&fd_timeout);
4979 cancel_delayed_work_sync(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004980
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004981 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004982 floppy_release_irq_and_dma();
4983
4984 /* eject disk, if any */
4985 fd_eject(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004986}
Joe Perches48c8cee2010-03-10 15:20:45 -08004987
Jon Schindler7afea3b2008-04-29 00:59:21 -07004988module_exit(floppy_module_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004989
4990module_param(floppy, charp, 0);
4991module_param(FLOPPY_IRQ, int, 0);
4992module_param(FLOPPY_DMA, int, 0);
4993MODULE_AUTHOR("Alain L. Knaff");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004994MODULE_LICENSE("GPL");
4995
Scott James Remnant83f9ef42009-04-02 16:56:47 -07004996/* This doesn't actually get used other than for module information */
4997static const struct pnp_device_id floppy_pnpids[] = {
Joe Perches48c8cee2010-03-10 15:20:45 -08004998 {"PNP0700", 0},
4999 {}
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005000};
Joe Perches48c8cee2010-03-10 15:20:45 -08005001
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005002MODULE_DEVICE_TABLE(pnp, floppy_pnpids);
5003
Linus Torvalds1da177e2005-04-16 15:20:36 -07005004#else
5005
5006__setup("floppy=", floppy_setup);
5007module_init(floppy_init)
5008#endif
5009
5010MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);