blob: 8a9d22207c59cdf7b33eeb49ff251e94336d7a48 [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>
Russell Kingd052d1b2005-10-29 19:07:23 +0100187#include <linux/platform_device.h>
Scott James Remnant83f9ef42009-04-02 16:56:47 -0700188#include <linux/mod_devicetable.h>
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800189#include <linux/mutex.h>
Joe Perchesd4937542010-03-10 15:20:44 -0800190#include <linux/io.h>
191#include <linux/uaccess.h>
Andi Kleen0cc15d032012-07-02 17:27:04 -0700192#include <linux/async.h>
Al Viro229b53c2017-06-27 15:47:56 -0400193#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
195/*
196 * PS/2 floppies have much slower step rates than regular floppies.
197 * It's been recommended that take about 1/4 of the default speed
198 * in some more extreme cases.
199 */
Arnd Bergmann2a48fc02010-06-02 14:28:52 +0200200static DEFINE_MUTEX(floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201static int slow_floppy;
202
203#include <asm/dma.h>
204#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206static int FLOPPY_IRQ = 6;
207static int FLOPPY_DMA = 2;
208static int can_use_virtual_dma = 2;
209/* =======
210 * can use virtual DMA:
211 * 0 = use of virtual DMA disallowed by config
212 * 1 = use of virtual DMA prescribed by config
213 * 2 = no virtual DMA preference configured. By default try hard DMA,
214 * but fall back on virtual DMA when not enough memory available
215 */
216
217static int use_virtual_dma;
218/* =======
219 * use virtual DMA
220 * 0 using hard DMA
221 * 1 using virtual DMA
222 * This variable is set to virtual when a DMA mem problem arises, and
223 * reset back in floppy_grab_irq_and_dma.
224 * It is not safe to reset it in other circumstances, because the floppy
225 * driver may have several buffers in use at once, and we do currently not
226 * record each buffers capabilities
227 */
228
229static DEFINE_SPINLOCK(floppy_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
231static unsigned short virtual_dma_port = 0x3f0;
David Howells7d12e782006-10-05 14:55:46 +0100232irqreturn_t floppy_interrupt(int irq, void *dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233static int set_dor(int fdc, char mask, char data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234
235#define K_64 0x10000 /* 64KB */
236
237/* the following is the mask of allowed drives. By default units 2 and
238 * 3 of both floppy controllers are disabled, because switching on the
239 * motor of these drives causes system hangs on some PCI computers. drive
240 * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
241 * a drive is allowed.
242 *
243 * NOTE: This must come before we include the arch floppy header because
244 * some ports reference this variable from there. -DaveM
245 */
246
247static int allowed_drive_mask = 0x33;
248
249#include <asm/floppy.h>
250
251static int irqdma_allocated;
252
Omar Sandovala9f38e12018-10-15 09:21:34 -0600253#include <linux/blk-mq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254#include <linux/blkpg.h>
255#include <linux/cdrom.h> /* for the compatibility eject ioctl */
256#include <linux/completion.h>
257
Omar Sandovala9f38e12018-10-15 09:21:34 -0600258static LIST_HEAD(floppy_reqs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259static struct request *current_req;
Jens Axboe48821182010-09-22 09:32:36 +0200260static int set_next_request(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262#ifndef fd_get_dma_residue
263#define fd_get_dma_residue() get_dma_residue(FLOPPY_DMA)
264#endif
265
266/* Dma Memory related stuff */
267
268#ifndef fd_dma_mem_free
269#define fd_dma_mem_free(addr, size) free_pages(addr, get_order(size))
270#endif
271
272#ifndef fd_dma_mem_alloc
Joe Perches48c8cee2010-03-10 15:20:45 -0800273#define fd_dma_mem_alloc(size) __get_dma_pages(GFP_KERNEL, get_order(size))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274#endif
275
Christoph Hellwigacfef4f2017-07-13 16:12:05 +0200276#ifndef fd_cacheflush
277#define fd_cacheflush(addr, size) /* nothing... */
278#endif
279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280static inline void fallback_on_nodma_alloc(char **addr, size_t l)
281{
282#ifdef FLOPPY_CAN_FALLBACK_ON_NODMA
283 if (*addr)
284 return; /* we have the memory */
285 if (can_use_virtual_dma != 2)
286 return; /* no fallback allowed */
Joe Perchesb46df352010-03-10 15:20:46 -0800287 pr_info("DMA memory shortage. Temporarily falling back on virtual DMA\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 *addr = (char *)nodma_mem_alloc(l);
289#else
290 return;
291#endif
292}
293
294/* End dma memory related stuff */
295
296static unsigned long fake_change;
Joe Perches29f1c782010-03-10 15:21:00 -0800297static bool initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Joe Perches48c8cee2010-03-10 15:20:45 -0800299#define ITYPE(x) (((x) >> 2) & 0x1f)
300#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
301#define UNIT(x) ((x) & 0x03) /* drive on fdc */
302#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700303 /* reverse mapping from unit and fdc to drive */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Joe Perches48c8cee2010-03-10 15:20:45 -0800306#define PH_HEAD(floppy, head) (((((floppy)->stretch & 2) >> 1) ^ head) << 2)
307#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
308
Willy Tarreau76dabe72020-02-24 22:23:51 +0100309/* read/write commands */
310#define COMMAND 0
311#define DR_SELECT 1
312#define TRACK 2
313#define HEAD 3
314#define SECTOR 4
315#define SIZECODE 5
316#define SECT_PER_TRACK 6
317#define GAP 7
318#define SIZECODE2 8
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319#define NR_RW 9
320
Willy Tarreau76dabe72020-02-24 22:23:51 +0100321/* format commands */
322#define F_SIZECODE 2
323#define F_SECT_PER_TRACK 3
324#define F_GAP 4
325#define F_FILL 5
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326#define NR_F 6
327
328/*
Joe Perches48c8cee2010-03-10 15:20:45 -0800329 * Maximum disk size (in kilobytes).
330 * This default is used whenever the current disk size is unknown.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 * [Now it is rather a minimum]
332 */
333#define MAX_DISK_SIZE 4 /* 3984 */
334
335/*
336 * globals used by 'result()'
337 */
Denis Efremovbd10a5f2020-05-01 16:44:15 +0300338static unsigned char reply_buffer[FD_RAW_REPLY_SIZE];
Joe Perches891eda82010-03-10 15:21:05 -0800339static int inr; /* size of reply buffer, when called from interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +0100340#define ST0 0
341#define ST1 1
342#define ST2 2
343#define ST3 0 /* result of GETSTATUS */
344#define R_TRACK 3
345#define R_HEAD 4
346#define R_SECTOR 5
347#define R_SIZECODE 6
Joe Perches48c8cee2010-03-10 15:20:45 -0800348
349#define SEL_DLY (2 * HZ / 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351/*
352 * this struct defines the different floppy drive types.
353 */
354static struct {
355 struct floppy_drive_params params;
356 const char *name; /* name printed while booting */
357} default_drive_params[] = {
358/* NOTE: the time values in jiffies should be in msec!
359 CMOS drive type
360 | Maximum data rate supported by drive type
361 | | Head load time, msec
362 | | | Head unload time, msec (not used)
363 | | | | Step rate interval, usec
364 | | | | | Time needed for spinup time (jiffies)
365 | | | | | | Timeout for spinning down (jiffies)
366 | | | | | | | Spindown offset (where disk stops)
367 | | | | | | | | Select delay
368 | | | | | | | | | RPS
369 | | | | | | | | | | Max number of tracks
370 | | | | | | | | | | | Interrupt timeout
371 | | | | | | | | | | | | Max nonintlv. sectors
372 | | | | | | | | | | | | | -Max Errors- flags */
373{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
374 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
375
376{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
377 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
378
379{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
380 0, { 2, 5, 6,23,10,20,12, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
381
382{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
383 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
384
385{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
386 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
387
388{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
389 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
390
391{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
392 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
393/* | --autodetected formats--- | | |
394 * read_track | | Name printed when booting
395 * | Native format
396 * Frequency of disk change checks */
397};
398
399static struct floppy_drive_params drive_params[N_DRIVE];
400static struct floppy_drive_struct drive_state[N_DRIVE];
401static struct floppy_write_errors write_errors[N_DRIVE];
402static struct timer_list motor_off_timer[N_DRIVE];
Omar Sandovala9f38e12018-10-15 09:21:34 -0600403static struct blk_mq_tag_set tag_sets[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404static struct block_device *opened_bdev[N_DRIVE];
Jes Sorensenb1c82b52006-03-23 03:00:26 -0800405static DEFINE_MUTEX(open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
407
408/*
409 * This struct defines the different floppy types.
410 *
411 * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
412 * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
413 * tells if the disk is in Commodore 1581 format, which means side 0 sectors
414 * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
415 * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
416 * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
417 * side 0 is on physical side 0 (but with the misnamed sector IDs).
418 * 'stretch' should probably be renamed to something more general, like
Keith Wansbrough9e491842008-09-22 14:57:17 -0700419 * 'options'.
420 *
421 * Bits 2 through 9 of 'stretch' tell the number of the first sector.
422 * The LSB (bit 2) is flipped. For most disks, the first sector
423 * is 1 (represented by 0x00<<2). For some CP/M and music sampler
424 * disks (such as Ensoniq EPS 16plus) it is 0 (represented as 0x01<<2).
425 * For Amstrad CPC disks it is 0xC1 (represented as 0xC0<<2).
426 *
427 * Other parameters should be self-explanatory (see also setfdprm(8)).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 */
429/*
430 Size
431 | Sectors per track
432 | | Head
433 | | | Tracks
434 | | | | Stretch
435 | | | | | Gap 1 size
436 | | | | | | Data rate, | 0x40 for perp
437 | | | | | | | Spec1 (stepping rate, head unload
438 | | | | | | | | /fmt gap (gap2) */
439static struct floppy_struct floppy_type[32] = {
440 { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
441 { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
442 { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
443 { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
444 { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
445 { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
446 { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
447 { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
448 { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
449 { 6240,39,2,80,0,0x1B,0x43,0xAF,0x28,"E3120" }, /* 9 3.12MB 3.5" */
450
451 { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
452 { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
453 { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
454 { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
455 { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
456 { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
457 { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
458 { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
459 { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
460 { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
461
462 { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
463 { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
464 { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
465 { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
466 { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
467 { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
468 { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
469 { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
470 { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700472
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
474 { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
475};
476
Christoph Hellwig302cfee2020-10-29 15:58:36 +0100477static struct gendisk *disks[N_DRIVE][ARRAY_SIZE(floppy_type)];
478
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479#define SECTSIZE (_FD_SECTSIZE(*floppy))
480
481/* Auto-detection: Disk type used until the next media change occurs. */
482static struct floppy_struct *current_type[N_DRIVE];
483
484/*
485 * User-provided type information. current_type points to
486 * the respective entry of this array.
487 */
488static struct floppy_struct user_params[N_DRIVE];
489
490static sector_t floppy_sizes[256];
491
Hannes Reinecke94fd0db2005-07-15 10:09:25 +0200492static char floppy_device_name[] = "floppy";
493
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494/*
495 * The driver is trying to determine the correct media format
496 * while probing is set. rw_interrupt() clears it after a
497 * successful access.
498 */
499static int probing;
500
501/* Synchronization of FDC access. */
Joe Perches48c8cee2010-03-10 15:20:45 -0800502#define FD_COMMAND_NONE -1
503#define FD_COMMAND_ERROR 2
504#define FD_COMMAND_OKAY 3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
506static volatile int command_status = FD_COMMAND_NONE;
507static unsigned long fdc_busy;
508static DECLARE_WAIT_QUEUE_HEAD(fdc_wait);
509static DECLARE_WAIT_QUEUE_HEAD(command_done);
510
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511/* Errors during formatting are counted here. */
512static int format_errors;
513
514/* Format request descriptor. */
515static struct format_descr format_req;
516
517/*
518 * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
519 * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
520 * H is head unload time (1=16ms, 2=32ms, etc)
521 */
522
523/*
524 * Track buffer
525 * Because these are written to by the DMA controller, they must
526 * not contain a 64k byte boundary crossing, or data will be
527 * corrupted/lost.
528 */
529static char *floppy_track_buffer;
530static int max_buffer_sectors;
531
532static int *errors;
Jesper Juhl06f748c2007-10-16 23:30:57 -0700533typedef void (*done_f)(int);
Stephen Hemminger3b06c212010-07-20 20:09:00 -0600534static const struct cont_t {
Joe Perches48c8cee2010-03-10 15:20:45 -0800535 void (*interrupt)(void);
536 /* this is called after the interrupt of the
537 * main command */
Jesper Juhl06f748c2007-10-16 23:30:57 -0700538 void (*redo)(void); /* this is called to retry the operation */
539 void (*error)(void); /* this is called to tally an error */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 done_f done; /* this is called to say if the operation has
541 * succeeded/failed */
542} *cont;
543
544static void floppy_ready(void);
545static void floppy_start(void);
546static void process_fd_request(void);
547static void recalibrate_floppy(void);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200548static void floppy_shutdown(struct work_struct *);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Philippe De Muyter5a74db02009-02-18 14:48:36 -0800550static int floppy_request_regions(int);
551static void floppy_release_regions(int);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552static int floppy_grab_irq_and_dma(void);
553static void floppy_release_irq_and_dma(void);
554
555/*
556 * The "reset" variable should be tested whenever an interrupt is scheduled,
557 * after the commands have been sent. This is to ensure that the driver doesn't
558 * get wedged when the interrupt doesn't come because of a failed command.
559 * reset doesn't need to be tested before sending commands, because
560 * output_byte is automatically disabled when reset is set.
561 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562static void reset_fdc(void);
Christoph Hellwig4a6f3d42020-09-08 16:53:32 +0200563static int floppy_revalidate(struct gendisk *disk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564
565/*
566 * These are global variables, as that's the easiest way to give
567 * information to interrupts. They are the data used for the current
568 * request.
569 */
Joe Perches48c8cee2010-03-10 15:20:45 -0800570#define NO_TRACK -1
571#define NEED_1_RECAL -2
572#define NEED_2_RECAL -3
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573
Stephen Hemminger575cfc62010-06-15 13:21:11 +0200574static atomic_t usage_count = ATOMIC_INIT(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576/* buffer related variables */
577static int buffer_track = -1;
578static int buffer_drive = -1;
579static int buffer_min = -1;
580static int buffer_max = -1;
581
582/* fdc related variables, should end up in a struct */
583static struct floppy_fdc_state fdc_state[N_FDC];
Willy Tarreaue83995c2020-03-01 20:55:55 +0100584static int current_fdc; /* current fdc */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200586static struct workqueue_struct *floppy_wq;
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588static struct floppy_struct *_floppy = floppy_type;
589static unsigned char current_drive;
590static long current_count_sectors;
591static unsigned char fsector_t; /* sector in track */
592static unsigned char in_sector_offset; /* offset within physical sector,
593 * expressed in units of 512 bytes */
594
Willy Tarreaue2032462020-03-01 20:55:54 +0100595static inline unsigned char fdc_inb(int fdc, int reg)
Willy Tarreauac701862020-03-01 20:55:53 +0100596{
Willy Tarreaue72e8bf2020-03-31 11:40:32 +0200597 return fd_inb(fdc_state[fdc].address, reg);
Willy Tarreauac701862020-03-01 20:55:53 +0100598}
599
Willy Tarreaue2032462020-03-01 20:55:54 +0100600static inline void fdc_outb(unsigned char value, int fdc, int reg)
Willy Tarreauac701862020-03-01 20:55:53 +0100601{
Willy Tarreaue72e8bf2020-03-31 11:40:32 +0200602 fd_outb(value, fdc_state[fdc].address, reg);
Willy Tarreauac701862020-03-01 20:55:53 +0100603}
604
Pekka Enberg2b51dca2010-11-08 14:44:34 +0100605static inline bool drive_no_geom(int drive)
606{
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100607 return !current_type[drive] && !ITYPE(drive_state[drive].fd_device);
Pekka Enberg2b51dca2010-11-08 14:44:34 +0100608}
609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610#ifndef fd_eject
611static inline int fd_eject(int drive)
612{
613 return -EINVAL;
614}
615#endif
616
617/*
618 * Debugging
619 * =========
620 */
621#ifdef DEBUGT
622static long unsigned debugtimer;
623
624static inline void set_debugt(void)
625{
626 debugtimer = jiffies;
627}
628
Joe Perchesded28632010-03-10 15:21:09 -0800629static inline void debugt(const char *func, const char *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630{
Willy Tarreau031faab2020-02-24 22:23:48 +0100631 if (drive_params[current_drive].flags & DEBUGT)
Joe Perchesded28632010-03-10 15:21:09 -0800632 pr_info("%s:%s dtime=%lu\n", func, msg, jiffies - debugtimer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633}
634#else
635static inline void set_debugt(void) { }
Joe Perchesded28632010-03-10 15:21:09 -0800636static inline void debugt(const char *func, const char *msg) { }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637#endif /* DEBUGT */
638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200640static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641static const char *timeout_message;
642
Joe Perches275176b2010-03-10 15:21:06 -0800643static void is_alive(const char *func, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
645 /* this routine checks whether the floppy driver is "alive" */
Joe Perchesc5297302010-03-10 15:20:58 -0800646 if (test_bit(0, &fdc_busy) && command_status < 2 &&
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200647 !delayed_work_pending(&fd_timeout)) {
Joe Perches275176b2010-03-10 15:21:06 -0800648 DPRINT("%s: timeout handler died. %s\n", func, message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 }
650}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651
Joe Perches48c8cee2010-03-10 15:20:45 -0800652static void (*do_floppy)(void) = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654#define OLOGSIZE 20
655
Joe Perches48c8cee2010-03-10 15:20:45 -0800656static void (*lasthandler)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657static unsigned long interruptjiffies;
658static unsigned long resultjiffies;
659static int resultsize;
660static unsigned long lastredo;
661
662static struct output_log {
663 unsigned char data;
664 unsigned char status;
665 unsigned long jiffies;
666} output_log[OLOGSIZE];
667
668static int output_log_pos;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670#define MAXTIMEOUT -2
671
Joe Perches73507e62010-03-10 15:21:03 -0800672static void __reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673{
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200674 unsigned long delay;
675
Eric Sesterhenn / Snakebyte4acb3e22007-05-23 13:58:15 -0700676 if (drive < 0 || drive >= N_DRIVE) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200677 delay = 20UL * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 drive = 0;
679 } else
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100680 delay = drive_params[drive].timeout;
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200681
Tejun Heoe7c2f962012-08-21 13:18:24 -0700682 mod_delayed_work(floppy_wq, &fd_timeout, delay);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100683 if (drive_params[drive].flags & FD_DEBUG)
Joe Perches73507e62010-03-10 15:21:03 -0800684 DPRINT("reschedule timeout %s\n", message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 timeout_message = message;
686}
687
Joe Perches73507e62010-03-10 15:21:03 -0800688static void reschedule_timeout(int drive, const char *message)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689{
690 unsigned long flags;
691
692 spin_lock_irqsave(&floppy_lock, flags);
Joe Perches73507e62010-03-10 15:21:03 -0800693 __reschedule_timeout(drive, message);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 spin_unlock_irqrestore(&floppy_lock, flags);
695}
696
Joe Perches48c8cee2010-03-10 15:20:45 -0800697#define INFBOUND(a, b) (a) = max_t(int, a, b)
698#define SUPBOUND(a, b) (a) = min_t(int, a, b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
700/*
701 * Bottom half floppy driver.
702 * ==========================
703 *
704 * This part of the file contains the code talking directly to the hardware,
705 * and also the main service loop (seek-configure-spinup-command)
706 */
707
708/*
709 * disk change.
710 * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
711 * and the last_checked date.
712 *
713 * last_checked is the date of the last check which showed 'no disk change'
714 * FD_DISK_CHANGE is set under two conditions:
715 * 1. The floppy has been changed after some i/o to that floppy already
716 * took place.
717 * 2. No floppy disk is in the drive. This is done in order to ensure that
718 * requests are quickly flushed in case there is no disk in the drive. It
719 * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
720 * the drive.
721 *
722 * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
723 * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
724 * each seek. If a disk is present, the disk change line should also be
725 * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
726 * change line is set, this means either that no disk is in the drive, or
727 * that it has been removed since the last seek.
728 *
729 * This means that we really have a third possibility too:
730 * The floppy has been changed after the last seek.
731 */
732
733static int disk_change(int drive)
734{
735 int fdc = FDC(drive);
Jesper Juhl06f748c2007-10-16 23:30:57 -0700736
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100737 if (time_before(jiffies, drive_state[drive].select_date + drive_params[drive].select_delay))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 DPRINT("WARNING disk change called early\n");
Willy Tarreaude6048b2020-02-24 22:23:43 +0100739 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))) ||
740 (fdc_state[fdc].dor & 3) != UNIT(drive) || fdc != FDC(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 DPRINT("probing disk change on unselected drive\n");
742 DPRINT("drive=%d fdc=%d dor=%x\n", drive, FDC(drive),
Willy Tarreaude6048b2020-02-24 22:23:43 +0100743 (unsigned int)fdc_state[fdc].dor);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100746 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800747 "checking disk change line for drive %d\n", drive);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100748 debug_dcl(drive_params[drive].flags, "jiffies=%lu\n", jiffies);
749 debug_dcl(drive_params[drive].flags, "disk change line=%x\n",
Willy Tarreauac701862020-03-01 20:55:53 +0100750 fdc_inb(fdc, FD_DIR) & 0x80);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100751 debug_dcl(drive_params[drive].flags, "flags=%lx\n",
752 drive_state[drive].flags);
Joe Perches87f530d2010-03-10 15:20:54 -0800753
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100754 if (drive_params[drive].flags & FD_BROKEN_DCL)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100755 return test_bit(FD_DISK_CHANGED_BIT,
756 &drive_state[drive].flags);
Willy Tarreauac701862020-03-01 20:55:53 +0100757 if ((fdc_inb(fdc, FD_DIR) ^ drive_params[drive].flags) & 0x80) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100758 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -0800759 /* verify write protection */
760
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100761 if (drive_state[drive].maxblock) /* mark it changed */
762 set_bit(FD_DISK_CHANGED_BIT,
763 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
765 /* invalidate its geometry */
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100766 if (drive_state[drive].keep_data >= 0) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100767 if ((drive_params[drive].flags & FTD_MSG) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 current_type[drive] != NULL)
Joe Perches891eda82010-03-10 15:21:05 -0800769 DPRINT("Disk type is undefined after disk change\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 current_type[drive] = NULL;
771 floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE << 1;
772 }
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 return 1;
775 } else {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100776 drive_state[drive].last_checked = jiffies;
777 clear_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
779 return 0;
780}
781
782static inline int is_selected(int dor, int unit)
783{
784 return ((dor & (0x10 << unit)) && (dor & 3) == unit);
785}
786
Joe Perches57584c52010-03-10 15:21:00 -0800787static bool is_ready_state(int status)
788{
789 int state = status & (STATUS_READY | STATUS_DIR | STATUS_DMA);
790 return state == STATUS_READY;
791}
792
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793static int set_dor(int fdc, char mask, char data)
794{
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700795 unsigned char unit;
796 unsigned char drive;
797 unsigned char newdor;
798 unsigned char olddor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Willy Tarreaude6048b2020-02-24 22:23:43 +0100800 if (fdc_state[fdc].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 return -1;
802
Willy Tarreaude6048b2020-02-24 22:23:43 +0100803 olddor = fdc_state[fdc].dor;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 newdor = (olddor & mask) | data;
805 if (newdor != olddor) {
806 unit = olddor & 0x3;
807 if (is_selected(olddor, unit) && !is_selected(newdor, unit)) {
808 drive = REVDRIVE(fdc, unit);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100809 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -0800810 "calling disk change from set_dor\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 disk_change(drive);
812 }
Willy Tarreaude6048b2020-02-24 22:23:43 +0100813 fdc_state[fdc].dor = newdor;
Willy Tarreauac701862020-03-01 20:55:53 +0100814 fdc_outb(newdor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815
816 unit = newdor & 0x3;
817 if (!is_selected(olddor, unit) && is_selected(newdor, unit)) {
818 drive = REVDRIVE(fdc, unit);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100819 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 }
821 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 return olddor;
823}
824
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200825static void twaddle(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200827 if (drive_params[drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 return;
Willy Tarreauc1f710b2020-03-31 11:40:40 +0200829 fdc_outb(fdc_state[fdc].dor & ~(0x10 << UNIT(drive)),
830 fdc, FD_DOR);
831 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
832 drive_state[drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833}
834
Joe Perches57584c52010-03-10 15:21:00 -0800835/*
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200836 * Reset all driver information about the specified fdc.
Joe Perches57584c52010-03-10 15:21:00 -0800837 * This is needed after a reset, and after a raw command.
838 */
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200839static void reset_fdc_info(int fdc, int mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840{
841 int drive;
842
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200843 fdc_state[fdc].spec1 = fdc_state[fdc].spec2 = -1;
844 fdc_state[fdc].need_configure = 1;
845 fdc_state[fdc].perp_mode = 1;
846 fdc_state[fdc].rawcmd = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 for (drive = 0; drive < N_DRIVE; drive++)
Willy Tarreauf3e0dc12020-03-31 11:40:41 +0200848 if (FDC(drive) == fdc &&
Willy Tarreaue83995c2020-03-01 20:55:55 +0100849 (mode || drive_state[drive].track != NEED_1_RECAL))
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100850 drive_state[drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851}
852
Willy Tarreauca1b4092020-04-10 12:19:04 +0200853/*
854 * selects the fdc and drive, and enables the fdc's input/dma.
855 * Both current_drive and current_fdc are changed to match the new drive.
856 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857static void set_fdc(int drive)
858{
Willy Tarreauca1b4092020-04-10 12:19:04 +0200859 unsigned int fdc;
Linus Torvalds2e90ca62020-02-21 12:43:35 -0800860
Willy Tarreauca1b4092020-04-10 12:19:04 +0200861 if (drive < 0 || drive >= N_DRIVE) {
862 pr_info("bad drive value %d\n", drive);
863 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 }
Willy Tarreauca1b4092020-04-10 12:19:04 +0200865
866 fdc = FDC(drive);
867 if (fdc >= N_FDC) {
Joe Perchesb46df352010-03-10 15:20:46 -0800868 pr_info("bad fdc value\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 return;
870 }
Willy Tarreauca1b4092020-04-10 12:19:04 +0200871
872 set_dor(fdc, ~0, 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873#if N_FDC > 1
Willy Tarreauca1b4092020-04-10 12:19:04 +0200874 set_dor(1 - fdc, ~8, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875#endif
Willy Tarreauca1b4092020-04-10 12:19:04 +0200876 if (fdc_state[fdc].rawcmd == 2)
877 reset_fdc_info(fdc, 1);
878 if (fdc_inb(fdc, FD_STATUS) != STATUS_READY)
879 fdc_state[fdc].reset = 1;
880
881 current_drive = drive;
882 current_fdc = fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883}
884
Willy Tarreauca1b4092020-04-10 12:19:04 +0200885/*
886 * locks the driver.
887 * Both current_drive and current_fdc are changed to match the new drive.
888 */
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +0100889static int lock_fdc(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200891 if (WARN(atomic_read(&usage_count) == 0,
892 "Trying to lock fdc while usage count=0\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Stephen Hemmingerb862f262010-06-15 13:21:11 +0200895 if (wait_event_interruptible(fdc_wait, !test_and_set_bit(0, &fdc_busy)))
896 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 command_status = FD_COMMAND_NONE;
899
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200900 reschedule_timeout(drive, "lock fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 set_fdc(drive);
902 return 0;
903}
904
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905/* unlocks the driver */
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +0200906static void unlock_fdc(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 if (!test_bit(0, &fdc_busy))
909 DPRINT("FDC access conflict!\n");
910
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200911 raw_cmd = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 command_status = FD_COMMAND_NONE;
Tejun Heo136b5722012-08-21 13:18:24 -0700913 cancel_delayed_work(&fd_timeout);
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200914 do_floppy = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 cont = NULL;
916 clear_bit(0, &fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 wake_up(&fdc_wait);
918}
919
920/* switches the motor off after a given timeout */
Kees Cookb1bf4212017-10-04 17:49:29 -0700921static void motor_off_callback(struct timer_list *t)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922{
Kees Cookb1bf4212017-10-04 17:49:29 -0700923 unsigned long nr = t - motor_off_timer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 unsigned char mask = ~(0x10 << UNIT(nr));
925
Kees Cookb1bf4212017-10-04 17:49:29 -0700926 if (WARN_ON_ONCE(nr >= N_DRIVE))
927 return;
928
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 set_dor(FDC(nr), mask, 0);
930}
931
932/* schedules motor off */
933static void floppy_off(unsigned int drive)
934{
935 unsigned long volatile delta;
Jesper Juhlfdc1ca82007-10-16 23:30:58 -0700936 int fdc = FDC(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Willy Tarreaude6048b2020-02-24 22:23:43 +0100938 if (!(fdc_state[fdc].dor & (0x10 << UNIT(drive))))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 return;
940
941 del_timer(motor_off_timer + drive);
942
943 /* make spindle stop in a position which minimizes spinup time
944 * next time */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100945 if (drive_params[drive].rps) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100946 delta = jiffies - drive_state[drive].first_read_date + HZ -
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100947 drive_params[drive].spindown_offset;
948 delta = ((delta * drive_params[drive].rps) % HZ) / drive_params[drive].rps;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 motor_off_timer[drive].expires =
Willy Tarreau1ce9ae92020-02-24 22:23:45 +0100950 jiffies + drive_params[drive].spindown - delta;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 }
952 add_timer(motor_off_timer + drive);
953}
954
955/*
956 * cycle through all N_DRIVE floppy drives, for disk change testing.
957 * stopping at current drive. This is done before any long operation, to
958 * be sure to have up to date disk change information.
959 */
960static void scandrives(void)
961{
Jesper Juhl06f748c2007-10-16 23:30:57 -0700962 int i;
963 int drive;
964 int saved_drive;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Willy Tarreau031faab2020-02-24 22:23:48 +0100966 if (drive_params[current_drive].select_delay)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 return;
968
969 saved_drive = current_drive;
970 for (i = 0; i < N_DRIVE; i++) {
971 drive = (saved_drive + i + 1) % N_DRIVE;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +0100972 if (drive_state[drive].fd_ref == 0 || drive_params[drive].select_delay != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 continue; /* skip closed drives */
974 set_fdc(drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +0100975 if (!(set_dor(current_fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 (0x10 << UNIT(drive))))
977 /* switch the motor off again, if it was off to
978 * begin with */
Willy Tarreaue83995c2020-03-01 20:55:55 +0100979 set_dor(current_fdc, ~(0x10 << UNIT(drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 }
981 set_fdc(saved_drive);
982}
983
984static void empty(void)
985{
986}
987
Tejun Heo75ddb382014-03-07 10:24:48 -0500988static void (*floppy_work_fn)(void);
989
990static void floppy_work_workfn(struct work_struct *work)
991{
992 floppy_work_fn();
993}
994
995static DECLARE_WORK(floppy_work, floppy_work_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Joe Perches48c8cee2010-03-10 15:20:45 -0800997static void schedule_bh(void (*handler)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998{
Jiri Kosina070ad7e2012-05-18 13:50:25 +0200999 WARN_ON(work_pending(&floppy_work));
1000
Tejun Heo75ddb382014-03-07 10:24:48 -05001001 floppy_work_fn = handler;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001002 queue_work(floppy_wq, &floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003}
1004
Tejun Heo75ddb382014-03-07 10:24:48 -05001005static void (*fd_timer_fn)(void) = NULL;
1006
1007static void fd_timer_workfn(struct work_struct *work)
1008{
1009 fd_timer_fn();
1010}
1011
1012static DECLARE_DELAYED_WORK(fd_timer, fd_timer_workfn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014static void cancel_activity(void)
1015{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 do_floppy = NULL;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001017 cancel_delayed_work_sync(&fd_timer);
1018 cancel_work_sync(&floppy_work);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019}
1020
1021/* this function makes sure that the disk stays in the drive during the
1022 * transfer */
Tejun Heo75ddb382014-03-07 10:24:48 -05001023static void fd_watchdog(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024{
Willy Tarreau031faab2020-02-24 22:23:48 +01001025 debug_dcl(drive_params[current_drive].flags,
1026 "calling disk change from watchdog\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027
1028 if (disk_change(current_drive)) {
1029 DPRINT("disk removed during i/o\n");
1030 cancel_activity();
1031 cont->done(0);
1032 reset_fdc();
1033 } else {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001034 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001035 fd_timer_fn = fd_watchdog;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001036 queue_delayed_work(floppy_wq, &fd_timer, HZ / 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 }
1038}
1039
1040static void main_command_interrupt(void)
1041{
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001042 cancel_delayed_work(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 cont->interrupt();
1044}
1045
1046/* waits for a delay (spinup or select) to pass */
Tejun Heo75ddb382014-03-07 10:24:48 -05001047static int fd_wait_for_completion(unsigned long expires,
1048 void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001050 if (fdc_state[current_fdc].reset) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 reset_fdc(); /* do the reset during sleep to win time
1052 * if we don't need to sleep, it's a good
1053 * occasion anyways */
1054 return 1;
1055 }
1056
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001057 if (time_before(jiffies, expires)) {
1058 cancel_delayed_work(&fd_timer);
Tejun Heo75ddb382014-03-07 10:24:48 -05001059 fd_timer_fn = function;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001060 queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 return 1;
1062 }
1063 return 0;
1064}
1065
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066static void setup_DMA(void)
1067{
1068 unsigned long f;
1069
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 if (raw_cmd->length == 0) {
Denis Efremov29ac6762020-05-01 16:44:13 +03001071 print_hex_dump(KERN_INFO, "zero dma transfer size: ",
1072 DUMP_PREFIX_NONE, 16, 1,
Denis Efremov08362752020-05-01 16:44:16 +03001073 raw_cmd->fullcmd, raw_cmd->cmd_count, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001075 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 return;
1077 }
1078 if (((unsigned long)raw_cmd->kernel_data) % 512) {
Joe Perchesb46df352010-03-10 15:20:46 -08001079 pr_info("non aligned address: %p\n", raw_cmd->kernel_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001081 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return;
1083 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 f = claim_dma_lock();
1085 fd_disable_dma();
1086#ifdef fd_dma_setup
1087 if (fd_dma_setup(raw_cmd->kernel_data, raw_cmd->length,
1088 (raw_cmd->flags & FD_RAW_READ) ?
Willy Tarreaue83995c2020-03-01 20:55:55 +01001089 DMA_MODE_READ : DMA_MODE_WRITE,
1090 fdc_state[current_fdc].address) < 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 release_dma_lock(f);
1092 cont->done(0);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001093 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 return;
1095 }
1096 release_dma_lock(f);
1097#else
1098 fd_clear_dma_ff();
1099 fd_cacheflush(raw_cmd->kernel_data, raw_cmd->length);
1100 fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ) ?
1101 DMA_MODE_READ : DMA_MODE_WRITE);
1102 fd_set_dma_addr(raw_cmd->kernel_data);
1103 fd_set_dma_count(raw_cmd->length);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001104 virtual_dma_port = fdc_state[current_fdc].address;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 fd_enable_dma();
1106 release_dma_lock(f);
1107#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108}
1109
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001110static void show_floppy(int fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111
1112/* waits until the fdc becomes ready */
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001113static int wait_til_ready(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001115 int status;
1116 int counter;
1117
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001118 if (fdc_state[fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 return -1;
1120 for (counter = 0; counter < 10000; counter++) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001121 status = fdc_inb(fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 if (status & STATUS_READY)
1123 return status;
1124 }
Joe Perches29f1c782010-03-10 15:21:00 -08001125 if (initialized) {
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001126 DPRINT("Getstatus times out (%x) on fdc %d\n", status, fdc);
1127 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 }
Willy Tarreau5ea00bf2020-03-31 11:40:43 +02001129 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001130 return -1;
1131}
1132
1133/* sends a command byte to the fdc */
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001134static int output_byte(int fdc, char byte)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135{
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001136 int status = wait_til_ready(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001138 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001140
1141 if (is_ready_state(status)) {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001142 fdc_outb(byte, fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 output_log[output_log_pos].data = byte;
1144 output_log[output_log_pos].status = status;
1145 output_log[output_log_pos].jiffies = jiffies;
1146 output_log_pos = (output_log_pos + 1) % OLOGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 return 0;
1148 }
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001149 fdc_state[fdc].reset = 1;
Joe Perches29f1c782010-03-10 15:21:00 -08001150 if (initialized) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 DPRINT("Unable to send byte %x to FDC. Fdc=%x Status=%x\n",
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001152 byte, fdc, status);
1153 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 }
1155 return -1;
1156}
1157
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158/* gets the response from the fdc */
Willy Tarreau96dad772020-03-31 11:40:45 +02001159static int result(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001161 int i;
1162 int status = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163
Denis Efremovbd10a5f2020-05-01 16:44:15 +03001164 for (i = 0; i < FD_RAW_REPLY_SIZE; i++) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001165 status = wait_til_ready(fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001166 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 break;
1168 status &= STATUS_DIR | STATUS_READY | STATUS_BUSY | STATUS_DMA;
1169 if ((status & ~STATUS_BUSY) == STATUS_READY) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001170 resultjiffies = jiffies;
1171 resultsize = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 return i;
1173 }
1174 if (status == (STATUS_DIR | STATUS_READY | STATUS_BUSY))
Willy Tarreau96dad772020-03-31 11:40:45 +02001175 reply_buffer[i] = fdc_inb(fdc, FD_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 else
1177 break;
1178 }
Joe Perches29f1c782010-03-10 15:21:00 -08001179 if (initialized) {
1180 DPRINT("get result error. Fdc=%d Last status=%x Read bytes=%d\n",
Willy Tarreau96dad772020-03-31 11:40:45 +02001181 fdc, status, i);
1182 show_floppy(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 }
Willy Tarreau96dad772020-03-31 11:40:45 +02001184 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 return -1;
1186}
1187
1188#define MORE_OUTPUT -2
1189/* does the fdc need more output? */
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001190static int need_more_output(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191{
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001192 int status = wait_til_ready(fdc);
Jesper Juhl06f748c2007-10-16 23:30:57 -07001193
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08001194 if (status < 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 return -1;
Joe Perches57584c52010-03-10 15:21:00 -08001196
1197 if (is_ready_state(status))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 return MORE_OUTPUT;
Joe Perches57584c52010-03-10 15:21:00 -08001199
Willy Tarreau3ab12a12020-03-31 11:40:46 +02001200 return result(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201}
1202
1203/* Set perpendicular mode as required, based on data rate, if supported.
1204 * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
1205 */
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001206static void perpendicular_mode(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207{
1208 unsigned char perp_mode;
1209
1210 if (raw_cmd->rate & 0x40) {
1211 switch (raw_cmd->rate & 3) {
1212 case 0:
1213 perp_mode = 2;
1214 break;
1215 case 3:
1216 perp_mode = 3;
1217 break;
1218 default:
1219 DPRINT("Invalid data rate for perpendicular mode!\n");
1220 cont->done(0);
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001221 fdc_state[fdc].reset = 1;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001222 /*
1223 * convenient way to return to
1224 * redo without too much hassle
1225 * (deep stack et al.)
1226 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 return;
1228 }
1229 } else
1230 perp_mode = 0;
1231
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001232 if (fdc_state[fdc].perp_mode == perp_mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 return;
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001234 if (fdc_state[fdc].version >= FDC_82077_ORIG) {
1235 output_byte(fdc, FD_PERPENDICULAR);
1236 output_byte(fdc, perp_mode);
1237 fdc_state[fdc].perp_mode = perp_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 } else if (perp_mode) {
1239 DPRINT("perpendicular mode not supported by this FDC.\n");
1240 }
1241} /* perpendicular_mode */
1242
1243static int fifo_depth = 0xa;
1244static int no_fifo;
1245
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001246static int fdc_configure(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247{
1248 /* Turn on FIFO */
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001249 output_byte(fdc, FD_CONFIGURE);
1250 if (need_more_output(fdc) != MORE_OUTPUT)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 return 0;
Willy Tarreaud5da6fa2020-03-31 11:40:48 +02001252 output_byte(fdc, 0);
1253 output_byte(fdc, 0x10 | (no_fifo & 0x20) | (fifo_depth & 0xf));
1254 output_byte(fdc, 0); /* pre-compensation from track 0 upwards */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 return 1;
1256}
1257
1258#define NOMINAL_DTR 500
1259
1260/* Issue a "SPECIFY" command to set the step rate time, head unload time,
1261 * head load time, and DMA disable flag to values needed by floppy.
1262 *
1263 * The value "dtr" is the data transfer rate in Kbps. It is needed
1264 * to account for the data rate-based scaling done by the 82072 and 82077
1265 * FDC types. This parameter is ignored for other types of FDCs (i.e.
1266 * 8272a).
1267 *
1268 * Note that changing the data transfer rate has a (probably deleterious)
1269 * effect on the parameters subject to scaling for 82072/82077 FDCs, so
1270 * fdc_specify is called again after each data transfer rate
1271 * change.
1272 *
1273 * srt: 1000 to 16000 in microseconds
1274 * hut: 16 to 240 milliseconds
1275 * hlt: 2 to 254 milliseconds
1276 *
1277 * These values are rounded up to the next highest available delay time.
1278 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001279static void fdc_specify(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001281 unsigned char spec1;
1282 unsigned char spec2;
1283 unsigned long srt;
1284 unsigned long hlt;
1285 unsigned long hut;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 unsigned long dtr = NOMINAL_DTR;
1287 unsigned long scale_dtr = NOMINAL_DTR;
1288 int hlt_max_code = 0x7f;
1289 int hut_max_code = 0xf;
1290
Willy Tarreau3631a672020-03-31 11:40:49 +02001291 if (fdc_state[fdc].need_configure &&
1292 fdc_state[fdc].version >= FDC_82072A) {
1293 fdc_configure(fdc);
1294 fdc_state[fdc].need_configure = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 }
1296
1297 switch (raw_cmd->rate & 0x03) {
1298 case 3:
1299 dtr = 1000;
1300 break;
1301 case 1:
1302 dtr = 300;
Willy Tarreau3631a672020-03-31 11:40:49 +02001303 if (fdc_state[fdc].version >= FDC_82078) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304 /* chose the default rate table, not the one
1305 * where 1 = 2 Mbps */
Willy Tarreau3631a672020-03-31 11:40:49 +02001306 output_byte(fdc, FD_DRIVESPEC);
1307 if (need_more_output(fdc) == MORE_OUTPUT) {
1308 output_byte(fdc, UNIT(drive));
1309 output_byte(fdc, 0xc0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 }
1311 }
1312 break;
1313 case 2:
1314 dtr = 250;
1315 break;
1316 }
1317
Willy Tarreau3631a672020-03-31 11:40:49 +02001318 if (fdc_state[fdc].version >= FDC_82072) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 scale_dtr = dtr;
1320 hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
1321 hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
1322 }
1323
1324 /* Convert step rate from microseconds to milliseconds and 4 bits */
Willy Tarreau3631a672020-03-31 11:40:49 +02001325 srt = 16 - DIV_ROUND_UP(drive_params[drive].srt * scale_dtr / 1000,
Willy Tarreau031faab2020-02-24 22:23:48 +01001326 NOMINAL_DTR);
Joe Perchesa81ee5442010-03-10 15:20:46 -08001327 if (slow_floppy)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328 srt = srt / 4;
Joe Perchesa81ee5442010-03-10 15:20:46 -08001329
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 SUPBOUND(srt, 0xf);
1331 INFBOUND(srt, 0);
1332
Willy Tarreau3631a672020-03-31 11:40:49 +02001333 hlt = DIV_ROUND_UP(drive_params[drive].hlt * scale_dtr / 2,
Willy Tarreau031faab2020-02-24 22:23:48 +01001334 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335 if (hlt < 0x01)
1336 hlt = 0x01;
1337 else if (hlt > 0x7f)
1338 hlt = hlt_max_code;
1339
Willy Tarreau3631a672020-03-31 11:40:49 +02001340 hut = DIV_ROUND_UP(drive_params[drive].hut * scale_dtr / 16,
Willy Tarreau031faab2020-02-24 22:23:48 +01001341 NOMINAL_DTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 if (hut < 0x1)
1343 hut = 0x1;
1344 else if (hut > 0xf)
1345 hut = hut_max_code;
1346
1347 spec1 = (srt << 4) | hut;
1348 spec2 = (hlt << 1) | (use_virtual_dma & 1);
1349
1350 /* If these parameters did not change, just return with success */
Willy Tarreau3631a672020-03-31 11:40:49 +02001351 if (fdc_state[fdc].spec1 != spec1 ||
1352 fdc_state[fdc].spec2 != spec2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001353 /* Go ahead and set spec1 and spec2 */
Willy Tarreau3631a672020-03-31 11:40:49 +02001354 output_byte(fdc, FD_SPECIFY);
1355 output_byte(fdc, fdc_state[fdc].spec1 = spec1);
1356 output_byte(fdc, fdc_state[fdc].spec2 = spec2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357 }
1358} /* fdc_specify */
1359
1360/* Set the FDC's data transfer rate on behalf of the specified drive.
1361 * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
1362 * of the specify command (i.e. using the fdc_specify function).
1363 */
1364static int fdc_dtr(void)
1365{
1366 /* If data rate not already set to desired value, set it. */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001367 if ((raw_cmd->rate & 3) == fdc_state[current_fdc].dtr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 return 0;
1369
1370 /* Set dtr */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001371 fdc_outb(raw_cmd->rate & 3, current_fdc, FD_DCR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
1373 /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
1374 * need a stabilization period of several milliseconds to be
1375 * enforced after data rate changes before R/W operations.
1376 * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
1377 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001378 fdc_state[current_fdc].dtr = raw_cmd->rate & 3;
Tejun Heo75ddb382014-03-07 10:24:48 -05001379 return fd_wait_for_completion(jiffies + 2UL * HZ / 100, floppy_ready);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380} /* fdc_dtr */
1381
1382static void tell_sector(void)
1383{
Joe Perchesb46df352010-03-10 15:20:46 -08001384 pr_cont(": track %d, head %d, sector %d, size %d",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001385 reply_buffer[R_TRACK], reply_buffer[R_HEAD],
1386 reply_buffer[R_SECTOR],
1387 reply_buffer[R_SIZECODE]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388} /* tell_sector */
1389
Joe Perchesb46df352010-03-10 15:20:46 -08001390static void print_errors(void)
1391{
1392 DPRINT("");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001393 if (reply_buffer[ST0] & ST0_ECE) {
Joe Perchesb46df352010-03-10 15:20:46 -08001394 pr_cont("Recalibrate failed!");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001395 } else if (reply_buffer[ST2] & ST2_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001396 pr_cont("data CRC error");
1397 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001398 } else if (reply_buffer[ST1] & ST1_CRC) {
Joe Perchesb46df352010-03-10 15:20:46 -08001399 pr_cont("CRC error");
1400 tell_sector();
Willy Tarreau8fb38452020-02-24 22:23:52 +01001401 } else if ((reply_buffer[ST1] & (ST1_MAM | ST1_ND)) ||
1402 (reply_buffer[ST2] & ST2_MAM)) {
Joe Perchesb46df352010-03-10 15:20:46 -08001403 if (!probing) {
1404 pr_cont("sector not found");
1405 tell_sector();
1406 } else
1407 pr_cont("probe failed...");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001408 } else if (reply_buffer[ST2] & ST2_WC) { /* seek error */
Joe Perchesb46df352010-03-10 15:20:46 -08001409 pr_cont("wrong cylinder");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001410 } else if (reply_buffer[ST2] & ST2_BC) { /* cylinder marked as bad */
Joe Perchesb46df352010-03-10 15:20:46 -08001411 pr_cont("bad cylinder");
1412 } else {
1413 pr_cont("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001414 reply_buffer[ST0], reply_buffer[ST1],
1415 reply_buffer[ST2]);
Joe Perchesb46df352010-03-10 15:20:46 -08001416 tell_sector();
1417 }
1418 pr_cont("\n");
1419}
1420
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421/*
1422 * OK, this error interpreting routine is called after a
1423 * DMA read/write has succeeded
1424 * or failed, so we check the results, and copy any buffers.
1425 * hhb: Added better error reporting.
1426 * ak: Made this into a separate routine.
1427 */
1428static int interpret_errors(void)
1429{
1430 char bad;
1431
1432 if (inr != 7) {
Joe Perches891eda82010-03-10 15:21:05 -08001433 DPRINT("-- FDC reply error\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001434 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 return 1;
1436 }
1437
1438 /* check IC to find cause of interrupt */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001439 switch (reply_buffer[ST0] & ST0_INTR) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440 case 0x40: /* error occurred during command execution */
Willy Tarreau8fb38452020-02-24 22:23:52 +01001441 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 return 0; /* occurs with pseudo-DMA */
1443 bad = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001444 if (reply_buffer[ST1] & ST1_WP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 DPRINT("Drive is write protected\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001446 clear_bit(FD_DISK_WRITABLE_BIT,
1447 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 cont->done(0);
1449 bad = 2;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001450 } else if (reply_buffer[ST1] & ST1_ND) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001451 set_bit(FD_NEED_TWADDLE_BIT,
1452 &drive_state[current_drive].flags);
Willy Tarreau8fb38452020-02-24 22:23:52 +01001453 } else if (reply_buffer[ST1] & ST1_OR) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001454 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 DPRINT("Over/Underrun - retrying\n");
1456 bad = 0;
Willy Tarreau031faab2020-02-24 22:23:48 +01001457 } else if (*errors >= drive_params[current_drive].max_errors.reporting) {
Joe Perchesb46df352010-03-10 15:20:46 -08001458 print_errors();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001460 if (reply_buffer[ST2] & ST2_WC || reply_buffer[ST2] & ST2_BC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 /* wrong cylinder => recal */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001462 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 return bad;
1464 case 0x80: /* invalid command given */
1465 DPRINT("Invalid FDC command given!\n");
1466 cont->done(0);
1467 return 2;
1468 case 0xc0:
1469 DPRINT("Abnormal termination caused by polling\n");
1470 cont->error();
1471 return 2;
1472 default: /* (0) Normal command termination */
1473 return 0;
1474 }
1475}
1476
1477/*
1478 * This routine is called when everything should be correctly set up
1479 * for the transfer (i.e. floppy motor is on, the correct floppy is
1480 * selected, and the head is sitting on the right track).
1481 */
1482static void setup_rw_floppy(void)
1483{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001484 int i;
1485 int r;
1486 int flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 unsigned long ready_date;
Tejun Heo75ddb382014-03-07 10:24:48 -05001488 void (*function)(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489
1490 flags = raw_cmd->flags;
1491 if (flags & (FD_RAW_READ | FD_RAW_WRITE))
1492 flags |= FD_RAW_INTR;
1493
1494 if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001495 ready_date = drive_state[current_drive].spinup_date + drive_params[current_drive].spinup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496 /* If spinup will take a long time, rerun scandrives
1497 * again just before spinup completion. Beware that
1498 * after scandrives, we must again wait for selection.
1499 */
Willy Tarreau031faab2020-02-24 22:23:48 +01001500 if (time_after(ready_date, jiffies + drive_params[current_drive].select_delay)) {
1501 ready_date -= drive_params[current_drive].select_delay;
Tejun Heo75ddb382014-03-07 10:24:48 -05001502 function = floppy_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 } else
Tejun Heo75ddb382014-03-07 10:24:48 -05001504 function = setup_rw_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505
1506 /* wait until the floppy is spinning fast enough */
1507 if (fd_wait_for_completion(ready_date, function))
1508 return;
1509 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
1511 setup_DMA();
1512
1513 if (flags & FD_RAW_INTR)
1514 do_floppy = main_command_interrupt;
1515
1516 r = 0;
1517 for (i = 0; i < raw_cmd->cmd_count; i++)
Denis Efremov08362752020-05-01 16:44:16 +03001518 r |= output_byte(current_fdc, raw_cmd->fullcmd[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
Joe Perchesded28632010-03-10 15:21:09 -08001520 debugt(__func__, "rw_command");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 if (r) {
1523 cont->error();
1524 reset_fdc();
1525 return;
1526 }
1527
1528 if (!(flags & FD_RAW_INTR)) {
Willy Tarreau96dad772020-03-31 11:40:45 +02001529 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 cont->interrupt();
1531 } else if (flags & FD_RAW_NEED_DISK)
Tejun Heo75ddb382014-03-07 10:24:48 -05001532 fd_watchdog();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533}
1534
1535static int blind_seek;
1536
1537/*
1538 * This is the routine called after every seek (or recalibrate) interrupt
1539 * from the floppy controller.
1540 */
1541static void seek_interrupt(void)
1542{
Joe Perchesded28632010-03-10 15:21:09 -08001543 debugt(__func__, "");
Willy Tarreau8fb38452020-02-24 22:23:52 +01001544 if (inr != 2 || (reply_buffer[ST0] & 0xF8) != 0x20) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 DPRINT("seek failed\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001546 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 cont->error();
1548 cont->redo();
1549 return;
1550 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001551 if (drive_state[current_drive].track >= 0 &&
1552 drive_state[current_drive].track != reply_buffer[ST1] &&
1553 !blind_seek) {
Willy Tarreau031faab2020-02-24 22:23:48 +01001554 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001555 "clearing NEWCHANGE flag because of effective seek\n");
Willy Tarreau031faab2020-02-24 22:23:48 +01001556 debug_dcl(drive_params[current_drive].flags, "jiffies=%lu\n",
1557 jiffies);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001558 clear_bit(FD_DISK_NEWCHANGE_BIT,
1559 &drive_state[current_drive].flags);
Joe Perchese0298532010-03-10 15:20:55 -08001560 /* effective seek */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001561 drive_state[current_drive].select_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 }
Willy Tarreau8fb38452020-02-24 22:23:52 +01001563 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 floppy_ready();
1565}
1566
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001567static void check_wp(int fdc, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568{
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001569 if (test_bit(FD_VERIFY_BIT, &drive_state[drive].flags)) {
Joe Perchese0298532010-03-10 15:20:55 -08001570 /* check write protection */
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001571 output_byte(fdc, FD_GETSTATUS);
1572 output_byte(fdc, UNIT(drive));
1573 if (result(fdc) != 1) {
1574 fdc_state[fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 return;
1576 }
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001577 clear_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001578 clear_bit(FD_NEED_TWADDLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001579 &drive_state[drive].flags);
1580 debug_dcl(drive_params[drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001581 "checking whether disk is write protected\n");
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001582 debug_dcl(drive_params[drive].flags, "wp=%x\n",
Willy Tarreau8fb38452020-02-24 22:23:52 +01001583 reply_buffer[ST3] & 0x40);
1584 if (!(reply_buffer[ST3] & 0x40))
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001585 set_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001586 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001588 clear_bit(FD_DISK_WRITABLE_BIT,
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001589 &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 }
1591}
1592
1593static void seek_floppy(void)
1594{
1595 int track;
1596
1597 blind_seek = 0;
1598
Willy Tarreau031faab2020-02-24 22:23:48 +01001599 debug_dcl(drive_params[current_drive].flags,
1600 "calling disk change from %s\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001601
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001602 if (!test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 disk_change(current_drive) && (raw_cmd->flags & FD_RAW_NEED_DISK)) {
1604 /* the media changed flag should be cleared after the seek.
1605 * If it isn't, this means that there is really no disk in
1606 * the drive.
1607 */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001608 set_bit(FD_DISK_CHANGED_BIT,
1609 &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001610 cont->done(0);
1611 cont->redo();
1612 return;
1613 }
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001614 if (drive_state[current_drive].track <= NEED_1_RECAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615 recalibrate_floppy();
1616 return;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001617 } else if (test_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 (raw_cmd->flags & FD_RAW_NEED_DISK) &&
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001619 (drive_state[current_drive].track <= NO_TRACK || drive_state[current_drive].track == raw_cmd->track)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 /* we seek to clear the media-changed condition. Does anybody
1621 * know a more elegant way, which works on all drives? */
1622 if (raw_cmd->track)
1623 track = raw_cmd->track - 1;
1624 else {
Willy Tarreau031faab2020-02-24 22:23:48 +01001625 if (drive_params[current_drive].flags & FD_SILENT_DCL_CLEAR) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001626 set_dor(current_fdc, ~(0x10 << UNIT(current_drive)), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 blind_seek = 1;
1628 raw_cmd->flags |= FD_RAW_NEED_SEEK;
1629 }
1630 track = 1;
1631 }
1632 } else {
Willy Tarreauc7af70b2020-03-31 11:40:50 +02001633 check_wp(current_fdc, current_drive);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001634 if (raw_cmd->track != drive_state[current_drive].track &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 (raw_cmd->flags & FD_RAW_NEED_SEEK))
1636 track = raw_cmd->track;
1637 else {
1638 setup_rw_floppy();
1639 return;
1640 }
1641 }
1642
1643 do_floppy = seek_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001644 output_byte(current_fdc, FD_SEEK);
1645 output_byte(current_fdc, UNIT(current_drive));
1646 if (output_byte(current_fdc, track) < 0) {
Joe Perches2300f902010-03-10 15:20:49 -08001647 reset_fdc();
1648 return;
1649 }
Joe Perchesded28632010-03-10 15:21:09 -08001650 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001651}
1652
1653static void recal_interrupt(void)
1654{
Joe Perchesded28632010-03-10 15:21:09 -08001655 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 if (inr != 2)
Willy Tarreaue83995c2020-03-01 20:55:55 +01001657 fdc_state[current_fdc].reset = 1;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001658 else if (reply_buffer[ST0] & ST0_ECE) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001659 switch (drive_state[current_drive].track) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001660 case NEED_1_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001661 debugt(__func__, "need 1 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662 /* after a second recalibrate, we still haven't
1663 * reached track 0. Probably no drive. Raise an
1664 * error, as failing immediately might upset
1665 * computers possessed by the Devil :-) */
1666 cont->error();
1667 cont->redo();
1668 return;
1669 case NEED_2_RECAL:
Joe Perchesded28632010-03-10 15:21:09 -08001670 debugt(__func__, "need 2 recal");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671 /* If we already did a recalibrate,
1672 * and we are not at track 0, this
1673 * means we have moved. (The only way
1674 * not to move at recalibration is to
1675 * be already at track 0.) Clear the
1676 * new change flag */
Willy Tarreau031faab2020-02-24 22:23:48 +01001677 debug_dcl(drive_params[current_drive].flags,
Joe Perches87f530d2010-03-10 15:20:54 -08001678 "clearing NEWCHANGE flag because of second recalibrate\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001680 clear_bit(FD_DISK_NEWCHANGE_BIT,
1681 &drive_state[current_drive].flags);
1682 drive_state[current_drive].select_date = jiffies;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05001683 fallthrough;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 default:
Joe Perchesded28632010-03-10 15:21:09 -08001685 debugt(__func__, "default");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 /* Recalibrate moves the head by at
1687 * most 80 steps. If after one
1688 * recalibrate we don't have reached
1689 * track 0, this might mean that we
1690 * started beyond track 80. Try
1691 * again. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001692 drive_state[current_drive].track = NEED_1_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 break;
1694 }
1695 } else
Willy Tarreau8fb38452020-02-24 22:23:52 +01001696 drive_state[current_drive].track = reply_buffer[ST1];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697 floppy_ready();
1698}
1699
1700static void print_result(char *message, int inr)
1701{
1702 int i;
1703
1704 DPRINT("%s ", message);
1705 if (inr >= 0)
1706 for (i = 0; i < inr; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001707 pr_cont("repl[%d]=%x ", i, reply_buffer[i]);
1708 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709}
1710
1711/* interrupt handler. Note that this can be called externally on the Sparc */
David Howells7d12e782006-10-05 14:55:46 +01001712irqreturn_t floppy_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714 int do_print;
1715 unsigned long f;
Jesper Juhl06f748c2007-10-16 23:30:57 -07001716 void (*handler)(void) = do_floppy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717
1718 lasthandler = handler;
1719 interruptjiffies = jiffies;
1720
1721 f = claim_dma_lock();
1722 fd_disable_dma();
1723 release_dma_lock(f);
1724
Linus Torvalds1da177e2005-04-16 15:20:36 -07001725 do_floppy = NULL;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001726 if (current_fdc >= N_FDC || fdc_state[current_fdc].address == -1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 /* we don't even know which FDC is the culprit */
Joe Perchesb46df352010-03-10 15:20:46 -08001728 pr_info("DOR0=%x\n", fdc_state[0].dor);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001729 pr_info("floppy interrupt on bizarre fdc %d\n", current_fdc);
Sakari Ailusd75f7732019-03-25 21:32:28 +02001730 pr_info("handler=%ps\n", handler);
Joe Perches275176b2010-03-10 15:21:06 -08001731 is_alive(__func__, "bizarre fdc");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 return IRQ_NONE;
1733 }
1734
Willy Tarreaue83995c2020-03-01 20:55:55 +01001735 fdc_state[current_fdc].reset = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 /* We have to clear the reset flag here, because apparently on boxes
1737 * with level triggered interrupts (PS/2, Sparc, ...), it is needed to
Willy Tarreaude6048b2020-02-24 22:23:43 +01001738 * emit SENSEI's to clear the interrupt line. And fdc_state[fdc].reset
1739 * blocks the emission of the SENSEI's.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740 * It is OK to emit floppy commands because we are in an interrupt
1741 * handler here, and thus we have to fear no interference of other
1742 * activity.
1743 */
1744
Joe Perches29f1c782010-03-10 15:21:00 -08001745 do_print = !handler && print_unex && initialized;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001746
Willy Tarreau96dad772020-03-31 11:40:45 +02001747 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 if (do_print)
1749 print_result("unexpected interrupt", inr);
1750 if (inr == 0) {
1751 int max_sensei = 4;
1752 do {
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001753 output_byte(current_fdc, FD_SENSEI);
Willy Tarreau96dad772020-03-31 11:40:45 +02001754 inr = result(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 if (do_print)
1756 print_result("sensei", inr);
1757 max_sensei--;
Willy Tarreau8fb38452020-02-24 22:23:52 +01001758 } while ((reply_buffer[ST0] & 0x83) != UNIT(current_drive) &&
Joe Perchesc5297302010-03-10 15:20:58 -08001759 inr == 2 && max_sensei);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760 }
1761 if (!handler) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001762 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763 return IRQ_NONE;
1764 }
1765 schedule_bh(handler);
Joe Perches275176b2010-03-10 15:21:06 -08001766 is_alive(__func__, "normal interrupt end");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767
1768 /* FIXME! Was it really for us? */
1769 return IRQ_HANDLED;
1770}
1771
1772static void recalibrate_floppy(void)
1773{
Joe Perchesded28632010-03-10 15:21:09 -08001774 debugt(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775 do_floppy = recal_interrupt;
Willy Tarreauf8a8e0f2020-03-31 11:40:44 +02001776 output_byte(current_fdc, FD_RECALIBRATE);
1777 if (output_byte(current_fdc, UNIT(current_drive)) < 0)
Joe Perches2300f902010-03-10 15:20:49 -08001778 reset_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779}
1780
1781/*
1782 * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
1783 */
1784static void reset_interrupt(void)
1785{
Joe Perchesded28632010-03-10 15:21:09 -08001786 debugt(__func__, "");
Willy Tarreau96dad772020-03-31 11:40:45 +02001787 result(current_fdc); /* get the status ready for set_fdc */
Willy Tarreaue83995c2020-03-01 20:55:55 +01001788 if (fdc_state[current_fdc].reset) {
Sakari Ailusd75f7732019-03-25 21:32:28 +02001789 pr_info("reset set in interrupt, calling %ps\n", cont->error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 cont->error(); /* a reset just after a reset. BAD! */
1791 }
1792 cont->redo();
1793}
1794
1795/*
1796 * reset is done by pulling bit 2 of DOR low for a while (old FDCs),
Willy Tarreau12aebfa2020-03-31 11:40:54 +02001797 * or by setting the self clearing bit 7 of STATUS (newer FDCs).
1798 * This WILL trigger an interrupt, causing the handlers in the current
1799 * cont's ->redo() to be called via reset_interrupt().
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 */
1801static void reset_fdc(void)
1802{
1803 unsigned long flags;
1804
1805 do_floppy = reset_interrupt;
Willy Tarreaue83995c2020-03-01 20:55:55 +01001806 fdc_state[current_fdc].reset = 0;
Willy Tarreauf3e0dc12020-03-31 11:40:41 +02001807 reset_fdc_info(current_fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808
1809 /* Pseudo-DMA may intercept 'reset finished' interrupt. */
1810 /* Irrelevant for systems with true DMA (i386). */
1811
1812 flags = claim_dma_lock();
1813 fd_disable_dma();
1814 release_dma_lock(flags);
1815
Willy Tarreaue83995c2020-03-01 20:55:55 +01001816 if (fdc_state[current_fdc].version >= FDC_82072A)
1817 fdc_outb(0x80 | (fdc_state[current_fdc].dtr & 3),
1818 current_fdc, FD_STATUS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 else {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001820 fdc_outb(fdc_state[current_fdc].dor & ~0x04, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001821 udelay(FD_RESET_DELAY);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001822 fdc_outb(fdc_state[current_fdc].dor, current_fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 }
1824}
1825
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001826static void show_floppy(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001827{
1828 int i;
1829
Joe Perchesb46df352010-03-10 15:20:46 -08001830 pr_info("\n");
1831 pr_info("floppy driver state\n");
1832 pr_info("-------------------\n");
Sakari Ailusd75f7732019-03-25 21:32:28 +02001833 pr_info("now=%lu last interrupt=%lu diff=%lu last called handler=%ps\n",
Joe Perchesb46df352010-03-10 15:20:46 -08001834 jiffies, interruptjiffies, jiffies - interruptjiffies,
1835 lasthandler);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836
Joe Perchesb46df352010-03-10 15:20:46 -08001837 pr_info("timeout_message=%s\n", timeout_message);
1838 pr_info("last output bytes:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001839 for (i = 0; i < OLOGSIZE; i++)
Joe Perchesb46df352010-03-10 15:20:46 -08001840 pr_info("%2x %2x %lu\n",
1841 output_log[(i + output_log_pos) % OLOGSIZE].data,
1842 output_log[(i + output_log_pos) % OLOGSIZE].status,
1843 output_log[(i + output_log_pos) % OLOGSIZE].jiffies);
1844 pr_info("last result at %lu\n", resultjiffies);
1845 pr_info("last redo_fd_request at %lu\n", lastredo);
1846 print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
1847 reply_buffer, resultsize, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001849 pr_info("status=%x\n", fdc_inb(fdc, FD_STATUS));
Joe Perchesb46df352010-03-10 15:20:46 -08001850 pr_info("fdc_busy=%lu\n", fdc_busy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001851 if (do_floppy)
Sakari Ailusd75f7732019-03-25 21:32:28 +02001852 pr_info("do_floppy=%ps\n", do_floppy);
David Howells365970a2006-11-22 14:54:49 +00001853 if (work_pending(&floppy_work))
Sakari Ailusd75f7732019-03-25 21:32:28 +02001854 pr_info("floppy_work.func=%ps\n", floppy_work.func);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001855 if (delayed_work_pending(&fd_timer))
1856 pr_info("delayed work.function=%p expires=%ld\n",
1857 fd_timer.work.func,
1858 fd_timer.timer.expires - jiffies);
1859 if (delayed_work_pending(&fd_timeout))
1860 pr_info("timer_function=%p expires=%ld\n",
1861 fd_timeout.work.func,
1862 fd_timeout.timer.expires - jiffies);
1863
Joe Perchesb46df352010-03-10 15:20:46 -08001864 pr_info("cont=%p\n", cont);
1865 pr_info("current_req=%p\n", current_req);
1866 pr_info("command_status=%d\n", command_status);
1867 pr_info("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868}
1869
Jiri Kosina070ad7e2012-05-18 13:50:25 +02001870static void floppy_shutdown(struct work_struct *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871{
1872 unsigned long flags;
1873
Joe Perches29f1c782010-03-10 15:21:00 -08001874 if (initialized)
Willy Tarreau6d494ed02020-03-31 11:40:42 +02001875 show_floppy(current_fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001876 cancel_activity();
1877
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 flags = claim_dma_lock();
1879 fd_disable_dma();
1880 release_dma_lock(flags);
1881
1882 /* avoid dma going to a random drive after shutdown */
1883
Joe Perches29f1c782010-03-10 15:21:00 -08001884 if (initialized)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 DPRINT("floppy timeout called\n");
Willy Tarreaue83995c2020-03-01 20:55:55 +01001886 fdc_state[current_fdc].reset = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 if (cont) {
1888 cont->done(0);
1889 cont->redo(); /* this will recall reset when needed */
1890 } else {
Joe Perchesb46df352010-03-10 15:20:46 -08001891 pr_info("no cont in shutdown!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892 process_fd_request();
1893 }
Joe Perches275176b2010-03-10 15:21:06 -08001894 is_alive(__func__, "");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001895}
1896
Linus Torvalds1da177e2005-04-16 15:20:36 -07001897/* start motor, check media-changed condition and write protection */
Jesper Juhl06f748c2007-10-16 23:30:57 -07001898static int start_motor(void (*function)(void))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899{
Jesper Juhl06f748c2007-10-16 23:30:57 -07001900 int mask;
1901 int data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902
1903 mask = 0xfc;
1904 data = UNIT(current_drive);
1905 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01001906 if (!(fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001907 set_debugt();
1908 /* no read since this drive is running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001909 drive_state[current_drive].first_read_date = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001910 /* note motor start time if motor is not yet running */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001911 drive_state[current_drive].spinup_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 data |= (0x10 << UNIT(current_drive));
1913 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01001914 } else if (fdc_state[current_fdc].dor & (0x10 << UNIT(current_drive)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 mask &= ~(0x10 << UNIT(current_drive));
1916
1917 /* starts motor and selects floppy */
1918 del_timer(motor_off_timer + current_drive);
Willy Tarreaue83995c2020-03-01 20:55:55 +01001919 set_dor(current_fdc, mask, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920
1921 /* wait_for_completion also schedules reset if needed. */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001922 return fd_wait_for_completion(drive_state[current_drive].select_date + drive_params[current_drive].select_delay,
Tejun Heo75ddb382014-03-07 10:24:48 -05001923 function);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924}
1925
1926static void floppy_ready(void)
1927{
Willy Tarreaue83995c2020-03-01 20:55:55 +01001928 if (fdc_state[current_fdc].reset) {
Joe Perches045f9832010-03-10 15:20:47 -08001929 reset_fdc();
1930 return;
1931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 if (start_motor(floppy_ready))
1933 return;
1934 if (fdc_dtr())
1935 return;
1936
Willy Tarreau031faab2020-02-24 22:23:48 +01001937 debug_dcl(drive_params[current_drive].flags,
1938 "calling disk change from floppy_ready\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
Willy Tarreau031faab2020-02-24 22:23:48 +01001940 disk_change(current_drive) && !drive_params[current_drive].select_delay)
Willy Tarreauc1f710b2020-03-31 11:40:40 +02001941 twaddle(current_fdc, current_drive); /* this clears the dcl on certain
Joe Perchesbb57f0c62010-03-10 15:20:50 -08001942 * drive/controller combinations */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943
1944#ifdef fd_chose_dma_mode
1945 if ((raw_cmd->flags & FD_RAW_READ) || (raw_cmd->flags & FD_RAW_WRITE)) {
1946 unsigned long flags = claim_dma_lock();
1947 fd_chose_dma_mode(raw_cmd->kernel_data, raw_cmd->length);
1948 release_dma_lock(flags);
1949 }
1950#endif
1951
1952 if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)) {
Willy Tarreau197c7ff2020-03-31 11:40:47 +02001953 perpendicular_mode(current_fdc);
Willy Tarreau3631a672020-03-31 11:40:49 +02001954 fdc_specify(current_fdc, current_drive); /* must be done here because of hut, hlt ... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955 seek_floppy();
1956 } else {
1957 if ((raw_cmd->flags & FD_RAW_READ) ||
1958 (raw_cmd->flags & FD_RAW_WRITE))
Willy Tarreau3631a672020-03-31 11:40:49 +02001959 fdc_specify(current_fdc, current_drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960 setup_rw_floppy();
1961 }
1962}
1963
1964static void floppy_start(void)
1965{
Willy Tarreau99ba6cc2020-04-10 12:19:03 +02001966 reschedule_timeout(current_drive, "floppy start");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967
1968 scandrives();
Willy Tarreau031faab2020-02-24 22:23:48 +01001969 debug_dcl(drive_params[current_drive].flags,
1970 "setting NEWCHANGE in floppy_start\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01001971 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001972 floppy_ready();
1973}
1974
1975/*
1976 * ========================================================================
1977 * here ends the bottom half. Exported routines are:
1978 * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
1979 * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
1980 * Initialization also uses output_byte, result, set_dor, floppy_interrupt
1981 * and set_dor.
1982 * ========================================================================
1983 */
1984/*
1985 * General purpose continuations.
1986 * ==============================
1987 */
1988
1989static void do_wakeup(void)
1990{
Joe Perches73507e62010-03-10 15:21:03 -08001991 reschedule_timeout(MAXTIMEOUT, "do wakeup");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 cont = NULL;
1993 command_status += 2;
1994 wake_up(&command_done);
1995}
1996
Stephen Hemminger3b06c212010-07-20 20:09:00 -06001997static const struct cont_t wakeup_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001998 .interrupt = empty,
1999 .redo = do_wakeup,
2000 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07002001 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002};
2003
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002004static const struct cont_t intr_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002005 .interrupt = empty,
2006 .redo = process_fd_request,
2007 .error = empty,
Jesper Juhl06f748c2007-10-16 23:30:57 -07002008 .done = (done_f)empty
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009};
2010
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002011/* schedules handler, waiting for completion. May be interrupted, will then
2012 * return -EINTR, in which case the driver will automatically be unlocked.
2013 */
Joe Perches74f63f42010-03-10 15:20:58 -08002014static int wait_til_done(void (*handler)(void), bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015{
2016 int ret;
2017
2018 schedule_bh(handler);
2019
Stephen Hemmingerb862f262010-06-15 13:21:11 +02002020 if (interruptible)
2021 wait_event_interruptible(command_done, command_status >= 2);
2022 else
2023 wait_event(command_done, command_status >= 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
2025 if (command_status < 2) {
2026 cancel_activity();
2027 cont = &intr_cont;
2028 reset_fdc();
2029 return -EINTR;
2030 }
2031
Willy Tarreaue83995c2020-03-01 20:55:55 +01002032 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033 command_status = FD_COMMAND_ERROR;
2034 if (command_status == FD_COMMAND_OKAY)
2035 ret = 0;
2036 else
2037 ret = -EIO;
2038 command_status = FD_COMMAND_NONE;
2039 return ret;
2040}
2041
2042static void generic_done(int result)
2043{
2044 command_status = result;
2045 cont = &wakeup_cont;
2046}
2047
2048static void generic_success(void)
2049{
2050 cont->done(1);
2051}
2052
2053static void generic_failure(void)
2054{
2055 cont->done(0);
2056}
2057
2058static void success_and_wakeup(void)
2059{
2060 generic_success();
2061 cont->redo();
2062}
2063
2064/*
2065 * formatting and rw support.
2066 * ==========================
2067 */
2068
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002069static int next_valid_format(int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002070{
2071 int probed_format;
2072
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002073 probed_format = drive_state[drive].probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074 while (1) {
Denis Efremov9c4c5a22020-05-01 16:44:14 +03002075 if (probed_format >= FD_AUTODETECT_SIZE ||
2076 !drive_params[drive].autodetect[probed_format]) {
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002077 drive_state[drive].probed_format = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002078 return 1;
2079 }
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002080 if (floppy_type[drive_params[drive].autodetect[probed_format]].sect) {
2081 drive_state[drive].probed_format = probed_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 return 0;
2083 }
2084 probed_format++;
2085 }
2086}
2087
2088static void bad_flp_intr(void)
2089{
2090 int err_count;
2091
2092 if (probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002093 drive_state[current_drive].probed_format++;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002094 if (!next_valid_format(current_drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 return;
2096 }
2097 err_count = ++(*errors);
Willy Tarreau2a348752020-02-24 22:23:50 +01002098 INFBOUND(write_errors[current_drive].badness, err_count);
Willy Tarreau031faab2020-02-24 22:23:48 +01002099 if (err_count > drive_params[current_drive].max_errors.abort)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 cont->done(0);
Willy Tarreau031faab2020-02-24 22:23:48 +01002101 if (err_count > drive_params[current_drive].max_errors.reset)
Willy Tarreaue83995c2020-03-01 20:55:55 +01002102 fdc_state[current_fdc].reset = 1;
Willy Tarreau031faab2020-02-24 22:23:48 +01002103 else if (err_count > drive_params[current_drive].max_errors.recal)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002104 drive_state[current_drive].track = NEED_2_RECAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105}
2106
2107static void set_floppy(int drive)
2108{
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01002109 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07002110
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 if (type)
2112 _floppy = floppy_type + type;
2113 else
2114 _floppy = current_type[drive];
2115}
2116
2117/*
2118 * formatting support.
2119 * ===================
2120 */
2121static void format_interrupt(void)
2122{
2123 switch (interpret_errors()) {
2124 case 1:
2125 cont->error();
2126 case 2:
2127 break;
2128 case 0:
2129 cont->done(1);
2130 }
2131 cont->redo();
2132}
2133
Joe Perches48c8cee2010-03-10 15:20:45 -08002134#define FM_MODE(x, y) ((y) & ~(((x)->rate & 0x80) >> 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135#define CT(x) ((x) | 0xc0)
Joe Perches48c8cee2010-03-10 15:20:45 -08002136
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137static void setup_format_params(int track)
2138{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002139 int n;
2140 int il;
2141 int count;
2142 int head_shift;
2143 int track_shift;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144 struct fparm {
2145 unsigned char track, head, sect, size;
2146 } *here = (struct fparm *)floppy_track_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
2148 raw_cmd = &default_raw_cmd;
2149 raw_cmd->track = track;
2150
Joe Perches48c8cee2010-03-10 15:20:45 -08002151 raw_cmd->flags = (FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
2152 FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002153 raw_cmd->rate = _floppy->rate & 0x43;
2154 raw_cmd->cmd_count = NR_F;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002155 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_FORMAT);
2156 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, format_req.head);
2157 raw_cmd->cmd[F_SIZECODE] = FD_SIZECODE(_floppy);
2158 raw_cmd->cmd[F_SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[F_SIZECODE];
2159 raw_cmd->cmd[F_GAP] = _floppy->fmt_gap;
2160 raw_cmd->cmd[F_FILL] = FD_FILL_BYTE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161
2162 raw_cmd->kernel_data = floppy_track_buffer;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002163 raw_cmd->length = 4 * raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164
Willy Tarreau76dabe72020-02-24 22:23:51 +01002165 if (!raw_cmd->cmd[F_SECT_PER_TRACK])
Denis Efremovf3554ae2019-07-12 21:55:20 +03002166 return;
2167
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 /* allow for about 30ms for data transport per track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002169 head_shift = (raw_cmd->cmd[F_SECT_PER_TRACK] + 5) / 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170
2171 /* a ``cylinder'' is two tracks plus a little stepping time */
2172 track_shift = 2 * head_shift + 3;
2173
2174 /* position of logical sector 1 on this track */
2175 n = (track_shift * format_req.track + head_shift * format_req.head)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002176 % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177
2178 /* determine interleave */
2179 il = 1;
2180 if (_floppy->fmt_gap < 0x22)
2181 il++;
2182
2183 /* initialize field */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002184 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 here[count].track = format_req.track;
2186 here[count].head = format_req.head;
2187 here[count].sect = 0;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002188 here[count].size = raw_cmd->cmd[F_SIZECODE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002189 }
2190 /* place logical sectors */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002191 for (count = 1; count <= raw_cmd->cmd[F_SECT_PER_TRACK]; ++count) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002192 here[n].sect = count;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002193 n = (n + il) % raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002194 if (here[n].sect) { /* sector busy, find next free sector */
2195 ++n;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002196 if (n >= raw_cmd->cmd[F_SECT_PER_TRACK]) {
2197 n -= raw_cmd->cmd[F_SECT_PER_TRACK];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198 while (here[n].sect)
2199 ++n;
2200 }
2201 }
2202 }
Keith Wansbrough9e491842008-09-22 14:57:17 -07002203 if (_floppy->stretch & FD_SECTBASEMASK) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002204 for (count = 0; count < raw_cmd->cmd[F_SECT_PER_TRACK]; count++)
Keith Wansbrough9e491842008-09-22 14:57:17 -07002205 here[count].sect += FD_SECTBASE(_floppy) - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002206 }
2207}
2208
2209static void redo_format(void)
2210{
2211 buffer_track = -1;
2212 setup_format_params(format_req.track << STRETCH(_floppy));
2213 floppy_start();
Joe Perchesded28632010-03-10 15:21:09 -08002214 debugt(__func__, "queue format request");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215}
2216
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002217static const struct cont_t format_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 .interrupt = format_interrupt,
2219 .redo = redo_format,
2220 .error = bad_flp_intr,
2221 .done = generic_done
2222};
2223
2224static int do_format(int drive, struct format_descr *tmp_format_req)
2225{
2226 int ret;
2227
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01002228 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08002229 return -EINTR;
2230
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231 set_floppy(drive);
2232 if (!_floppy ||
Willy Tarreau031faab2020-02-24 22:23:48 +01002233 _floppy->track > drive_params[current_drive].tracks ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 tmp_format_req->track >= _floppy->track ||
2235 tmp_format_req->head >= _floppy->head ||
2236 (_floppy->sect << 2) % (1 << FD_SIZECODE(_floppy)) ||
2237 !_floppy->fmt_gap) {
2238 process_fd_request();
2239 return -EINVAL;
2240 }
2241 format_req = *tmp_format_req;
2242 format_errors = 0;
2243 cont = &format_cont;
2244 errors = &format_errors;
Joe Perches74f63f42010-03-10 15:20:58 -08002245 ret = wait_til_done(redo_format, true);
Joe Perches55eee802010-03-10 15:20:57 -08002246 if (ret == -EINTR)
2247 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248 process_fd_request();
2249 return ret;
2250}
2251
2252/*
2253 * Buffer read/write and support
2254 * =============================
2255 */
2256
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002257static void floppy_end_request(struct request *req, blk_status_t error)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258{
2259 unsigned int nr_sectors = current_count_sectors;
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002260 unsigned int drive = (unsigned long)req->rq_disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261
2262 /* current_count_sectors can be zero if transfer failed */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002263 if (error)
Tejun Heo83096eb2009-05-07 22:24:39 +09002264 nr_sectors = blk_rq_cur_sectors(req);
Omar Sandovala9f38e12018-10-15 09:21:34 -06002265 if (blk_update_request(req, error, nr_sectors << 9))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 return;
Omar Sandovala9f38e12018-10-15 09:21:34 -06002267 __blk_mq_end_request(req, error);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
2269 /* We're done with the request */
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002270 floppy_off(drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 current_req = NULL;
2272}
2273
2274/* new request_done. Can handle physical sectors which are smaller than a
2275 * logical buffer */
2276static void request_done(int uptodate)
2277{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278 struct request *req = current_req;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 int block;
Joe Perches73507e62010-03-10 15:21:03 -08002280 char msg[sizeof("request done ") + sizeof(int) * 3];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281
2282 probing = 0;
Joe Perches73507e62010-03-10 15:21:03 -08002283 snprintf(msg, sizeof(msg), "request done %d", uptodate);
2284 reschedule_timeout(MAXTIMEOUT, msg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 if (!req) {
Joe Perchesb46df352010-03-10 15:20:46 -08002287 pr_info("floppy.c: no request in request_done\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288 return;
2289 }
2290
2291 if (uptodate) {
2292 /* maintain values for invalidation on geometry
2293 * change */
Tejun Heo83096eb2009-05-07 22:24:39 +09002294 block = current_count_sectors + blk_rq_pos(req);
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002295 INFBOUND(drive_state[current_drive].maxblock, block);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 if (block > _floppy->sect)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002297 drive_state[current_drive].maxtrack = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Kiyoshi Ueda1c5093b2008-01-28 10:36:21 +01002299 floppy_end_request(req, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300 } else {
2301 if (rq_data_dir(req) == WRITE) {
2302 /* record write error information */
Willy Tarreau2a348752020-02-24 22:23:50 +01002303 write_errors[current_drive].write_errors++;
2304 if (write_errors[current_drive].write_errors == 1) {
2305 write_errors[current_drive].first_error_sector = blk_rq_pos(req);
2306 write_errors[current_drive].first_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307 }
Willy Tarreau2a348752020-02-24 22:23:50 +01002308 write_errors[current_drive].last_error_sector = blk_rq_pos(req);
2309 write_errors[current_drive].last_error_generation = drive_state[current_drive].generation;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 }
Christoph Hellwig2a842ac2017-06-03 09:38:04 +02002311 floppy_end_request(req, BLK_STS_IOERR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312 }
2313}
2314
2315/* Interrupt handler evaluating the result of the r/w operation */
2316static void rw_interrupt(void)
2317{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002318 int eoc;
2319 int ssize;
2320 int heads;
2321 int nr_sectors;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322
Willy Tarreau8fb38452020-02-24 22:23:52 +01002323 if (reply_buffer[R_HEAD] >= 2) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 /* some Toshiba floppy controllers occasionnally seem to
2325 * return bogus interrupts after read/write operations, which
2326 * can be recognized by a bad head number (>= 2) */
2327 return;
2328 }
2329
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002330 if (!drive_state[current_drive].first_read_date)
2331 drive_state[current_drive].first_read_date = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332
2333 nr_sectors = 0;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002334 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335
Willy Tarreau8fb38452020-02-24 22:23:52 +01002336 if (reply_buffer[ST1] & ST1_EOC)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002337 eoc = 1;
2338 else
2339 eoc = 0;
2340
Willy Tarreau76dabe72020-02-24 22:23:51 +01002341 if (raw_cmd->cmd[COMMAND] & 0x80)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 heads = 2;
2343 else
2344 heads = 1;
2345
Willy Tarreau8fb38452020-02-24 22:23:52 +01002346 nr_sectors = (((reply_buffer[R_TRACK] - raw_cmd->cmd[TRACK]) * heads +
2347 reply_buffer[R_HEAD] - raw_cmd->cmd[HEAD]) * raw_cmd->cmd[SECT_PER_TRACK] +
2348 reply_buffer[R_SECTOR] - raw_cmd->cmd[SECTOR] + eoc) << raw_cmd->cmd[SIZECODE] >> 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 if (nr_sectors / ssize >
Julia Lawall061837b2008-09-22 14:57:16 -07002351 DIV_ROUND_UP(in_sector_offset + current_count_sectors, ssize)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002352 DPRINT("long rw: %x instead of %lx\n",
2353 nr_sectors, current_count_sectors);
Willy Tarreau8fb38452020-02-24 22:23:52 +01002354 pr_info("rs=%d s=%d\n", reply_buffer[R_SECTOR],
2355 raw_cmd->cmd[SECTOR]);
2356 pr_info("rh=%d h=%d\n", reply_buffer[R_HEAD],
2357 raw_cmd->cmd[HEAD]);
2358 pr_info("rt=%d t=%d\n", reply_buffer[R_TRACK],
2359 raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002360 pr_info("heads=%d eoc=%d\n", heads, eoc);
2361 pr_info("spt=%d st=%d ss=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002362 raw_cmd->cmd[SECT_PER_TRACK], fsector_t, ssize);
Joe Perchesb46df352010-03-10 15:20:46 -08002363 pr_info("in_sector_offset=%d\n", in_sector_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365
2366 nr_sectors -= in_sector_offset;
2367 INFBOUND(nr_sectors, 0);
2368 SUPBOUND(current_count_sectors, nr_sectors);
2369
2370 switch (interpret_errors()) {
2371 case 2:
2372 cont->redo();
2373 return;
2374 case 1:
2375 if (!current_count_sectors) {
2376 cont->error();
2377 cont->redo();
2378 return;
2379 }
2380 break;
2381 case 0:
2382 if (!current_count_sectors) {
2383 cont->redo();
2384 return;
2385 }
2386 current_type[current_drive] = _floppy;
2387 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2388 break;
2389 }
2390
2391 if (probing) {
Willy Tarreau031faab2020-02-24 22:23:48 +01002392 if (drive_params[current_drive].flags & FTD_MSG)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002393 DPRINT("Auto-detected floppy type %s in fd%d\n",
2394 _floppy->name, current_drive);
2395 current_type[current_drive] = _floppy;
2396 floppy_sizes[TOMINOR(current_drive)] = _floppy->size;
2397 probing = 0;
2398 }
2399
Christoph Hellwig3d867392021-04-06 08:17:55 +02002400 if (CT(raw_cmd->cmd[COMMAND]) != FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 /* transfer directly from buffer */
2402 cont->done(1);
Christoph Hellwig3d867392021-04-06 08:17:55 +02002403 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002404 buffer_track = raw_cmd->track;
2405 buffer_drive = current_drive;
2406 INFBOUND(buffer_max, nr_sectors + fsector_t);
2407 }
2408 cont->redo();
2409}
2410
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411/* Compute the maximal transfer size */
2412static int transfer_size(int ssize, int max_sector, int max_size)
2413{
2414 SUPBOUND(max_sector, fsector_t + max_size);
2415
2416 /* alignment */
2417 max_sector -= (max_sector % _floppy->sect) % ssize;
2418
2419 /* transfer size, beginning not aligned */
2420 current_count_sectors = max_sector - fsector_t;
2421
2422 return max_sector;
2423}
2424
2425/*
2426 * Move data from/to the track buffer to/from the buffer cache.
2427 */
2428static void copy_buffer(int ssize, int max_sector, int max_sector_2)
2429{
2430 int remaining; /* number of transferred 512-byte sectors */
Kent Overstreet79886132013-11-23 17:19:00 -08002431 struct bio_vec bv;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002432 char *dma_buffer;
NeilBrown5705f702007-09-25 12:35:59 +02002433 int size;
2434 struct req_iterator iter;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435
2436 max_sector = transfer_size(ssize,
2437 min(max_sector, max_sector_2),
Tejun Heo83096eb2009-05-07 22:24:39 +09002438 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439
Willy Tarreau76dabe72020-02-24 22:23:51 +01002440 if (current_count_sectors <= 0 && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Tejun Heo83096eb2009-05-07 22:24:39 +09002441 buffer_max > fsector_t + blk_rq_sectors(current_req))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002442 current_count_sectors = min_t(int, buffer_max - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002443 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
2445 remaining = current_count_sectors << 9;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002446 if (remaining > blk_rq_bytes(current_req) && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002447 DPRINT("in copy buffer\n");
Joe Perchesb46df352010-03-10 15:20:46 -08002448 pr_info("current_count_sectors=%ld\n", current_count_sectors);
2449 pr_info("remaining=%d\n", remaining >> 9);
2450 pr_info("current_req->nr_sectors=%u\n",
2451 blk_rq_sectors(current_req));
2452 pr_info("current_req->current_nr_sectors=%u\n",
2453 blk_rq_cur_sectors(current_req));
2454 pr_info("max_sector=%d\n", max_sector);
2455 pr_info("ssize=%d\n", ssize);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457
2458 buffer_max = max(max_sector, buffer_max);
2459
2460 dma_buffer = floppy_track_buffer + ((fsector_t - buffer_min) << 9);
2461
Tejun Heo1011c1b2009-05-07 22:24:45 +09002462 size = blk_rq_cur_bytes(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463
NeilBrown5705f702007-09-25 12:35:59 +02002464 rq_for_each_segment(bv, current_req, iter) {
2465 if (!remaining)
2466 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467
Kent Overstreet79886132013-11-23 17:19:00 -08002468 size = bv.bv_len;
NeilBrown5705f702007-09-25 12:35:59 +02002469 SUPBOUND(size, remaining);
NeilBrown5705f702007-09-25 12:35:59 +02002470 if (dma_buffer + size >
2471 floppy_track_buffer + (max_buffer_sectors << 10) ||
2472 dma_buffer < floppy_track_buffer) {
2473 DPRINT("buffer overrun in copy buffer %d\n",
Joe Perchesb46df352010-03-10 15:20:46 -08002474 (int)((floppy_track_buffer - dma_buffer) >> 9));
2475 pr_info("fsector_t=%d buffer_min=%d\n",
2476 fsector_t, buffer_min);
2477 pr_info("current_count_sectors=%ld\n",
2478 current_count_sectors);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002479 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Joe Perchesb46df352010-03-10 15:20:46 -08002480 pr_info("read\n");
Willy Tarreau76dabe72020-02-24 22:23:51 +01002481 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
Joe Perchesb46df352010-03-10 15:20:46 -08002482 pr_info("write\n");
NeilBrown5705f702007-09-25 12:35:59 +02002483 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 }
Joe Perches1a23d132010-03-10 15:21:04 -08002485
Willy Tarreau76dabe72020-02-24 22:23:51 +01002486 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Christoph Hellwig3d867392021-04-06 08:17:55 +02002487 memcpy_to_page(bv.bv_page, bv.bv_offset, dma_buffer,
2488 size);
NeilBrown5705f702007-09-25 12:35:59 +02002489 else
Christoph Hellwig3d867392021-04-06 08:17:55 +02002490 memcpy_from_page(dma_buffer, bv.bv_page, bv.bv_offset,
2491 size);
NeilBrown5705f702007-09-25 12:35:59 +02002492
2493 remaining -= size;
2494 dma_buffer += size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496 if (remaining) {
2497 if (remaining > 0)
2498 max_sector -= remaining >> 9;
2499 DPRINT("weirdness: remaining %d\n", remaining >> 9);
2500 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501}
2502
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503/* work around a bug in pseudo DMA
2504 * (on some FDCs) pseudo DMA does not stop when the CPU stops
2505 * sending data. Hence we need a different way to signal the
Willy Tarreau76dabe72020-02-24 22:23:51 +01002506 * transfer length: We use raw_cmd->cmd[SECT_PER_TRACK]. Unfortunately, this
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 * does not work with MT, hence we can only transfer one head at
2508 * a time
2509 */
2510static void virtualdmabug_workaround(void)
2511{
Jesper Juhl06f748c2007-10-16 23:30:57 -07002512 int hard_sectors;
2513 int end_sector;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002514
Willy Tarreau76dabe72020-02-24 22:23:51 +01002515 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
2516 raw_cmd->cmd[COMMAND] &= ~0x80; /* switch off multiple track mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517
Willy Tarreau76dabe72020-02-24 22:23:51 +01002518 hard_sectors = raw_cmd->length >> (7 + raw_cmd->cmd[SIZECODE]);
2519 end_sector = raw_cmd->cmd[SECTOR] + hard_sectors - 1;
2520 if (end_sector > raw_cmd->cmd[SECT_PER_TRACK]) {
Joe Perchesb46df352010-03-10 15:20:46 -08002521 pr_info("too many sectors %d > %d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002522 end_sector, raw_cmd->cmd[SECT_PER_TRACK]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 return;
2524 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002525 raw_cmd->cmd[SECT_PER_TRACK] = end_sector;
2526 /* make sure raw_cmd->cmd[SECT_PER_TRACK]
Joe Perches48c8cee2010-03-10 15:20:45 -08002527 * points to end of transfer */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 }
2529}
2530
2531/*
2532 * Formulate a read/write request.
2533 * this routine decides where to load the data (directly to buffer, or to
2534 * tmp floppy area), how much data to load (the size of the buffer, the whole
2535 * track, or a single sector)
2536 * All floppy_track_buffer handling goes in here. If we ever add track buffer
2537 * allocation on the fly, it should be done here. No other part should need
2538 * modification.
2539 */
2540
2541static int make_raw_rw_request(void)
2542{
2543 int aligned_sector_t;
Jesper Juhl06f748c2007-10-16 23:30:57 -07002544 int max_sector;
2545 int max_size;
2546 int tracksize;
2547 int ssize;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002549 if (WARN(max_buffer_sectors == 0, "VFS: Block I/O scheduled on unopened device\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551
2552 set_fdc((long)current_req->rq_disk->private_data);
2553
2554 raw_cmd = &default_raw_cmd;
Fengguang Wu2fb2ca62012-07-28 19:45:59 +08002555 raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 raw_cmd->cmd_count = NR_RW;
2557 if (rq_data_dir(current_req) == READ) {
2558 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002559 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002560 } else if (rq_data_dir(current_req) == WRITE) {
2561 raw_cmd->flags |= FD_RAW_WRITE;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002562 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_WRITE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563 } else {
Joe Perches275176b2010-03-10 15:21:06 -08002564 DPRINT("%s: unknown command\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 return 0;
2566 }
2567
2568 max_sector = _floppy->sect * _floppy->head;
2569
Willy Tarreau76dabe72020-02-24 22:23:51 +01002570 raw_cmd->cmd[TRACK] = (int)blk_rq_pos(current_req) / max_sector;
Tejun Heo83096eb2009-05-07 22:24:39 +09002571 fsector_t = (int)blk_rq_pos(current_req) % max_sector;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002572 if (_floppy->track && raw_cmd->cmd[TRACK] >= _floppy->track) {
Tejun Heo83096eb2009-05-07 22:24:39 +09002573 if (blk_rq_cur_sectors(current_req) & 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 current_count_sectors = 1;
2575 return 1;
2576 } else
2577 return 0;
2578 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002579 raw_cmd->cmd[HEAD] = fsector_t / _floppy->sect;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580
Keith Wansbrough9e491842008-09-22 14:57:17 -07002581 if (((_floppy->stretch & (FD_SWAPSIDES | FD_SECTBASEMASK)) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002582 test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags)) &&
Joe Perchese0298532010-03-10 15:20:55 -08002583 fsector_t < _floppy->sect)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 max_sector = _floppy->sect;
2585
2586 /* 2M disks have phantom sectors on the first track */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002587 if ((_floppy->rate & FD_2M) && (!raw_cmd->cmd[TRACK]) && (!raw_cmd->cmd[HEAD])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 max_sector = 2 * _floppy->sect / 3;
2589 if (fsector_t >= max_sector) {
2590 current_count_sectors =
2591 min_t(int, _floppy->sect - fsector_t,
Tejun Heo83096eb2009-05-07 22:24:39 +09002592 blk_rq_sectors(current_req));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 return 1;
2594 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002595 raw_cmd->cmd[SIZECODE] = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002596 } else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002597 raw_cmd->cmd[SIZECODE] = FD_SIZECODE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002598 raw_cmd->rate = _floppy->rate & 0x43;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002599 if ((_floppy->rate & FD_2M) &&
2600 (raw_cmd->cmd[TRACK] || raw_cmd->cmd[HEAD]) && raw_cmd->rate == 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601 raw_cmd->rate = 1;
2602
Willy Tarreau76dabe72020-02-24 22:23:51 +01002603 if (raw_cmd->cmd[SIZECODE])
2604 raw_cmd->cmd[SIZECODE2] = 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002605 else
Willy Tarreau76dabe72020-02-24 22:23:51 +01002606 raw_cmd->cmd[SIZECODE2] = 0x80;
2607 raw_cmd->track = raw_cmd->cmd[TRACK] << STRETCH(_floppy);
2608 raw_cmd->cmd[DR_SELECT] = UNIT(current_drive) + PH_HEAD(_floppy, raw_cmd->cmd[HEAD]);
2609 raw_cmd->cmd[GAP] = _floppy->gap;
2610 ssize = DIV_ROUND_UP(1 << raw_cmd->cmd[SIZECODE], 4);
2611 raw_cmd->cmd[SECT_PER_TRACK] = _floppy->sect << 2 >> raw_cmd->cmd[SIZECODE];
2612 raw_cmd->cmd[SECTOR] = ((fsector_t % _floppy->sect) << 2 >> raw_cmd->cmd[SIZECODE]) +
Keith Wansbrough9e491842008-09-22 14:57:17 -07002613 FD_SECTBASE(_floppy);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614
2615 /* tracksize describes the size which can be filled up with sectors
2616 * of size ssize.
2617 */
2618 tracksize = _floppy->sect - _floppy->sect % ssize;
2619 if (tracksize < _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002620 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002621 if (tracksize <= fsector_t % _floppy->sect)
Willy Tarreau76dabe72020-02-24 22:23:51 +01002622 raw_cmd->cmd[SECTOR]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623
2624 /* if we are beyond tracksize, fill up using smaller sectors */
2625 while (tracksize <= fsector_t % _floppy->sect) {
2626 while (tracksize + ssize > _floppy->sect) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002627 raw_cmd->cmd[SIZECODE]--;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628 ssize >>= 1;
2629 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002630 raw_cmd->cmd[SECTOR]++;
2631 raw_cmd->cmd[SECT_PER_TRACK]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 tracksize += ssize;
2633 }
Willy Tarreau76dabe72020-02-24 22:23:51 +01002634 max_sector = raw_cmd->cmd[HEAD] * _floppy->sect + tracksize;
2635 } else if (!raw_cmd->cmd[TRACK] && !raw_cmd->cmd[HEAD] && !(_floppy->rate & FD_2M) && probing) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002636 max_sector = _floppy->sect;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002637 } else if (!raw_cmd->cmd[HEAD] && CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638 /* for virtual DMA bug workaround */
2639 max_sector = _floppy->sect;
2640 }
2641
2642 in_sector_offset = (fsector_t % _floppy->sect) % ssize;
2643 aligned_sector_t = fsector_t - in_sector_offset;
Tejun Heo83096eb2009-05-07 22:24:39 +09002644 max_size = blk_rq_sectors(current_req);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 if ((raw_cmd->track == buffer_track) &&
2646 (current_drive == buffer_drive) &&
2647 (fsector_t >= buffer_min) && (fsector_t < buffer_max)) {
2648 /* data already in track buffer */
Willy Tarreau76dabe72020-02-24 22:23:51 +01002649 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650 copy_buffer(1, max_sector, buffer_max);
2651 return 1;
2652 }
Tejun Heo83096eb2009-05-07 22:24:39 +09002653 } else if (in_sector_offset || blk_rq_sectors(current_req) < ssize) {
Willy Tarreau76dabe72020-02-24 22:23:51 +01002654 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08002655 unsigned int sectors;
2656
2657 sectors = fsector_t + blk_rq_sectors(current_req);
2658 if (sectors > ssize && sectors < ssize + ssize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659 max_size = ssize + ssize;
2660 else
2661 max_size = ssize;
2662 }
2663 raw_cmd->flags &= ~FD_RAW_WRITE;
2664 raw_cmd->flags |= FD_RAW_READ;
Willy Tarreau76dabe72020-02-24 22:23:51 +01002665 raw_cmd->cmd[COMMAND] = FM_MODE(_floppy, FD_READ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 }
2667
Willy Tarreau76dabe72020-02-24 22:23:51 +01002668 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002669 max_size = max_sector; /* unbounded */
2670
2671 /* claim buffer track if needed */
2672 if (buffer_track != raw_cmd->track || /* bad track */
2673 buffer_drive != current_drive || /* bad drive */
2674 fsector_t > buffer_max ||
2675 fsector_t < buffer_min ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002676 ((CT(raw_cmd->cmd[COMMAND]) == FD_READ ||
Tejun Heo83096eb2009-05-07 22:24:39 +09002677 (!in_sector_offset && blk_rq_sectors(current_req) >= ssize)) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678 max_sector > 2 * max_buffer_sectors + buffer_min &&
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002679 max_size + fsector_t > 2 * max_buffer_sectors + buffer_min)) {
2680 /* not enough space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681 buffer_track = -1;
2682 buffer_drive = current_drive;
2683 buffer_max = buffer_min = aligned_sector_t;
2684 }
2685 raw_cmd->kernel_data = floppy_track_buffer +
Joe Perchesbb57f0c62010-03-10 15:20:50 -08002686 ((aligned_sector_t - buffer_min) << 9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687
Willy Tarreau76dabe72020-02-24 22:23:51 +01002688 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 /* copy write buffer to track buffer.
2690 * if we get here, we know that the write
2691 * is either aligned or the data already in the buffer
2692 * (buffer will be overwritten) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693 if (in_sector_offset && buffer_track == -1)
2694 DPRINT("internal error offset !=0 on write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 buffer_track = raw_cmd->track;
2696 buffer_drive = current_drive;
2697 copy_buffer(ssize, max_sector,
2698 2 * max_buffer_sectors + buffer_min);
2699 } else
2700 transfer_size(ssize, max_sector,
2701 2 * max_buffer_sectors + buffer_min -
2702 aligned_sector_t);
2703
2704 /* round up current_count_sectors to get dma xfer size */
2705 raw_cmd->length = in_sector_offset + current_count_sectors;
2706 raw_cmd->length = ((raw_cmd->length - 1) | (ssize - 1)) + 1;
2707 raw_cmd->length <<= 9;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 if ((raw_cmd->length < current_count_sectors << 9) ||
Christoph Hellwig3d867392021-04-06 08:17:55 +02002709 (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710 (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
2711 aligned_sector_t < buffer_min)) ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01002712 raw_cmd->length % (128 << raw_cmd->cmd[SIZECODE]) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 raw_cmd->length <= 0 || current_count_sectors <= 0) {
2714 DPRINT("fractionary current count b=%lx s=%lx\n",
2715 raw_cmd->length, current_count_sectors);
Christoph Hellwig3d867392021-04-06 08:17:55 +02002716 pr_info("addr=%d, length=%ld\n",
2717 (int)((raw_cmd->kernel_data -
2718 floppy_track_buffer) >> 9),
2719 current_count_sectors);
Joe Perchesb46df352010-03-10 15:20:46 -08002720 pr_info("st=%d ast=%d mse=%d msi=%d\n",
2721 fsector_t, aligned_sector_t, max_sector, max_size);
Willy Tarreau76dabe72020-02-24 22:23:51 +01002722 pr_info("ssize=%x SIZECODE=%d\n", ssize, raw_cmd->cmd[SIZECODE]);
Joe Perchesb46df352010-03-10 15:20:46 -08002723 pr_info("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
Willy Tarreau76dabe72020-02-24 22:23:51 +01002724 raw_cmd->cmd[COMMAND], raw_cmd->cmd[SECTOR],
2725 raw_cmd->cmd[HEAD], raw_cmd->cmd[TRACK]);
Joe Perchesb46df352010-03-10 15:20:46 -08002726 pr_info("buffer drive=%d\n", buffer_drive);
2727 pr_info("buffer track=%d\n", buffer_track);
2728 pr_info("buffer_min=%d\n", buffer_min);
2729 pr_info("buffer_max=%d\n", buffer_max);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 return 0;
2731 }
2732
Christoph Hellwig3d867392021-04-06 08:17:55 +02002733 if (raw_cmd->kernel_data < floppy_track_buffer ||
2734 current_count_sectors < 0 ||
2735 raw_cmd->length < 0 ||
2736 raw_cmd->kernel_data + raw_cmd->length >
2737 floppy_track_buffer + (max_buffer_sectors << 10)) {
2738 DPRINT("buffer overrun in schedule dma\n");
2739 pr_info("fsector_t=%d buffer_min=%d current_count=%ld\n",
2740 fsector_t, buffer_min, raw_cmd->length >> 9);
2741 pr_info("current_count_sectors=%ld\n",
2742 current_count_sectors);
2743 if (CT(raw_cmd->cmd[COMMAND]) == FD_READ)
2744 pr_info("read\n");
2745 if (CT(raw_cmd->cmd[COMMAND]) == FD_WRITE)
2746 pr_info("write\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748 }
2749 if (raw_cmd->length == 0) {
2750 DPRINT("zero dma transfer attempted from make_raw_request\n");
2751 return 0;
2752 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753
2754 virtualdmabug_workaround();
2755 return 2;
2756}
2757
Jens Axboe48821182010-09-22 09:32:36 +02002758static int set_next_request(void)
2759{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002760 current_req = list_first_entry_or_null(&floppy_reqs, struct request,
2761 queuelist);
2762 if (current_req) {
2763 current_req->error_count = 0;
2764 list_del_init(&current_req->queuelist);
2765 }
Jens Axboe48821182010-09-22 09:32:36 +02002766 return current_req != NULL;
2767}
2768
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002769/* Starts or continues processing request. Will automatically unlock the
2770 * driver at end of request.
2771 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002772static void redo_fd_request(void)
2773{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774 int drive;
2775 int tmp;
2776
2777 lastredo = jiffies;
2778 if (current_drive < N_DRIVE)
2779 floppy_off(current_drive);
2780
Joe Perches0da31322010-03-10 15:21:03 -08002781do_request:
2782 if (!current_req) {
Jens Axboe48821182010-09-22 09:32:36 +02002783 int pending;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784
Jens Axboe48821182010-09-22 09:32:36 +02002785 spin_lock_irq(&floppy_lock);
2786 pending = set_next_request();
2787 spin_unlock_irq(&floppy_lock);
Jens Axboe48821182010-09-22 09:32:36 +02002788 if (!pending) {
Joe Perches0da31322010-03-10 15:21:03 -08002789 do_floppy = NULL;
2790 unlock_fdc();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002791 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 }
Joe Perches0da31322010-03-10 15:21:03 -08002794 drive = (long)current_req->rq_disk->private_data;
2795 set_fdc(drive);
Willy Tarreau99ba6cc2020-04-10 12:19:03 +02002796 reschedule_timeout(current_drive, "redo fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002797
2798 set_floppy(drive);
2799 raw_cmd = &default_raw_cmd;
2800 raw_cmd->flags = 0;
2801 if (start_motor(redo_fd_request))
2802 return;
2803
2804 disk_change(current_drive);
2805 if (test_bit(current_drive, &fake_change) ||
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002806 test_bit(FD_DISK_CHANGED_BIT, &drive_state[current_drive].flags)) {
Joe Perches0da31322010-03-10 15:21:03 -08002807 DPRINT("disk absent or changed during operation\n");
2808 request_done(0);
2809 goto do_request;
2810 }
2811 if (!_floppy) { /* Autodetection */
2812 if (!probing) {
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002813 drive_state[current_drive].probed_format = 0;
Willy Tarreau43d81bb2020-03-31 11:40:51 +02002814 if (next_valid_format(current_drive)) {
Joe Perches0da31322010-03-10 15:21:03 -08002815 DPRINT("no autodetectable formats\n");
2816 _floppy = NULL;
2817 request_done(0);
2818 goto do_request;
2819 }
2820 }
2821 probing = 1;
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002822 _floppy = floppy_type + drive_params[current_drive].autodetect[drive_state[current_drive].probed_format];
Joe Perches0da31322010-03-10 15:21:03 -08002823 } else
2824 probing = 0;
Christoph Hellwig45908792017-04-20 16:03:12 +02002825 errors = &(current_req->error_count);
Joe Perches0da31322010-03-10 15:21:03 -08002826 tmp = make_raw_rw_request();
2827 if (tmp < 2) {
2828 request_done(tmp);
2829 goto do_request;
2830 }
2831
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002832 if (test_bit(FD_NEED_TWADDLE_BIT, &drive_state[current_drive].flags))
Willy Tarreauc1f710b2020-03-31 11:40:40 +02002833 twaddle(current_fdc, current_drive);
Joe Perches0da31322010-03-10 15:21:03 -08002834 schedule_bh(floppy_start);
Joe Perchesded28632010-03-10 15:21:09 -08002835 debugt(__func__, "queue fd request");
Joe Perches0da31322010-03-10 15:21:03 -08002836 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837}
2838
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002839static const struct cont_t rw_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 .interrupt = rw_interrupt,
2841 .redo = redo_fd_request,
2842 .error = bad_flp_intr,
2843 .done = request_done
2844};
2845
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002846/* schedule the request and automatically unlock the driver on completion */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847static void process_fd_request(void)
2848{
2849 cont = &rw_cont;
2850 schedule_bh(redo_fd_request);
2851}
2852
Omar Sandovala9f38e12018-10-15 09:21:34 -06002853static blk_status_t floppy_queue_rq(struct blk_mq_hw_ctx *hctx,
2854 const struct blk_mq_queue_data *bd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855{
Omar Sandovala9f38e12018-10-15 09:21:34 -06002856 blk_mq_start_request(bd->rq);
2857
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002858 if (WARN(max_buffer_sectors == 0,
2859 "VFS: %s called on non-open device\n", __func__))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002860 return BLK_STS_IOERR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861
Stephen Hemminger01b6b672010-06-15 13:21:11 +02002862 if (WARN(atomic_read(&usage_count) == 0,
Christoph Hellwigaebf5262017-01-31 16:57:31 +01002863 "warning: usage count=0, current_req=%p sect=%ld flags=%llx\n",
2864 current_req, (long)blk_rq_pos(current_req),
Jens Axboe59533162013-05-23 12:25:08 +02002865 (unsigned long long) current_req->cmd_flags))
Omar Sandovala9f38e12018-10-15 09:21:34 -06002866 return BLK_STS_IOERR;
2867
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002868 if (test_and_set_bit(0, &fdc_busy)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 /* fdc busy, this new request will be treated when the
2870 current one is done */
Joe Perches275176b2010-03-10 15:21:06 -08002871 is_alive(__func__, "old request running");
Jiri Kosina263c6152020-05-26 11:49:18 +02002872 return BLK_STS_RESOURCE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 }
Omar Sandovala9f38e12018-10-15 09:21:34 -06002874
Jiri Kosina263c6152020-05-26 11:49:18 +02002875 spin_lock_irq(&floppy_lock);
2876 list_add_tail(&bd->rq->queuelist, &floppy_reqs);
2877 spin_unlock_irq(&floppy_lock);
2878
Jiri Kosina070ad7e2012-05-18 13:50:25 +02002879 command_status = FD_COMMAND_NONE;
2880 __reschedule_timeout(MAXTIMEOUT, "fd_request");
2881 set_fdc(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 process_fd_request();
Joe Perches275176b2010-03-10 15:21:06 -08002883 is_alive(__func__, "");
Omar Sandovala9f38e12018-10-15 09:21:34 -06002884 return BLK_STS_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885}
2886
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002887static const struct cont_t poll_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 .interrupt = success_and_wakeup,
2889 .redo = floppy_ready,
2890 .error = generic_failure,
2891 .done = generic_done
2892};
2893
Joe Perches74f63f42010-03-10 15:20:58 -08002894static int poll_drive(bool interruptible, int flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 /* no auto-sense, just clear dcl */
2897 raw_cmd = &default_raw_cmd;
2898 raw_cmd->flags = flag;
2899 raw_cmd->track = 0;
2900 raw_cmd->cmd_count = 0;
2901 cont = &poll_cont;
Willy Tarreau031faab2020-02-24 22:23:48 +01002902 debug_dcl(drive_params[current_drive].flags,
2903 "setting NEWCHANGE in poll_drive\n");
Willy Tarreau3bd7f872020-02-24 22:23:49 +01002904 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[current_drive].flags);
Joe Perches55eee802010-03-10 15:20:57 -08002905
2906 return wait_til_done(floppy_ready, interruptible);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907}
2908
2909/*
2910 * User triggered reset
2911 * ====================
2912 */
2913
2914static void reset_intr(void)
2915{
Joe Perchesb46df352010-03-10 15:20:46 -08002916 pr_info("weird, reset interrupt called\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917}
2918
Stephen Hemminger3b06c212010-07-20 20:09:00 -06002919static const struct cont_t reset_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002920 .interrupt = reset_intr,
2921 .redo = success_and_wakeup,
2922 .error = generic_failure,
2923 .done = generic_done
2924};
2925
Willy Tarreauca1b4092020-04-10 12:19:04 +02002926/*
2927 * Resets the FDC connected to drive <drive>.
2928 * Both current_drive and current_fdc are changed to match the new drive.
2929 */
Joe Perches74f63f42010-03-10 15:20:58 -08002930static int user_reset_fdc(int drive, int arg, bool interruptible)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931{
2932 int ret;
2933
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01002934 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08002935 return -EINTR;
2936
Linus Torvalds1da177e2005-04-16 15:20:36 -07002937 if (arg == FD_RESET_ALWAYS)
Willy Tarreaue83995c2020-03-01 20:55:55 +01002938 fdc_state[current_fdc].reset = 1;
2939 if (fdc_state[current_fdc].reset) {
Willy Tarreau12aebfa2020-03-31 11:40:54 +02002940 /* note: reset_fdc will take care of unlocking the driver
2941 * on completion.
2942 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 cont = &reset_cont;
Joe Perches55eee802010-03-10 15:20:57 -08002944 ret = wait_til_done(reset_fdc, interruptible);
2945 if (ret == -EINTR)
2946 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 }
2948 process_fd_request();
Joe Perches52a0d61f2010-03-10 15:20:53 -08002949 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950}
2951
2952/*
2953 * Misc Ioctl's and support
2954 * ========================
2955 */
2956static inline int fd_copyout(void __user *param, const void *address,
2957 unsigned long size)
2958{
2959 return copy_to_user(param, address, size) ? -EFAULT : 0;
2960}
2961
Joe Perches48c8cee2010-03-10 15:20:45 -08002962static inline int fd_copyin(void __user *param, void *address,
2963 unsigned long size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964{
2965 return copy_from_user(address, param, size) ? -EFAULT : 0;
2966}
2967
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02002968static const char *drive_name(int type, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002969{
2970 struct floppy_struct *floppy;
2971
2972 if (type)
2973 floppy = floppy_type + type;
2974 else {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01002975 if (drive_params[drive].native_format)
2976 floppy = floppy_type + drive_params[drive].native_format;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002977 else
2978 return "(null)";
2979 }
2980 if (floppy->name)
2981 return floppy->name;
2982 else
2983 return "(null)";
2984}
2985
2986/* raw commands */
2987static void raw_cmd_done(int flag)
2988{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989 if (!flag) {
2990 raw_cmd->flags |= FD_RAW_FAILURE;
2991 raw_cmd->flags |= FD_RAW_HARDFAILURE;
2992 } else {
2993 raw_cmd->reply_count = inr;
Denis Efremovbd10a5f2020-05-01 16:44:15 +03002994 if (raw_cmd->reply_count > FD_RAW_REPLY_SIZE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002995 raw_cmd->reply_count = 0;
Denis Efremovfa6b8852021-04-16 11:34:48 +03002996 memcpy(raw_cmd->reply, reply_buffer, raw_cmd->reply_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997
2998 if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
2999 unsigned long flags;
3000 flags = claim_dma_lock();
3001 raw_cmd->length = fd_get_dma_residue();
3002 release_dma_lock(flags);
3003 }
3004
3005 if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
3006 (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
3007 raw_cmd->flags |= FD_RAW_FAILURE;
3008
3009 if (disk_change(current_drive))
3010 raw_cmd->flags |= FD_RAW_DISK_CHANGE;
3011 else
3012 raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
3013 if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
Kees Cookb1bf4212017-10-04 17:49:29 -07003014 motor_off_callback(&motor_off_timer[current_drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003015
3016 if (raw_cmd->next &&
3017 (!(raw_cmd->flags & FD_RAW_FAILURE) ||
3018 !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
3019 ((raw_cmd->flags & FD_RAW_FAILURE) ||
3020 !(raw_cmd->flags & FD_RAW_STOP_IF_SUCCESS))) {
3021 raw_cmd = raw_cmd->next;
3022 return;
3023 }
3024 }
3025 generic_done(flag);
3026}
3027
Stephen Hemminger3b06c212010-07-20 20:09:00 -06003028static const struct cont_t raw_cmd_cont = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003029 .interrupt = success_and_wakeup,
3030 .redo = floppy_start,
3031 .error = generic_failure,
3032 .done = raw_cmd_done
3033};
3034
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003035static int raw_cmd_copyout(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036 struct floppy_raw_cmd *ptr)
3037{
3038 int ret;
3039
3040 while (ptr) {
Matthew Daley2145e152014-04-28 19:05:21 +12003041 struct floppy_raw_cmd cmd = *ptr;
3042 cmd.next = NULL;
3043 cmd.kernel_data = NULL;
3044 ret = copy_to_user(param, &cmd, sizeof(cmd));
Joe Perches86b12b42010-03-10 15:20:56 -08003045 if (ret)
3046 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003047 param += sizeof(struct floppy_raw_cmd);
3048 if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003049 if (ptr->length >= 0 &&
3050 ptr->length <= ptr->buffer_length) {
3051 long length = ptr->buffer_length - ptr->length;
Joe Perches4575b552010-03-10 15:20:55 -08003052 ret = fd_copyout(ptr->data, ptr->kernel_data,
3053 length);
3054 if (ret)
3055 return ret;
Joe Perchesbb57f0c62010-03-10 15:20:50 -08003056 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 }
3058 ptr = ptr->next;
3059 }
Joe Perches7f252712010-03-10 15:21:08 -08003060
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061 return 0;
3062}
3063
3064static void raw_cmd_free(struct floppy_raw_cmd **ptr)
3065{
Jesper Juhl06f748c2007-10-16 23:30:57 -07003066 struct floppy_raw_cmd *next;
3067 struct floppy_raw_cmd *this;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068
3069 this = *ptr;
3070 *ptr = NULL;
3071 while (this) {
3072 if (this->buffer_length) {
3073 fd_dma_mem_free((unsigned long)this->kernel_data,
3074 this->buffer_length);
3075 this->buffer_length = 0;
3076 }
3077 next = this->next;
3078 kfree(this);
3079 this = next;
3080 }
3081}
3082
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003083static int raw_cmd_copyin(int cmd, void __user *param,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 struct floppy_raw_cmd **rcmd)
3085{
3086 struct floppy_raw_cmd *ptr;
3087 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003088
3089 *rcmd = NULL;
Joe Perches7f252712010-03-10 15:21:08 -08003090
3091loop:
Vlastimil Babka1661f2e2017-01-04 11:19:31 +01003092 ptr = kmalloc(sizeof(struct floppy_raw_cmd), GFP_KERNEL);
Joe Perches7f252712010-03-10 15:21:08 -08003093 if (!ptr)
3094 return -ENOMEM;
3095 *rcmd = ptr;
3096 ret = copy_from_user(ptr, param, sizeof(*ptr));
Joe Perches7f252712010-03-10 15:21:08 -08003097 ptr->next = NULL;
3098 ptr->buffer_length = 0;
Matthew Daleyef87dbe2014-04-28 19:05:20 +12003099 ptr->kernel_data = NULL;
3100 if (ret)
3101 return -EFAULT;
Joe Perches7f252712010-03-10 15:21:08 -08003102 param += sizeof(struct floppy_raw_cmd);
Denis Efremovbd10a5f2020-05-01 16:44:15 +03003103 if (ptr->cmd_count > FD_RAW_CMD_FULLSIZE)
Joe Perches7f252712010-03-10 15:21:08 -08003104 return -EINVAL;
3105
Denis Efremovf6df18f2021-04-16 11:34:47 +03003106 memset(ptr->reply, 0, FD_RAW_REPLY_SIZE);
Joe Perches7f252712010-03-10 15:21:08 -08003107 ptr->resultcode = 0;
Joe Perches7f252712010-03-10 15:21:08 -08003108
3109 if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
3110 if (ptr->length <= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003111 return -EINVAL;
Joe Perches7f252712010-03-10 15:21:08 -08003112 ptr->kernel_data = (char *)fd_dma_mem_alloc(ptr->length);
3113 fallback_on_nodma_alloc(&ptr->kernel_data, ptr->length);
3114 if (!ptr->kernel_data)
3115 return -ENOMEM;
3116 ptr->buffer_length = ptr->length;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117 }
Joe Perches7f252712010-03-10 15:21:08 -08003118 if (ptr->flags & FD_RAW_WRITE) {
3119 ret = fd_copyin(ptr->data, ptr->kernel_data, ptr->length);
3120 if (ret)
3121 return ret;
3122 }
3123
3124 if (ptr->flags & FD_RAW_MORE) {
3125 rcmd = &(ptr->next);
3126 ptr->rate &= 0x43;
3127 goto loop;
3128 }
3129
3130 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003131}
3132
3133static int raw_cmd_ioctl(int cmd, void __user *param)
3134{
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135 struct floppy_raw_cmd *my_raw_cmd;
Jesper Juhl06f748c2007-10-16 23:30:57 -07003136 int drive;
3137 int ret2;
3138 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003139
Willy Tarreaue83995c2020-03-01 20:55:55 +01003140 if (fdc_state[current_fdc].rawcmd <= 1)
3141 fdc_state[current_fdc].rawcmd = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003142 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003143 if (FDC(drive) != current_fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003144 continue;
3145 if (drive == current_drive) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003146 if (drive_state[drive].fd_ref > 1) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003147 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003148 break;
3149 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003150 } else if (drive_state[drive].fd_ref) {
Willy Tarreaue83995c2020-03-01 20:55:55 +01003151 fdc_state[current_fdc].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003152 break;
3153 }
3154 }
3155
Willy Tarreaue83995c2020-03-01 20:55:55 +01003156 if (fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157 return -EIO;
3158
3159 ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
3160 if (ret) {
3161 raw_cmd_free(&my_raw_cmd);
3162 return ret;
3163 }
3164
3165 raw_cmd = my_raw_cmd;
3166 cont = &raw_cmd_cont;
Joe Perches74f63f42010-03-10 15:20:58 -08003167 ret = wait_til_done(floppy_start, true);
Willy Tarreau031faab2020-02-24 22:23:48 +01003168 debug_dcl(drive_params[current_drive].flags,
3169 "calling disk change from raw_cmd ioctl\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170
Willy Tarreaue83995c2020-03-01 20:55:55 +01003171 if (ret != -EINTR && fdc_state[current_fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003172 ret = -EIO;
3173
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003174 drive_state[current_drive].track = NO_TRACK;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003175
3176 ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
3177 if (!ret)
3178 ret = ret2;
3179 raw_cmd_free(&my_raw_cmd);
3180 return ret;
3181}
3182
3183static int invalidate_drive(struct block_device *bdev)
3184{
3185 /* invalidate the buffer track to force a reread */
3186 set_bit((long)bdev->bd_disk->private_data, &fake_change);
3187 process_fd_request();
Christoph Hellwig4a6f3d42020-09-08 16:53:32 +02003188 if (bdev_check_media_change(bdev))
3189 floppy_revalidate(bdev->bd_disk);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003190 return 0;
3191}
3192
Stephen Hemmingerbe7a12b2010-06-15 13:21:11 +02003193static int set_geometry(unsigned int cmd, struct floppy_struct *g,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 int drive, int type, struct block_device *bdev)
3195{
3196 int cnt;
3197
3198 /* sanity checking for parameters. */
Denis Efremovda994662019-07-12 21:55:23 +03003199 if ((int)g->sect <= 0 ||
3200 (int)g->head <= 0 ||
3201 /* check for overflow in max_sector */
3202 (int)(g->sect * g->head) <= 0 ||
Willy Tarreau76dabe72020-02-24 22:23:51 +01003203 /* check for zero in raw_cmd->cmd[F_SECT_PER_TRACK] */
Denis Efremovf3554ae2019-07-12 21:55:20 +03003204 (unsigned char)((g->sect << 2) >> FD_SIZECODE(g)) == 0 ||
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003205 g->track <= 0 || g->track > drive_params[drive].tracks >> STRETCH(g) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003206 /* check if reserved bits are set */
Keith Wansbrough9e491842008-09-22 14:57:17 -07003207 (g->stretch & ~(FD_STRETCH | FD_SWAPSIDES | FD_SECTBASEMASK)) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003208 return -EINVAL;
3209 if (type) {
3210 if (!capable(CAP_SYS_ADMIN))
3211 return -EPERM;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003212 mutex_lock(&open_lock);
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003213 if (lock_fdc(drive)) {
Jiri Slaby8516a502009-06-30 11:41:44 -07003214 mutex_unlock(&open_lock);
3215 return -EINTR;
3216 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003217 floppy_type[type] = *g;
3218 floppy_type[type].name = "user format";
3219 for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
3220 floppy_sizes[cnt] = floppy_sizes[cnt + 0x80] =
3221 floppy_type[type].size + 1;
3222 process_fd_request();
3223 for (cnt = 0; cnt < N_DRIVE; cnt++) {
3224 struct block_device *bdev = opened_bdev[cnt];
3225 if (!bdev || ITYPE(drive_state[cnt].fd_device) != type)
3226 continue;
NeilBrown93b270f2011-02-24 17:25:47 +11003227 __invalidate_device(bdev, true);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003228 }
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003229 mutex_unlock(&open_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003230 } else {
3231 int oldStretch;
Joe Perches52a0d61f2010-03-10 15:20:53 -08003232
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003233 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003234 return -EINTR;
Joe Perches4575b552010-03-10 15:20:55 -08003235 if (cmd != FDDEFPRM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236 /* notice a disk change immediately, else
3237 * we lose our settings immediately*/
Joe Perches74f63f42010-03-10 15:20:58 -08003238 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003239 return -EINTR;
3240 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003241 oldStretch = g->stretch;
3242 user_params[drive] = *g;
3243 if (buffer_drive == drive)
3244 SUPBOUND(buffer_max, user_params[drive].sect);
3245 current_type[drive] = &user_params[drive];
3246 floppy_sizes[drive] = user_params[drive].size;
3247 if (cmd == FDDEFPRM)
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003248 drive_state[current_drive].keep_data = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003249 else
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003250 drive_state[current_drive].keep_data = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003251 /* invalidation. Invalidate only when needed, i.e.
3252 * when there are already sectors in the buffer cache
3253 * whose number will change. This is useful, because
3254 * mtools often changes the geometry of the disk after
3255 * looking at the boot block */
Willy Tarreau3bd7f872020-02-24 22:23:49 +01003256 if (drive_state[current_drive].maxblock > user_params[drive].sect ||
3257 drive_state[current_drive].maxtrack ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003258 ((user_params[drive].sect ^ oldStretch) &
Keith Wansbrough9e491842008-09-22 14:57:17 -07003259 (FD_SWAPSIDES | FD_SECTBASEMASK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003260 invalidate_drive(bdev);
3261 else
3262 process_fd_request();
3263 }
3264 return 0;
3265}
3266
3267/* handle obsolete ioctl's */
Stephen Hemminger21af5442010-06-15 13:21:11 +02003268static unsigned int ioctl_table[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003269 FDCLRPRM,
3270 FDSETPRM,
3271 FDDEFPRM,
3272 FDGETPRM,
3273 FDMSGON,
3274 FDMSGOFF,
3275 FDFMTBEG,
3276 FDFMTTRK,
3277 FDFMTEND,
3278 FDSETEMSGTRESH,
3279 FDFLUSH,
3280 FDSETMAXERRS,
3281 FDGETMAXERRS,
3282 FDGETDRVTYP,
3283 FDSETDRVPRM,
3284 FDGETDRVPRM,
3285 FDGETDRVSTAT,
3286 FDPOLLDRVSTAT,
3287 FDRESET,
3288 FDGETFDCSTAT,
3289 FDWERRORCLR,
3290 FDWERRORGET,
3291 FDRAWCMD,
3292 FDEJECT,
3293 FDTWADDLE
3294};
3295
Stephen Hemminger21af5442010-06-15 13:21:11 +02003296static int normalize_ioctl(unsigned int *cmd, int *size)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003297{
3298 int i;
3299
3300 for (i = 0; i < ARRAY_SIZE(ioctl_table); i++) {
3301 if ((*cmd & 0xffff) == (ioctl_table[i] & 0xffff)) {
3302 *size = _IOC_SIZE(*cmd);
3303 *cmd = ioctl_table[i];
3304 if (*size > _IOC_SIZE(*cmd)) {
Joe Perchesb46df352010-03-10 15:20:46 -08003305 pr_info("ioctl not yet supported\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306 return -EFAULT;
3307 }
3308 return 0;
3309 }
3310 }
3311 return -EINVAL;
3312}
3313
3314static int get_floppy_geometry(int drive, int type, struct floppy_struct **g)
3315{
3316 if (type)
3317 *g = &floppy_type[type];
3318 else {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003319 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003320 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003321 if (poll_drive(false, 0) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003322 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323 process_fd_request();
3324 *g = current_type[drive];
3325 }
3326 if (!*g)
3327 return -ENODEV;
3328 return 0;
3329}
3330
Christoph Hellwiga885c8c2006-01-08 01:02:50 -08003331static int fd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
3332{
3333 int drive = (long)bdev->bd_disk->private_data;
3334 int type = ITYPE(drive_state[drive].fd_device);
3335 struct floppy_struct *g;
3336 int ret;
3337
3338 ret = get_floppy_geometry(drive, type, &g);
3339 if (ret)
3340 return ret;
3341
3342 geo->heads = g->head;
3343 geo->sectors = g->sect;
3344 geo->cylinders = g->track;
3345 return 0;
3346}
3347
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003348static bool valid_floppy_drive_params(const short autodetect[FD_AUTODETECT_SIZE],
Denis Efremov9b046092019-07-12 21:55:22 +03003349 int native_format)
Denis Efremov5635f892019-07-12 21:55:21 +03003350{
3351 size_t floppy_type_size = ARRAY_SIZE(floppy_type);
3352 size_t i = 0;
3353
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003354 for (i = 0; i < FD_AUTODETECT_SIZE; ++i) {
Denis Efremov5635f892019-07-12 21:55:21 +03003355 if (autodetect[i] < 0 ||
3356 autodetect[i] >= floppy_type_size)
3357 return false;
3358 }
3359
Denis Efremov9b046092019-07-12 21:55:22 +03003360 if (native_format < 0 || native_format >= floppy_type_size)
3361 return false;
3362
Denis Efremov5635f892019-07-12 21:55:21 +03003363 return true;
3364}
3365
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003366static int fd_locked_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003367 unsigned long param)
3368{
Al Viroa4af9b42008-03-02 09:27:55 -05003369 int drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003370 int type = ITYPE(drive_state[drive].fd_device);
Jesper Juhl06f748c2007-10-16 23:30:57 -07003371 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 int ret;
3373 int size;
3374 union inparam {
3375 struct floppy_struct g; /* geometry */
3376 struct format_descr f;
3377 struct floppy_max_errors max_errors;
3378 struct floppy_drive_params dp;
3379 } inparam; /* parameters coming from user space */
Joe Perches724ee622010-03-10 15:21:11 -08003380 const void *outparam; /* parameters passed back to user space */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003381
3382 /* convert compatibility eject ioctls into floppy eject ioctl.
3383 * We do this in order to provide a means to eject floppy disks before
3384 * installing the new fdutils package */
3385 if (cmd == CDROMEJECT || /* CD-ROM eject */
Joe Perchesa81ee5442010-03-10 15:20:46 -08003386 cmd == 0x6470) { /* SunOS floppy eject */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003387 DPRINT("obsolete eject ioctl\n");
3388 DPRINT("please use floppycontrol --eject\n");
3389 cmd = FDEJECT;
3390 }
3391
Joe Perchesa81ee5442010-03-10 15:20:46 -08003392 if (!((cmd & 0xff00) == 0x0200))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003393 return -EINVAL;
3394
Joe Perchesa81ee5442010-03-10 15:20:46 -08003395 /* convert the old style command into a new style command */
Joe Perches4575b552010-03-10 15:20:55 -08003396 ret = normalize_ioctl(&cmd, &size);
3397 if (ret)
3398 return ret;
Joe Perchesa81ee5442010-03-10 15:20:46 -08003399
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 /* permission checks */
Joe Perches0aad92c2010-03-10 15:21:10 -08003401 if (((cmd & 0x40) && !(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL))) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 ((cmd & 0x80) && !capable(CAP_SYS_ADMIN)))
3403 return -EPERM;
3404
Arjan van de Ven2886a8b2009-12-14 18:00:11 -08003405 if (WARN_ON(size < 0 || size > sizeof(inparam)))
3406 return -EINVAL;
3407
Linus Torvalds1da177e2005-04-16 15:20:36 -07003408 /* copyin */
Joe Perchesb87c9e02010-03-10 15:20:50 -08003409 memset(&inparam, 0, sizeof(inparam));
Joe Perches4575b552010-03-10 15:20:55 -08003410 if (_IOC_DIR(cmd) & _IOC_WRITE) {
3411 ret = fd_copyin((void __user *)param, &inparam, size);
3412 if (ret)
3413 return ret;
3414 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003415
Joe Perchesda2736532010-03-10 15:20:52 -08003416 switch (cmd) {
3417 case FDEJECT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003418 if (drive_state[drive].fd_ref != 1)
Joe Perchesda2736532010-03-10 15:20:52 -08003419 /* somebody else has this drive open */
3420 return -EBUSY;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003421 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003422 return -EINTR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003423
Joe Perchesda2736532010-03-10 15:20:52 -08003424 /* do the actual eject. Fails on
3425 * non-Sparc architectures */
3426 ret = fd_eject(UNIT(drive));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003427
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003428 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
3429 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Joe Perchesda2736532010-03-10 15:20:52 -08003430 process_fd_request();
3431 return ret;
3432 case FDCLRPRM:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003433 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003434 return -EINTR;
Joe Perchesda2736532010-03-10 15:20:52 -08003435 current_type[drive] = NULL;
3436 floppy_sizes[drive] = MAX_DISK_SIZE << 1;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003437 drive_state[drive].keep_data = 0;
Joe Perchesda2736532010-03-10 15:20:52 -08003438 return invalidate_drive(bdev);
3439 case FDSETPRM:
3440 case FDDEFPRM:
3441 return set_geometry(cmd, &inparam.g, drive, type, bdev);
3442 case FDGETPRM:
Joe Perches4575b552010-03-10 15:20:55 -08003443 ret = get_floppy_geometry(drive, type,
Joe Perches724ee622010-03-10 15:21:11 -08003444 (struct floppy_struct **)&outparam);
Joe Perches4575b552010-03-10 15:20:55 -08003445 if (ret)
3446 return ret;
Andy Whitcroft65eea8e2018-09-20 09:09:48 -06003447 memcpy(&inparam.g, outparam,
3448 offsetof(struct floppy_struct, name));
3449 outparam = &inparam.g;
Joe Perchesda2736532010-03-10 15:20:52 -08003450 break;
3451 case FDMSGON:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003452 drive_params[drive].flags |= FTD_MSG;
Joe Perchesda2736532010-03-10 15:20:52 -08003453 return 0;
3454 case FDMSGOFF:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003455 drive_params[drive].flags &= ~FTD_MSG;
Joe Perchesda2736532010-03-10 15:20:52 -08003456 return 0;
3457 case FDFMTBEG:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003458 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003459 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003460 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003461 return -EINTR;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003462 ret = drive_state[drive].flags;
Joe Perchesda2736532010-03-10 15:20:52 -08003463 process_fd_request();
3464 if (ret & FD_VERIFY)
3465 return -ENODEV;
3466 if (!(ret & FD_DISK_WRITABLE))
3467 return -EROFS;
3468 return 0;
3469 case FDFMTTRK:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003470 if (drive_state[drive].fd_ref != 1)
Joe Perchesda2736532010-03-10 15:20:52 -08003471 return -EBUSY;
3472 return do_format(drive, &inparam.f);
3473 case FDFMTEND:
3474 case FDFLUSH:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003475 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003476 return -EINTR;
Joe Perchesda2736532010-03-10 15:20:52 -08003477 return invalidate_drive(bdev);
3478 case FDSETEMSGTRESH:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003479 drive_params[drive].max_errors.reporting = (unsigned short)(param & 0x0f);
Joe Perchesda2736532010-03-10 15:20:52 -08003480 return 0;
3481 case FDGETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003482 outparam = &drive_params[drive].max_errors;
Joe Perchesda2736532010-03-10 15:20:52 -08003483 break;
3484 case FDSETMAXERRS:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003485 drive_params[drive].max_errors = inparam.max_errors;
Joe Perchesda2736532010-03-10 15:20:52 -08003486 break;
3487 case FDGETDRVTYP:
3488 outparam = drive_name(type, drive);
Joe Perches724ee622010-03-10 15:21:11 -08003489 SUPBOUND(size, strlen((const char *)outparam) + 1);
Joe Perchesda2736532010-03-10 15:20:52 -08003490 break;
3491 case FDSETDRVPRM:
Denis Efremov9b046092019-07-12 21:55:22 +03003492 if (!valid_floppy_drive_params(inparam.dp.autodetect,
3493 inparam.dp.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003494 return -EINVAL;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003495 drive_params[drive] = inparam.dp;
Joe Perchesda2736532010-03-10 15:20:52 -08003496 break;
3497 case FDGETDRVPRM:
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003498 outparam = &drive_params[drive];
Joe Perchesda2736532010-03-10 15:20:52 -08003499 break;
3500 case FDPOLLDRVSTAT:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003501 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003502 return -EINTR;
Joe Perches74f63f42010-03-10 15:20:58 -08003503 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
Joe Perches4575b552010-03-10 15:20:55 -08003504 return -EINTR;
Joe Perchesda2736532010-03-10 15:20:52 -08003505 process_fd_request();
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -05003506 fallthrough;
Joe Perchesda2736532010-03-10 15:20:52 -08003507 case FDGETDRVSTAT:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003508 outparam = &drive_state[drive];
Joe Perchesda2736532010-03-10 15:20:52 -08003509 break;
3510 case FDRESET:
Joe Perches74f63f42010-03-10 15:20:58 -08003511 return user_reset_fdc(drive, (int)param, true);
Joe Perchesda2736532010-03-10 15:20:52 -08003512 case FDGETFDCSTAT:
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003513 outparam = &fdc_state[FDC(drive)];
Joe Perchesda2736532010-03-10 15:20:52 -08003514 break;
3515 case FDWERRORCLR:
Willy Tarreau121e2972020-02-24 22:23:47 +01003516 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Joe Perchesda2736532010-03-10 15:20:52 -08003517 return 0;
3518 case FDWERRORGET:
Willy Tarreau121e2972020-02-24 22:23:47 +01003519 outparam = &write_errors[drive];
Joe Perchesda2736532010-03-10 15:20:52 -08003520 break;
3521 case FDRAWCMD:
3522 if (type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003523 return -EINVAL;
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003524 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003525 return -EINTR;
Joe Perchesda2736532010-03-10 15:20:52 -08003526 set_floppy(drive);
Joe Perches4575b552010-03-10 15:20:55 -08003527 i = raw_cmd_ioctl(cmd, (void __user *)param);
3528 if (i == -EINTR)
3529 return -EINTR;
Joe Perchesda2736532010-03-10 15:20:52 -08003530 process_fd_request();
3531 return i;
3532 case FDTWADDLE:
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01003533 if (lock_fdc(drive))
Joe Perches52a0d61f2010-03-10 15:20:53 -08003534 return -EINTR;
Willy Tarreauc1f710b2020-03-31 11:40:40 +02003535 twaddle(current_fdc, current_drive);
Joe Perchesda2736532010-03-10 15:20:52 -08003536 process_fd_request();
3537 return 0;
3538 default:
3539 return -EINVAL;
3540 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003541
3542 if (_IOC_DIR(cmd) & _IOC_READ)
3543 return fd_copyout((void __user *)param, outparam, size);
Joe Perchesda2736532010-03-10 15:20:52 -08003544
3545 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003546}
3547
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003548static int fd_ioctl(struct block_device *bdev, fmode_t mode,
3549 unsigned int cmd, unsigned long param)
3550{
3551 int ret;
3552
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003553 mutex_lock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003554 ret = fd_locked_ioctl(bdev, mode, cmd, param);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003555 mutex_unlock(&floppy_mutex);
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02003556
3557 return ret;
3558}
3559
Al Viro229b53c2017-06-27 15:47:56 -04003560#ifdef CONFIG_COMPAT
3561
3562struct compat_floppy_drive_params {
3563 char cmos;
3564 compat_ulong_t max_dtr;
3565 compat_ulong_t hlt;
3566 compat_ulong_t hut;
3567 compat_ulong_t srt;
3568 compat_ulong_t spinup;
3569 compat_ulong_t spindown;
3570 unsigned char spindown_offset;
3571 unsigned char select_delay;
3572 unsigned char rps;
3573 unsigned char tracks;
3574 compat_ulong_t timeout;
3575 unsigned char interleave_sect;
3576 struct floppy_max_errors max_errors;
3577 char flags;
3578 char read_track;
Denis Efremov9c4c5a22020-05-01 16:44:14 +03003579 short autodetect[FD_AUTODETECT_SIZE];
Al Viro229b53c2017-06-27 15:47:56 -04003580 compat_int_t checkfreq;
3581 compat_int_t native_format;
3582};
3583
3584struct compat_floppy_drive_struct {
3585 signed char flags;
3586 compat_ulong_t spinup_date;
3587 compat_ulong_t select_date;
3588 compat_ulong_t first_read_date;
3589 short probed_format;
3590 short track;
3591 short maxblock;
3592 short maxtrack;
3593 compat_int_t generation;
3594 compat_int_t keep_data;
3595 compat_int_t fd_ref;
3596 compat_int_t fd_device;
3597 compat_int_t last_checked;
3598 compat_caddr_t dmabuf;
3599 compat_int_t bufblocks;
3600};
3601
3602struct compat_floppy_fdc_state {
3603 compat_int_t spec1;
3604 compat_int_t spec2;
3605 compat_int_t dtr;
3606 unsigned char version;
3607 unsigned char dor;
3608 compat_ulong_t address;
3609 unsigned int rawcmd:2;
3610 unsigned int reset:1;
3611 unsigned int need_configure:1;
3612 unsigned int perp_mode:2;
3613 unsigned int has_fifo:1;
3614 unsigned int driver_version;
3615 unsigned char track[4];
3616};
3617
3618struct compat_floppy_write_errors {
3619 unsigned int write_errors;
3620 compat_ulong_t first_error_sector;
3621 compat_int_t first_error_generation;
3622 compat_ulong_t last_error_sector;
3623 compat_int_t last_error_generation;
3624 compat_uint_t badness;
3625};
3626
3627#define FDSETPRM32 _IOW(2, 0x42, struct compat_floppy_struct)
3628#define FDDEFPRM32 _IOW(2, 0x43, struct compat_floppy_struct)
3629#define FDSETDRVPRM32 _IOW(2, 0x90, struct compat_floppy_drive_params)
3630#define FDGETDRVPRM32 _IOR(2, 0x11, struct compat_floppy_drive_params)
3631#define FDGETDRVSTAT32 _IOR(2, 0x12, struct compat_floppy_drive_struct)
3632#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct compat_floppy_drive_struct)
3633#define FDGETFDCSTAT32 _IOR(2, 0x15, struct compat_floppy_fdc_state)
3634#define FDWERRORGET32 _IOR(2, 0x17, struct compat_floppy_write_errors)
3635
3636static int compat_set_geometry(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3637 struct compat_floppy_struct __user *arg)
3638{
3639 struct floppy_struct v;
3640 int drive, type;
3641 int err;
3642
3643 BUILD_BUG_ON(offsetof(struct floppy_struct, name) !=
3644 offsetof(struct compat_floppy_struct, name));
3645
3646 if (!(mode & (FMODE_WRITE | FMODE_WRITE_IOCTL)))
3647 return -EPERM;
3648
3649 memset(&v, 0, sizeof(struct floppy_struct));
3650 if (copy_from_user(&v, arg, offsetof(struct floppy_struct, name)))
3651 return -EFAULT;
3652
3653 mutex_lock(&floppy_mutex);
3654 drive = (long)bdev->bd_disk->private_data;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003655 type = ITYPE(drive_state[drive].fd_device);
Al Viro229b53c2017-06-27 15:47:56 -04003656 err = set_geometry(cmd == FDSETPRM32 ? FDSETPRM : FDDEFPRM,
3657 &v, drive, type, bdev);
3658 mutex_unlock(&floppy_mutex);
3659 return err;
3660}
3661
3662static int compat_get_prm(int drive,
3663 struct compat_floppy_struct __user *arg)
3664{
3665 struct compat_floppy_struct v;
3666 struct floppy_struct *p;
3667 int err;
3668
3669 memset(&v, 0, sizeof(v));
3670 mutex_lock(&floppy_mutex);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003671 err = get_floppy_geometry(drive, ITYPE(drive_state[drive].fd_device),
3672 &p);
Al Viro229b53c2017-06-27 15:47:56 -04003673 if (err) {
3674 mutex_unlock(&floppy_mutex);
3675 return err;
3676 }
3677 memcpy(&v, p, offsetof(struct floppy_struct, name));
3678 mutex_unlock(&floppy_mutex);
3679 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_struct)))
3680 return -EFAULT;
3681 return 0;
3682}
3683
3684static int compat_setdrvprm(int drive,
3685 struct compat_floppy_drive_params __user *arg)
3686{
3687 struct compat_floppy_drive_params v;
3688
3689 if (!capable(CAP_SYS_ADMIN))
3690 return -EPERM;
3691 if (copy_from_user(&v, arg, sizeof(struct compat_floppy_drive_params)))
3692 return -EFAULT;
Denis Efremov9b046092019-07-12 21:55:22 +03003693 if (!valid_floppy_drive_params(v.autodetect, v.native_format))
Denis Efremov5635f892019-07-12 21:55:21 +03003694 return -EINVAL;
Al Viro229b53c2017-06-27 15:47:56 -04003695 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003696 drive_params[drive].cmos = v.cmos;
3697 drive_params[drive].max_dtr = v.max_dtr;
3698 drive_params[drive].hlt = v.hlt;
3699 drive_params[drive].hut = v.hut;
3700 drive_params[drive].srt = v.srt;
3701 drive_params[drive].spinup = v.spinup;
3702 drive_params[drive].spindown = v.spindown;
3703 drive_params[drive].spindown_offset = v.spindown_offset;
3704 drive_params[drive].select_delay = v.select_delay;
3705 drive_params[drive].rps = v.rps;
3706 drive_params[drive].tracks = v.tracks;
3707 drive_params[drive].timeout = v.timeout;
3708 drive_params[drive].interleave_sect = v.interleave_sect;
3709 drive_params[drive].max_errors = v.max_errors;
3710 drive_params[drive].flags = v.flags;
3711 drive_params[drive].read_track = v.read_track;
3712 memcpy(drive_params[drive].autodetect, v.autodetect,
3713 sizeof(v.autodetect));
3714 drive_params[drive].checkfreq = v.checkfreq;
3715 drive_params[drive].native_format = v.native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003716 mutex_unlock(&floppy_mutex);
3717 return 0;
3718}
3719
3720static int compat_getdrvprm(int drive,
3721 struct compat_floppy_drive_params __user *arg)
3722{
3723 struct compat_floppy_drive_params v;
3724
3725 memset(&v, 0, sizeof(struct compat_floppy_drive_params));
3726 mutex_lock(&floppy_mutex);
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003727 v.cmos = drive_params[drive].cmos;
3728 v.max_dtr = drive_params[drive].max_dtr;
3729 v.hlt = drive_params[drive].hlt;
3730 v.hut = drive_params[drive].hut;
3731 v.srt = drive_params[drive].srt;
3732 v.spinup = drive_params[drive].spinup;
3733 v.spindown = drive_params[drive].spindown;
3734 v.spindown_offset = drive_params[drive].spindown_offset;
3735 v.select_delay = drive_params[drive].select_delay;
3736 v.rps = drive_params[drive].rps;
3737 v.tracks = drive_params[drive].tracks;
3738 v.timeout = drive_params[drive].timeout;
3739 v.interleave_sect = drive_params[drive].interleave_sect;
3740 v.max_errors = drive_params[drive].max_errors;
3741 v.flags = drive_params[drive].flags;
3742 v.read_track = drive_params[drive].read_track;
3743 memcpy(v.autodetect, drive_params[drive].autodetect,
3744 sizeof(v.autodetect));
3745 v.checkfreq = drive_params[drive].checkfreq;
3746 v.native_format = drive_params[drive].native_format;
Al Viro229b53c2017-06-27 15:47:56 -04003747 mutex_unlock(&floppy_mutex);
3748
Jann Horn52f6f9d2019-03-26 23:03:48 +01003749 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_params)))
Al Viro229b53c2017-06-27 15:47:56 -04003750 return -EFAULT;
3751 return 0;
3752}
3753
3754static int compat_getdrvstat(int drive, bool poll,
3755 struct compat_floppy_drive_struct __user *arg)
3756{
3757 struct compat_floppy_drive_struct v;
3758
3759 memset(&v, 0, sizeof(struct compat_floppy_drive_struct));
3760 mutex_lock(&floppy_mutex);
3761
3762 if (poll) {
3763 if (lock_fdc(drive))
3764 goto Eintr;
3765 if (poll_drive(true, FD_RAW_NEED_DISK) == -EINTR)
3766 goto Eintr;
3767 process_fd_request();
3768 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003769 v.spinup_date = drive_state[drive].spinup_date;
3770 v.select_date = drive_state[drive].select_date;
3771 v.first_read_date = drive_state[drive].first_read_date;
3772 v.probed_format = drive_state[drive].probed_format;
3773 v.track = drive_state[drive].track;
3774 v.maxblock = drive_state[drive].maxblock;
3775 v.maxtrack = drive_state[drive].maxtrack;
3776 v.generation = drive_state[drive].generation;
3777 v.keep_data = drive_state[drive].keep_data;
3778 v.fd_ref = drive_state[drive].fd_ref;
3779 v.fd_device = drive_state[drive].fd_device;
3780 v.last_checked = drive_state[drive].last_checked;
3781 v.dmabuf = (uintptr_t) drive_state[drive].dmabuf;
3782 v.bufblocks = drive_state[drive].bufblocks;
Al Viro229b53c2017-06-27 15:47:56 -04003783 mutex_unlock(&floppy_mutex);
3784
Jann Horn52f6f9d2019-03-26 23:03:48 +01003785 if (copy_to_user(arg, &v, sizeof(struct compat_floppy_drive_struct)))
Al Viro229b53c2017-06-27 15:47:56 -04003786 return -EFAULT;
3787 return 0;
3788Eintr:
3789 mutex_unlock(&floppy_mutex);
3790 return -EINTR;
3791}
3792
3793static int compat_getfdcstat(int drive,
3794 struct compat_floppy_fdc_state __user *arg)
3795{
3796 struct compat_floppy_fdc_state v32;
3797 struct floppy_fdc_state v;
3798
3799 mutex_lock(&floppy_mutex);
Willy Tarreauf9d322b2020-02-24 22:23:44 +01003800 v = fdc_state[FDC(drive)];
Al Viro229b53c2017-06-27 15:47:56 -04003801 mutex_unlock(&floppy_mutex);
3802
3803 memset(&v32, 0, sizeof(struct compat_floppy_fdc_state));
3804 v32.spec1 = v.spec1;
3805 v32.spec2 = v.spec2;
3806 v32.dtr = v.dtr;
3807 v32.version = v.version;
3808 v32.dor = v.dor;
3809 v32.address = v.address;
3810 v32.rawcmd = v.rawcmd;
3811 v32.reset = v.reset;
3812 v32.need_configure = v.need_configure;
3813 v32.perp_mode = v.perp_mode;
3814 v32.has_fifo = v.has_fifo;
3815 v32.driver_version = v.driver_version;
3816 memcpy(v32.track, v.track, 4);
3817 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_fdc_state)))
3818 return -EFAULT;
3819 return 0;
3820}
3821
3822static int compat_werrorget(int drive,
3823 struct compat_floppy_write_errors __user *arg)
3824{
3825 struct compat_floppy_write_errors v32;
3826 struct floppy_write_errors v;
3827
3828 memset(&v32, 0, sizeof(struct compat_floppy_write_errors));
3829 mutex_lock(&floppy_mutex);
Willy Tarreau121e2972020-02-24 22:23:47 +01003830 v = write_errors[drive];
Al Viro229b53c2017-06-27 15:47:56 -04003831 mutex_unlock(&floppy_mutex);
3832 v32.write_errors = v.write_errors;
3833 v32.first_error_sector = v.first_error_sector;
3834 v32.first_error_generation = v.first_error_generation;
3835 v32.last_error_sector = v.last_error_sector;
3836 v32.last_error_generation = v.last_error_generation;
3837 v32.badness = v.badness;
3838 if (copy_to_user(arg, &v32, sizeof(struct compat_floppy_write_errors)))
3839 return -EFAULT;
3840 return 0;
3841}
3842
3843static int fd_compat_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
3844 unsigned long param)
3845{
3846 int drive = (long)bdev->bd_disk->private_data;
3847 switch (cmd) {
Arnd Bergmann9452b1a2019-11-28 15:48:10 +01003848 case CDROMEJECT: /* CD-ROM eject */
3849 case 0x6470: /* SunOS floppy eject */
3850
Al Viro229b53c2017-06-27 15:47:56 -04003851 case FDMSGON:
3852 case FDMSGOFF:
3853 case FDSETEMSGTRESH:
3854 case FDFLUSH:
3855 case FDWERRORCLR:
3856 case FDEJECT:
3857 case FDCLRPRM:
3858 case FDFMTBEG:
3859 case FDRESET:
3860 case FDTWADDLE:
3861 return fd_ioctl(bdev, mode, cmd, param);
3862 case FDSETMAXERRS:
3863 case FDGETMAXERRS:
3864 case FDGETDRVTYP:
3865 case FDFMTEND:
3866 case FDFMTTRK:
3867 case FDRAWCMD:
3868 return fd_ioctl(bdev, mode, cmd,
3869 (unsigned long)compat_ptr(param));
3870 case FDSETPRM32:
3871 case FDDEFPRM32:
3872 return compat_set_geometry(bdev, mode, cmd, compat_ptr(param));
3873 case FDGETPRM32:
3874 return compat_get_prm(drive, compat_ptr(param));
3875 case FDSETDRVPRM32:
3876 return compat_setdrvprm(drive, compat_ptr(param));
3877 case FDGETDRVPRM32:
3878 return compat_getdrvprm(drive, compat_ptr(param));
3879 case FDPOLLDRVSTAT32:
3880 return compat_getdrvstat(drive, true, compat_ptr(param));
3881 case FDGETDRVSTAT32:
3882 return compat_getdrvstat(drive, false, compat_ptr(param));
3883 case FDGETFDCSTAT32:
3884 return compat_getfdcstat(drive, compat_ptr(param));
3885 case FDWERRORGET32:
3886 return compat_werrorget(drive, compat_ptr(param));
3887 }
3888 return -EINVAL;
3889}
3890#endif
3891
Linus Torvalds1da177e2005-04-16 15:20:36 -07003892static void __init config_types(void)
3893{
Joe Perchesb46df352010-03-10 15:20:46 -08003894 bool has_drive = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003895 int drive;
3896
3897 /* read drive info out of physical CMOS */
3898 drive = 0;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003899 if (!drive_params[drive].cmos)
3900 drive_params[drive].cmos = FLOPPY0_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003901 drive = 1;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003902 if (!drive_params[drive].cmos)
3903 drive_params[drive].cmos = FLOPPY1_TYPE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003904
Jesper Juhl06f748c2007-10-16 23:30:57 -07003905 /* FIXME: additional physical CMOS drive detection should go here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003906
3907 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003908 unsigned int type = drive_params[drive].cmos;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003909 struct floppy_drive_params *params;
3910 const char *name = NULL;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003911 char temparea[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003912
Tobias Klauser945f3902006-01-08 01:05:11 -08003913 if (type < ARRAY_SIZE(default_drive_params)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003914 params = &default_drive_params[type].params;
3915 if (type) {
3916 name = default_drive_params[type].name;
3917 allowed_drive_mask |= 1 << drive;
3918 } else
3919 allowed_drive_mask &= ~(1 << drive);
3920 } else {
3921 params = &default_drive_params[0].params;
Rasmus Villemoesbcf42992015-12-01 15:54:01 +01003922 snprintf(temparea, sizeof(temparea),
3923 "unknown type %d (usb?)", type);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003924 name = temparea;
3925 }
3926 if (name) {
Joe Perchesb46df352010-03-10 15:20:46 -08003927 const char *prepend;
3928 if (!has_drive) {
3929 prepend = "";
3930 has_drive = true;
3931 pr_info("Floppy drive(s):");
3932 } else {
3933 prepend = ",";
Linus Torvalds1da177e2005-04-16 15:20:36 -07003934 }
Joe Perchesb46df352010-03-10 15:20:46 -08003935
3936 pr_cont("%s fd%d is %s", prepend, drive, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003937 }
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003938 drive_params[drive] = *params;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003939 }
Joe Perchesb46df352010-03-10 15:20:46 -08003940
3941 if (has_drive)
3942 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003943}
3944
Al Virodb2a1442013-05-05 21:52:57 -04003945static void floppy_release(struct gendisk *disk, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003946{
Al Viroa4af9b42008-03-02 09:27:55 -05003947 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003948
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003949 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003950 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003951 if (!drive_state[drive].fd_ref--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003952 DPRINT("floppy_release with fd_ref == 0");
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003953 drive_state[drive].fd_ref = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003954 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003955 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003956 opened_bdev[drive] = NULL;
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003957 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003958 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003959}
3960
3961/*
3962 * floppy_open check for aliasing (/dev/fd0 can be the same as
3963 * /dev/PS0 etc), and disallows simultaneous access to the same
3964 * drive with different device numbers.
3965 */
Al Viroa4af9b42008-03-02 09:27:55 -05003966static int floppy_open(struct block_device *bdev, fmode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003967{
Al Viroa4af9b42008-03-02 09:27:55 -05003968 int drive = (long)bdev->bd_disk->private_data;
3969 int old_dev, new_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003970 int try;
3971 int res = -EBUSY;
3972 char *tmp;
3973
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02003974 mutex_lock(&floppy_mutex);
Jes Sorensenb1c82b52006-03-23 03:00:26 -08003975 mutex_lock(&open_lock);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003976 old_dev = drive_state[drive].fd_device;
Al Viroa4af9b42008-03-02 09:27:55 -05003977 if (opened_bdev[drive] && opened_bdev[drive] != bdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003978 goto out2;
3979
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003980 if (!drive_state[drive].fd_ref && (drive_params[drive].flags & FD_BROKEN_DCL)) {
3981 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
3982 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003983 }
3984
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01003985 drive_state[drive].fd_ref++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003986
Al Viroa4af9b42008-03-02 09:27:55 -05003987 opened_bdev[drive] = bdev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003988
3989 res = -ENXIO;
3990
3991 if (!floppy_track_buffer) {
3992 /* if opening an ED drive, reserve a big buffer,
3993 * else reserve a small one */
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01003994 if ((drive_params[drive].cmos == 6) || (drive_params[drive].cmos == 5))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003995 try = 64; /* Only 48 actually useful */
3996 else
3997 try = 32; /* Only 24 actually useful */
3998
3999 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4000 if (!tmp && !floppy_track_buffer) {
4001 try >>= 1; /* buffer only one side */
4002 INFBOUND(try, 16);
4003 tmp = (char *)fd_dma_mem_alloc(1024 * try);
4004 }
Joe Perchesa81ee5442010-03-10 15:20:46 -08004005 if (!tmp && !floppy_track_buffer)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004006 fallback_on_nodma_alloc(&tmp, 2048 * try);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004007 if (!tmp && !floppy_track_buffer) {
4008 DPRINT("Unable to allocate DMA memory\n");
4009 goto out;
4010 }
4011 if (floppy_track_buffer) {
4012 if (tmp)
4013 fd_dma_mem_free((unsigned long)tmp, try * 1024);
4014 } else {
4015 buffer_min = buffer_max = -1;
4016 floppy_track_buffer = tmp;
4017 max_buffer_sectors = try;
4018 }
4019 }
4020
Al Viroa4af9b42008-03-02 09:27:55 -05004021 new_dev = MINOR(bdev->bd_dev);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004022 drive_state[drive].fd_device = new_dev;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004023 set_capacity(disks[drive][ITYPE(new_dev)], floppy_sizes[new_dev]);
Al Viroa4af9b42008-03-02 09:27:55 -05004024 if (old_dev != -1 && old_dev != new_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004025 if (buffer_drive == drive)
4026 buffer_track = -1;
4027 }
4028
Willy Tarreauf9d322b2020-02-24 22:23:44 +01004029 if (fdc_state[FDC(drive)].rawcmd == 1)
4030 fdc_state[FDC(drive)].rawcmd = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004031
Jiri Kosina8a0c0142021-01-22 12:13:20 +01004032 if (mode & (FMODE_READ|FMODE_WRITE)) {
4033 drive_state[drive].last_checked = 0;
4034 clear_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags);
4035 if (bdev_check_media_change(bdev))
4036 floppy_revalidate(bdev->bd_disk);
4037 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags))
4038 goto out;
4039 if (test_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags))
Jens Axboef2791e72016-08-25 08:56:51 -06004040 goto out;
4041 }
Jiri Kosina8a0c0142021-01-22 12:13:20 +01004042
4043 res = -EROFS;
4044
4045 if ((mode & FMODE_WRITE) &&
4046 !test_bit(FD_DISK_WRITABLE_BIT, &drive_state[drive].flags))
4047 goto out;
4048
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004049 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004050 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004051 return 0;
4052out:
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004053 drive_state[drive].fd_ref--;
Jiri Kosinabfa10b82012-05-18 13:50:28 +02004054
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004055 if (!drive_state[drive].fd_ref)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004056 opened_bdev[drive] = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004057out2:
Jes Sorensenb1c82b52006-03-23 03:00:26 -08004058 mutex_unlock(&open_lock);
Arnd Bergmann2a48fc02010-06-02 14:28:52 +02004059 mutex_unlock(&floppy_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004060 return res;
4061}
4062
4063/*
4064 * Check if the disk has been changed or if a change has been faked.
4065 */
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004066static unsigned int floppy_check_events(struct gendisk *disk,
4067 unsigned int clearing)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004068{
4069 int drive = (long)disk->private_data;
4070
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004071 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4072 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004073 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004074
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004075 if (time_after(jiffies, drive_state[drive].last_checked + drive_params[drive].checkfreq)) {
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004076 if (lock_fdc(drive))
Yufen Yu96d7cb92019-01-29 16:34:04 +08004077 return 0;
Joe Perches74f63f42010-03-10 15:20:58 -08004078 poll_drive(false, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004079 process_fd_request();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004080 }
4081
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004082 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4083 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07004084 test_bit(drive, &fake_change) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004085 drive_no_geom(drive))
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004086 return DISK_EVENT_MEDIA_CHANGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004087 return 0;
4088}
4089
4090/*
4091 * This implements "read block 0" for floppy_revalidate().
4092 * Needed for format autodetection, checking whether there is
4093 * a disk in the drive, and whether that disk is writable.
4094 */
4095
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004096struct rb0_cbdata {
4097 int drive;
4098 struct completion complete;
4099};
4100
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004101static void floppy_rb0_cb(struct bio *bio)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004102{
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004103 struct rb0_cbdata *cbdata = (struct rb0_cbdata *)bio->bi_private;
4104 int drive = cbdata->drive;
4105
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004106 if (bio->bi_status) {
Christoph Hellwig4246a0b2015-07-20 15:29:37 +02004107 pr_info("floppy: error %d while reading block 0\n",
Christoph Hellwig4e4cbee2017-06-03 09:38:06 +02004108 bio->bi_status);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004109 set_bit(FD_OPEN_SHOULD_FAIL_BIT, &drive_state[drive].flags);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004110 }
4111 complete(&cbdata->complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004112}
4113
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004114static int __floppy_read_block_0(struct block_device *bdev, int drive)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004115{
4116 struct bio bio;
4117 struct bio_vec bio_vec;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004118 struct page *page;
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004119 struct rb0_cbdata cbdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004120
4121 page = alloc_page(GFP_NOIO);
4122 if (!page) {
4123 process_fd_request();
4124 return -ENOMEM;
4125 }
4126
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004127 cbdata.drive = drive;
4128
Ming Lei3a83f462016-11-22 08:57:21 -07004129 bio_init(&bio, &bio_vec, 1);
Christoph Hellwig74d46992017-08-23 19:10:32 +02004130 bio_set_dev(&bio, bdev);
Christoph Hellwigfe4ec122020-06-26 10:01:52 +02004131 bio_add_page(&bio, page, block_size(bdev), 0);
Ming Lei2c73a602016-11-11 20:05:31 +08004132
Kent Overstreet4f024f32013-10-11 15:44:27 -07004133 bio.bi_iter.bi_sector = 0;
Jiri Kosina6314a102014-05-28 11:55:23 +02004134 bio.bi_flags |= (1 << BIO_QUIET);
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004135 bio.bi_private = &cbdata;
4136 bio.bi_end_io = floppy_rb0_cb;
Mike Christie95fe6c12016-06-05 14:31:48 -05004137 bio_set_op_attrs(&bio, REQ_OP_READ, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004138
Jens Axboede7b75d2018-11-09 15:58:40 -07004139 init_completion(&cbdata.complete);
4140
Mike Christie4e49ea42016-06-05 14:31:41 -05004141 submit_bio(&bio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004142 process_fd_request();
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004143
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004144 wait_for_completion(&cbdata.complete);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004145
4146 __free_page(page);
4147
4148 return 0;
4149}
4150
4151/* revalidate the floppy disk, i.e. trigger format autodetection by reading
4152 * the bootblock (block 0). "Autodetection" is also needed to check whether
4153 * there is a disk in the drive at all... Thus we also do it for fixed
4154 * geometry formats */
4155static int floppy_revalidate(struct gendisk *disk)
4156{
4157 int drive = (long)disk->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004158 int cf;
4159 int res = 0;
4160
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004161 if (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4162 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags) ||
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004163 test_bit(drive, &fake_change) ||
4164 drive_no_geom(drive)) {
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004165 if (WARN(atomic_read(&usage_count) == 0,
4166 "VFS: revalidate called on non-open device.\n"))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004167 return -EFAULT;
Stephen Hemminger01b6b672010-06-15 13:21:11 +02004168
Jiri Kosinaa0c80ef2016-02-01 11:19:17 +01004169 res = lock_fdc(drive);
4170 if (res)
4171 return res;
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004172 cf = (test_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags) ||
4173 test_bit(FD_VERIFY_BIT, &drive_state[drive].flags));
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004174 if (!(cf || test_bit(drive, &fake_change) || drive_no_geom(drive))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004175 process_fd_request(); /*already done by another thread */
4176 return 0;
4177 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004178 drive_state[drive].maxblock = 0;
4179 drive_state[drive].maxtrack = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004180 if (buffer_drive == drive)
4181 buffer_track = -1;
4182 clear_bit(drive, &fake_change);
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004183 clear_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004184 if (cf)
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004185 drive_state[drive].generation++;
Pekka Enberg2b51dca2010-11-08 14:44:34 +01004186 if (drive_no_geom(drive)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004187 /* auto-sensing */
Jiri Kosina7b7b68bb2014-01-10 02:08:13 +01004188 res = __floppy_read_block_0(opened_bdev[drive], drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004189 } else {
4190 if (cf)
Joe Perches74f63f42010-03-10 15:20:58 -08004191 poll_drive(false, FD_RAW_NEED_DISK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004192 process_fd_request();
4193 }
4194 }
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004195 set_capacity(disk, floppy_sizes[drive_state[drive].fd_device]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004196 return res;
4197}
4198
Alexey Dobriyan83d5cde2009-09-21 17:01:13 -07004199static const struct block_device_operations floppy_fops = {
Jesper Juhl06f748c2007-10-16 23:30:57 -07004200 .owner = THIS_MODULE,
Al Viroa4af9b42008-03-02 09:27:55 -05004201 .open = floppy_open,
4202 .release = floppy_release,
Arnd Bergmann8a6cfeb2010-07-08 10:18:46 +02004203 .ioctl = fd_ioctl,
Jesper Juhl06f748c2007-10-16 23:30:57 -07004204 .getgeo = fd_getgeo,
Tejun Heo1a8a74f2011-03-09 19:54:27 +01004205 .check_events = floppy_check_events,
Al Viro229b53c2017-06-27 15:47:56 -04004206#ifdef CONFIG_COMPAT
4207 .compat_ioctl = fd_compat_ioctl,
4208#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004209};
Linus Torvalds1da177e2005-04-16 15:20:36 -07004210
Linus Torvalds1da177e2005-04-16 15:20:36 -07004211/*
4212 * Floppy Driver initialization
4213 * =============================
4214 */
4215
4216/* Determine the floppy disk controller type */
4217/* This routine was written by David C. Niemi */
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004218static char __init get_fdc_version(int fdc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004219{
4220 int r;
4221
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004222 output_byte(fdc, FD_DUMPREGS); /* 82072 and better know DUMPREGS */
4223 if (fdc_state[fdc].reset)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004224 return FDC_NONE;
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004225 r = result(fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004226 if (r <= 0x00)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004227 return FDC_NONE; /* No FDC present ??? */
Denis Efremov67c07162021-04-16 11:34:46 +03004228 if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004229 pr_info("FDC %d is an 8272A\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004230 return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
4231 }
4232 if (r != 10) {
Joe Perchesb46df352010-03-10 15:20:46 -08004233 pr_info("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004234 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004235 return FDC_UNKNOWN;
4236 }
4237
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004238 if (!fdc_configure(fdc)) {
4239 pr_info("FDC %d is an 82072\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004240 return FDC_82072; /* 82072 doesn't know CONFIGURE */
4241 }
4242
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004243 output_byte(fdc, FD_PERPENDICULAR);
4244 if (need_more_output(fdc) == MORE_OUTPUT) {
4245 output_byte(fdc, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004246 } else {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004247 pr_info("FDC %d is an 82072A\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004248 return FDC_82072A; /* 82072A as found on Sparcs. */
4249 }
4250
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004251 output_byte(fdc, FD_UNLOCK);
4252 r = result(fdc);
Denis Efremov67c07162021-04-16 11:34:46 +03004253 if ((r == 1) && (reply_buffer[ST0] == 0x80)) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004254 pr_info("FDC %d is a pre-1991 82077\n", fdc);
Joe Perchesd7b2b2e2010-03-10 15:20:48 -08004255 return FDC_82077_ORIG; /* Pre-1991 82077, doesn't know
Linus Torvalds1da177e2005-04-16 15:20:36 -07004256 * LOCK/UNLOCK */
4257 }
Denis Efremov67c07162021-04-16 11:34:46 +03004258 if ((r != 1) || (reply_buffer[ST0] != 0x00)) {
Joe Perchesb46df352010-03-10 15:20:46 -08004259 pr_info("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004260 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004261 return FDC_UNKNOWN;
4262 }
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004263 output_byte(fdc, FD_PARTID);
4264 r = result(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004265 if (r != 1) {
Joe Perchesb46df352010-03-10 15:20:46 -08004266 pr_info("FDC %d init: PARTID: unexpected return of %d bytes.\n",
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004267 fdc, r);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004268 return FDC_UNKNOWN;
4269 }
Denis Efremov67c07162021-04-16 11:34:46 +03004270 if (reply_buffer[ST0] == 0x80) {
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004271 pr_info("FDC %d is a post-1991 82077\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004272 return FDC_82077; /* Revised 82077AA passes all the tests */
4273 }
Denis Efremov67c07162021-04-16 11:34:46 +03004274 switch (reply_buffer[ST0] >> 5) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004275 case 0x0:
4276 /* Either a 82078-1 or a 82078SL running at 5Volt */
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004277 pr_info("FDC %d is an 82078.\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004278 return FDC_82078;
4279 case 0x1:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004280 pr_info("FDC %d is a 44pin 82078\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004281 return FDC_82078;
4282 case 0x2:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004283 pr_info("FDC %d is a S82078B\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004284 return FDC_S82078B;
4285 case 0x3:
Willy Tarreaue5a9c952020-03-31 11:40:52 +02004286 pr_info("FDC %d is a National Semiconductor PC87306\n", fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004287 return FDC_87306;
4288 default:
Joe Perchesb46df352010-03-10 15:20:46 -08004289 pr_info("FDC %d init: 82078 variant with unknown PARTID=%d.\n",
Denis Efremov67c07162021-04-16 11:34:46 +03004290 fdc, reply_buffer[ST0] >> 5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004291 return FDC_82078_UNKN;
4292 }
4293} /* get_fdc_version */
4294
4295/* lilo configuration */
4296
4297static void __init floppy_set_flags(int *ints, int param, int param2)
4298{
4299 int i;
4300
4301 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4302 if (param)
4303 default_drive_params[i].params.flags |= param2;
4304 else
4305 default_drive_params[i].params.flags &= ~param2;
4306 }
4307 DPRINT("%s flag 0x%x\n", param2 ? "Setting" : "Clearing", param);
4308}
4309
4310static void __init daring(int *ints, int param, int param2)
4311{
4312 int i;
4313
4314 for (i = 0; i < ARRAY_SIZE(default_drive_params); i++) {
4315 if (param) {
4316 default_drive_params[i].params.select_delay = 0;
4317 default_drive_params[i].params.flags |=
4318 FD_SILENT_DCL_CLEAR;
4319 } else {
4320 default_drive_params[i].params.select_delay =
4321 2 * HZ / 100;
4322 default_drive_params[i].params.flags &=
4323 ~FD_SILENT_DCL_CLEAR;
4324 }
4325 }
4326 DPRINT("Assuming %s floppy hardware\n", param ? "standard" : "broken");
4327}
4328
4329static void __init set_cmos(int *ints, int dummy, int dummy2)
4330{
4331 int current_drive = 0;
4332
4333 if (ints[0] != 2) {
4334 DPRINT("wrong number of parameters for CMOS\n");
4335 return;
4336 }
4337 current_drive = ints[1];
4338 if (current_drive < 0 || current_drive >= 8) {
4339 DPRINT("bad drive for set_cmos\n");
4340 return;
4341 }
4342#if N_FDC > 1
4343 if (current_drive >= 4 && !FDC2)
4344 FDC2 = 0x370;
4345#endif
Willy Tarreau031faab2020-02-24 22:23:48 +01004346 drive_params[current_drive].cmos = ints[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004347 DPRINT("setting CMOS code to %d\n", ints[2]);
4348}
4349
4350static struct param_table {
4351 const char *name;
4352 void (*fn) (int *ints, int param, int param2);
4353 int *var;
4354 int def_param;
4355 int param2;
4356} config_params[] __initdata = {
4357 {"allowed_drive_mask", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4358 {"all_drives", NULL, &allowed_drive_mask, 0xff, 0}, /* obsolete */
4359 {"asus_pci", NULL, &allowed_drive_mask, 0x33, 0},
4360 {"irq", NULL, &FLOPPY_IRQ, 6, 0},
4361 {"dma", NULL, &FLOPPY_DMA, 2, 0},
4362 {"daring", daring, NULL, 1, 0},
4363#if N_FDC > 1
4364 {"two_fdc", NULL, &FDC2, 0x370, 0},
4365 {"one_fdc", NULL, &FDC2, 0, 0},
4366#endif
4367 {"thinkpad", floppy_set_flags, NULL, 1, FD_INVERTED_DCL},
4368 {"broken_dcl", floppy_set_flags, NULL, 1, FD_BROKEN_DCL},
4369 {"messages", floppy_set_flags, NULL, 1, FTD_MSG},
4370 {"silent_dcl_clear", floppy_set_flags, NULL, 1, FD_SILENT_DCL_CLEAR},
4371 {"debug", floppy_set_flags, NULL, 1, FD_DEBUG},
4372 {"nodma", NULL, &can_use_virtual_dma, 1, 0},
4373 {"omnibook", NULL, &can_use_virtual_dma, 1, 0},
4374 {"yesdma", NULL, &can_use_virtual_dma, 0, 0},
4375 {"fifo_depth", NULL, &fifo_depth, 0xa, 0},
4376 {"nofifo", NULL, &no_fifo, 0x20, 0},
4377 {"usefifo", NULL, &no_fifo, 0, 0},
4378 {"cmos", set_cmos, NULL, 0, 0},
4379 {"slow", NULL, &slow_floppy, 1, 0},
4380 {"unexpected_interrupts", NULL, &print_unex, 1, 0},
4381 {"no_unexpected_interrupts", NULL, &print_unex, 0, 0},
4382 {"L40SX", NULL, &print_unex, 0, 0}
4383
4384 EXTRA_FLOPPY_PARAMS
4385};
4386
4387static int __init floppy_setup(char *str)
4388{
4389 int i;
4390 int param;
4391 int ints[11];
4392
4393 str = get_options(str, ARRAY_SIZE(ints), ints);
4394 if (str) {
4395 for (i = 0; i < ARRAY_SIZE(config_params); i++) {
4396 if (strcmp(str, config_params[i].name) == 0) {
4397 if (ints[0])
4398 param = ints[1];
4399 else
4400 param = config_params[i].def_param;
4401 if (config_params[i].fn)
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004402 config_params[i].fn(ints, param,
4403 config_params[i].
4404 param2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004405 if (config_params[i].var) {
4406 DPRINT("%s=%d\n", str, param);
4407 *config_params[i].var = param;
4408 }
4409 return 1;
4410 }
4411 }
4412 }
4413 if (str) {
4414 DPRINT("unknown floppy option [%s]\n", str);
4415
4416 DPRINT("allowed options are:");
4417 for (i = 0; i < ARRAY_SIZE(config_params); i++)
Joe Perchesb46df352010-03-10 15:20:46 -08004418 pr_cont(" %s", config_params[i].name);
4419 pr_cont("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004420 } else
4421 DPRINT("botched floppy option\n");
Mauro Carvalho Chehabe7751612019-06-18 11:47:10 -03004422 DPRINT("Read Documentation/admin-guide/blockdev/floppy.rst\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004423 return 0;
4424}
4425
4426static int have_no_fdc = -ENODEV;
4427
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004428static ssize_t floppy_cmos_show(struct device *dev,
4429 struct device_attribute *attr, char *buf)
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004430{
Eric Miao71b3e0c2009-01-31 22:47:44 +08004431 struct platform_device *p = to_platform_device(dev);
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004432 int drive;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004433
Andrew Morton9a8af6b2005-07-27 17:37:34 -07004434 drive = p->id;
Willy Tarreau1ce9ae92020-02-24 22:23:45 +01004435 return sprintf(buf, "%X\n", drive_params[drive].cmos);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004436}
Joe Perches48c8cee2010-03-10 15:20:45 -08004437
Joe Perches5657a812018-05-24 13:38:59 -06004438static DEVICE_ATTR(cmos, 0444, floppy_cmos_show, NULL);
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004439
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004440static struct attribute *floppy_dev_attrs[] = {
4441 &dev_attr_cmos.attr,
4442 NULL
4443};
4444
4445ATTRIBUTE_GROUPS(floppy_dev);
4446
Linus Torvalds1da177e2005-04-16 15:20:36 -07004447static void floppy_device_release(struct device *dev)
4448{
Linus Torvalds1da177e2005-04-16 15:20:36 -07004449}
4450
Frans Popc90cd332009-07-25 22:24:54 +02004451static int floppy_resume(struct device *dev)
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004452{
4453 int fdc;
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004454 int saved_drive;
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004455
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004456 saved_drive = current_drive;
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004457 for (fdc = 0; fdc < N_FDC; fdc++)
Willy Tarreaude6048b2020-02-24 22:23:43 +01004458 if (fdc_state[fdc].address != -1)
Willy Tarreau6111a4f2020-04-10 12:19:02 +02004459 user_reset_fdc(REVDRIVE(fdc, 0), FD_RESET_ALWAYS, false);
4460 set_fdc(saved_drive);
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004461 return 0;
4462}
4463
Alexey Dobriyan47145212009-12-14 18:00:08 -08004464static const struct dev_pm_ops floppy_pm_ops = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004465 .resume = floppy_resume,
Frans Popc90cd332009-07-25 22:24:54 +02004466 .restore = floppy_resume,
4467};
4468
4469static struct platform_driver floppy_driver = {
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004470 .driver = {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004471 .name = "floppy",
4472 .pm = &floppy_pm_ops,
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004473 },
4474};
4475
Omar Sandovala9f38e12018-10-15 09:21:34 -06004476static const struct blk_mq_ops floppy_mq_ops = {
4477 .queue_rq = floppy_queue_rq,
4478};
4479
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004480static struct platform_device floppy_device[N_DRIVE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07004481
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004482static bool floppy_available(int drive)
4483{
4484 if (!(allowed_drive_mask & (1 << drive)))
4485 return false;
4486 if (fdc_state[FDC(drive)].version == FDC_NONE)
4487 return false;
4488 return true;
4489}
4490
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004491static int floppy_alloc_disk(unsigned int drive, unsigned int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004492{
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004493 struct gendisk *disk;
4494 int err;
4495
4496 disk = alloc_disk(1);
4497 if (!disk)
4498 return -ENOMEM;
4499
4500 disk->queue = blk_mq_init_queue(&tag_sets[drive]);
4501 if (IS_ERR(disk->queue)) {
4502 err = PTR_ERR(disk->queue);
4503 disk->queue = NULL;
4504 put_disk(disk);
4505 return err;
4506 }
4507
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004508 blk_queue_max_hw_sectors(disk->queue, 64);
4509 disk->major = FLOPPY_MAJOR;
4510 disk->first_minor = TOMINOR(drive) | (type << 2);
4511 disk->fops = &floppy_fops;
4512 disk->events = DISK_EVENT_MEDIA_CHANGE;
4513 if (type)
4514 sprintf(disk->disk_name, "fd%d_type%d", drive, type);
4515 else
4516 sprintf(disk->disk_name, "fd%d", drive);
4517 /* to be cleaned up... */
4518 disk->private_data = (void *)(long)drive;
4519 disk->flags |= GENHD_FL_REMOVABLE;
4520
4521 disks[drive][type] = disk;
4522 return 0;
4523}
4524
4525static DEFINE_MUTEX(floppy_probe_lock);
4526
4527static void floppy_probe(dev_t dev)
4528{
4529 unsigned int drive = (MINOR(dev) & 3) | ((MINOR(dev) & 0x80) >> 5);
4530 unsigned int type = (MINOR(dev) >> 2) & 0x1f;
4531
4532 if (drive >= N_DRIVE || !floppy_available(drive) ||
4533 type >= ARRAY_SIZE(floppy_type))
4534 return;
4535
4536 mutex_lock(&floppy_probe_lock);
4537 if (!disks[drive][type]) {
4538 if (floppy_alloc_disk(drive, type) == 0)
4539 add_disk(disks[drive][type]);
4540 }
4541 mutex_unlock(&floppy_probe_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004542}
4543
Andi Kleen0cc15d032012-07-02 17:27:04 -07004544static int __init do_floppy_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004545{
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004546 int i, unit, drive, err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004547
Stephen Hemminger285203c2010-06-15 13:21:11 +02004548 set_debugt();
4549 interruptjiffies = resultjiffies = jiffies;
4550
Kumar Gala68e1ee62008-09-22 14:41:31 -07004551#if defined(CONFIG_PPC)
Olaf Heringef16b512006-08-31 21:27:41 -07004552 if (check_legacy_ioport(FDC1))
4553 return -ENODEV;
4554#endif
4555
Linus Torvalds1da177e2005-04-16 15:20:36 -07004556 raw_cmd = NULL;
4557
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004558 floppy_wq = alloc_ordered_workqueue("floppy", 0);
4559 if (!floppy_wq)
4560 return -ENOMEM;
4561
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004562 for (drive = 0; drive < N_DRIVE; drive++) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004563 memset(&tag_sets[drive], 0, sizeof(tag_sets[drive]));
4564 tag_sets[drive].ops = &floppy_mq_ops;
4565 tag_sets[drive].nr_hw_queues = 1;
4566 tag_sets[drive].nr_maps = 1;
4567 tag_sets[drive].queue_depth = 2;
4568 tag_sets[drive].numa_node = NUMA_NO_NODE;
4569 tag_sets[drive].flags = BLK_MQ_F_SHOULD_MERGE;
4570 err = blk_mq_alloc_tag_set(&tag_sets[drive]);
4571 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004572 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004573
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004574 err = floppy_alloc_disk(drive, 0);
4575 if (err)
Herton Ronaldo Krzesinskib54e1f82012-08-27 20:56:51 -03004576 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004577
Kees Cookb1bf4212017-10-04 17:49:29 -07004578 timer_setup(&motor_off_timer[drive], motor_off_callback, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004579 }
4580
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004581 err = __register_blkdev(FLOPPY_MAJOR, "fd", floppy_probe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004582 if (err)
Greg Kroah-Hartman8ab5e4c2005-06-20 21:15:16 -07004583 goto out_put_disk;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004584
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004585 err = platform_driver_register(&floppy_driver);
4586 if (err)
4587 goto out_unreg_blkdev;
4588
Linus Torvalds1da177e2005-04-16 15:20:36 -07004589 for (i = 0; i < 256; i++)
4590 if (ITYPE(i))
4591 floppy_sizes[i] = floppy_type[ITYPE(i)].size;
4592 else
4593 floppy_sizes[i] = MAX_DISK_SIZE << 1;
4594
Joe Perches73507e62010-03-10 15:21:03 -08004595 reschedule_timeout(MAXTIMEOUT, "floppy init");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004596 config_types();
4597
4598 for (i = 0; i < N_FDC; i++) {
Willy Tarreau05f5e312020-04-10 11:30:23 +02004599 memset(&fdc_state[i], 0, sizeof(*fdc_state));
4600 fdc_state[i].dtr = -1;
4601 fdc_state[i].dor = 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004602#if defined(__sparc__) || defined(__mc68000__)
Joe Perches96534f12010-03-10 15:20:51 -08004603 /*sparcs/sun3x don't have a DOR reset which we can fall back on to */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004604#ifdef __mc68000__
4605 if (MACH_IS_SUN3X)
4606#endif
Willy Tarreau05f5e312020-04-10 11:30:23 +02004607 fdc_state[i].version = FDC_82072A;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004608#endif
4609 }
4610
4611 use_virtual_dma = can_use_virtual_dma & 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004612 fdc_state[0].address = FDC1;
4613 if (fdc_state[0].address == -1) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004614 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004615 err = -ENODEV;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004616 goto out_unreg_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004617 }
4618#if N_FDC > 1
4619 fdc_state[1].address = FDC2;
4620#endif
4621
Willy Tarreaue83995c2020-03-01 20:55:55 +01004622 current_fdc = 0; /* reset fdc in case of unexpected interrupt */
Linus Torvalds1da177e2005-04-16 15:20:36 -07004623 err = floppy_grab_irq_and_dma();
4624 if (err) {
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004625 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004626 err = -EBUSY;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004627 goto out_unreg_driver;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004628 }
4629
4630 /* initialise drive state */
4631 for (drive = 0; drive < N_DRIVE; drive++) {
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004632 memset(&drive_state[drive], 0, sizeof(drive_state[drive]));
Willy Tarreau121e2972020-02-24 22:23:47 +01004633 memset(&write_errors[drive], 0, sizeof(write_errors[drive]));
Willy Tarreau8d9d34e22020-02-24 22:23:46 +01004634 set_bit(FD_DISK_NEWCHANGE_BIT, &drive_state[drive].flags);
4635 set_bit(FD_DISK_CHANGED_BIT, &drive_state[drive].flags);
4636 set_bit(FD_VERIFY_BIT, &drive_state[drive].flags);
4637 drive_state[drive].fd_device = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004638 floppy_track_buffer = NULL;
4639 max_buffer_sectors = 0;
4640 }
4641 /*
4642 * Small 10 msec delay to let through any interrupt that
4643 * initialization might have triggered, to not
4644 * confuse detection:
4645 */
4646 msleep(10);
4647
4648 for (i = 0; i < N_FDC; i++) {
Willy Tarreau05f5e312020-04-10 11:30:23 +02004649 fdc_state[i].driver_version = FD_DRIVER_VERSION;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004650 for (unit = 0; unit < 4; unit++)
Willy Tarreau05f5e312020-04-10 11:30:23 +02004651 fdc_state[i].track[unit] = 0;
4652 if (fdc_state[i].address == -1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004653 continue;
Willy Tarreau05f5e312020-04-10 11:30:23 +02004654 fdc_state[i].rawcmd = 2;
4655 if (user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004656 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004657 floppy_release_regions(i);
4658 fdc_state[i].address = -1;
4659 fdc_state[i].version = FDC_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004660 continue;
4661 }
4662 /* Try to determine the floppy controller type */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004663 fdc_state[i].version = get_fdc_version(i);
4664 if (fdc_state[i].version == FDC_NONE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07004665 /* free ioports reserved by floppy_grab_irq_and_dma() */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004666 floppy_release_regions(i);
4667 fdc_state[i].address = -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004668 continue;
4669 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004670 if (can_use_virtual_dma == 2 &&
Willy Tarreau05f5e312020-04-10 11:30:23 +02004671 fdc_state[i].version < FDC_82072A)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004672 can_use_virtual_dma = 0;
4673
4674 have_no_fdc = 0;
4675 /* Not all FDCs seem to be able to handle the version command
4676 * properly, so force a reset for the standard FDC clones,
4677 * to avoid interrupt garbage.
4678 */
Willy Tarreau05f5e312020-04-10 11:30:23 +02004679 user_reset_fdc(REVDRIVE(i, 0), FD_RESET_ALWAYS, false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004680 }
Willy Tarreaue83995c2020-03-01 20:55:55 +01004681 current_fdc = 0;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004682 cancel_delayed_work(&fd_timeout);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004683 current_drive = 0;
Joe Perches29f1c782010-03-10 15:21:00 -08004684 initialized = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004685 if (have_no_fdc) {
4686 DPRINT("no floppy controllers found\n");
4687 err = have_no_fdc;
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004688 goto out_release_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004689 }
4690
Linus Torvalds1da177e2005-04-16 15:20:36 -07004691 for (drive = 0; drive < N_DRIVE; drive++) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004692 if (!floppy_available(drive))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004693 continue;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004694
4695 floppy_device[drive].name = floppy_device_name;
4696 floppy_device[drive].id = drive;
4697 floppy_device[drive].dev.release = floppy_device_release;
Takashi Iwaib7f120b2015-02-02 17:08:45 +01004698 floppy_device[drive].dev.groups = floppy_dev_groups;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004699
4700 err = platform_device_register(&floppy_device[drive]);
4701 if (err)
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004702 goto out_remove_drives;
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004703
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004704 device_add_disk(&floppy_device[drive].dev, disks[drive][0],
4705 NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004706 }
4707
4708 return 0;
4709
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004710out_remove_drives:
4711 while (drive--) {
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004712 if (floppy_available(drive)) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004713 del_gendisk(disks[drive][0]);
Herton Ronaldo Krzesinskid60e7ec2012-08-27 20:56:54 -03004714 platform_device_unregister(&floppy_device[drive]);
4715 }
4716 }
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004717out_release_dma:
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004718 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004719 floppy_release_irq_and_dma();
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004720out_unreg_driver:
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004721 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004722out_unreg_blkdev:
4723 unregister_blkdev(FLOPPY_MAJOR, "fd");
Linus Torvalds1da177e2005-04-16 15:20:36 -07004724out_put_disk:
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01004725 destroy_workqueue(floppy_wq);
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004726 for (drive = 0; drive < N_DRIVE; drive++) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004727 if (!disks[drive][0])
Herton Ronaldo Krzesinski1a4ae432012-10-30 08:36:07 +01004728 break;
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004729 del_timer_sync(&motor_off_timer[drive]);
4730 blk_cleanup_queue(disks[drive][0]->queue);
4731 disks[drive][0]->queue = NULL;
4732 blk_mq_free_tag_set(&tag_sets[drive]);
4733 put_disk(disks[drive][0]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004734 }
4735 return err;
4736}
4737
Andi Kleen0cc15d032012-07-02 17:27:04 -07004738#ifndef MODULE
4739static __init void floppy_async_init(void *data, async_cookie_t cookie)
4740{
4741 do_floppy_init();
4742}
4743#endif
4744
4745static int __init floppy_init(void)
4746{
4747#ifdef MODULE
4748 return do_floppy_init();
4749#else
4750 /* Don't hold up the bootup by the floppy initialization */
4751 async_schedule(floppy_async_init, NULL);
4752 return 0;
4753#endif
4754}
4755
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004756static const struct io_region {
4757 int offset;
4758 int size;
4759} io_regions[] = {
4760 { 2, 1 },
4761 /* address + 3 is sometimes reserved by pnp bios for motherboard */
4762 { 4, 2 },
4763 /* address + 6 is reserved, and may be taken by IDE.
4764 * Unfortunately, Adaptec doesn't know this :-(, */
4765 { 7, 1 },
4766};
4767
4768static void floppy_release_allocated_regions(int fdc, const struct io_region *p)
4769{
4770 while (p != io_regions) {
4771 p--;
Willy Tarreaude6048b2020-02-24 22:23:43 +01004772 release_region(fdc_state[fdc].address + p->offset, p->size);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004773 }
4774}
4775
4776#define ARRAY_END(X) (&((X)[ARRAY_SIZE(X)]))
4777
4778static int floppy_request_regions(int fdc)
4779{
4780 const struct io_region *p;
4781
4782 for (p = io_regions; p < ARRAY_END(io_regions); p++) {
Willy Tarreaude6048b2020-02-24 22:23:43 +01004783 if (!request_region(fdc_state[fdc].address + p->offset,
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004784 p->size, "floppy")) {
4785 DPRINT("Floppy io-port 0x%04lx in use\n",
Willy Tarreaude6048b2020-02-24 22:23:43 +01004786 fdc_state[fdc].address + p->offset);
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004787 floppy_release_allocated_regions(fdc, p);
4788 return -EBUSY;
4789 }
4790 }
4791 return 0;
4792}
4793
4794static void floppy_release_regions(int fdc)
4795{
4796 floppy_release_allocated_regions(fdc, ARRAY_END(io_regions));
4797}
4798
Linus Torvalds1da177e2005-04-16 15:20:36 -07004799static int floppy_grab_irq_and_dma(void)
4800{
Willy Tarreau82a63012020-03-31 11:40:53 +02004801 int fdc;
4802
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004803 if (atomic_inc_return(&usage_count) > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004804 return 0;
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004805
4806 /*
4807 * We might have scheduled a free_irq(), wait it to
4808 * drain first:
4809 */
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004810 flush_workqueue(floppy_wq);
Ingo Molnar6dc659d2006-03-26 01:36:54 -08004811
Linus Torvalds1da177e2005-04-16 15:20:36 -07004812 if (fd_request_irq()) {
4813 DPRINT("Unable to grab IRQ%d for the floppy driver\n",
4814 FLOPPY_IRQ);
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004815 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004816 return -1;
4817 }
4818 if (fd_request_dma()) {
4819 DPRINT("Unable to grab DMA%d for the floppy driver\n",
4820 FLOPPY_DMA);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004821 if (can_use_virtual_dma & 2)
4822 use_virtual_dma = can_use_virtual_dma = 1;
4823 if (!(can_use_virtual_dma & 1)) {
4824 fd_free_irq();
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004825 atomic_dec(&usage_count);
Jan Beulich2e9c47c2007-10-16 23:27:32 -07004826 return -1;
4827 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004828 }
4829
Willy Tarreau82a63012020-03-31 11:40:53 +02004830 for (fdc = 0; fdc < N_FDC; fdc++) {
4831 if (fdc_state[fdc].address != -1) {
4832 if (floppy_request_regions(fdc))
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004833 goto cleanup;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004834 }
4835 }
Willy Tarreau82a63012020-03-31 11:40:53 +02004836 for (fdc = 0; fdc < N_FDC; fdc++) {
4837 if (fdc_state[fdc].address != -1) {
4838 reset_fdc_info(fdc, 1);
4839 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004840 }
4841 }
Willy Tarreau82a63012020-03-31 11:40:53 +02004842
Linus Torvalds1da177e2005-04-16 15:20:36 -07004843 set_dor(0, ~0, 8); /* avoid immediate interrupt */
4844
Willy Tarreau82a63012020-03-31 11:40:53 +02004845 for (fdc = 0; fdc < N_FDC; fdc++)
4846 if (fdc_state[fdc].address != -1)
4847 fdc_outb(fdc_state[fdc].dor, fdc, FD_DOR);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004848 /*
Jesper Juhl06f748c2007-10-16 23:30:57 -07004849 * The driver will try and free resources and relies on us
4850 * to know if they were allocated or not.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004851 */
Willy Tarreaue83995c2020-03-01 20:55:55 +01004852 current_fdc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004853 irqdma_allocated = 1;
4854 return 0;
Philippe De Muyter5a74db02009-02-18 14:48:36 -08004855cleanup:
Linus Torvalds1da177e2005-04-16 15:20:36 -07004856 fd_free_irq();
4857 fd_free_dma();
Willy Tarreau82a63012020-03-31 11:40:53 +02004858 while (--fdc >= 0)
4859 floppy_release_regions(fdc);
4860 current_fdc = 0;
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004861 atomic_dec(&usage_count);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004862 return -1;
4863}
4864
4865static void floppy_release_irq_and_dma(void)
4866{
Willy Tarreau82a63012020-03-31 11:40:53 +02004867 int fdc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004868#ifndef __sparc__
4869 int drive;
4870#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004871 long tmpsize;
4872 unsigned long tmpaddr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004873
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004874 if (!atomic_dec_and_test(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004875 return;
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004876
Linus Torvalds1da177e2005-04-16 15:20:36 -07004877 if (irqdma_allocated) {
4878 fd_disable_dma();
4879 fd_free_dma();
Ingo Molnar3e541a4a2006-07-03 00:24:23 -07004880 fd_free_irq();
Linus Torvalds1da177e2005-04-16 15:20:36 -07004881 irqdma_allocated = 0;
4882 }
4883 set_dor(0, ~0, 8);
4884#if N_FDC > 1
4885 set_dor(1, ~8, 0);
4886#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07004887
4888 if (floppy_track_buffer && max_buffer_sectors) {
4889 tmpsize = max_buffer_sectors * 1024;
4890 tmpaddr = (unsigned long)floppy_track_buffer;
4891 floppy_track_buffer = NULL;
4892 max_buffer_sectors = 0;
4893 buffer_min = buffer_max = -1;
4894 fd_dma_mem_free(tmpaddr, tmpsize);
4895 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004896#ifndef __sparc__
4897 for (drive = 0; drive < N_FDC * 4; drive++)
4898 if (timer_pending(motor_off_timer + drive))
Joe Perchesb46df352010-03-10 15:20:46 -08004899 pr_info("motor off timer %d still active\n", drive);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004900#endif
4901
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004902 if (delayed_work_pending(&fd_timeout))
Joe Perchesb46df352010-03-10 15:20:46 -08004903 pr_info("floppy timer still active:%s\n", timeout_message);
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004904 if (delayed_work_pending(&fd_timer))
Joe Perchesb46df352010-03-10 15:20:46 -08004905 pr_info("auxiliary floppy timer still active\n");
David Howells365970a2006-11-22 14:54:49 +00004906 if (work_pending(&floppy_work))
Joe Perchesb46df352010-03-10 15:20:46 -08004907 pr_info("work still pending\n");
Willy Tarreau82a63012020-03-31 11:40:53 +02004908 for (fdc = 0; fdc < N_FDC; fdc++)
4909 if (fdc_state[fdc].address != -1)
4910 floppy_release_regions(fdc);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004911}
4912
4913#ifdef MODULE
4914
4915static char *floppy;
4916
Linus Torvalds1da177e2005-04-16 15:20:36 -07004917static void __init parse_floppy_cfg_string(char *cfg)
4918{
4919 char *ptr;
4920
4921 while (*cfg) {
Joe Perchesbb57f0c62010-03-10 15:20:50 -08004922 ptr = cfg;
4923 while (*cfg && *cfg != ' ' && *cfg != '\t')
4924 cfg++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004925 if (*cfg) {
4926 *cfg = '\0';
4927 cfg++;
4928 }
4929 if (*ptr)
4930 floppy_setup(ptr);
4931 }
4932}
4933
Jon Schindler7afea3b2008-04-29 00:59:21 -07004934static int __init floppy_module_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004935{
4936 if (floppy)
4937 parse_floppy_cfg_string(floppy);
4938 return floppy_init();
4939}
Jon Schindler7afea3b2008-04-29 00:59:21 -07004940module_init(floppy_module_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004941
Jon Schindler7afea3b2008-04-29 00:59:21 -07004942static void __exit floppy_module_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07004943{
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004944 int drive, i;
Linus Torvalds1da177e2005-04-16 15:20:36 -07004945
Linus Torvalds1da177e2005-04-16 15:20:36 -07004946 unregister_blkdev(FLOPPY_MAJOR, "fd");
Ondrej Zary5e50b9e2009-06-10 12:57:09 -07004947 platform_driver_unregister(&floppy_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004948
Jiri Kosinaeac7cc52012-11-06 11:47:13 +01004949 destroy_workqueue(floppy_wq);
4950
Linus Torvalds1da177e2005-04-16 15:20:36 -07004951 for (drive = 0; drive < N_DRIVE; drive++) {
4952 del_timer_sync(&motor_off_timer[drive]);
4953
Herton Ronaldo Krzesinski8d3ab4e2012-08-27 20:56:55 -03004954 if (floppy_available(drive)) {
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004955 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4956 if (disks[drive][i])
4957 del_gendisk(disks[drive][i]);
4958 }
Hannes Reinecke94fd0db2005-07-15 10:09:25 +02004959 platform_device_unregister(&floppy_device[drive]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004960 }
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004961 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4962 if (disks[drive][i])
4963 blk_cleanup_queue(disks[drive][i]->queue);
4964 }
Omar Sandovala9f38e12018-10-15 09:21:34 -06004965 blk_mq_free_tag_set(&tag_sets[drive]);
Vivek Goyal4609dff2012-02-08 20:03:39 +01004966
4967 /*
4968 * These disks have not called add_disk(). Don't put down
4969 * queue reference in put_disk().
4970 */
4971 if (!(allowed_drive_mask & (1 << drive)) ||
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004972 fdc_state[FDC(drive)].version == FDC_NONE) {
4973 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4974 if (disks[drive][i])
4975 disks[drive][i]->queue = NULL;
4976 }
4977 }
Vivek Goyal4609dff2012-02-08 20:03:39 +01004978
Christoph Hellwig302cfee2020-10-29 15:58:36 +01004979 for (i = 0; i < ARRAY_SIZE(floppy_type); i++) {
4980 if (disks[drive][i])
4981 put_disk(disks[drive][i]);
4982 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004983 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07004984
Jiri Kosina070ad7e2012-05-18 13:50:25 +02004985 cancel_delayed_work_sync(&fd_timeout);
4986 cancel_delayed_work_sync(&fd_timer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004987
Stephen Hemminger575cfc62010-06-15 13:21:11 +02004988 if (atomic_read(&usage_count))
Linus Torvalds1da177e2005-04-16 15:20:36 -07004989 floppy_release_irq_and_dma();
4990
4991 /* eject disk, if any */
4992 fd_eject(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004993}
Joe Perches48c8cee2010-03-10 15:20:45 -08004994
Jon Schindler7afea3b2008-04-29 00:59:21 -07004995module_exit(floppy_module_exit);
Linus Torvalds1da177e2005-04-16 15:20:36 -07004996
4997module_param(floppy, charp, 0);
4998module_param(FLOPPY_IRQ, int, 0);
4999module_param(FLOPPY_DMA, int, 0);
5000MODULE_AUTHOR("Alain L. Knaff");
Linus Torvalds1da177e2005-04-16 15:20:36 -07005001MODULE_LICENSE("GPL");
5002
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005003/* This doesn't actually get used other than for module information */
5004static const struct pnp_device_id floppy_pnpids[] = {
Joe Perches48c8cee2010-03-10 15:20:45 -08005005 {"PNP0700", 0},
5006 {}
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005007};
Joe Perches48c8cee2010-03-10 15:20:45 -08005008
Scott James Remnant83f9ef42009-04-02 16:56:47 -07005009MODULE_DEVICE_TABLE(pnp, floppy_pnpids);
5010
Linus Torvalds1da177e2005-04-16 15:20:36 -07005011#else
5012
5013__setup("floppy=", floppy_setup);
5014module_init(floppy_init)
5015#endif
5016
5017MODULE_ALIAS_BLOCKDEV_MAJOR(FLOPPY_MAJOR);