diff -u --recursive --new-file v1.1.40/linux/CREDITS linux/CREDITS --- v1.1.40/linux/CREDITS Mon Jul 25 17:56:37 1994 +++ linux/CREDITS Sun Aug 7 00:31:14 1994 @@ -419,11 +419,11 @@ S: Finland N: David C. Niemi -E: David.Niemi@oasis.gtegsc.com +E: niemidc@slma.com D: FSSTND, The XFree86 Project -D: DMA memory support and future floppy driver +D: DMA memory support, floppy driver S: 2364 Old Trail Drive -S: Reston, VA 22091 +S: Reston, Virginia 22091 S: USA N: Kai Petzke diff -u --recursive --new-file v1.1.40/linux/Makefile linux/Makefile --- v1.1.40/linux/Makefile Sun Aug 7 03:30:37 1994 +++ linux/Makefile Sun Aug 7 00:29:52 1994 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 40 +SUBLEVEL = 41 all: Version zImage diff -u --recursive --new-file v1.1.40/linux/boot/head.S linux/boot/head.S --- v1.1.40/linux/boot/head.S Mon Jul 18 14:48:28 1994 +++ linux/boot/head.S Sun Aug 7 00:46:56 1994 @@ -14,10 +14,12 @@ .globl _empty_bad_page .globl _empty_bad_page_table .globl _empty_zero_page -.globl _tmp_floppy_area,_floppy_track_buffer +.globl _floppy_track_buffer #include #include +#define ASSEMBLER +#include #define CL_MAGIC_ADDR 0x90020 #define CL_MAGIC 0xA33F @@ -276,20 +278,13 @@ .org 0x6000 /* - * tmp_floppy_area is used by the floppy-driver when DMA cannot - * reach to a buffer-block. It needs to be aligned, so that it isn't - * on a 64kB border. - */ -_tmp_floppy_area: - .fill 1024,1,0 -/* * floppy_track_buffer is used to buffer one track of floppy data: it * has to be separate from the tmp_floppy area, as otherwise a single- * sector read/write can mess it up. It can contain one full cylinder (sic) of * data (36*2*512 bytes). */ _floppy_track_buffer: - .fill 512*2*36,1,0 + .fill 512*2*MAX_BUFFER_SECTORS,1,0 /* This is the default interrupt "handler" :-) */ int_msg: diff -u --recursive --new-file v1.1.40/linux/drivers/block/blk.h linux/drivers/block/blk.h --- v1.1.40/linux/drivers/block/blk.h Sat Aug 6 21:32:05 1994 +++ linux/drivers/block/blk.h Sun Aug 7 00:46:56 1994 @@ -124,7 +124,7 @@ #define DEVICE_NAME "floppy" #define DEVICE_INTR do_floppy #define DEVICE_REQUEST do_fd_request -#define DEVICE_NR(device) ((device) & 3) +#define DEVICE_NR(device) ( ((device) & 3) | (((device) & 0x80 ) >> 5 )) #define DEVICE_ON(device) floppy_on(DEVICE_NR(device)) #define DEVICE_OFF(device) floppy_off(DEVICE_NR(device)) diff -u --recursive --new-file v1.1.40/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v1.1.40/linux/drivers/block/floppy.c Wed Aug 3 09:32:23 1994 +++ linux/drivers/block/floppy.c Sun Aug 7 00:46:57 1994 @@ -2,8 +2,25 @@ * linux/kernel/floppy.c * * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1993, 1994 Alain Knaff */ +/* Configuration */ +/* The following does some extra sanity checks */ +#define SANITY + +/* Undefine the following if you have to floppy disk controllers: + * This is untested. If you get two controllers working, with drives attached + * too both, please mail me: Alain.Knaff@imag.fr */ +/* #define HAVE_2_CONTROLLERS */ + +/* Undefine the following if you have problems accessing ED disks, but don't + * have problems accessing them with the stock driver. If that is the case, + * please mail me: Alain.Knaff@imag.fr */ +/* #define FDC_FIFO_BUG */ + +/* End of configuration */ + /* * 02.12.91 - Changed to static variables to indicate need for reset * and recalibrate. This makes some things easier (output_byte reset @@ -16,7 +33,7 @@ * but I don't like programming floppies, and I have only one anyway. * Urgel. I should check for more errors, and do more graceful error * recovery. Seems there are problems with several drives. I've tried to - * correct them. No promises. + * correct them. No promises. */ /* @@ -39,7 +56,7 @@ */ /* - * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed + * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed * FDC data overrun bug, added some preliminary stuff for vertical * recording support. * @@ -50,7 +67,7 @@ /* 1992/9/20 * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl) - * modelled after the freeware MS/DOS program fdformat/88 V1.8 by + * modelled after the freeware MS/DOS program fdformat/88 V1.8 by * Christoph H. Hochst\"atter. * I have fixed the shift values to the ones I always use. Maybe a new * ioctl() should be created to be able to modify them. @@ -65,7 +82,7 @@ */ /* 1994/6/24 --bbroad-- added the floppy table entries and made - * minor modifications to allow 2.88 floppies to be run. + * minor modifications to allow 2.88 floppies to be run. */ /* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more @@ -76,17 +93,21 @@ #define REALLY_SLOW_IO #define FLOPPY_IRQ 6 #define FLOPPY_DMA 2 -#define FDC_FIFO_UNTESTED /* -bb */ -#define FDC_FIFO_BUG +#define DEBUGT 2 #include #include #include #include +#define FDPATCHES #include #include #include +#include +#include +#include +#include #include #include @@ -97,39 +118,57 @@ #define MAJOR_NR FLOPPY_MAJOR #include "blk.h" -static unsigned int changed_floppies = 0, fake_change = 0, read_only = 0; - -static int initial_reset_flag = 0; -static int need_configure = 1; /* for 82077 */ -static int recalibrate = 0; -static int reset = 0; -static int recover = 0; /* recalibrate immediately after resetting */ -static int seek = 0; +static unsigned int changed_floppies = 0, fake_change = 0; +static int initialising=1; -static unsigned char current_DOR = 0x0C; -static unsigned char running = 0; -#define TYPE(x) ((x)>>2) -#define DRIVE(x) ((x)&0x03) +#ifdef HAVE_2_CONTROLLERS +#define N_FDC 2 +#define N_DRIVE 8 +#else +#define N_FDC 1 +#define N_DRIVE 4 +#endif -/* - * Note that MAX_ERRORS=X doesn't imply that we retry every bad read - * max X times - some types of errors increase the errorcount by 2 or - * even 3, so we might actually retry only X/2 times before giving up. - */ -#define MAX_ERRORS 12 +#define TYPE(x) ( ((x)>>2) & 0x1f ) +#define DRIVE(x) ( ((x)&0x03) | (((x)&0x80 ) >> 5)) +#define UNIT(x) ( (x) & 0x03 ) /* drive on fdc */ +#define FDC(x) ( ((x) & 0x04) >> 2 ) /* fdc of drive */ +#define REVDRIVE(fdc, unit) ( (unit) + ((fdc) << 2 )) + /* reverse mapping from unit and fdc to drive */ +#define DP (&drive_params[current_drive]) +#define DRS (&drive_state[current_drive]) +#define FDCS (&fdc_state[fdc]) + +#define UDP (&drive_params[drive]) +#define UDRS (&drive_state[drive]) +#define UFDCS (&fdc_state[FDC(drive)]) + +/* read/write */ +#define COMMAND raw_cmd.cmd[0] +#define DR_SELECT raw_cmd.cmd[1] +#define TRACK raw_cmd.cmd[2] +#define HEAD raw_cmd.cmd[3] +#define SECTOR raw_cmd.cmd[4] +#define SIZECODE raw_cmd.cmd[5] +#define SECT_PER_TRACK raw_cmd.cmd[6] +#define GAP raw_cmd.cmd[7] +#define SIZECODE2 raw_cmd.cmd[8] +#define NR_RW 9 + +/* format */ +#define F_SIZECODE raw_cmd.cmd[2] +#define F_SECT_PER_TRACK raw_cmd.cmd[3] +#define F_GAP raw_cmd.cmd[4] +#define F_FILL raw_cmd.cmd[5] +#define NR_F 6 /* * Maximum disk size (in kilobytes). This default is used whenever the * current disk size is unknown. */ -#define MAX_DISK_SIZE 2880 /* was 1440 -bb */ +#define MAX_DISK_SIZE 3984 -/* - * Maximum number of sectors in a track buffer. Track buffering is disabled - * if tracks are bigger. - */ -#define MAX_BUFFER_SECTORS 36 /* was 18 -bb */ /* @@ -140,18 +179,76 @@ * driver otherwise. It doesn't matter much for performance anyway, as most * floppy accesses go through the track buffer. */ -#define LAST_DMA_ADDR (0x100000 - BLOCK_SIZE) +#define LAST_DMA_ADDR (0x1000000) +#define K_64 (0x10000) /* 64 k */ /* * globals used by 'result()' */ -#define MAX_REPLIES 7 +#define MAX_REPLIES 10 static unsigned char reply_buffer[MAX_REPLIES]; +static int inr; /* size of reply buffer, when called from interrupt */ #define ST0 (reply_buffer[0]) #define ST1 (reply_buffer[1]) #define ST2 (reply_buffer[2]) #define ST3 (reply_buffer[0]) /* result of GETSTATUS */ +#define R_TRACK (reply_buffer[3]) +#define R_HEAD (reply_buffer[4]) +#define R_SECTOR (reply_buffer[5]) +#define R_SIZECODE (reply_buffer[6]) + +/* + * this struct defines the different floppy drive types. + */ +static struct { + struct floppy_drive_params params; + char *name; /* name printed while booting */ +} default_drive_params[]= { +/* NOTE: the time values in jiffies should be in msec! + CMOS drive type + | Maximum data rate supported by drive type + | | Head load time, msec + | | | Head unload time, msec (not used) + | | | | Step rate interval, usec + | | | | | Time needed for spinup time (jiffies) + | | | | | | Timeout for spinning down (jiffies) + | | | | | | | Spindown offset (where disk stops) + | | | | | | | | Select delay + | | | | | | | | | RPS + | | | | | | | | | | Max number of tracks + | | | | | | | | | | | Interrupt timeout + | | | | | | | | | | | | Max nonintlv. sectors + | | | | | | | | | | | | | -Max Errors- flags */ +{{0, 500, 16, 16, 8000, 100, 300, 0, 2, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0, + 0, { 7, 4, 8, 2, 1, 5, 3,10}, 150, 0 }, "unknown" }, + +{{1, 300, 16, 16, 8000, 100, 300, 0, 2, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0, + 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 150, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/ + +{{2, 500, 16, 16, 6000, 40, 300, 14, 2, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0, + 0, { 2, 5,18,31,11,11, 0, 0}, 150, 2 }, "1.2M" }, /*5 1/4 HD AT*/ + +{{3, 250, 16, 16, 3000, 100, 300, 0, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, + 0, { 4,14,13,10, 0, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/ + +{{4, 500, 16, 16, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, + 0, { 7, 4,24,14,20,13,23,21}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/ + +{{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, + 0, { 7, 8, 4,24,27,14,20,13}, 150, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/ + +{{6, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, + 0, { 7, 8, 4,24,27,14,20,13}, 150, 8 }, "2.88M" } /*3 1/2 ED*/ +/* | ---autodetected formats-- | | | + read_track | | Name printed when booting + | Native format + Frequency of disk change checks */ +}; +static struct floppy_drive_params drive_params[N_DRIVE]; +static struct floppy_drive_struct volatile drive_state[N_DRIVE]; +static struct floppy_raw_cmd raw_cmd; + /* * This struct defines the different floppy types. * @@ -159,96 +256,61 @@ * types (ie 360kB diskette in 1.2MB drive etc). Others should * be self-explanatory. */ -static struct floppy_struct floppy_type[] = { - { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* no testing */ - { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB PC diskettes */ - { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,NULL }, /* 1.2 MB AT-diskettes */ - { 720, 9,2,40,1,0x2A,0x02,0xDF,0x50,NULL }, /* 360kB in 720kB drive */ - { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,NULL }, /* 3.5" 720kB diskette */ - { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,NULL }, /* 360kB in 1.2MB drive */ - { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,NULL }, /* 720kB in 1.2MB drive */ - { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }, /* 1.44MB diskette */ - { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,NULL }, /* 2.88MB diskette */ +static struct floppy_struct floppy_type[32] = { + { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */ + { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360kB PC/QD */ + { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 *1.2 MB AT */ + { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360kB SS 3.5" */ + { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */ + { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 *360kB AT */ + { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720kB AT */ + { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 *1.44MB 3.5" */ + { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 *2.88MB 3.5" */ + { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"}, /* 9 2.88MB 3.5" */ + + { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 *1.44MB 5.25" */ + { 3360,21,2,80,0,0x1c,0x00,0xcf,0x6c,"H1680" }, /* 11 *1.68MB 3.5" */ + { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */ + { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */ + { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */ + { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */ + { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */ + { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */ + { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */ + { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */ + + { 1760,11,2,80,0,0x1c,0x09,0xcf,0x6c,"d880" }, /* 20 880k 5.25" */ + { 2080,13,2,80,0,0x1c,0x0a,0xcf,0x6c,"D1040" }, /* 21 1.04MB 3.5" */ + { 2240,14,2,80,0,0x1c,0x1a,0xcf,0x6c,"D1120" }, /* 22 *1.12MB 3.5" */ + { 3200,20,2,80,0,0x1c,0x20,0xcf,0x6c,"h1600" }, /* 23 1.6MB 5.25" */ + { 3520,22,2,80,0,0x1c,0x08,0xcf,0x6c,"H1760" }, /* 24 1.76MB 3.5" */ + { 3840,24,2,80,0,0x1c,0x18,0xcf,0x6c,"H1920" }, /* 25 *1.92MB 3.5" */ + { 6400,40,2,80,0,0x25,0x5b,0xcf,0x6c,"E3200" }, /* 26 3.20MB 3.5" */ + { 7040,44,2,80,0,0x25,0x5b,0xcf,0x6c,"E3520" }, /* 27 3.52MB 3.5" */ + { 7680,48,2,80,0,0x25,0x63,0xcf,0x6c,"E3840" }, /* 28 *3.84MB 3.5" */ + + { 3680,23,2,80,0,0x1c,0x10,0xcf,0x6c,"H1840" }, /* 29 1.84MB 3.5" */ + { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"H800" }, /* 30 *800k 3.5" */ + { 3200,20,2,80,0,0x1c,0x00,0xcf,0x6c,"H1600" }, /* 31 *1.6MB 3.5" */ }; -/* - * Auto-detection. Each drive type has a number of formats which are - * used in succession to try to read the disk. If the FDC cannot lock onto - * the disk, the next format is tried. This uses the variable 'probing'. - */ #define NUMBER(x) (sizeof(x) / sizeof(*(x))) - -static struct floppy_struct floppy_type_1[] = { - { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"360k/PC" }, /* 360kB PC diskettes */ -}; - -static struct floppy_struct floppy_type_2[] = { - { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"1.2M" }, /* 1.2 MB AT-diskettes */ - { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"360k/AT" }, /* 360kB in 1.2MB drive */ -}; - -static struct floppy_struct floppy_type_3[] = { - { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k" }, /* 3.5" 720kB diskette */ -}; - -static struct floppy_struct floppy_type_4[] = { - { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" }, /* 1.44MB diskette */ - { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */ -}; +#define SECTSIZE ( _FD_SECTSIZE(*floppy)) -static struct floppy_struct floppy_type_5[] = { - { 5760,36,2,80,0,0x1B,0x43,0xAF,0x50,"2.88M" }, /* 2.88MB diskette */ - { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"1.44M" }, /* 1.44MB diskette */ - { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"720k/AT" }, /* 3.5" 720kB diskette */ -}; - -#define floppy_type_6 floppy_type_5 /* 5 and 6 are the same */ - - -static struct floppy_struct *floppy_types[] = { - floppy_type_1, - floppy_type_2, - floppy_type_3, - floppy_type_4, - floppy_type_5, - floppy_type_6, -}; - -static int floppy_num_types[] = { - NUMBER(floppy_type_1), - NUMBER(floppy_type_2), - NUMBER(floppy_type_3), - NUMBER(floppy_type_4), - NUMBER(floppy_type_5), - NUMBER(floppy_type_6), -}; - /* Auto-detection: Disk type used until the next media change occurs. */ -struct floppy_struct *current_type[4] = { NULL, NULL, NULL, NULL }; - -/* This type is tried first. */ -struct floppy_struct *base_type[4] = { NULL, NULL, NULL, NULL }; - -/* This gives the end of the list */ -struct floppy_struct *base_type_end[4]; +struct floppy_struct *current_type[N_DRIVE] = { NULL, NULL, NULL, NULL +#ifdef HAVE_2_CONTROLLERS + ,NULL, NULL, NULL, NULL +#endif + }; /* * User-provided type information. current_type points to * the respective entry of this array. */ -struct floppy_struct user_params[4]; +struct floppy_struct user_params[N_DRIVE]; -static int floppy_sizes[] ={ - MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, MAX_DISK_SIZE, - 360, 360, 360, 360, - 1200,1200,1200,1200, - 360, 360, 360, 360, - 720, 720, 720, 720, - 360, 360, 360, 360, - 720, 720, 720, 720, - 1440,1440,1440,1440, - 2880,2880,2880,2880 /* -bb */ -}; +static int floppy_sizes[256]; /* * The driver is trying to determine the correct media format @@ -257,28 +319,14 @@ */ static int probing = 0; -/* - * (User-provided) media information is _not_ discarded after a media change - * if the corresponding keep_data flag is non-zero. Positive values are - * decremented after each probe. - */ -static int keep_data[4] = { 0,0,0,0 }; - -/* - * Announce successful media type detection and media information loss after - * disk changes. - * Also used to enable/disable printing of overrun warnings. - */ -static ftd_msg[4] = { 0,0,0,0 }; - -/* Prevent "aliased" accesses. */ - -static fd_ref[4] = { 0,0,0,0 }; -static fd_device[4] = { 0,0,0,0 }; - /* Synchronization of FDC access. */ -static volatile int format_status = FORMAT_NONE, fdc_busy = 0; -static struct wait_queue *fdc_wait = NULL, *format_done = NULL; +#define FD_COMMAND_DETECT -2 +#define FD_COMMAND_NONE -1 +#define FD_COMMAND_ERROR 2 +#define FD_COMMAND_OKAY 3 + +static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0; +static struct wait_queue *fdc_wait = NULL, *command_done = NULL; /* Errors during formatting are counted here. */ static int format_errors; @@ -287,477 +335,649 @@ static struct format_descr format_req; /* - * Current device number. Taken either from the block header or from the - * format request descriptor. - */ -#define CURRENT_DEVICE (format_status == FORMAT_BUSY ? format_req.device : \ - (CURRENT->dev)) - -/* Current error count. */ -#define CURRENT_ERRORS (format_status == FORMAT_BUSY ? format_errors : \ - (CURRENT->errors)) - -/* - * Threshold for reporting FDC errors to the console. - * Setting this to zero may flood your screen when using - * ultra cheap floppies ;-) - */ -static unsigned short min_report_error_cnt[4] = {2, 2, 2, 2}; - -/* * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc), * H is head unload time (1=16ms, 2=32ms, etc) - * - * Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) - * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). */ /* - * Track buffer and block buffer (in case track buffering doesn't work). + * Track buffer * Because these are written to by the DMA controller, they must * not contain a 64k byte boundary crossing, or data will be * corrupted/lost. Alignment of these is enforced in boot/head.S. * Note that you must not change the sizes below without updating head.S. */ -extern char tmp_floppy_area[BLOCK_SIZE]; extern char floppy_track_buffer[512*2*MAX_BUFFER_SECTORS]; -static void redo_fd_request(void); +int *errors; +typedef void (*done_f)(int); +struct cont_t { +void (*interrupt)(void); /* this is called after the interrupt of the + * main command */ +void (*redo)(void); /* this is called to retry the operation */ +void (*error)(void); /* this is called to tally an error */ +done_f done; /* this is called to say if the operation has succeeded/failed */ +} *cont; + static void floppy_ready(void); static void recalibrate_floppy(void); +static void seek_floppy(void); +static void floppy_shutdown(void); int floppy_grab_irq_and_dma(void); void floppy_release_irq_and_dma(void); /* + * The "reset" variable should be tested whenever an interrupt is scheduled, + * after the commands have been sent. This is to ensure that the driver doesn't + * get wedged when the interrupt doesn't come because of a failed command. + * reset doesn't need to be tested before sending commands, because + * output_byte is automatically disabled when reset is set. + */ +#define CHECK_RESET { if ( FDCS->reset ){ reset_fdc(); return ; } } +static void reset_fdc(void); + +/* * These are global variables, as that's the easiest way to give * information to interrupts. They are the data used for the current * request. */ -#define NO_TRACK 255 +#define NO_TRACK -1 +#define NEED_1_RECAL -2 +#define NEED_2_RECAL -3 -static int read_track = 0; /* flag to indicate if we want to read entire track */ +/* buffer related variables */ static int buffer_track = -1; static int buffer_drive = -1; -static int cur_spec1 = -1; -static int cur_rate = -1; +static int buffer_min = -1; +static int buffer_max = -1; + +#ifdef FDC_FIFO_BUG +static int force=0; +#endif + +/* fdc related variables, should end up in a struct */ +static struct floppy_fdc_state fdc_state[N_FDC]; +int fdc; /* current fdc */ + static struct floppy_struct * floppy = floppy_type; static unsigned char current_drive = 255; -static unsigned char sector = 0; -static unsigned char head = 0; -static unsigned char track = 0; -static unsigned char seek_track = 0; -static unsigned char current_track = NO_TRACK; -static unsigned char command = 0; -static unsigned char fdc_version = 0x90; /* FDC version code */ +static long current_count_sectors = 0; +static char *current_addr = 0; +static unsigned char sector_t; /* sector in track */ -static void select_callback(unsigned long unused) +#ifdef DEBUGT +long unsigned debugtimer; +#endif + +/* + * Debugging + * ========= + */ +static inline void set_debugt(void) { - floppy_ready(); +#ifdef DEBUGT + debugtimer = jiffies; +#endif +} + +static inline void debugt(char *message) +{ +#ifdef DEBUGT + if ( DP->flags & DEBUGT ) + printk("%s dtime=%lu\n", message, jiffies-debugtimer ); +#endif } -static void floppy_select(unsigned int nr) +/* + * Bottom half floppy driver. + * ========================== + * + * This part of the file contains the code talking directly to the hardware, + * and also the main service loop (seek-configure-spinup-command) + */ +static int set_dor(int fdc, char mask, char data) { - static struct timer_list select = { NULL, NULL, 0, 0, select_callback }; + register unsigned char drive, unit, newdor,olddor; - if (current_drive == (current_DOR & 3)) { - floppy_ready(); - return; + cli(); + olddor = FDCS->dor; + newdor = (olddor & mask) | data; + if ( newdor != olddor ){ + unit = olddor & 0x3; + drive = REVDRIVE(fdc,unit); + if ( olddor & ( 0x10 << unit )){ + if ( inb_p( FD_DIR ) & 0x80 ) + UDRS->flags |= FD_VERIFY; + else + UDRS->last_checked=jiffies; + } + FDCS->dor = newdor; + outb_p( newdor, FD_DOR); } - seek = 1; - current_track = NO_TRACK; - current_DOR &= 0xFC; - current_DOR |= current_drive; - outb(current_DOR,FD_DOR); - del_timer(&select); - select.expires = 2; - add_timer(&select); -} - -static void motor_on_callback(unsigned long nr) -{ - running |= 0x10 << nr; - floppy_select(nr); -} - -static struct timer_list motor_on_timer[4] = { - { NULL, NULL, 0, 0, motor_on_callback }, - { NULL, NULL, 0, 1, motor_on_callback }, - { NULL, NULL, 0, 2, motor_on_callback }, - { NULL, NULL, 0, 3, motor_on_callback } -}; + sti(); + return olddor; +} -static void motor_off_callback(unsigned long nr) +static void twaddle(void) +{ + cli(); + outb_p(FDCS->dor & ~(0x10<dor, FD_DOR); + sti(); +} + +/* reset all driver information about the current fdc. This is needed after + * a reset, and after a raw command. */ +static void reset_fdc_info(int mode) +{ + int drive; + + FDCS->spec1 = FDCS->spec2 = -1; + FDCS->need_configure = 1; + FDCS->perp_mode = 1; + FDCS->rawcmd = 0; + for ( drive = 0; drive < N_DRIVE; drive++) + if (FDC(drive) == fdc && + ( mode || UDRS->track != NEED_1_RECAL)) + UDRS->track = NEED_2_RECAL; +} + +/* selects the fdc and drive, and enables the fdc's its input/dma. */ +static void set_fdc(int drive) +{ + if ( drive >= 0 ){ + fdc = FDC(drive); + current_drive = drive; + } + set_dor(fdc,~0,8); +#ifdef HAVE_2_CONTROLLERS + set_dor(1-fdc, ~8, 0); +#endif + if ( FDCS->rawcmd == 2 ) + reset_fdc_info(1); + if( inb_p(FD_STATUS) != STATUS_READY ) + FDCS->reset = 1; +} + +/* locks the driver */ +static void lock_fdc(int drive) { - unsigned char mask = ~(0x10 << nr); cli(); - running &= mask; - current_DOR &= mask; - outb(current_DOR,FD_DOR); + while (fdc_busy) sleep_on(&fdc_wait); + fdc_busy = 1; sti(); + command_status = FD_COMMAND_NONE; + set_fdc(drive); + if ( drive >= 0 ){ + timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout; + timer_active |= 1 << FLOPPY_TIMER; + } +} + +/* unlocks the driver */ +static inline int unlock_fdc(void) +{ + if (current_drive < N_DRIVE) + floppy_off(current_drive); + if (!fdc_busy) + printk(DEVICE_NAME ": FDC access conflict!\n"); + + if ( DEVICE_INTR ) + printk(DEVICE_NAME + ":device interrupt still active at FDC release: %p!\n", + DEVICE_INTR); + command_status = FD_COMMAND_NONE; + timer_active &= ~(1 << FLOPPY_TIMER); + fdc_busy = 0; + wake_up(&fdc_wait); + return 0; +} + +/* switches the motor off after a given timeout */ +static void motor_off_callback(unsigned long nr) +{ + unsigned char mask = ~(0x10 << UNIT(nr)); + + set_dor( FDC(nr), mask, 0 ); } -static struct timer_list motor_off_timer[4] = { +static struct timer_list motor_off_timer[N_DRIVE] = { { NULL, NULL, 0, 0, motor_off_callback }, { NULL, NULL, 0, 1, motor_off_callback }, { NULL, NULL, 0, 2, motor_off_callback }, { NULL, NULL, 0, 3, motor_off_callback } +#ifdef HAVE_2_CONTROLLERS + ,{ NULL, NULL, 0, 4, motor_off_callback }, + { NULL, NULL, 0, 5, motor_off_callback }, + { NULL, NULL, 0, 6, motor_off_callback }, + { NULL, NULL, 0, 7, motor_off_callback } +#endif }; -static void floppy_on(unsigned int nr) -{ - unsigned char mask = 0x10 << nr; - - del_timer(motor_off_timer + nr); - if (mask & running) - floppy_select(nr); - if (!(mask & current_DOR)) { - del_timer(motor_on_timer + nr); - motor_on_timer[nr].expires = HZ; - add_timer(motor_on_timer + nr); - } - current_DOR &= 0xFC; - current_DOR |= mask; - current_DOR |= nr; - outb(current_DOR,FD_DOR); -} - +/* schedules motor off */ static void floppy_off(unsigned int nr) { + unsigned long volatile delta; + + cli(); del_timer(motor_off_timer+nr); - motor_off_timer[nr].expires = 3*HZ; + + /* make spindle stop in a position which minimizes spinup time + * next time */ + if ( drive_params[nr].rps ){ + delta = jiffies - drive_state[nr].first_read_date + HZ - + drive_params[nr].spindown_offset; + delta = (( delta * drive_params[nr].rps) % HZ ) / + drive_params[nr].rps; + motor_off_timer[nr].expires = drive_params[nr].spindown - delta; + } add_timer(motor_off_timer+nr); + sti(); } -void request_done(int uptodate) +/* + * cycle through all N_DRIVE floppy drives, for disk change testing. + * stopping at current drive. This is done before any long operation, to + * be sure to have up to date disk change information. + */ +static void scandrives(void) +{ + int i, drive, saved_drive; + + saved_drive = current_drive % N_DRIVE; + for(i=0; i< N_DRIVE; i++){ + drive = (saved_drive + i + 1 ) % N_DRIVE; + if ( UDRS->fd_ref == 0 ) + continue; /* skip closed drives */ + set_fdc(drive); + UDRS->select_date = jiffies; + if(! (set_dor( fdc, ~3, UNIT(drive) | ( 0x10 << UNIT(drive))) & + (0x10 << UNIT(drive)))) + /* switch the motor off again, if it was off to + * begin with */ + set_dor( fdc, ~( 0x10 << UNIT(drive) ), 0 ); + } + current_drive = saved_drive; +} + +typedef void (*timeout_fn)(unsigned long); +static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 }; + +/* this function makes sure that the disk stays in the drive during the + * transfer */ +static void fd_watchdog(void) { - timer_active &= ~(1 << FLOPPY_TIMER); - probing = 0; - if (format_status != FORMAT_BUSY) - end_request(uptodate); - else { - format_status = uptodate ? FORMAT_OKAY : FORMAT_ERROR; - wake_up(&format_done); + if ( inb_p( FD_DIR ) & 0x80 ){ + floppy_shutdown(); + } else { + cli(); + del_timer(&fd_timer); + fd_timer.function = (timeout_fn) fd_watchdog; + fd_timer.expires = 10; + add_timer(&fd_timer); + sti(); } } -/* - * floppy-change is never called from an interrupt, so we can relax a bit - * here, sleep etc. Note that floppy-on tries to set current_DOR to point - * to the desired drive, but it will probably not survive the sleep if - * several floppies are used at the same time: thus the loop. - */ -static int floppy_change(struct buffer_head * bh) +static void main_command_interrupt(void) { - unsigned int mask = 1 << (bh->b_dev & 0x03); + del_timer(&fd_timer); + cont->interrupt(); +} - if (MAJOR(bh->b_dev) != MAJOR_NR) { - printk("floppy_changed: not a floppy\n"); - return 0; - } - if (fake_change & mask) { - buffer_track = -1; - fake_change &= ~mask; -/* omitting the next line breaks formatting in a horrible way ... */ - changed_floppies &= ~mask; - return 1; - } - if (changed_floppies & mask) { - buffer_track = -1; - changed_floppies &= ~mask; - recalibrate = 1; +/* waits for a delay (spinup or select) to pass */ +static int wait_for_completion(int nr, int delay, timeout_fn function) +{ + if ( FDCS->reset ){ + reset_fdc(); /* do the reset during sleep to win time + * if we don't need to sleep, it's a good + * occasion anyways */ return 1; } - if (!bh) - return 0; - if (bh->b_dirt) - ll_rw_block(WRITE, 1, &bh); - else { - buffer_track = -1; - bh->b_uptodate = 0; - ll_rw_block(READ, 1, &bh); - } - wait_on_buffer(bh); - if (changed_floppies & mask) { - changed_floppies &= ~mask; - recalibrate = 1; + + if ( jiffies < delay ){ + del_timer(&fd_timer); + fd_timer.function = function; + fd_timer.expires = delay - jiffies; + add_timer(&fd_timer); return 1; } return 0; } -#define copy_buffer(from,to) \ -__asm__("cld ; rep ; movsl" \ - : \ - :"c" (BLOCK_SIZE/4),"S" ((long)(from)),"D" ((long)(to)) \ - :"cx","di","si") - static void setup_DMA(void) { - unsigned long addr,count; - unsigned char dma_code; - - dma_code = DMA_WRITE; - if (command == FD_READ) - dma_code = DMA_READ; - if (command == FD_FORMAT) { - addr = (long) tmp_floppy_area; - count = floppy->sect*4; - } else { - addr = (long) CURRENT->buffer; - count = 1024; +#ifdef SANITY + if ((!CURRENT || + CURRENT->buffer != current_addr || + raw_cmd.length > 512 * CURRENT->nr_sectors) && + (current_addr < floppy_track_buffer || + current_addr + raw_cmd.length > + floppy_track_buffer + 1024 * MAX_BUFFER_SECTORS)){ + printk("bad adrress. start=%p lg=%lx tb=%p\n", + current_addr, raw_cmd.length, floppy_track_buffer); + if ( CURRENT ){ + printk("buffer=%p nr=%lx cnr=%lx\n", + CURRENT->buffer, CURRENT->nr_sectors, + CURRENT->current_nr_sectors); + } + cont->done(0); + FDCS->reset=1; + return; + } + if ((long) current_addr % 512 ){ + printk("non aligned address: %p\n", current_addr ); + cont->done(0); + FDCS->reset=1; + return; } - if (read_track) { -/* mark buffer-track bad, in case all this fails.. */ - buffer_drive = buffer_track = -1; - count = floppy->sect*floppy->head*512; - addr = (long) floppy_track_buffer; - } else if (addr >= LAST_DMA_ADDR) { - addr = (long) tmp_floppy_area; - if (command == FD_WRITE) - copy_buffer(CURRENT->buffer,tmp_floppy_area); + if ( ( (long)current_addr & ~(64*1024-1) ) != + ((long)(current_addr + raw_cmd.length-1) & ~(64*1024-1))){ + printk("DMA crossing 64-K boundary %p-%p\n", + current_addr, current_addr + raw_cmd.length); + cont->done(0); + FDCS->reset=1; + return; } + +#endif cli(); disable_dma(FLOPPY_DMA); clear_dma_ff(FLOPPY_DMA); - set_dma_mode(FLOPPY_DMA, (command == FD_READ)? DMA_MODE_READ : DMA_MODE_WRITE); - set_dma_addr(FLOPPY_DMA, addr); - set_dma_count(FLOPPY_DMA, count); + set_dma_mode(FLOPPY_DMA, + (raw_cmd.flags & FD_RAW_READ)? + DMA_MODE_READ : DMA_MODE_WRITE); + set_dma_addr(FLOPPY_DMA, (long) current_addr); + set_dma_count(FLOPPY_DMA, raw_cmd.length); enable_dma(FLOPPY_DMA); sti(); } -static void output_byte(char byte) +/* sends a command byte to the fdc */ +static int output_byte(char byte) { int counter; unsigned char status; - if (reset) - return; + if (FDCS->reset) + return -1; for(counter = 0 ; counter < 10000 ; counter++) { - status = inb_p(FD_STATUS) & (STATUS_READY | STATUS_DIR); - if (status == STATUS_READY) { - outb(byte,FD_DATA); - return; - } + status = inb_p(FD_STATUS) &(STATUS_READY|STATUS_DIR|STATUS_DMA); + if (!(status & STATUS_READY)) + continue; + if (status == STATUS_READY +#ifdef FDC_FIFO_BUG + || ((status == STATUS_READY|STATUS_DIR|STATUS_BUSY) &&force) +#endif + ) + { + outb_p(byte,FD_DATA); + return 0; + } else + break; } - current_track = NO_TRACK; - reset = 1; - printk("Unable to send byte to FDC\n"); + FDCS->reset = 1; + if ( !initialising ) + printk(DEVICE_NAME ": Unable to send byte to FDC %d (%x)\n", + fdc, status); + return -1; } +#define LAST_OUT(x) if(output_byte(x)){ reset_fdc();return;} #ifdef FDC_FIFO_BUG - -static void output_byte_force(char byte) -{ - int counter; - unsigned char status; - - if (reset) - return; - for (counter = 0 ; counter < 10000 ; counter++) { - status = inb_p(FD_STATUS); - if ((status & (STATUS_READY | STATUS_DIR)) == STATUS_READY) { - outb(byte,FD_DATA); - return; - } - if ((status & (STATUS_READY | STATUS_BUSY)) == (STATUS_READY | STATUS_BUSY)) { - outb(byte,FD_DATA); - return; - } - } - current_track = NO_TRACK; - reset = 1; - printk("Unable to send byte to FDC\n"); -} -#endif /* FDC_FIFO_BUG */ +#define output_byte_force(x) force=1;output_byte(x);force=0; +#else +#define output_byte_force(x) output_byte(x); +#endif +/* gets the response from the fdc */ static int result(void) { int i = 0, counter, status; - if (reset) + if (FDCS->reset) return -1; for (counter = 0 ; counter < 10000 ; counter++) { - status = inb_p(FD_STATUS)&(STATUS_DIR|STATUS_READY|STATUS_BUSY); - if (status == STATUS_READY) { + status = inb_p(FD_STATUS)& + (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA); + if (!(status & STATUS_READY)) + continue; + if (status == STATUS_READY) return i; - } + if (status & STATUS_DMA ) + break; if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) { if (i >= MAX_REPLIES) { - printk("floppy_stat reply overrun\n"); + printk(DEVICE_NAME + ": floppy_stat reply overrun\n"); break; } reply_buffer[i++] = inb_p(FD_DATA); } } - reset = 1; - current_track = NO_TRACK; - printk("Getstatus times out\n"); + FDCS->reset = 1; + if ( !initialising ) + printk(DEVICE_NAME ": Getstatus times out (%x) on fdc %d [%d]\n", + status, fdc,i); return -1; } -static void bad_flp_intr(void) -{ - int errors; - - if (probing) { - int device = MINOR(CURRENT_DEVICE) & 3; - ++floppy; - if (floppy < base_type_end[device]) - return; - floppy = base_type[device]; - } - current_track = NO_TRACK; - if (format_status == FORMAT_BUSY) - errors = ++format_errors; - else if (!CURRENT) { - printk(DEVICE_NAME ": no current request\n"); - reset = recalibrate = 1; - return; - } else - errors = ++CURRENT->errors; - if (errors > MAX_ERRORS) { - request_done(0); - } - if (errors > MAX_ERRORS/2) - reset = 1; - else - recalibrate = 1; -} - - /* Set perpendicular mode as required, based on data rate, if supported. * 82077 Untested! 1Mbps data rate only possible with 82077-1. * TODO: increase MAX_BUFFER_SECTORS, add floppy_type entries. */ -static inline void perpendicular_mode(unsigned char rate) +static inline void perpendicular_mode(void) { - if (fdc_version == FDC_TYPE_82077) { - output_byte(FD_PERPENDICULAR); - if (rate & 0x40) { - unsigned char r = rate & 0x03; - if (r == 0) -#ifndef FDC_FIFO_BUG - output_byte(2); /* perpendicular, 500 kbps */ -#else - output_byte_force(2); /* perpendicular, 500 kbps */ -#endif - else if (r == 3) -#ifndef FDC_FIFO_BUG - output_byte(3); /* perpendicular, 1Mbps */ -#else - output_byte_force(3); /* perpendicular, 1Mbps */ -#endif - else { - printk(DEVICE_NAME ": Invalid data rate for perpendicular mode!\n"); - reset = 1; - } - } else -#ifndef FDC_FIFO_BUG - output_byte(0); /* conventional mode */ -#else - output_byte_force(0); /* conventional mode */ -#endif - } else { - if (rate & 0x40) { - printk(DEVICE_NAME ": perpendicular mode not supported by this FDC.\n"); - reset = 1; + unsigned char perp_mode; + + if (!floppy) + return; + if (floppy->rate & 0x40){ + switch(raw_cmd.rate){ + case 0: + perp_mode=/*2*/3; + break; + case 3: + perp_mode=3; + break; + default: + printk(DEVICE_NAME + ": Invalid data rate for perpendicular mode!\n"); + cont->done(0); + FDCS->reset = 1; /* convenient way to return to + * redo without to much hassle (deep + * stack et al. */ + return; } + } else + perp_mode = 0; + + if ( FDCS->perp_mode == perp_mode ) + return; + if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) { + output_byte(FD_PERPENDICULAR); + output_byte_force(perp_mode); + FDCS->perp_mode = perp_mode; + } else if (perp_mode) { + printk(DEVICE_NAME + ": perpendicular mode not supported by this FDC.\n"); } } /* perpendicular_mode */ +#define NOMINAL_DTR 500 -/* - * This has only been tested for the case fdc_version == FDC_TYPE_STD. - * In case you have a 82077 and want to test it, you'll have to compile - * with `FDC_FIFO_UNTESTED' defined. You may also want to add support for - * recognizing drives with vertical recording support. +/* Issue a "SPECIFY" command to set the step rate time, head unload time, + * head load time, and DMA disable flag to values needed by floppy. + * + * The value "dtr" is the data transfer rate in Kbps. It is needed + * to account for the data rate-based scaling done by the 82072 and 82077 + * FDC types. This parameter is ignored for other types of FDCs (i.e. + * 8272a). + * + * Note that changing the data transfer rate has a (probably deleterious) + * effect on the parameters subject to scaling for 82072/82077 FDCs, so + * fdc_specify is called again after each data transfer rate + * change. + * + * srt: 1000 to 16000 in microseconds + * hut: 16 to 240 milliseconds + * hlt: 2 to 254 milliseconds + * + * These values are rounded up to the next highest available delay time. */ -static void configure_fdc_mode(void) +static void fdc_specify(void) { - if (need_configure && (fdc_version == FDC_TYPE_82077)) { - /* Enhanced version with FIFO & vertical recording. */ + unsigned char spec1, spec2; + int srt, hlt, hut; + unsigned long dtr = NOMINAL_DTR; + unsigned long scale_dtr = NOMINAL_DTR; + int hlt_max_code = 0x7f; + int hut_max_code = 0xf; + + if (FDCS->need_configure && FDCS->has_fifo) { + if ( FDCS->reset ) + return; + /* Turn on FIFO for 82077-class FDC (improves performance) */ + /* TODO: lock this in via LOCK during initialization */ output_byte(FD_CONFIGURE); -#ifndef FDC_FIFO_BUG - output_byte(0); -#else output_byte_force(0); -#endif output_byte(0x1A); /* FIFO on, polling off, 10 byte threshold */ -#ifndef FDC_FIFO_BUG - output_byte(0); /* precompensation from track 0 upwards */ -#else output_byte_force(0); /* precompensation from track 0 upwards */ -#endif - need_configure = 0; - printk(DEVICE_NAME ": FIFO enabled\n"); + if ( FDCS->reset ){ + FDCS->has_fifo=0; + return; + } + FDCS->need_configure = 0; + /*printk(DEVICE_NAME ": FIFO enabled\n");*/ } - if (cur_spec1 != floppy->spec1) { - cur_spec1 = floppy->spec1; + + switch (raw_cmd.rate & 0x03) { + case 3: + dtr = 1000; + break; + case 1: + dtr = 300; + break; + case 2: + dtr = 250; + break; + } + + if (FDCS->version >= FDC_82072) { + scale_dtr = dtr; + hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */ + hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */ + } + + /* Convert step rate from microseconds to milliseconds and 4 bits */ + srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (srt > 0xf) + srt = 0xf; + else if (srt < 0) + srt = 0; + + hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hlt < 0x01) + hlt = 0x01; + else if (hlt > 0x7f) + hlt = hlt_max_code; + + hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR; + if (hut < 0x1) + hut = 0x1; + else if (hut > 0xf) + hut = hut_max_code; + + spec1 = (srt << 4) | hut; +#define fd_disable_dma 0 + spec2 = (hlt << 1) | fd_disable_dma; + + /* If these parameters did not change, just return with success */ + if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) { + /* Go ahead and set spec1 and spec2 */ output_byte(FD_SPECIFY); - output_byte(cur_spec1); /* hut etc */ - output_byte(6); /* Head load time =6ms, DMA */ + output_byte(FDCS->spec1 = spec1); + output_byte(FDCS->spec2 = spec2); } - if (cur_rate != floppy->rate) { - /* use bit 6 of floppy->rate to indicate perpendicular mode */ - perpendicular_mode(floppy->rate); - outb_p((cur_rate = (floppy->rate)) & ~0x40, FD_DCR); - } -} /* configure_fdc_mode */ +} /* fdc_specify */ +/* Set the FDC's data transfer rate on behalf of the specified drive. + * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue + * of the specify command (i.e. using the fdc_specify function). + */ +static void fdc_dtr(void) +{ + /* If data rate not already set to desired value, set it. */ + if ( raw_cmd.rate == FDCS->dtr) + return; + + /* Set dtr */ + outb_p(raw_cmd.rate, FD_DCR); + + /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB) + * need a stabilization period of several milliseconds to be + * enforced after data rate changes before R/W operations. + * Pause 5 msec to avoid trouble. + */ + udelay(5000); + FDCS->dtr = raw_cmd.rate; +} /* fdc_dtr */ -static void tell_sector(int nr) +static void tell_sector(void) { - if (nr!=7) { - printk(" -- FDC reply errror"); - reset = 1; - } else - printk(": track %d, head %d, sector %d", reply_buffer[3], - reply_buffer[4], reply_buffer[5]); + printk(": track %d, head %d, sector %d, size %d", + R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE); } /* tell_sector */ /* - * Ok, this interrupt is called after a DMA read/write has succeeded + * Ok, this error interpreting routine is called after a + * DMA read/write has succeeded * or failed, so we check the results, and copy any buffers. * hhb: Added better error reporting. + * ak: Made this into a separate routine. */ -static void rw_interrupt(void) +static int interpret_errors(void) { - char * buffer_area; - int nr; char bad; - nr = result(); + if (inr!=7) { + printk(DEVICE_NAME ": -- FDC reply errror"); + FDCS->reset = 1; + return 1; + } + /* check IC to find cause of interrupt */ switch ((ST0 & ST0_INTR)>>6) { case 1: /* error occured during command execution */ bad = 1; if (ST1 & ST1_WP) { printk(DEVICE_NAME ": Drive %d is write protected\n", current_drive); - request_done(0); - bad = 0; + DRS->flags &= ~FD_DISK_WRITABLE; + cont->done(0); + bad = 2; + } else if (ST1 & ST1_ND) { + DRS->flags |= FD_NEED_TWADDLE; } else if (ST1 & ST1_OR) { - if (ftd_msg[ST0 & ST0_DS]) + if (DP->flags & FTD_MSG ) printk(DEVICE_NAME ": Over/Underrun - retrying\n"); /* could continue from where we stopped, but ... */ bad = 0; - } else if (CURRENT_ERRORS > min_report_error_cnt[ST0 & ST0_DS]) { + }else if(*errors >= DP->max_errors.reporting){ printk(DEVICE_NAME " %d: ", ST0 & ST0_DS); if (ST0 & ST0_ECE) { printk("Recalibrate failed!"); } else if (ST2 & ST2_CRC) { printk("data CRC error"); - tell_sector(nr); + tell_sector(); } else if (ST1 & ST1_CRC) { printk("CRC error"); - tell_sector(nr); + tell_sector(); } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) { if (!probing) { printk("sector not found"); - tell_sector(nr); + tell_sector(); } else printk("probe failed..."); } else if (ST2 & ST2_WC) { /* seek error */ @@ -770,175 +990,244 @@ printk("\n"); } - if (bad) - bad_flp_intr(); - redo_fd_request(); - return; + if ( ST2 & ST2_WC || ST2 & ST2_BC) + /* wrong cylinder => recal */ + DRS->track = NEED_2_RECAL; + return bad; case 2: /* invalid command given */ printk(DEVICE_NAME ": Invalid FDC command given!\n"); - request_done(0); - return; + cont->done(0); + return 2; case 3: printk(DEVICE_NAME ": Abnormal termination caused by polling\n"); - bad_flp_intr(); - redo_fd_request(); - return; + cont->error(); + return 2; default: /* (0) Normal command termination */ - break; - } - - if (probing) { - int drive = MINOR(CURRENT->dev); - - if (ftd_msg[drive]) - printk("Auto-detected floppy type %s in fd%d\n", - floppy->name,drive); - current_type[drive] = floppy; - floppy_sizes[drive] = floppy->size >> 1; - probing = 0; + return 0; } - if (read_track) { - buffer_track = seek_track; - buffer_drive = current_drive; - buffer_area = floppy_track_buffer + - ((sector-1 + head*floppy->sect)<<9); - copy_buffer(buffer_area,CURRENT->buffer); - } else if (command == FD_READ && - (unsigned long)(CURRENT->buffer) >= LAST_DMA_ADDR) - copy_buffer(tmp_floppy_area,CURRENT->buffer); - request_done(1); - redo_fd_request(); } /* - * We try to read tracks, but if we get too many errors, we - * go back to reading just one sector at a time. - * - * This means we should be able to read a sector even if there - * are other bad sectors on this track. + * This routine is called when everything should be correctly set up + * for the transfer (ie floppy motor is on, the correct floppy is + * selected, and the head is sitting on the right track). */ -inline void setup_rw_floppy(void) +static void setup_rw_floppy(void) { - setup_DMA(); - do_floppy = rw_interrupt; - output_byte(command); - if (command != FD_FORMAT) { - if (read_track) { - output_byte(current_drive); - output_byte(track); - output_byte(0); - output_byte(1); - } else { - output_byte(head<<2 | current_drive); - output_byte(track); - output_byte(head); - output_byte(sector); - } - output_byte(2); /* sector size = 512 */ - output_byte(floppy->sect); - output_byte(floppy->gap); - output_byte(0xFF); /* sector size (0xff when n!=0 ?) */ - } else { - output_byte(head<<2 | current_drive); - output_byte(2); - output_byte(floppy->sect); - output_byte(floppy->fmt_gap); - output_byte(FD_FILL_BYTE); + int i,ready_date,r, flags,dflags; + timeout_fn function; + + flags = raw_cmd.flags; + if ( flags & ( FD_RAW_READ | FD_RAW_WRITE)) + flags |= FD_RAW_INTR; + + if (flags & FD_RAW_SPIN){ + ready_date = DRS->spinup_date + DP->spinup; + /* If spinup will take a long time, rerun scandrives + * again just before spinup completion. Beware that + * after scandrives, we must again wait for selection. + */ + if ( ready_date > jiffies + DP->select_delay){ + ready_date -= DP->select_delay; + function = (timeout_fn) floppy_on; + } else + function = (timeout_fn) setup_rw_floppy; + + /* wait until the floppy is spinning fast enough */ + if (wait_for_completion(current_drive,ready_date,function)) + return; } - if (reset) - redo_fd_request(); + dflags = DRS->flags; + + if ( (flags & FD_RAW_READ) || (flags & FD_RAW_WRITE)){ + if ( flags & FD_RAW_USER_SUPPLIED ) + buffer_track = -1; + setup_DMA(); + } + + if ( flags & FD_RAW_INTR ) + SET_INTR(main_command_interrupt); + + r=0; + for(i=0; i< raw_cmd.cmd_count; i++) + r|=output_byte( raw_cmd.cmd[i] ); + +#ifdef DEBUGT + debugt("rw_command: "); +#endif + if ( r ){ + reset_fdc(); + return; + } + + if ( ! ( flags & FD_RAW_INTR )){ + inr = result(); + cont->interrupt(); + } else if ( flags & FD_RAW_NEED_DISK ) + fd_watchdog(); } /* * This is the routine called after every seek (or recalibrate) interrupt - * from the floppy controller. Note that the "unexpected interrupt" routine - * also does a recalibrate, but doesn't come here. + * from the floppy controller. */ static void seek_interrupt(void) { -/* sense drive status */ - output_byte(FD_SENSEI); - if (result() != 2 || (ST0 & 0xF8) != 0x20 || ST1 != seek_track) { +#ifdef DEBUGT + debugt("seek interrupt:"); +#endif + if (inr != 2 || (ST0 & 0xF8) != 0x20 ) { printk(DEVICE_NAME ": seek failed\n"); - recalibrate = 1; - bad_flp_intr(); - redo_fd_request(); + DRS->track = NEED_2_RECAL; + cont->error(); + cont->redo(); return; } - current_track = ST1; - setup_rw_floppy(); -} - - -/* - * This routine is called when everything should be correctly set up - * for the transfer (ie floppy motor is on and the correct floppy is - * selected). - */ -static void transfer(void) -{ - read_track = (command == FD_READ) && (CURRENT_ERRORS < 4) && - (floppy->sect <= MAX_BUFFER_SECTORS); - - configure_fdc_mode(); - - if (reset) { - redo_fd_request(); + if ( DRS->track >= 0 && DRS->track != ST1 ) + DRS->flags &= ~FD_DISK_NEWCHANGE; + DRS->track = ST1; + seek_floppy(); +} + +static void seek_floppy(void) +{ + int track; + + if ((raw_cmd.flags & FD_RAW_NEED_DISK) && + !(DRS->flags & FD_DISK_NEWCHANGE ) && + (inb_p(FD_DIR) & 0x80)){ + /* the media changed flag should be cleared after the seek. + * If it isn't, this means that there is really no disk in + * the drive. + */ + cont->done(0); + cont->redo(); return; } - if (!seek) { + if ( DRS->track <= NEED_1_RECAL ){ + recalibrate_floppy(); + return; + } else if ((DRS->flags & FD_DISK_NEWCHANGE) && + (DRS->track <= NO_TRACK || DRS->track == raw_cmd.track)) { + /* we seek to clear the media-changed condition. Does anybody + * know a more elegant way, which works on all drives? */ + if ( raw_cmd.track ) + track = raw_cmd.track - 1; + else + track = 1; + } else if (raw_cmd.track != DRS->track) + track = raw_cmd.track; + else { setup_rw_floppy(); return; } - do_floppy = seek_interrupt; - output_byte(FD_SEEK); - if (read_track) - output_byte(current_drive); - else - output_byte((head<<2) | current_drive); - output_byte(seek_track); - if (reset) - redo_fd_request(); + if ( !track && DRS->track >= 0 && DRS->track < 80 ){ + DRS->flags &= ~FD_DISK_NEWCHANGE; + /* if we go to track 0 anyways, we can just as well use + * recalibrate */ + recalibrate_floppy(); + } else { + SET_INTR(seek_interrupt); + output_byte(FD_SEEK); + output_byte(UNIT(current_drive)); + LAST_OUT(track); +#ifdef DEBUGT + debugt("seek command:"); +#endif + } } -/* - * Special case - used after a unexpected interrupt (or reset) - */ - static void recal_interrupt(void) { - output_byte(FD_SENSEI); - current_track = NO_TRACK; - if (result()!=2 || (ST0 & 0xE0) == 0x60) - reset = 1; -/* Recalibrate until track 0 is reached. Might help on some errors. */ - if ((ST0 & 0x10) == 0x10) - recalibrate_floppy(); /* FIXME: should limit nr of recalibrates */ - else - redo_fd_request(); +#ifdef DEBUGT + debugt("recal interrupt:"); +#endif + if (inr !=2 ) + FDCS->reset = 1; + else if (ST0 & ST0_ECE) { + switch(DRS->track){ + case NEED_1_RECAL: + /* after a second recalibrate, we still haven't + * reached track 0. Probably no drive */ + cont->error(); + cont->redo(); + return; + case NEED_2_RECAL: + /* If we already did a recalibrate, and we are not at + * track 0, this means we have moved. (The only way + * not to move at recalibration is to be already at + * track 0.) Clear the new change flag + */ + DRS->flags &= ~FD_DISK_NEWCHANGE; + /* fall through */ + default: + /* Recalibrate moves the head by at most 80 steps. If + * after one recalibrate we don't have reached track + * 0, this might mean that we started beyond track 80. + * Try again. + */ + DRS->track = NEED_1_RECAL; + break; + } + } else + DRS->track = ST1; + seek_floppy(); } +/* + * Unexpected interrupt - Print as much debugging info as we can... + * All bets are off... + */ static void unexpected_floppy_interrupt(void) { - current_track = NO_TRACK; - output_byte(FD_SENSEI); + int i; + if ( initialising ) + return; printk(DEVICE_NAME ": unexpected interrupt\n"); - if (result()!=2 || (ST0 & 0xE0) == 0x60) - reset = 1; - else - recalibrate = 1; + if ( inr >= 0 ) + for(i=0; ireset = 1; +} + +/* interrupt handler */ +static void floppy_interrupt(int unused) +{ + void (*handler)(void) = DEVICE_INTR; + + CLEAR_INTR; + if ( fdc >= N_FDC ) /* we don't even know which FDC is the culprit */ + return; + inr = result(); + if (!handler){ + unexpected_floppy_interrupt(); + return; + } + if ( inr == 0 ){ + do { + output_byte(FD_SENSEI); + inr = result(); + } while ( (ST0 & 0x83) != UNIT(current_drive) && inr == 2); + } + sti(); /* this should help improve interrupt latency. */ + handler(); } static void recalibrate_floppy(void) { - recalibrate = 0; - current_track = 0; - do_floppy = recal_interrupt; + SET_INTR(recal_interrupt); output_byte(FD_RECALIBRATE); - output_byte(head<<2 | current_drive); - if (reset) - redo_fd_request(); + LAST_OUT(UNIT(current_drive)); } /* @@ -946,407 +1235,1335 @@ */ static void reset_interrupt(void) { - short i; +#ifdef DEBUGT + debugt("reset interrupt:"); +#endif + fdc_specify(); /* reprogram fdc */ + result(); /* get the status ready for set_fdc */ + if ( FDCS->reset ) + cont->error(); /* a reset just after a reset. BAD! */ + cont->redo(); +} - for (i=0; i<4; i++) { - output_byte(FD_SENSEI); - (void) result(); +/* + * reset is done by pulling bit 2 of DOR low for a while (old FDC's), + * or by setting the self clearing bit 7 of STATUS (newer FDC's) + */ +static void reset_fdc(void) +{ + SET_INTR(reset_interrupt); + FDCS->reset = 0; + reset_fdc_info(0); + if ( FDCS->version >= FDC_82077 ) + outb_p(0x80 | ( FDCS->dtr &3), FD_STATUS); + else { + cli(); + outb_p(FDCS->dor & ~0x04, FD_DOR); + udelay(FD_RESET_DELAY); + outb(FDCS->dor, FD_DOR); + sti(); } - output_byte(FD_SPECIFY); - output_byte(cur_spec1); /* hut etc */ - output_byte(6); /* Head load time =6ms, DMA */ - configure_fdc_mode(); /* reprogram fdc */ - if (initial_reset_flag) { - initial_reset_flag = 0; - recalibrate = 1; - reset = 0; +} + +static void floppy_shutdown(void) +{ + cli(); + if ( !DEVICE_INTR ){ /* no interrupt pending. Probably has just been + * served */ + sti(); return; } - if (!recover) - redo_fd_request(); + CLEAR_INTR; + sti(); + if ( !initialising ) + printk(DEVICE_NAME ": timeout\n"); + FDCS->reset = 1; + cont->done(0); + cont->redo(); /* this will recall reset when needed */ +} + +/* start motor, check media-changed condition and write protection */ +static int start_motor(void) +{ + int cnt; + int dir; + + if ( (FDCS->dor & 0x03) != UNIT(current_drive) ) + /* notes select time if floppy is not yet selected */ + DRS->select_date = jiffies; + + if ( ! ( FDCS->dor & ( 0x10 << UNIT(current_drive) ) )){ + set_debugt(); + /* no read since this drive is running */ + DRS->first_read_date = 0; + /* note motor start time if motor is not yet running */ + DRS->spinup_date = jiffies; + } + + /* starts motor and selects floppy */ + del_timer(motor_off_timer + current_drive); + set_dor( fdc, 0xfc, + ( 0x10 << UNIT(current_drive) ) | UNIT(current_drive) ); + + dir = inb_p( FD_DIR) & 0x80; + if ( ! (dir & 0x80) ) + DRS->last_checked =jiffies; + if ( dir || ( DRS->flags & FD_VERIFY )) { + DRS->flags &= FD_DRIVE_PRESENT | FD_DISK_NEWCHANGE; + DRS->flags |= FD_DISK_WRITABLE; + + /* check write protection */ + output_byte( FD_GETSTATUS ); + output_byte( UNIT(current_drive) ); + if ( (cnt=result()) != 1 ){ + changed_floppies |= 1 << current_drive; + FDCS->reset = 1; + DRS->flags |= FD_VERIFY; + return -1; + } + if ( ( ST3 & 0x60 ) == 0x60 ) + DRS->flags &= ~FD_DISK_WRITABLE; + + if ( ! ( DRS->flags & FD_DISK_NEWCHANGE) ){ + /* the following code is only executed the first time + * a particular disk change has been detected */ + changed_floppies |= 1 << current_drive; + if (DRS->keep_data >= 0) { + if ((DP->flags & FTD_MSG) && + current_type[current_drive] != NULL) + printk(DEVICE_NAME + ": Disk type is undefined after " + "disk change in fd%d\n", + current_drive); + current_type[current_drive] = NULL; + floppy_sizes[current_drive] = MAX_DISK_SIZE; + } + if ( ST3 & 0x10 ) + DRS->track = 0; + } + } + if ( dir ) /* check if media changed is still on */ + DRS->flags |= FD_DISK_NEWCHANGE; else { - recalibrate_floppy(); - recover = 0; + DRS->flags &= ~FD_DISK_NEWCHANGE; + DRS->last_checked =jiffies; } + + return DRS->flags; +} + +static void floppy_ready(void) +{ + CHECK_RESET; + start_motor(); + + /* wait_for_completion also schedules reset if needed. */ + if(wait_for_completion(current_drive, + DRS->select_date+DP->select_delay, + (timeout_fn) floppy_ready)) + return; + fdc_dtr(); + if ( raw_cmd.flags & FD_RAW_NEED_SEEK ){ + perpendicular_mode(); + fdc_specify(); /* must be done here because of hut, hlt ... */ + seek_floppy(); + } else + setup_rw_floppy(); +} + +static void floppy_on(unsigned int drive) +{ + timer_table[FLOPPY_TIMER].expires = jiffies + DP->timeout; + timer_active |= 1 << FLOPPY_TIMER; + scandrives(); + floppy_ready(); } /* - * reset is done by pulling bit 2 of DOR low for a while. + * ======================================================================== + * here ends the bottom half. Exported routines are: + * floppy_on, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc, + * start_motor, reset_fdc, reset_fdc_info, interpret_errors. + * Initialisation also uses output_byte, result, set_dor, floppy_interrupt + * and set_dor. + * ======================================================================== + */ +/* + * General purpose continuations. + * ============================== */ -static void reset_floppy(void) +static void empty(void) { - int i; +} - do_floppy = reset_interrupt; - reset = 0; - current_track = NO_TRACK; - cur_spec1 = -1; - cur_rate = -1; - recalibrate = 1; - need_configure = 1; - if (!initial_reset_flag) - printk("Reset-floppy called\n"); - cli(); - outb_p(current_DOR & ~0x04, FD_DOR); - for (i=0 ; i<1000 ; i++) - __asm__("nop"); - outb(current_DOR, FD_DOR); - sti(); +static void do_wakeup(void) +{ + timer_active &= ~(1 << FLOPPY_TIMER); + command_status += 2; + wake_up(&command_done); + cont = 0; } -static void floppy_shutdown(void) +static struct cont_t wakeup_cont={ + empty, + do_wakeup, + empty, + (done_f)empty +}; + +static int wait_til_done(void) { - cli(); - do_floppy = NULL; - request_done(0); - recover = 1; - reset_floppy(); - sti(); - redo_fd_request(); + int ret; + while(command_status < 2) + if (current->pid) + sleep_on( & command_done ); + if ( FDCS->reset ) + command_status = FD_COMMAND_ERROR; + if ( command_status == FD_COMMAND_OKAY ) + ret=0; + else + ret=-EIO; + command_status = FD_COMMAND_NONE; + return ret; } -static void shake_done(void) +static void generic_done(int result) { - current_track = NO_TRACK; - if (inb(FD_DIR) & 0x80) - request_done(0); - redo_fd_request(); + command_status = result; + cont = &wakeup_cont; } -static int retry_recal(void (*proc)(void)) +static void generic_success(void) { - output_byte(FD_SENSEI); - if (result() == 2 && (ST0 & 0x10) != 0x10) return 0; - do_floppy = proc; - output_byte(FD_RECALIBRATE); - output_byte(head<<2 | current_drive); - return 1; + generic_done(1); } -static void shake_zero(void) +static void generic_failure(void) { - if (!retry_recal(shake_zero)) shake_done(); + generic_done(0); } -static void shake_one(void) +static void success_and_wakeup(void) { - if (retry_recal(shake_one)) return; - do_floppy = shake_done; - output_byte(FD_SEEK); - output_byte(head << 2 | current_drive); - output_byte(1); + generic_success(); + do_wakeup(); } -static void check_readonly(void) +static void failure_and_wakeup(void) { - unsigned long mask = 1 << current_drive; + generic_failure(); + do_wakeup(); +} + +/* + * formatting and rw support. + * ========================== + */ - read_only &= ~mask; - output_byte(FD_GETSTATUS); - output_byte(current_drive); - if ((result() == 1) && (ST3 & ST3_RY) && !(ST3 & ST3_FT) && (ST3 & ST3_WP)) - read_only |= mask; +static int next_valid_format(void) +{ + int probed_format; + while(1){ + probed_format = DRS->probed_format; + if ( probed_format > N_DRIVE || + ! DP->autodetect[probed_format] ){ + DRS->probed_format = 0; + return 1; + } + if ( floppy_type[DP->autodetect[probed_format]].sect ){ + DRS->probed_format = probed_format; + return 0; + } + probed_format++; + } } -static void floppy_ready(void) +static void bad_flp_intr(void) { - check_readonly(); - if (inb(FD_DIR) & 0x80) { - changed_floppies |= 1< 0) - keep_data[current_drive]--; - } else { - if (ftd_msg[current_drive] && current_type[current_drive] != NULL) - printk("Disk type is undefined after disk " - "change in fd%d\n",current_drive); - current_type[current_drive] = NULL; - floppy_sizes[current_drive] = MAX_DISK_SIZE; - } -/* Forcing the drive to seek makes the "media changed" condition go away. - * There should be a cleaner solution for that ... - */ - if (!reset && !recalibrate) { - if (current_track && current_track != NO_TRACK) - do_floppy = shake_zero; - else - do_floppy = shake_one; - output_byte(FD_RECALIBRATE); - output_byte(head<<2 | current_drive); + if ( probing ){ + DRS->probed_format++; + if ( !next_valid_format()) return; + } + (*errors)++; + if (*errors > DP->max_errors.abort) + cont->done(0); + if (*errors > DP->max_errors.reset) + FDCS->reset = 1; + else if (*errors > DP->max_errors.recal) + DRS->track = NEED_2_RECAL; +} + +static void set_floppy(int device) +{ + if (TYPE(device)) + floppy = TYPE(device) + floppy_type; + else + floppy = current_type[ DRIVE(device) ]; +} + +/* + * formatting and support. + * ======================= + */ +static void format_interrupt(void) +{ + switch (interpret_errors()){ + case 1: + cont->error(); + case 2: + break; + case 0: + cont->done(1); + } + cont->redo(); +} + +static void setup_format_params(void) +{ + struct fparm { + unsigned char track,head,sect,size; + } *here = (void *)floppy_track_buffer; + int ssize,il,n; + int count,head_shift,track_shift; + + raw_cmd.flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN | + FD_RAW_NEED_DISK | FD_RAW_NEED_SEEK; + raw_cmd.rate = floppy->rate & 0x3; + raw_cmd.cmd_count = NR_F; + COMMAND = FD_FORMAT; + DR_SELECT = UNIT(current_drive) + ( format_req.head << 2 ); + F_SIZECODE = FD_SIZECODE(floppy); + ssize = 1 << ( F_SIZECODE - 2 ); + F_SECT_PER_TRACK = floppy->sect / ssize; + F_GAP = floppy->fmt_gap; + F_FILL = FD_FILL_BYTE; + + current_addr = floppy_track_buffer; + raw_cmd.length = 4 * F_SECT_PER_TRACK; + + /* allow for about 30ms for data transport per track */ + head_shift = (F_SECT_PER_TRACK + 5) / 6; + + /* a ``cylinder'' is two tracks plus a little stepping time */ + track_shift = 2 * head_shift + 1; + + /* position of logical sector 1 on this track */ + n = (track_shift * format_req.track + head_shift * format_req.head ) + % F_SECT_PER_TRACK; + + /* determine interleave */ + il = 1; + if (floppy->sect > DP->interleave_sect && ssize==1) + il++; + + /* initialize field */ + for (count = 0; count < F_SECT_PER_TRACK; ++count) { + here[count].track = format_req.track; + here[count].head = format_req.head; + here[count].sect = 0; + here[count].size = F_SIZECODE; + } + /* place logical sectors */ + for (count = 1; count <= F_SECT_PER_TRACK; ++count) { + here[n].sect = count; + n = (n+il) % F_SECT_PER_TRACK; + if (here[n].sect) { /* sector busy, find next free sector */ + ++n; + if (n>= F_SECT_PER_TRACK) { + n-=F_SECT_PER_TRACK; + while (here[n].sect) ++n; + } } } - if (reset) { - reset_floppy(); +} + +static void redo_format(void) +{ + raw_cmd.track = format_req.track << floppy->stretch; + buffer_track = -1; + setup_format_params(); + floppy_on(current_drive); +#ifdef DEBUGT + debugt("queue format request"); +#endif +} + +static struct cont_t format_cont={ + format_interrupt, + redo_format, + bad_flp_intr, + generic_done }; + +static int do_format(int device, struct format_descr *tmp_format_req) +{ + int okay; + + lock_fdc(DRIVE(device)); + set_floppy(device); + if (!floppy || + tmp_format_req->track >= floppy->track || + tmp_format_req->head >= floppy->head){ + unlock_fdc(); + return -EINVAL; + } + format_req = *tmp_format_req; + format_errors = 0; + cont = &format_cont; + errors = &format_errors; + redo_format(); + okay=wait_til_done(); + unlock_fdc(); + return okay; +} + +/* + * Buffer read/write and support + * ============================= + */ + +/* new request_done. Can handle physical sectors which are smaller than a + * logical buffer */ +static void request_done(int uptodate) +{ + int block; + + probing = 0; + timer_active &= ~(1 << FLOPPY_TIMER); + + if (!CURRENT){ + printk(DEVICE_NAME + ": request list destroyed in floppy request done\n"); return; } - if (recalibrate) { - recalibrate_floppy(); + if (uptodate){ + /* maintain values for invalidation on geometry + change */ + block = current_count_sectors + CURRENT->sector; + if (block > DRS->maxblock) + DRS->maxblock=block; + if ( block > floppy->sect) + DRS->maxtrack = 1; + + /* unlock chained buffers */ + while (current_count_sectors && CURRENT && + current_count_sectors >= CURRENT->current_nr_sectors ){ + current_count_sectors -= CURRENT->current_nr_sectors; + CURRENT->nr_sectors -= CURRENT->current_nr_sectors; + CURRENT->sector += CURRENT->current_nr_sectors; + end_request(1); + } + if ( current_count_sectors && CURRENT){ + /* "unlock" last subsector */ + CURRENT->buffer += current_count_sectors <<9; + CURRENT->current_nr_sectors -= current_count_sectors; + CURRENT->nr_sectors -= current_count_sectors; + CURRENT->sector += current_count_sectors; + return; + } + + if ( current_count_sectors && ! CURRENT ) + printk(DEVICE_NAME "request list destroyed in floppy request done\n"); + + } else { +#ifdef NO_WEIRD_UNLOCKED + if ( CURRENT->bh ) + /* avoid those pesky "Weird unlocked ... errors" */ + CURRENT->bh->b_req = 0; +#endif + end_request(0); + } +} + +/* Interrupt handler evaluating the result of the r/w operation */ +static void rw_interrupt(void) +{ +#if 0 + int i; +#endif + int nr_sectors, ssize; + char bad; + + if ( ! DRS->first_read_date ) + DRS->first_read_date = jiffies; + + nr_sectors = 0; + ssize = 1 << (SIZECODE - 2); + nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) * + floppy->sect + (R_SECTOR-SECTOR) * ssize - + (sector_t % floppy->sect) % ssize; + +#ifdef SANITY + if ( nr_sectors > current_count_sectors + ssize - + (current_count_sectors + sector_t) % ssize + + sector_t % ssize){ + printk(DEVICE_NAME ": long rw: %x instead of %lx\n", + nr_sectors, current_count_sectors); + printk("rs=%d s=%d\n", R_SECTOR, SECTOR); + printk("rh=%d h=%d\n", R_HEAD, HEAD); + printk("rt=%d t=%d\n", R_TRACK, TRACK); + printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK, + sector_t, ssize); + } +#endif + if ( nr_sectors < 0 ) + nr_sectors = 0; + if ( nr_sectors < current_count_sectors ){ +#if 0 + printk(DEVICE_NAME ": short read got %d instead of %ld\n", + nr_sectors, current_count_sectors); +#endif +#if 0 + printk("command: "); + for(i=0; iredo(); return; + case 1: + if ( !current_count_sectors){ + cont->error(); + cont->redo(); + return; + } + break; + case 0: + if ( !current_count_sectors){ + int i; + printk(DEVICE_NAME ": dma problem?\n"); + for(i=0; i< inr ; i++) + printk("%2x,", reply_buffer[i]); + printk("\n"); + bad=1; + cont->error(); + cont->redo(); + return; + } + current_type[current_drive] = floppy; + break; + } + + if (probing) { + if (DP->flags & FTD_MSG) + printk(DEVICE_NAME + ": Auto-detected floppy type %s in fd%d\n", + floppy->name,current_drive); + current_type[current_drive] = floppy; + floppy_sizes[DRIVE(current_drive) + (FDC(current_drive) << 7)] = + floppy->size >> 1; + probing = 0; + } + + if ( COMMAND != FD_READ || current_addr == CURRENT->buffer ){ + /* transfer directly from buffer */ + cont->done(1); + } else if ( COMMAND == FD_READ){ + buffer_track = raw_cmd.track; + buffer_drive = current_drive; + if ( nr_sectors + sector_t > buffer_max ) + buffer_max = nr_sectors + sector_t; } - transfer(); + cont->redo(); } -static void setup_format_params(void) +/* Compute maximal contiguous buffer size. */ +static int buffer_chain_size(void) +{ + struct buffer_head *bh; + int size; + char *base; + + base = CURRENT->buffer; + size = CURRENT->current_nr_sectors << 9; + bh = CURRENT->bh; + +#ifdef SANITY + if ( !bh ){ + printk(DEVICE_NAME ": null request in buffer_chain_size\n"); + return size >> 9; + } +#endif + + bh = bh->b_reqnext; + while ( bh && bh->b_data == base + size ){ + size += bh->b_size; + bh = bh->b_reqnext; + } + return size >> 9; +} + +/* Compute the maximal transfer size */ +static int transfer_size(int ssize, int max_sector, int max_size) +{ + if ( max_sector > sector_t + max_size) + max_sector = sector_t + max_size; + + /* alignment */ + max_sector -= (max_sector % floppy->sect ) % ssize; + + /* transfer size, beginning not aligned */ + current_count_sectors = max_sector - sector_t ; + + return max_sector; +} + +/* + * Move data from/to the track buffer to/from the buffer cache. + */ +static void copy_buffer(int ssize, int max_sector, int max_sector_2) { - unsigned char *here = (unsigned char *) tmp_floppy_area; - int count,head_shift,track_shift,total_shift; + int remaining; /* number of transferred 512-byte sectors */ + struct buffer_head *bh; + char *buffer, *dma_buffer; + int size; + + if ( max_sector > max_sector_2 ) + max_sector = max_sector_2; + + max_sector = transfer_size(ssize, max_sector, CURRENT->nr_sectors); + + if (current_count_sectors <= 0 && COMMAND == FD_WRITE && + buffer_max > sector_t + CURRENT->nr_sectors){ + current_count_sectors = buffer_max - sector_t; + if ( current_count_sectors > CURRENT->nr_sectors ) + current_count_sectors = CURRENT->nr_sectors; + } + remaining = current_count_sectors << 9; +#ifdef SANITY + if ((remaining >> 9) > CURRENT->nr_sectors && COMMAND == 0xc5 ){ + printk(DEVICE_NAME ": in copy buffer\n"); + printk("current_count_sectors=%ld\n", current_count_sectors); + printk("remaining=%d\n", remaining >> 9); + printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors); + printk("CURRENT->current_nr_sectors=%ld\n", + CURRENT->current_nr_sectors); + printk("max_sector=%d\n", max_sector); + printk("ssize=%d\n", ssize); + } +#endif + + if ( max_sector > buffer_max ) + buffer_max = max_sector; + + dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9); + + bh = CURRENT->bh; + size = CURRENT->current_nr_sectors << 9; + buffer = CURRENT->buffer; + + while ( remaining > 0){ + if ( size > remaining ) + size = remaining; +#ifdef SANITY + if (!bh){ + printk(DEVICE_NAME + ": bh=null in copy buffer before copy\n"); + break; + } + if (dma_buffer + size > + floppy_track_buffer + ( 2 * MAX_BUFFER_SECTORS << 9 ) || + dma_buffer < floppy_track_buffer ){ + printk(DEVICE_NAME + ": buffer overrun in copy buffer %d\n", + (floppy_track_buffer - dma_buffer) >>9); + printk("sector_t=%d buffer_min=%d\n", + sector_t, buffer_min); + printk("current_count_sectors=%ld\n", + current_count_sectors); + if ( COMMAND == FD_READ ) + printk("read\n"); + if ( COMMAND == FD_READ ) + printk("write\n"); + break; + } + if ( ((int)buffer) % 512 ) + printk(DEVICE_NAME ": %p buffer not aligned\n", buffer); +#endif + if ( COMMAND == FD_READ ) + memcpy( buffer, dma_buffer, size); + else + memcpy( dma_buffer, buffer, size); + remaining -= size; + if ( !remaining) + break; + + dma_buffer += size; + bh = bh->b_reqnext; +#ifdef SANITY + if ( !bh){ + printk(DEVICE_NAME + ": bh=null in copy buffer after copy\n"); + break; + } +#endif + size = bh->b_size; + buffer = bh->b_data; + } +#ifdef SANITY + if ( remaining ){ + if ( remaining > 0 ) + max_sector -= remaining >> 9; + printk(DEVICE_NAME + ": weirdness: remaining %d\n", remaining>>9); + } +#endif +} + +/* + * Formulate a read/write request. + * this routine decides where to load the data (directly to buffer, or to + * tmp floppy area), how much data to load (the size of the buffer, the whole + * track, or a single sector) + * All floppy_track_buffer handling goes in here. If we ever add track buffer + * allocation on the fly, it should be done here. No other part should need + * modification. + */ + +static int make_raw_rw_request(void) +{ + int aligned_sector_t; + int max_sector, max_size, tracksize, ssize; + + current_drive = DRIVE(CURRENT->dev); + + raw_cmd.flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK | + FD_RAW_NEED_SEEK; + raw_cmd.cmd_count = NR_RW; + if (CURRENT->cmd == READ){ + raw_cmd.flags |= FD_RAW_READ; + COMMAND = FD_READ; + } else if (CURRENT->cmd == WRITE){ + raw_cmd.flags |= FD_RAW_WRITE; + COMMAND = FD_WRITE; + } else { + printk(DEVICE_NAME + ": make_raw_rw_request: unknown command\n"); + return 0; + } + + max_sector = floppy->sect * floppy->head; + TRACK = CURRENT->sector / max_sector; + sector_t = CURRENT->sector % max_sector; + if ( floppy->track && TRACK >= floppy->track ) + return 0; + HEAD = sector_t / floppy->sect; + + if ( (DRS->flags & FD_NEED_TWADDLE) && sector_t < floppy->sect ) + max_sector = floppy->sect; + + /* 2M disks have phantom sectors on the first track */ + if ( (floppy->rate & FD_2M ) && (!TRACK) && (!HEAD)){ + max_sector = 2 * floppy->sect / 3; + if (sector_t >= max_sector){ + current_count_sectors = (floppy->sect - sector_t); + if ( current_count_sectors > CURRENT->nr_sectors ) + current_count_sectors = CURRENT->nr_sectors; + return 1; + } + SIZECODE = 2; + } else + SIZECODE = FD_SIZECODE(floppy); + raw_cmd.rate = floppy->rate & 3; + if ((floppy->rate & FD_2M) && + (TRACK || HEAD ) && + raw_cmd.rate == 2) + raw_cmd.rate = 1; + + SIZECODE2 = 0xff; + raw_cmd.track = TRACK << floppy->stretch; + DR_SELECT = UNIT(current_drive) + ( HEAD << 2 ); + GAP = floppy->gap; + ssize = 1 << (SIZECODE - 2 ); + SECT_PER_TRACK = floppy->sect / ssize; + SECTOR = (sector_t % floppy->sect) / ssize + 1; + tracksize = floppy->sect - floppy->sect % ssize; + if ( tracksize < floppy->sect ){ + SECT_PER_TRACK ++; + if ( tracksize <= sector_t % floppy->sect) + SECTOR--; + while ( tracksize <= sector_t % floppy->sect){ + while( tracksize + ssize > floppy->sect ){ + SIZECODE--; + ssize >>= 1; + } + SECTOR++; SECT_PER_TRACK ++; + tracksize += ssize; + } + max_sector = HEAD * floppy->sect + tracksize; + } else if ( !TRACK && !HEAD && !( floppy->rate & FD_2M ) && probing) + max_sector = floppy->sect; + + aligned_sector_t = sector_t - ( sector_t % floppy->sect ) % ssize; + max_size = CURRENT->nr_sectors; + if ((raw_cmd.track == buffer_track) && (current_drive == buffer_drive) && + (sector_t >= buffer_min) && (sector_t < buffer_max)) { + /* data already in track buffer */ + if (COMMAND == FD_READ) { + copy_buffer(1, max_sector, buffer_max); + return 1; + } + } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){ + if (COMMAND == FD_WRITE){ + if(sector_t + CURRENT->nr_sectors > ssize && + sector_t + CURRENT->nr_sectors < ssize + ssize) + max_size = ssize + ssize; + else + max_size = ssize; + } + raw_cmd.flags &= ~FD_RAW_WRITE; + raw_cmd.flags |= FD_RAW_READ; + COMMAND = FD_READ; + } else if ((long)CURRENT->buffer <= LAST_DMA_ADDR ) { + int direct, indirect; + + indirect= transfer_size(ssize,max_sector,MAX_BUFFER_SECTORS*2) - + sector_t; + + max_size = buffer_chain_size(); + if ( max_size > ( LAST_DMA_ADDR - ((long) CURRENT->buffer))>>9) + max_size=(LAST_DMA_ADDR - ((long)CURRENT->buffer))>>9; + /* 64 kb boundaries */ + if ( ((max_size << 9) + ((long) CURRENT->buffer)) / K_64 != + ((long) CURRENT->buffer ) / K_64 ) + max_size = ( K_64 - ((long) CURRENT->buffer) % K_64)>>9; + direct = transfer_size(ssize,max_sector,max_size) - sector_t; + /* + * We try to read tracks, but if we get too many errors, we + * go back to reading just one sector at a time. + * + * This means we should be able to read a sector even if there + * are other bad sectors on this track. + */ + if ((indirect - sector_t) * 2 > (direct - sector_t) * 3 && + *errors < DP->max_errors.read_track && + /*!(DRS->flags & FD_NEED_TWADDLE) &&*/ + ( ( !probing || (DP->read_track & + (1 <probed_format))))){ + max_size = CURRENT->nr_sectors; + } else { + current_addr = CURRENT->buffer; + raw_cmd.length = current_count_sectors << 9; + return 2; + } + } + + if ( COMMAND == FD_READ ) + max_size = max_sector; /* unbounded */ + + /* claim buffer track if needed */ + if (buffer_track != raw_cmd.track || /* bad track */ + buffer_drive !=current_drive || /* bad drive */ + sector_t < buffer_min || + ((COMMAND == FD_READ || + (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize ))&& + max_sector > 2 * MAX_BUFFER_SECTORS + buffer_min && + max_size + sector_t > 2 * MAX_BUFFER_SECTORS + buffer_min) + /* not enough space */ ){ + buffer_track = -1; + buffer_drive = current_drive; + buffer_max = buffer_min = aligned_sector_t; + } + current_addr = floppy_track_buffer +((aligned_sector_t-buffer_min )<<9); - /* allow for about 30ms for data transport per track */ - head_shift = floppy->sect / 6; - /* a ``cylinder'' is two tracks plus a little stepping time */ - track_shift = 2 * head_shift + 1; - /* count backwards */ - total_shift = floppy->sect - - ((track_shift * track + head_shift * head) % floppy->sect); - - /* XXX: should do a check to see this fits in tmp_floppy_area!! */ - for (count = 0; count < floppy->sect; count++) { - *here++ = track; - *here++ = head; - *here++ = 1 + (( count + total_shift ) % floppy->sect); - *here++ = 2; /* 512 bytes */ - } + if ( COMMAND == FD_WRITE ){ + /* copy write buffer to track buffer. + * if we get here, we know that the write + * is either aligned or the data already in the buffer + * (buffer will be overwritten) */ +#ifdef SANITY + if (sector_t != aligned_sector_t && buffer_track == -1 ) + printk(DEVICE_NAME + ": internal error offset !=0 on write\n"); +#endif + buffer_track = raw_cmd.track; + buffer_drive = current_drive; + copy_buffer(ssize, max_sector, 2*MAX_BUFFER_SECTORS+buffer_min); + } else + transfer_size(ssize, max_sector, + 2*MAX_BUFFER_SECTORS+buffer_min-aligned_sector_t); + + /* round up current_count_sectors to get dma xfer size */ + raw_cmd.length = sector_t+current_count_sectors-aligned_sector_t; + raw_cmd.length = ((raw_cmd.length -1)|(ssize-1))+1; + raw_cmd.length <<= 9; +#ifdef SANITY + if ((raw_cmd.length < current_count_sectors << 9) || + (current_addr != CURRENT->buffer && COMMAND == FD_WRITE && + (aligned_sector_t + (raw_cmd.length >> 9) > buffer_max || + aligned_sector_t < buffer_min )) || + raw_cmd.length % ( 512 << ( SIZECODE -2 )) || + raw_cmd.length <= 0 || current_count_sectors <= 0){ + printk(DEVICE_NAME ": fractionary current count b=%lx s=%lx\n", + raw_cmd.length, current_count_sectors); + if ( current_addr != CURRENT->buffer ) + printk("addr=%d, length=%ld\n", + (current_addr - floppy_track_buffer ) >> 9, + current_count_sectors); + printk("st=%d ast=%d mse=%d msi=%d\n", + sector_t, aligned_sector_t, max_sector, max_size); + printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE); + printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n", + COMMAND, SECTOR, HEAD, TRACK); + printk("buffer drive=%d\n", buffer_drive); + printk("buffer track=%d\n", buffer_track); + printk("buffer_min=%d\n", buffer_min ); + printk("buffer_max=%d\n", buffer_max ); + return 0; + } + + if (current_addr != CURRENT->buffer ){ + if (current_addr < floppy_track_buffer || + current_count_sectors < 0 || + raw_cmd.length < 0 || + current_addr + raw_cmd.length > + floppy_track_buffer + ( 2 * MAX_BUFFER_SECTORS << 9 )){ + printk(DEVICE_NAME + ": buffer overrun in schedule dma\n"); + printk("sector_t=%d buffer_min=%d current_count=%ld\n", + sector_t, buffer_min, + raw_cmd.length >> 9 ); + printk("current_count_sectors=%ld\n", + current_count_sectors); + if ( COMMAND == FD_READ ) + printk("read\n"); + if ( COMMAND == FD_READ ) + printk("write\n"); + return 0; + } + } else if (raw_cmd.length > CURRENT->nr_sectors << 9 || + current_count_sectors > CURRENT->nr_sectors){ + printk(DEVICE_NAME ": buffer overrun in direct transfer\n"); + return 0; + } else if ( raw_cmd.length < current_count_sectors << 9 ){ + printk(DEVICE_NAME ": more sectors than bytes\n"); + printk("bytes=%ld\n", raw_cmd.length >> 9 ); + printk("sectors=%ld\n", current_count_sectors); + } +#endif + return 2; } static void redo_fd_request(void) { - unsigned int block; - char * buffer_area; +#define REPEAT {request_done(0); continue; } int device; + int tmp; if (CURRENT && CURRENT->dev < 0) return; - -repeat: - if (format_status == FORMAT_WAIT) - format_status = FORMAT_BUSY; - if (format_status != FORMAT_BUSY) { + + /* hooray, the goto is gone! */ + while(1){ if (!CURRENT) { - if (!fdc_busy) - printk("FDC access conflict!"); - fdc_busy = 0; - wake_up(&fdc_wait); CLEAR_INTR; + unlock_fdc(); return; } if (MAJOR(CURRENT->dev) != MAJOR_NR) - panic(DEVICE_NAME ": request list destroyed"); \ - if (CURRENT->bh) { - if (!CURRENT->bh->b_lock) - panic(DEVICE_NAME ": block not locked"); - } - } - seek = 0; - device = MINOR(CURRENT_DEVICE); - if (device > 3) - floppy = (device >> 2) + floppy_type; - else { /* Auto-detection */ - if (!probing) { - floppy = current_type[device & 3]; - if (!floppy) { - probing = 1; - floppy = base_type[device & 3]; - if (!floppy) { - request_done(0); - goto repeat; + panic(DEVICE_NAME ": request list destroyed"); + if (CURRENT->bh && !CURRENT->bh->b_lock) + panic(DEVICE_NAME ": block not locked"); + + device = MINOR(CURRENT->dev); + set_fdc( DRIVE(device)); + CHECK_RESET; + start_motor(); + if (( changed_floppies | fake_change) & ( 1 << DRIVE(device))){ + printk(DEVICE_NAME + ": disk absent or changed during operation\n"); + REPEAT; + } + set_floppy(device); + if (!floppy) { /* Autodetection */ + if (!probing){ + DRS->probed_format = 0; + if ( next_valid_format() ){ + printk(DEVICE_NAME + ": no autodetectable formats\n"); + floppy = NULL; + REPEAT; } } + probing = 1; + floppy = floppy_type+DP->autodetect[DRS->probed_format]; + } else + probing = 0; + errors = & (CURRENT->errors); + tmp = make_raw_rw_request(); + if ( tmp < 2 ){ + request_done(tmp); + continue; } + if ( DRS->flags & FD_NEED_TWADDLE ) + twaddle(); + floppy_on(current_drive); +#ifdef DEBUGT + debugt("queue fd request"); +#endif + return; } - if (format_status != FORMAT_BUSY) { - if (current_drive != CURRENT_DEV) { - current_track = NO_TRACK; - current_drive = CURRENT_DEV; - } - block = CURRENT->sector; - if (block+2 > floppy->size) { - request_done(0); - goto repeat; - } - sector = block % floppy->sect; - block /= floppy->sect; - head = block % floppy->head; - track = block / floppy->head; - seek_track = track << floppy->stretch; - if (CURRENT->cmd == READ) - command = FD_READ; - else if (CURRENT->cmd == WRITE) - command = FD_WRITE; - else { - printk("do_fd_request: unknown command\n"); - request_done(0); - goto repeat; - } - } else { - if (current_drive != (format_req.device & 3)) - current_track = NO_TRACK; - current_drive = format_req.device & 3; - if (((unsigned) format_req.track) >= floppy->track || - (format_req.head & 0xfffe) || probing) { - request_done(0); - goto repeat; - } - head = format_req.head; - track = format_req.track; - seek_track = track << floppy->stretch; - if (seek_track == buffer_track) buffer_track = -1; - command = FD_FORMAT; - setup_format_params(); +#undef REPEAT +} + +static struct cont_t rw_cont={ + rw_interrupt, + redo_fd_request, + bad_flp_intr, + request_done }; + +void do_fd_request(void) +{ + lock_fdc(-1); + cont = &rw_cont; + redo_fd_request(); +} + +/* + * User triggered reset + * ==================== + */ + +static void reset_intr(void) +{ + printk("weird, reset interrupt called\n"); +} + +static struct cont_t reset_cont={ + reset_intr, + success_and_wakeup, + generic_failure, + generic_done }; + +static int user_reset_fdc(int drive, int arg) +{ + int result; + + result=0; + lock_fdc(drive); + switch(arg){ + case FD_RESET_ALWAYS: + FDCS->reset=1; + break; + case FD_RESET_IF_RAWCMD: + if(FDCS->rawcmd == 2 ) + reset_fdc_info(1); + break; } - timer_table[FLOPPY_TIMER].expires = jiffies+10*HZ; - timer_active |= 1 << FLOPPY_TIMER; - if ((seek_track == buffer_track) && - (current_drive == buffer_drive)) { - buffer_area = floppy_track_buffer + - ((sector + head*floppy->sect)<<9); - if (command == FD_READ) { - copy_buffer(buffer_area,CURRENT->buffer); - request_done(1); - goto repeat; - } else if (command == FD_WRITE) - copy_buffer(CURRENT->buffer,buffer_area); - } - if (seek_track != current_track) - seek = 1; - sector++; - del_timer(motor_off_timer + current_drive); + if ( FDCS->reset ){ + cont = &reset_cont; + timer_table[FLOPPY_TIMER].expires = jiffies + 5; + timer_active |= 1 << FLOPPY_TIMER; + reset_fdc(); + result=wait_til_done(); + } + unlock_fdc(); + return result; +} + +/* + * Misc Ioctl's and support + * ======================== + */ +static int fd_copyout(void *param, volatile void *address, int size) +{ + int i; + + i = verify_area(VERIFY_WRITE,param,size); + if (i) + return i; + memcpy_tofs(param,(void *) address, size); + return 0; +} +#define COPYOUT(x) (fd_copyout( (void *)param, &(x), sizeof(x))) +#define COPYIN(x) (memcpy_fromfs( &(x), (void *) param, sizeof(x)),0) + +static void poll_drive(int drive) +{ + lock_fdc(drive); + start_motor(); + unlock_fdc(); +} + +static char *drive_name(int type, int drive ) +{ + struct floppy_struct *floppy; + + if ( type ) + floppy = floppy_type + type; + else { + if ( UDP->native_format ) + floppy = floppy_type + UDP->native_format; + else + return "(null)"; + } + if ( floppy->name ) + return floppy->name; + else + return "(null)"; +} + +/* raw commands */ +static struct cont_t raw_cmd_cont={ + success_and_wakeup, + failure_and_wakeup, + generic_failure, + generic_done }; + +static int raw_cmd_ioctl(int drive, void *param) +{ + int i, count, ret; + + if ( FDCS->rawcmd <= 1 ) + FDCS->rawcmd = 1; + for ( i= 0; i < N_DRIVE; i++){ + if ( FDC(i) != fdc) + continue; + if ( i == drive ){ + if ( drive_state[i].fd_ref > 1 ) + return -EBUSY; + } else if ( drive_state[i].fd_ref ) + return -EBUSY; + } + + if(FDCS->reset) + return -EIO; + + COPYIN(raw_cmd); + raw_cmd.rate &= 0x03; + count = raw_cmd.length; + if ((raw_cmd.flags & (FD_RAW_WRITE | FD_RAW_READ)) && + count > MAX_BUFFER_SECTORS * 512 * 2 ) + return -ENOMEM; + + if ( raw_cmd.flags & FD_RAW_WRITE ){ + i = verify_area(VERIFY_READ, raw_cmd.data, count ); + if (i) + return i; + buffer_track = -1; + memcpy_fromfs(floppy_track_buffer, raw_cmd.data, count); + } + + current_addr = floppy_track_buffer; + raw_cmd.flags |= FD_RAW_USER_SUPPLIED; + cont = &raw_cmd_cont; floppy_on(current_drive); + ret=wait_til_done(); + + if ( !ret && !FDCS->reset ){ + raw_cmd.reply_count = inr; + for( i=0; i< raw_cmd.reply_count; i++) + raw_cmd.reply[i] = reply_buffer[i]; + if ( raw_cmd.flags & ( FD_RAW_READ | FD_RAW_WRITE )) + raw_cmd.length = get_dma_residue(FLOPPY_DMA); + } else + ret = -EIO; + DRS->track = NO_TRACK; + if ( ret ) + return ret; + + if ( raw_cmd.flags & FD_RAW_READ ){ + i=fd_copyout( raw_cmd.data, floppy_track_buffer, count); + if (i) + return i; + } + + return COPYOUT(raw_cmd); } -void do_fd_request(void) +static int invalidate_drive(int rdev) { + /* invalidate the buffer track to force a reread */ cli(); - while (fdc_busy) sleep_on(&fdc_wait); - fdc_busy = 1; + fake_change |= 1 << DRIVE(rdev); sti(); - redo_fd_request(); + unlock_fdc(); + check_disk_change(rdev); + return 0; } static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param) { - int i,drive,cnt,okay; +#define IOCTL_ALLOWED (filp && (filp->f_mode & 8 )) + + struct floppy_struct newparams; + struct format_descr tmp_format_req; + int i,device,drive,type,cnt; struct floppy_struct *this_floppy; + char *name; + device = MINOR(inode->i_rdev); switch (cmd) { - RO_IOCTLS(inode->i_rdev,param); + RO_IOCTLS(device,param); } - drive = MINOR(inode->i_rdev); + type = TYPE(MINOR(device)); + drive = DRIVE(MINOR(device)); switch (cmd) { - case FDFMTBEG: - if (!suser()) - return -EPERM; - return 0; - case FDFMTEND: - if (!suser()) - return -EPERM; - cli(); - fake_change |= 1 << (drive & 3); - sti(); - drive &= 3; - cmd = FDCLRPRM; - break; - case FDGETPRM: - if (drive > 3) this_floppy = &floppy_type[drive >> 2]; - else if ((this_floppy = current_type[drive & 3]) == NULL) - return -ENODEV; - i = verify_area(VERIFY_WRITE,(void *) param,sizeof(struct floppy_struct)); - if (i) - return i; - for (cnt = 0; cnt < sizeof(struct floppy_struct); cnt++) - put_fs_byte(((char *) this_floppy)[cnt], - (char *) param+cnt); - return 0; - case FDFMTTRK: - if (!suser()) + case FDGETDRVTYP: + i=verify_area(VERIFY_WRITE,(void *) param,16); + if (i) + return i; + name = drive_name(type,drive); + for ( cnt=0; cnt<16; cnt++){ + put_fs_byte(name[cnt], + ((char*)param)+cnt); + if ( ! *name ) + break; + } + return 0; + case FDGETMAXERRS: + return COPYOUT(UDP->max_errors); + case FDGETPRM: + if (type) + this_floppy = &floppy_type[type]; + else if ((this_floppy = current_type[drive]) == + NULL) + return -ENODEV; + return COPYOUT(this_floppy[0]); + case FDPOLLDRVSTAT: + poll_drive(drive); + /* fall through */ + case FDGETDRVSTAT: + return COPYOUT(*UDRS); + case FDGETFDCSTAT: + return COPYOUT(*UFDCS); + case FDGETDRVPRM: + return COPYOUT(*UDP); + } + if (!IOCTL_ALLOWED) + return -EPERM; + switch (cmd) { + case FDRAWCMD: + if (type) + return -EINVAL; + lock_fdc(drive); + set_floppy(device); + i = raw_cmd_ioctl(drive, (void *) param); + unlock_fdc(); + return i; + case FDFMTTRK: + if (UDRS->fd_ref != 1) + return -EBUSY; + if (! (UDRS->flags & FD_DRIVE_PRESENT )) + return -ENXIO; + COPYIN(tmp_format_req); + return do_format(device, &tmp_format_req); + case FDSETMAXERRS: + return COPYIN(UDP->max_errors); + case FDFMTBEG: + return 0; + case FDCLRPRM: + lock_fdc(drive); + current_type[drive] = NULL; + floppy_sizes[drive] = 2; + UDRS->keep_data = 0; + return invalidate_drive(device); + case FDFMTEND: + case FDFLUSH: + lock_fdc(drive); + return invalidate_drive(device); + case FDSETPRM: + case FDDEFPRM: + COPYIN(newparams); + /* sanity checking for parameters.*/ + if(newparams.sect <= 0 || + newparams.head <= 0 || + newparams.track <= 0 || + newparams.track > + UDP->tracks>>newparams.stretch) + return -EINVAL; + if ( type){ + if ( !suser() ) return -EPERM; - if (fd_ref[drive & 3] != 1) - return -EBUSY; - cli(); - while (format_status != FORMAT_NONE) - sleep_on(&format_done); - for (cnt = 0; cnt < sizeof(struct format_descr); cnt++) - ((char *) &format_req)[cnt] = get_fs_byte( - (char *) param+cnt); - format_req.device = drive; - format_status = FORMAT_WAIT; - format_errors = 0; - while (format_status != FORMAT_OKAY && format_status != - FORMAT_ERROR) { - if (fdc_busy) sleep_on(&fdc_wait); - else { - fdc_busy = 1; - redo_fd_request(); + lock_fdc(-1); + for ( cnt = 0; cnt < N_DRIVE; cnt++){ + if (TYPE(drive_state[cnt].fd_device) == type && + drive_state[cnt].fd_ref){ + cli(); fake_change |= 1 << cnt; sti(); } } - while (format_status != FORMAT_OKAY && format_status != - FORMAT_ERROR) - sleep_on(&format_done); - sti(); - okay = format_status == FORMAT_OKAY; - format_status = FORMAT_NONE; - floppy_off(drive & 3); - wake_up(&format_done); - return okay ? 0 : -EIO; - case FDFLUSH: - if (!permission(inode, 2)) - return -EPERM; - cli(); - fake_change |= 1 << (drive & 3); - sti(); - check_disk_change(inode->i_rdev); + floppy_type[type] = newparams; + floppy_type[type].name="user format"; + for (cnt = type << 2 ; + cnt < (type << 2 ) + 4 ; + cnt++) + floppy_sizes[cnt]= +#ifdef HAVE_2_CONTROLLERS + floppy_sizes[cnt+0x80]= +#endif + floppy_type[type].size>>1; + unlock_fdc(); + for ( cnt = 0; cnt < N_DRIVE; cnt++){ + if (TYPE(drive_state[cnt].fd_device) == type && + drive_state[cnt].fd_ref) + check_disk_change(drive_state[cnt]. + fd_device); + } return 0; - } - if (!suser()) + } + + lock_fdc(drive); + if ( cmd != FDDEFPRM ) + /* notice a disk change immediately, else + * we loose our settings immediately*/ + start_motor(); + user_params[drive] = newparams; + if (buffer_drive == drive && + buffer_max > user_params[drive].sect) + buffer_max=user_params[drive].sect; + current_type[drive] = &user_params[drive]; + floppy_sizes[drive] = user_params[drive].size >> 1; + if (cmd == FDDEFPRM) + DRS->keep_data = -1; + else + DRS->keep_data = 1; + /* invalidation. Invalidate only when needed, i.e. + * when there are already sectors in the buffer cache + * whose number will change. This is useful, because + * mtools often changes the geometry of the disk after + * looking at the boot block */ + if (DRS->maxblock > + user_params[drive].sect || + DRS->maxtrack ) + return invalidate_drive(device); + else + return unlock_fdc(); + case FDRESET: + return user_reset_fdc( drive, (int)param); + case FDMSGON: + UDP->flags |= FTD_MSG; + return 0; + case FDMSGOFF: + UDP->flags &= ~FTD_MSG; + return 0; + case FDSETEMSGTRESH: + UDP->max_errors.reporting = + (unsigned short) (param & 0x0f); + return 0; + case FDTWADDLE: + lock_fdc(drive); + twaddle(); + unlock_fdc(); + } + if ( ! suser() ) return -EPERM; - if (drive < 0 || drive > 3) + switch(cmd){ + case FDSETDRVPRM: + return COPYIN(*UDP); + default: return -EINVAL; - switch (cmd) { - case FDCLRPRM: - current_type[drive] = NULL; - floppy_sizes[drive] = MAX_DISK_SIZE; - keep_data[drive] = 0; - break; - case FDSETPRM: - case FDDEFPRM: - memcpy_fromfs(user_params+drive, - (void *) param, - sizeof(struct floppy_struct)); - current_type[drive] = &user_params[drive]; - floppy_sizes[drive] = user_params[drive].size >> 1; - if (cmd == FDDEFPRM) - keep_data[drive] = -1; - else { - cli(); - while (fdc_busy) sleep_on(&fdc_wait); - fdc_busy = 1; - sti(); - outb_p((current_DOR & 0xfc) | drive | - (0x10 << drive),FD_DOR); - for (cnt = 0; cnt < 1000; cnt++) __asm__("nop"); - if (inb(FD_DIR) & 0x80) - keep_data[drive] = 1; - else - keep_data[drive] = 0; - outb_p(current_DOR,FD_DOR); - fdc_busy = 0; - wake_up(&fdc_wait); - } - break; - case FDMSGON: - ftd_msg[drive] = 1; - break; - case FDMSGOFF: - ftd_msg[drive] = 0; - break; - case FDSETEMSGTRESH: - min_report_error_cnt[drive] = (unsigned short) (param & 0x0f); - break; - default: - return -EINVAL; } return 0; +#undef IOCTL_ALLOWED } #define CMOS_READ(addr) ({ \ @@ -1356,13 +2573,12 @@ static void set_base_type(int drive,int code) { - struct floppy_struct *base; - - if (code > 0 && code <= NUMBER(floppy_types)) { - base = floppy_types[code-1]; - printk("fd%d is %s",drive,base->name); - base_type[drive] = base; - base_type_end[drive] = base + floppy_num_types[code-1]; + if (code > 0 && code <= NUMBER(default_drive_params)) { + memcpy((char *) UDP, + (char *) (&default_drive_params[code].params), + sizeof( struct floppy_drive_params )); + printk("fd%d is %s", drive, default_drive_params[code].name); + return; } else if (!code) printk("fd%d is not installed", drive); else @@ -1371,6 +2587,13 @@ static void config_types(void) { + int drive; + + for (drive=0; driveparams), + sizeof( struct floppy_drive_params )); + } printk("Floppy drive(s): "); set_base_type(0, (CMOS_READ(0x10) >> 4) & 15); if (CMOS_READ(0x10) & 15) { @@ -1380,14 +2603,52 @@ printk("\n"); } + +static void maybe_check_change(int device) +{ + register int drive; + + drive = DRIVE(device); + if (UDRS->last_checked + UDP->checkfreq < jiffies || + UDRS->flags & FD_VERIFY || + (( changed_floppies | fake_change ) & ( 1 << drive))) + check_disk_change(device); +} + +int floppy_is_wp( int minor) +{ + maybe_check_change(minor + (MAJOR_NR << 8)); + return ! ( drive_state[ DRIVE(minor) ].flags & FD_DISK_WRITABLE ); +} + + +#define WRAPPER(op) \ +static int floppy_##op(struct inode * inode, struct file * filp, \ + char * buf, int count) \ +{ \ + maybe_check_change(inode->i_rdev); \ + if ( changed_floppies & ( 1 << DRIVE(inode->i_rdev) )) \ + return -ENXIO; \ + return block_##op(inode, filp, buf, count); \ +} + +WRAPPER(read) +WRAPPER(write) + +static int exclusive = 0; static void floppy_release(struct inode * inode, struct file * filp) { + int drive= DRIVE(inode->i_rdev); + fsync_dev(inode->i_rdev); - if (!fd_ref[inode->i_rdev & 3]--) { - printk("floppy_release with fd_ref == 0"); - fd_ref[inode->i_rdev & 3] = 0; + if ( UDRS->fd_ref < 0) + UDRS->fd_ref=0; + else if (!UDRS->fd_ref--) { + printk(DEVICE_NAME ": floppy_release with fd_ref == 0"); + UDRS->fd_ref = 0; } floppy_release_irq_and_dma(); + exclusive=0; } /* @@ -1395,51 +2656,171 @@ * /dev/PS0 etc), and disallows simultaneous access to the same * drive with different device numbers. */ +#define RETERR(x) \ + do{floppy_release(inode,filp); \ + return -(x);}while(0) +static int usage_count = 0; static int floppy_open(struct inode * inode, struct file * filp) { int drive; int old_dev; - drive = inode->i_rdev & 3; - old_dev = fd_device[drive]; - if (fd_ref[drive]) - if (old_dev != inode->i_rdev) - return -EBUSY; + if (exclusive) + return -EBUSY; + if ( !filp ){ + printk(DEVICE_NAME ": Weird, open called with filp=0\n"); + return -EIO; + } + + drive = DRIVE(inode->i_rdev); + if ( drive >= N_DRIVE ) + return -ENXIO; + + if (command_status == FD_COMMAND_DETECT && drive >= current_drive){ + lock_fdc(-1); + unlock_fdc(); + } + + if ( TYPE(inode->i_rdev) >= NUMBER(floppy_type)) + return -ENXIO; + + if (filp->f_mode & 3){ + if ( !(UDRS->flags & FD_DRIVE_PRESENT)) + return -ENXIO; + } + + old_dev = UDRS->fd_device; + if (UDRS->fd_ref && old_dev != inode->i_rdev) + return -EBUSY; if (floppy_grab_irq_and_dma()) return -EBUSY; - fd_ref[drive]++; - fd_device[drive] = inode->i_rdev; - buffer_drive = buffer_track = -1; - if (old_dev && old_dev != inode->i_rdev) + + if (filp->f_flags & O_EXCL ){ + if (usage_count>1){ + floppy_release_irq_and_dma(); + return -EBUSY; + }else + exclusive=1; + } + + /* filp->f_op is NULL if the disk is mounted, and non-NULL if opened */ + if (filp->f_op){ + if (UDRS->fd_ref == -1) + return -EBUSY; + UDRS->fd_ref++; + UDRS->fd_device = inode->i_rdev; + } else { + if (UDRS->fd_ref ){ + floppy_release_irq_and_dma(); + return -EBUSY; + } + UDRS->fd_ref=-1; + } + + if (old_dev && old_dev != inode->i_rdev){ + if ( buffer_drive == drive ) + buffer_track = -1; invalidate_buffers(old_dev); - if (filp && filp->f_mode) { + } + + if (filp->f_mode & 2 || permission(inode,2)) + filp->f_mode |= 8; /* keep mtools working */ + + if (UFDCS->rawcmd == 1) + UFDCS->rawcmd = 2; + + if (filp->f_flags & O_NDELAY) + return 0; + if (filp->f_mode && !(UDRS->flags & FD_DRIVE_PRESENT)) + RETERR(ENXIO); + if(user_reset_fdc(drive, FD_RESET_IF_NEEDED)) + RETERR(EIO); + + if (filp->f_mode & 3){ check_disk_change(inode->i_rdev); - if (filp->f_mode & 2) { - if (1 & (read_only >> drive)) { - floppy_release(inode, filp); - return -EROFS; - } + if ( changed_floppies & ( 1 << drive )){ + if ( suser() && filp->f_op) + /* prevent dosemu from crashing */ + filp->f_mode &= ~3; + else + RETERR(ENXIO); } } + if ((filp->f_mode & 2) && UDRS->flags < FD_DISK_WRITABLE){ + if ( suser() && filp->f_op) + /* prevent dosemu from crashing */ + filp->f_mode &= ~2; + else + RETERR(EROFS); + } return 0; +#undef RETERR +} + +/* + * Acknowledge disk change + */ +static int ack_change(int drive) +{ + unsigned int mask = 1 << drive; + UDRS->maxblock = 0; + UDRS->maxtrack = 0; + if ( buffer_drive == drive ) + buffer_track = -1; + fake_change &= ~mask; + changed_floppies &= ~mask; + return 1; } +/* + * Check if the disk has been changed or if a change has been faked. + */ static int check_floppy_change(dev_t dev) { - int i; + int drive = DRIVE( dev ); + unsigned int mask = 1 << drive; + + if (MAJOR(dev) != MAJOR_NR) { + printk(DEVICE_NAME ": floppy_changed: not a floppy\n"); + return 0; + } + + if (fake_change & mask) + return ack_change(drive); + + if ((UDRS->flags & FD_VERIFY ) || (changed_floppies & mask) || + UDRS->last_checked + UDP->checkfreq < + jiffies){ + user_reset_fdc(drive, FD_RESET_IF_NEEDED); + poll_drive(drive); + if (changed_floppies & mask){ + UDRS->generation++; + return ack_change(drive); + } + } + return 0; +} + +/* revalidate the floppy disk, i.e. trigger format autodetection by reading + * the bootblock (block 0) */ +static int floppy_revalidate(dev_t dev) +{ struct buffer_head * bh; - if (!(bh = getblk(dev,0,1024))) + if ( TYPE(dev) || current_type[DRIVE(dev)] ) return 0; - i = floppy_change(bh); + if (!(bh = getblk(dev,0,1024))) + return 1; + if ( bh && ! bh->b_uptodate) + ll_rw_block(READ, 1, &bh); brelse(bh); - return i; + return 0; } static struct file_operations floppy_fops = { NULL, /* lseek - default */ - block_read, /* read - general block-dev read */ - block_write, /* write - general block-dev write */ + floppy_read, /* read - general block-dev read */ + floppy_write, /* write - general block-dev write */ NULL, /* readdir - bad */ NULL, /* select */ fd_ioctl, /* ioctl */ @@ -1449,20 +2830,88 @@ block_fsync, /* fsync */ NULL, /* fasync */ check_floppy_change, /* media_change */ - NULL /* revalidate */ + floppy_revalidate, /* revalidate */ }; - -static void floppy_interrupt(int unused) -{ - void (*handler)(void) = DEVICE_INTR; +/* + * Floppy Driver initialisation + * ============================= + */ - DEVICE_INTR = NULL; - if (!handler) - handler = unexpected_floppy_interrupt; - handler(); +/* Determine the floppy disk controller type */ +/* This routine was written by David C. Niemi */ +static char get_fdc_version(void) +{ + int r; + + output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */ + if ( FDCS->reset ) + return FDC_NONE; + if ( (r = result()) <= 0x00) + return FDC_NONE; /* No FDC present ??? */ + if ((r==1) && (reply_buffer[0] == 0x80)){ + printk("FDC %d is a 8272A\n",fdc); + return FDC_8272A; /* 8272a/765 don't know DUMPREGS */ + } + if (r != 10) { + printk("FDC init: DUMPREGS: unexpected return of %d bytes.\n", r); + return FDC_UNKNOWN; + } + output_byte(FD_VERSION); + r = result(); + if ((r == 1) && (reply_buffer[0] == 0x80)){ + printk("FDC %d is a 82072\n",fdc); + return FDC_82072; /* 82072 doesn't know VERSION */ + } + if ((r != 1) || (reply_buffer[0] != 0x90)) { + printk("FDC init: VERSION: unexpected return of %d bytes.\n", r); + return FDC_UNKNOWN; + } + output_byte(FD_UNLOCK); + r = result(); + if ((r == 1) && (reply_buffer[0] == 0x80)){ + printk("FDC %d is a pre-1991 82077\n", fdc); + return FDC_82077_ORIG; /* Pre-1991 82077 doesn't know LOCK/UNLOCK */ + } + if ((r != 1) || (reply_buffer[0] != 0x00)) { + printk("FDC init: UNLOCK: unexpected return of %d bytes.\n", r); + return FDC_UNKNOWN; + } + printk("FDC %d is a post-1991 82077\n",fdc); + return FDC_82077; /* Revised 82077AA passes all the tests */ +} /* fdc_init */ + +/* + * Drive detection routine. This runs in the background while the kernel + * does other (non floppy related) initialisation work. + */ +static void detect_interrupt(void) +{ + if ( DRS->track == 0 ) + DRS->flags |= FD_DRIVE_PRESENT | FD_VERIFY; + else if ( DRS->track != NEED_1_RECAL ){ + floppy_ready(); + return; + } + floppy_off(current_drive); + current_drive++; + if (current_drive == N_DRIVE || + fdc_state[FDC(current_drive)].version == FDC_NONE){ + set_fdc(0); + unlock_fdc(); + floppy_release_irq_and_dma(); + return; + } + set_fdc(current_drive); + floppy_ready(); /* next */ } +static struct cont_t detect_cont={ + detect_interrupt, + detect_interrupt, + empty, + (done_f) empty }; + /* * This is the floppy IRQ description. The SA_INTERRUPT in sa_flags * means we run the IRQ-handler with interrupts disabled. @@ -1476,51 +2925,88 @@ void floppy_init(void) { - outb(current_DOR,FD_DOR); + int i; + if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) { printk("Unable to get major %d for floppy\n",MAJOR_NR); return; } + + for(i=0; i<256; i++) + if ( TYPE(i)) + floppy_sizes[i] = floppy_type[TYPE(i)].size >> 1; + else + floppy_sizes[i] = MAX_DISK_SIZE; + blk_size[MAJOR_NR] = floppy_sizes; blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; timer_table[FLOPPY_TIMER].fn = floppy_shutdown; timer_active &= ~(1 << FLOPPY_TIMER); config_types(); - /* Try to determine the floppy controller type */ - output_byte(FD_VERSION); /* get FDC version code */ - if (result() != 1) { - printk(DEVICE_NAME ": FDC failed to return version byte\n"); - fdc_version = FDC_TYPE_STD; - } else - fdc_version = reply_buffer[0]; - if (fdc_version != FDC_TYPE_STD) - printk(DEVICE_NAME ": FDC version 0x%x\n", fdc_version); -#ifndef FDC_FIFO_UNTESTED - fdc_version = FDC_TYPE_STD; /* force std fdc type; can't test other. */ -#endif - /* Not all FDCs seem to be able to handle the version command - * properly, so force a reset for the standard FDC clones, - * to avoid interrupt garbage. - */ - if (fdc_version == FDC_TYPE_STD) { - initial_reset_flag = 1; - reset_floppy(); - } + fdc_state[0].address = 0x3f0; + fdc_state[1].address = 0x370; + for(fdc = 0 ; fdc < N_FDC; fdc++){ + FDCS->dtr = -1; + FDCS->dor = 0; + FDCS->reset = 0; + FDCS->version = FDC_NONE; + set_dor(fdc, ~0, 0xc ); + } + + /* initialise drive state */ + for ( current_drive=0; current_drive < N_DRIVE ; current_drive++){ + DRS->flags = 0; + DRS->generation = 0; + DRS->keep_data = 0; + DRS->fd_ref = 0; + DRS->fd_device = 0; + } + + floppy_grab_irq_and_dma(); + for(fdc = 0 ; fdc < N_FDC; fdc++){ + FDCS->rawcmd = 2; + if(user_reset_fdc(-1,FD_RESET_IF_NEEDED)) + continue; + /* Try to determine the floppy controller type */ + FDCS->version = get_fdc_version(); + if (FDCS->version == FDC_NONE) + continue; + + /* Not all FDCs seem to be able to handle the version command + * properly, so force a reset for the standard FDC clones, + * to avoid interrupt garbage. + */ + FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG; + user_reset_fdc(-1,FD_RESET_ALWAYS); + } + fdc=0; + current_drive = 0; + lock_fdc(-1); + initialising =0; + command_status = FD_COMMAND_DETECT; + cont = &detect_cont; + raw_cmd.cmd_count = 0; + raw_cmd.flags = FD_RAW_NEED_SEEK; + raw_cmd.track = 0; + floppy_ready(); + cli(); } -static int usage_count = 0; - int floppy_grab_irq_and_dma(void) { if (usage_count++) return 0; if (irqaction(FLOPPY_IRQ,&floppy_sigaction)) { - printk("Unable to grab IRQ%d for the floppy driver\n", FLOPPY_IRQ); + printk(DEVICE_NAME + ": Unable to grab IRQ%d for the floppy driver\n", + FLOPPY_IRQ); return -1; } if (request_dma(FLOPPY_DMA)) { - printk("Unable to grab DMA%d for the floppy driver\n", FLOPPY_DMA); + printk(DEVICE_NAME + ": Unable to grab DMA%d for the floppy driver\n", + FLOPPY_DMA); free_irq(FLOPPY_IRQ); return -1; } @@ -1536,4 +3022,10 @@ free_dma(FLOPPY_DMA); disable_irq(FLOPPY_IRQ); free_irq(FLOPPY_IRQ); +#ifdef HAVE_2_CONTROLLERS + /* switch on first controller. + * This saves us trouble on the next reboot. */ + set_dor(0, ~0, 8 ); + set_dor(1, ~8, 0 ); +#endif } diff -u --recursive --new-file v1.1.40/linux/drivers/block/hd.c linux/drivers/block/hd.c --- v1.1.40/linux/drivers/block/hd.c Sat Aug 6 21:32:06 1994 +++ linux/drivers/block/hd.c Sun Aug 7 00:28:07 1994 @@ -73,10 +73,6 @@ static int reset = 0; static int hd_error = 0; -#if (HD_DELAY > 0) -unsigned long last_req, read_timer(); -#endif - /* * This struct defines the HD's and their types. */ @@ -100,17 +96,20 @@ static int hd_blocksizes[MAX_HD<<6] = {0, }; #if (HD_DELAY > 0) +unsigned long last_req; + unsigned long read_timer(void) { - unsigned long t; + unsigned long t, flags; int i; + save_flags(flags); cli(); t = jiffies * 11932; outb_p(0, 0x43); i = inb_p(0x40); i |= inb(0x40) << 8; - sti(); + restore_flags(flags); return(t - i); } #endif @@ -287,6 +286,9 @@ insw(HD_DATA,(char *)&id,sizeof(id)/2); insw(HD_DATA,(char *)&id,sizeof(id)/2); } +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif hd_request(); return; } @@ -307,6 +309,9 @@ else printk (" hd%c: disabled multiple mode\n", dev+'a'); } +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif hd_request(); return; } @@ -425,7 +430,6 @@ if (unmask_intr[dev]) sti(); /* permit other IRQs during xfer */ -read_next: do { i = (unsigned) inb_p(HD_STATUS); if (i & BUSY_STAT) @@ -442,7 +446,6 @@ printk("hd%c: read_intr: error = 0x%02x\n",dev+'a',hd_error); } bad_rw_intr(); - cli(); hd_request(); return; ok_to_read: @@ -467,7 +470,7 @@ end_request(1); if (i > 0) { if (msect) - goto read_next; + goto ok_to_read; SET_INTR(&read_intr); return; } @@ -535,7 +538,6 @@ printk("hd:%c multwrite_intr: error = 0x%02x\n",dev+'a',hd_error); } bad_rw_intr(); - cli(); hd_request(); } @@ -544,6 +546,8 @@ int i; int retries = 100000; + if (unmask_intr[DEVICE_NR(WCURRENT.dev)]) + sti(); do { i = (unsigned) inb_p(HD_STATUS); if (i & BUSY_STAT) @@ -560,7 +564,6 @@ printk("HD: write_intr: error = 0x%02x\n",hd_error); } bad_rw_intr(); - cli(); hd_request(); return; ok_to_write: @@ -587,6 +590,9 @@ { if (win_result()) bad_rw_intr(); +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif hd_request(); } @@ -1067,17 +1073,19 @@ int max_p; int start; int i; + long flags; target = DEVICE_NR(MINOR(dev)); gdev = &GENDISK_STRUCT; + save_flags(flags); cli(); if (DEVICE_BUSY || USAGE > maxusage) { - sti(); + restore_flags(flags); return -EBUSY; }; DEVICE_BUSY = 1; - sti(); + restore_flags(flags); max_p = gdev->max_p; start = target << gdev->minor_shift; diff -u --recursive --new-file v1.1.40/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v1.1.40/linux/drivers/block/ll_rw_blk.c Mon Jul 18 14:48:28 1994 +++ linux/drivers/block/ll_rw_blk.c Sun Aug 7 00:46:57 1994 @@ -125,6 +125,7 @@ major = MAJOR(dev); minor = MINOR(dev); + if ( major == FLOPPY_MAJOR && floppy_is_wp( minor) ) return 1; if (major < 0 || major >= MAX_BLKDEV) return 0; return ro_bits[major][minor >> 5] & (1 << (minor & 31)); } @@ -225,6 +226,7 @@ * to add links to the top entry for scsi devices. */ if ((major == HD_MAJOR + || major == FLOPPY_MAJOR || major == SCSI_DISK_MAJOR || major == SCSI_CDROM_MAJOR) && (req = blk_dev[major].current_request)) diff -u --recursive --new-file v1.1.40/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c --- v1.1.40/linux/drivers/char/n_tty.c Sat Jul 9 16:30:46 1994 +++ linux/drivers/char/n_tty.c Sun Aug 7 00:28:44 1994 @@ -111,7 +111,7 @@ if (O_ONLCR(tty)) { if (space < 2) return -1; - tty->driver.write(tty, 0, "\r", 1); + tty->driver.put_char(tty, '\r'); tty->column = 0; } tty->canon_column = tty->column; diff -u --recursive --new-file v1.1.40/linux/drivers/char/pty.c linux/drivers/char/pty.c --- v1.1.40/linux/drivers/char/pty.c Wed Jun 22 14:57:08 1994 +++ linux/drivers/char/pty.c Sun Aug 7 00:28:44 1994 @@ -200,7 +200,7 @@ pty_driver.init_termios = tty_std_termios; pty_driver.init_termios.c_iflag = 0; pty_driver.init_termios.c_oflag = 0; - pty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD; + pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD; pty_driver.init_termios.c_lflag = 0; pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW; pty_driver.refcount = &pty_refcount; @@ -221,6 +221,7 @@ pty_slave_driver.subtype = PTY_TYPE_SLAVE; pty_slave_driver.minor_start = 192; pty_slave_driver.init_termios = tty_std_termios; + pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD; pty_slave_driver.table = ttyp_table; pty_slave_driver.termios = ttyp_termios; pty_slave_driver.termios_locked = ttyp_termios_locked; diff -u --recursive --new-file v1.1.40/linux/drivers/char/serial.c linux/drivers/char/serial.c --- v1.1.40/linux/drivers/char/serial.c Sat Jul 9 16:30:46 1994 +++ linux/drivers/char/serial.c Sun Aug 7 00:28:44 1994 @@ -69,6 +69,7 @@ #undef SERIAL_DEBUG_INTR #undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW #define _INLINE_ inline @@ -186,6 +187,18 @@ #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the memcpy_fromfs blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf = 0; +static struct semaphore tmp_buf_sem = MUTEX; + static inline int serial_paranoia_check(struct async_struct *info, dev_t device, const char *routine) { @@ -283,12 +296,6 @@ if (serial_paranoia_check(info, tty->device, "rs_stop")) return; - if (info->flags & ASYNC_CLOSING) { - tty->stopped = 0; - tty->hw_stopped = 0; - return; - } - save_flags(flags); cli(); if (info->IER & UART_IER_THRI) { info->IER &= ~UART_IER_THRI; @@ -405,7 +412,8 @@ *intr_done = 0; return; } - if (!info->xmit_cnt || info->tty->stopped || info->tty->hw_stopped) { + if ((info->xmit_cnt <= 0) || info->tty->stopped || + info->tty->hw_stopped) { info->IER &= ~UART_IER_THRI; #ifdef CONFIG_SERIAL_NEW_ISR serial_out(info, UART_IER, info->IER); @@ -417,7 +425,7 @@ do { serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); - if (--info->xmit_cnt == 0) + if (--info->xmit_cnt <= 0) break; } while (--count > 0); @@ -430,7 +438,7 @@ if (intr_done) *intr_done = 0; - if (info->xmit_cnt == 0) { + if (info->xmit_cnt <= 0) { info->IER &= ~UART_IER_THRI; #ifdef CONFIG_SERIAL_NEW_ISR serial_out(info, UART_IER, info->IER); @@ -462,7 +470,7 @@ if (info->flags & ASYNC_CTS_FLOW) { if (info->tty->hw_stopped) { if (status & UART_MSR_CTS) { -#ifdef SERIAL_DEBUG_INTR +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif info->tty->hw_stopped = 0; @@ -475,7 +483,7 @@ } } else { if (!(status & UART_MSR_CTS)) { -#ifdef SERIAL_DEBUG_INTR +#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif info->tty->hw_stopped = 1; @@ -926,6 +934,8 @@ info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS; } + if (info->irq == 0) + info->MCR = info->MCR_noint; serial_outp(info, UART_MCR, info->MCR); /* @@ -1035,7 +1045,7 @@ free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } - + info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ if (info->flags & ASYNC_FOURPORT) { @@ -1190,6 +1200,7 @@ static void rs_put_char(struct tty_struct *tty, unsigned char ch) { struct async_struct *info = tty->driver_data; + unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; @@ -1197,12 +1208,16 @@ if (!tty || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; - if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) + save_flags(flags); cli(); + if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { + restore_flags(flags); return; + } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE-1; info->xmit_cnt++; + restore_flags(flags); } static void rs_flush_chars(struct tty_struct *tty) @@ -1213,7 +1228,7 @@ if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; - if (info->xmit_cnt == 0 || tty->stopped || tty->hw_stopped || + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; @@ -1233,29 +1248,33 @@ if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; - if (!tty || !info->xmit_buf) + if (!tty || !info->xmit_buf || !tmp_buf) return 0; + save_flags(flags); while (1) { + cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); - if (!c) + if (c <= 0) break; - if (from_user) - memcpy_fromfs(info->xmit_buf + info->xmit_head, - buf, c); - else + if (from_user) { + down(&tmp_buf_sem); + memcpy_fromfs(tmp_buf, buf, c); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + up(&tmp_buf_sem); + } else memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - cli(); info->xmit_cnt += c; - sti(); + restore_flags(flags); buf += c; count -= c; total += c; } - save_flags(flags); cli(); if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; @@ -1268,10 +1287,14 @@ static int rs_write_room(struct tty_struct *tty) { struct async_struct *info = tty->driver_data; + int ret; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; - return SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + return ret; } static int rs_chars_in_buffer(struct tty_struct *tty) @@ -1770,7 +1793,6 @@ if (info->count) return; info->flags |= ASYNC_CLOSING; - info->flags &= ~ASYNC_CTS_FLOW; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. @@ -1779,12 +1801,8 @@ info->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->callout_termios = *tty->termios; - tty->stopped = 0; /* Force flush to succeed */ - tty->hw_stopped = 0; - if (info->flags & ASYNC_INITIALIZED) { - rs_start(tty); - wait_until_sent(tty, 6000); /* 60 seconds timeout */ - } + if (info->flags & ASYNC_INITIALIZED) + wait_until_sent(tty, 3000); /* 30 seconds timeout */ shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); @@ -1971,7 +1989,7 @@ info = rs_table + line; if (serial_paranoia_check(info, tty->device, "rs_open")) return -ENODEV; - + #ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->count); @@ -1979,6 +1997,12 @@ info->count++; tty->driver_data = info; info->tty = tty; + + if (!tmp_buf) { + tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + } if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) diff -u --recursive --new-file v1.1.40/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c --- v1.1.40/linux/drivers/char/tty_ioctl.c Sun Aug 7 03:30:38 1994 +++ linux/drivers/char/tty_ioctl.c Sun Aug 7 00:28:44 1994 @@ -31,6 +31,13 @@ # define PRINTK(x) /**/ #endif +/* + * Internal flag options for termios setting behavior + */ +#define TERMIOS_FLUSH 1 +#define TERMIOS_WAIT 2 +#define TERMIOS_TERMIO 4 + void wait_until_sent(struct tty_struct * tty, int timeout) { struct wait_queue wait = { current, NULL }; @@ -79,13 +86,41 @@ old->c_cc[i] : termios->c_cc[i]; } -static int set_termios_2(struct tty_struct * tty, struct termios * termios) +static int set_termios(struct tty_struct * tty, unsigned long arg, int opt) { + struct termio tmp_termio; + struct termios tmp_termios; struct termios old_termios = *tty->termios; - int canon_change; + int retval, canon_change; + + retval = tty_check_change(tty); + if (retval) + return retval; + + if (opt & TERMIOS_TERMIO) { + tmp_termios = *tty->termios; + memcpy_fromfs(&tmp_termio, (struct termio *) arg, + sizeof (struct termio)); + +#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y)) + SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag); + SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag); + SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag); + SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag); + memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC); +#undef SET_LOW_BITS + } else + memcpy_fromfs(&tmp_termios, (struct termios *) arg, + sizeof (struct termios)); + + if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + if (opt & TERMIOS_WAIT) + wait_until_sent(tty, 0); + cli(); - *tty->termios = *termios; + *tty->termios = tmp_termios; unset_locked_termios(tty->termios, &old_termios, tty->termios_locked); canon_change = (old_termios.c_lflag ^ tty->termios->c_lflag) & ICANON; if (canon_change) { @@ -127,14 +162,6 @@ return 0; } -static int set_termios(struct tty_struct * tty, struct termios * termios) -{ - struct termios tmp_termios; - - memcpy_fromfs(&tmp_termios, termios, sizeof (struct termios)); - return set_termios_2(tty, &tmp_termios); -} - static int get_termio(struct tty_struct * tty, struct termio * termio) { int i; @@ -154,27 +181,6 @@ return 0; } -static int set_termio(struct tty_struct * tty, struct termio * termio) -{ - struct termio tmp_termio; - struct termios tmp_termios; - - tmp_termios = *tty->termios; - memcpy_fromfs(&tmp_termio, termio, sizeof (struct termio)); - -#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y)) - - SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag); - SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag); - SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag); - SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag); - memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC); - -#undef SET_LOW_BITS - - return set_termios_2(tty, &tmp_termios); -} - static unsigned long inq_canon(struct tty_struct * tty) { int nr, head, tail; @@ -199,6 +205,7 @@ { struct tty_struct * real_tty; int retval; + int opt = 0; if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) @@ -217,34 +224,19 @@ sizeof (struct termios)); return 0; case TCSETSF: + opt |= TERMIOS_FLUSH; case TCSETSW: + opt |= TERMIOS_WAIT; case TCSETS: - retval = tty_check_change(real_tty); - if (retval) - return retval; - if (cmd == TCSETSF || cmd == TCSETSW) { - if (cmd == TCSETSF && - real_tty->ldisc.flush_buffer) - real_tty->ldisc.flush_buffer(real_tty); - wait_until_sent(real_tty, 0); - } - return set_termios(real_tty, - (struct termios *) arg); + return set_termios(real_tty, arg, opt); case TCGETA: return get_termio(real_tty,(struct termio *) arg); case TCSETAF: + opt |= TERMIOS_FLUSH; case TCSETAW: + opt |= TERMIOS_WAIT; case TCSETA: - retval = tty_check_change(real_tty); - if (retval) - return retval; - if (cmd == TCSETAF || cmd == TCSETAW) { - if (cmd == TCSETAF && - real_tty->ldisc.flush_buffer) - real_tty->ldisc.flush_buffer(real_tty); - wait_until_sent(real_tty, 0); - } - return set_termio(real_tty, (struct termio *) arg); + return set_termios(real_tty, arg, opt|TERMIOS_TERMIO); case TCXONC: retval = tty_check_change(tty); if (retval) diff -u --recursive --new-file v1.1.40/linux/fs/block_dev.c linux/fs/block_dev.c --- v1.1.40/linux/fs/block_dev.c Tue Apr 19 10:53:19 1994 +++ linux/fs/block_dev.c Sun Aug 7 00:46:57 1994 @@ -32,6 +32,8 @@ register char * p; dev = inode->i_rdev; + if ( is_read_only( inode->i_rdev )) + return -EPERM; blocksize = BLOCK_SIZE; if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)]) blocksize = blksize_size[MAJOR(dev)][MINOR(dev)]; diff -u --recursive --new-file v1.1.40/linux/fs/super.c linux/fs/super.c --- v1.1.40/linux/fs/super.c Sun Aug 7 03:30:42 1994 +++ linux/fs/super.c Sun Aug 7 00:46:57 1994 @@ -479,6 +479,9 @@ { int retval; + if (!(flags & MS_RDONLY ) && sb->s_dev && is_read_only(sb->s_dev)) + return -EACCES; + /*flags |= MS_RDONLY;*/ /* If we are remounting RDONLY, make sure there are no rw files open */ if ((flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY)) if (!fs_may_remount_ro(sb->s_dev)) diff -u --recursive --new-file v1.1.40/linux/include/i386/string.h linux/include/i386/string.h --- v1.1.40/linux/include/i386/string.h Thu Jan 1 02:00:00 1970 +++ linux/include/i386/string.h Sun Aug 7 14:35:15 1994 @@ -0,0 +1,429 @@ +#ifndef _I386_STRING_H_ +#define _I386_STRING_H_ + +/* + * This string-include defines all string functions as inline + * functions. Use gcc. It also assumes ds=es=data space, this should be + * normal. Most of the string-functions are rather heavily hand-optimized, + * see especially strtok,strstr,str[c]spn. They should work, but are not + * very easy to understand. Everything is done entirely within the register + * set, making the functions fast and clean. String instructions have been + * used through-out, making for "slightly" unclear code :-) + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +extern inline char * strcpy(char * dest,const char *src) +{ +__asm__ __volatile__( + "cld\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + : /* no output */ + :"S" (src),"D" (dest):"si","di","ax","memory"); +return dest; +} + +extern inline char * strncpy(char * dest,const char *src,size_t count) +{ +__asm__ __volatile__( + "cld\n" + "1:\tdecl %2\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "rep\n\t" + "stosb\n" + "2:" + : /* no output */ + :"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory"); +return dest; +} + +extern inline char * strcat(char * dest,const char * src) +{ +__asm__ __volatile__( + "cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n" + "1:\tlodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b" + : /* no output */ + :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx"); +return dest; +} + +extern inline char * strncat(char * dest,const char * src,size_t count) +{ +__asm__ __volatile__( + "cld\n\t" + "repne\n\t" + "scasb\n\t" + "decl %1\n\t" + "movl %4,%3\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "stosb\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %2,%2\n\t" + "stosb" + : /* no output */ + :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count) + :"si","di","ax","cx","memory"); +return dest; +} + +extern inline int strcmp(const char * cs,const char * ct) +{ +register int __res; +__asm__ __volatile__( + "cld\n" + "1:\tlodsb\n\t" + "scasb\n\t" + "jne 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "jmp 3f\n" + "2:\tsbbl %%eax,%%eax\n\t" + "orb $1,%%eax\n" + "3:" + :"=a" (__res):"S" (cs),"D" (ct):"si","di"); +return __res; +} + +extern inline int strncmp(const char * cs,const char * ct,size_t count) +{ +register int __res; +__asm__ __volatile__( + "cld\n" + "1:\tdecl %3\n\t" + "js 2f\n\t" + "lodsb\n\t" + "scasb\n\t" + "jne 3f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n" + "2:\txorl %%eax,%%eax\n\t" + "jmp 4f\n" + "3:\tsbbl %%eax,%%eax\n\t" + "orb $1,%%al\n" + "4:" + :"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx"); +return __res; +} + +extern inline char * strchr(const char * s,char c) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "je 2f\n\t" + "testb %%al,%%al\n\t" + "jne 1b\n\t" + "movl $1,%1\n" + "2:\tmovl %1,%0\n\t" + "decl %0" + :"=a" (__res):"S" (s),"0" (c):"si"); +return __res; +} + +extern inline char * strrchr(const char * s,char c) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" + "movb %%al,%%ah\n" + "1:\tlodsb\n\t" + "cmpb %%ah,%%al\n\t" + "jne 2f\n\t" + "leal -1(%%esi),%0\n" + "2:\ttestb %%al,%%al\n\t" + "jne 1b" + :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si"); +return __res; +} + +extern inline size_t strspn(const char * cs, const char * ct) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline size_t strcspn(const char * cs, const char * ct) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n" + "2:\tdecl %0" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res-cs; +} + +extern inline char * strpbrk(const char * cs,const char * ct) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "movl %%ecx,%%edx\n" + "1:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 2f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 1b\n\t" + "decl %0\n\t" + "jmp 3f\n" + "2:\txorl %0,%0\n" + "3:" + :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) + :"ax","cx","dx","di"); +return __res; +} + +extern inline char * strstr(const char * cs,const char * ct) +{ +register char * __res; +__asm__ __volatile__( + "cld\n\t" \ + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */ + "movl %%ecx,%%edx\n" + "1:\tmovl %4,%%edi\n\t" + "movl %%esi,%%eax\n\t" + "movl %%edx,%%ecx\n\t" + "repe\n\t" + "cmpsb\n\t" + "je 2f\n\t" /* also works for empty string, see above */ + "xchgl %%eax,%%esi\n\t" + "incl %%esi\n\t" + "cmpb $0,-1(%%eax)\n\t" + "jne 1b\n\t" + "xorl %%eax,%%eax\n\t" + "2:" + :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct) + :"cx","dx","di","si"); +return __res; +} + +extern inline size_t strlen(const char * s) +{ +register int __res; +__asm__ __volatile__( + "cld\n\t" + "repne\n\t" + "scasb\n\t" + "notl %0\n\t" + "decl %0" + :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di"); +return __res; +} + +extern char * ___strtok; + +extern inline char * strtok(char * s,const char * ct) +{ +register char * __res; +__asm__ __volatile__( + "testl %1,%1\n\t" + "jne 1f\n\t" + "testl %0,%0\n\t" + "je 8f\n\t" + "movl %0,%1\n" + "1:\txorl %0,%0\n\t" + "movl $-1,%%ecx\n\t" + "xorl %%eax,%%eax\n\t" + "cld\n\t" + "movl %4,%%edi\n\t" + "repne\n\t" + "scasb\n\t" + "notl %%ecx\n\t" + "decl %%ecx\n\t" + "je 7f\n\t" /* empty delimeter-string */ + "movl %%ecx,%%edx\n" + "2:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 7f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "je 2b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 7f\n\t" + "movl %1,%0\n" + "3:\tlodsb\n\t" + "testb %%al,%%al\n\t" + "je 5f\n\t" + "movl %4,%%edi\n\t" + "movl %%edx,%%ecx\n\t" + "repne\n\t" + "scasb\n\t" + "jne 3b\n\t" + "decl %1\n\t" + "cmpb $0,(%1)\n\t" + "je 5f\n\t" + "movb $0,(%1)\n\t" + "incl %1\n\t" + "jmp 6f\n" + "5:\txorl %1,%1\n" + "6:\tcmpb $0,(%0)\n\t" + "jne 7f\n\t" + "xorl %0,%0\n" + "7:\ttestl %0,%0\n\t" + "jne 8f\n\t" + "movl %0,%1\n" + "8:" + :"=b" (__res),"=S" (___strtok) + :"0" (___strtok),"1" (s),"g" (ct) + :"ax","cx","dx","di","memory"); +return __res; +} + +extern inline void * memcpy(void * to, const void * from, size_t n) +{ +__asm__ __volatile__( + "cld\n\t" + "movl %%edx, %%ecx\n\t" + "shrl $2,%%ecx\n\t" + "rep ; movsl\n\t" + "testb $1,%%dl\n\t" + "je 1f\n\t" + "movsb\n" + "1:\ttestb $2,%%dl\n\t" + "je 2f\n\t" + "movsw\n" + "2:\n" + : /* no output */ + :"d" (n),"D" ((long) to),"S" ((long) from) + : "cx","di","si","memory"); +return (to); +} + +extern inline void * memmove(void * dest,const void * src, size_t n) +{ +if (destrate ) & FD_SIZECODEMASK) >> 3)+ 2) +#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \ + 512 : 128 << FD_SIZECODE(floppy) ) +#define FD_PERP 0x40 +#ifndef ASSEMBLER struct floppy_struct { - unsigned int size, /* nr of 512-byte sectors total */ + unsigned int size, /* nr of sectors total */ sect, /* sectors per track */ head, /* nr of heads */ track, /* nr of tracks */ @@ -39,5 +73,142 @@ struct format_descr { unsigned int device,head,track; }; + +struct floppy_max_errors { + unsigned int + abort, /* number of errors to be reached before aborting */ + read_track, /* maximal number of errors permitted to read an + * entire track at once */ + reset, /* maximal number of errors before a reset is tried */ + recal, /* maximal number of errors before a recalibrate is + * tried */ + + /* + * Threshold for reporting FDC errors to the console. + * Setting this to zero may flood your screen when using + * ultra cheap floppies ;-) + */ + reporting; + +}; + +struct floppy_drive_params { + char cmos; /* cmos type */ + + /* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms etc) + * and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA). + */ + unsigned long max_dtr; /* Step rate, usec */ + unsigned long hlt; /* Head load/settle time, msec */ + unsigned long hut; /* Head unload time (remnant of 8" drives) */ + unsigned long srt; /* Step rate, usec */ + + unsigned long spinup; /* time needed for spinup ( in jiffies) */ + unsigned long spindown; /* timeout needed for spindown */ + unsigned char spindown_offset;/* decides in which position the disk + * will stop */ + unsigned char select_delay; /* delay to wait after select */ + unsigned char rps; /* rotations per second */ + unsigned char tracks; /* maximum number of tracks */ + unsigned long timeout; /* timeout for interrupt requests */ + + unsigned char interleave_sect;/* if there are more sectors, use interleave */ + + struct floppy_max_errors max_errors; + + char flags; /* various flags, including ftd_msg */ +/* + * Announce successful media type detection and media information loss after + * disk changes. + * Also used to enable/disable printing of overrun warnings. + */ +#define FTD_MSG 0x10 + + char read_track; /* use readtrack during probing? */ + +/* + * Auto-detection. Each drive type has eight formats which are + * used in succession to try to read the disk. If the FDC cannot lock onto + * the disk, the next format is tried. This uses the variable 'probing'. + */ + short autodetect[8]; /* autodetected formats */ + + int checkfreq; /* how often should the drive be checked for disk changes */ + int native_format; /* native format of this drive */ +}; + +struct floppy_drive_struct { + signed char flags; + +/* values for these flags */ +#define FD_NEED_TWADDLE 1 /* more magic */ +#define FD_VERIFY 2 /* this is set at bootup to force an initial drive status + inquiry*/ +#define FD_DISK_NEWCHANGE 4 /* change detected, and no action undertaken yet to + clear media change status */ +#define FD_DRIVE_PRESENT 8 +#define FD_DISK_WRITABLE 32 + + unsigned long volatile spinup_date; + unsigned long volatile select_date; + unsigned long volatile first_read_date; + short probed_format; + short track; /* current track */ + short maxblock; /* id of highest block read */ + short maxtrack; /* id of highest half track read */ + int generation; /* how many diskchanges? */ + +/* + * (User-provided) media information is _not_ discarded after a media change + * if the corresponding keep_data flag is non-zero. Positive values are + * decremented after each probe. + */ + int keep_data; + + /* Prevent "aliased" accesses. */ + int fd_ref; + int fd_device; + int last_checked; /* when was the drive last checked for a disk change? */ + +}; + +struct floppy_fdc_state { + int spec1; /* spec1 value last used */ + int spec2; /* spec2 value last used */ + int dtr; + unsigned char version; /* FDC version code */ + unsigned char dor; + int address; /* io address */ + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; +}; + +struct floppy_raw_cmd { + void *data; + long length; + + unsigned char rate; + unsigned char flags; + unsigned char cmd_count; + unsigned char cmd[9]; + unsigned char reply_count; + unsigned char reply[7]; + int track; +}; +#endif + +/* meaning of the various bytes */ + +/* flags */ +#define FD_RAW_READ 1 +#define FD_RAW_WRITE 2 +#define FD_RAW_INTR 8 +#define FD_RAW_SPIN 16 +#define FD_RAW_NEED_DISK 64 +#define FD_RAW_NEED_SEEK 128 +#define FD_RAW_USER_SUPPLIED 256 #endif diff -u --recursive --new-file v1.1.40/linux/include/linux/fdreg.h linux/include/linux/fdreg.h --- v1.1.40/linux/include/linux/fdreg.h Tue Jul 19 10:19:13 1994 +++ linux/include/linux/fdreg.h Sun Aug 7 00:46:58 1994 @@ -6,13 +6,30 @@ * Handbook", Sanches and Canton. */ +#ifdef FDPATCHES /* Fd controller regs. S&C, about page 340 */ +#define FD_STATUS (4 + fdc_state[fdc].address ) +#define FD_DATA (5 + fdc_state[fdc].address ) + +/* Digital Output Register */ +#define FD_DOR (2 + fdc_state[fdc].address ) + +/* Digital Input Register (read) */ +#define FD_DIR (7 + fdc_state[fdc].address ) + +/* Diskette Control Register (write)*/ +#define FD_DCR (7 + fdc_state[fdc].address ) + +#else + #define FD_STATUS 0x3f4 #define FD_DATA 0x3f5 #define FD_DOR 0x3f2 /* Digital Output Register */ #define FD_DIR 0x3f7 /* Digital Input Register (read) */ #define FD_DCR 0x3f7 /* Diskette Control Register (write)*/ +#endif + /* Bits of main status register */ #define STATUS_BUSYMASK 0x0F /* drive busy mask */ #define STATUS_BUSY 0x10 /* FDC busy */ @@ -65,13 +82,23 @@ #define FD_CONFIGURE 0x13 /* configure FIFO operation */ #define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */ #define FD_GETSTATUS 0x04 /* read ST3 */ +#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */ +#define FD_READID 0xEA /* prints the header of a sector */ +#define FD_UNLOCK 0x14 /* Fifo config unlock */ +#define FD_LOCK 0x94 /* Fifo config lock */ /* DMA commands */ #define DMA_READ 0x46 #define DMA_WRITE 0x4A /* FDC version return types */ -#define FDC_TYPE_STD 0x80 /* normal 8272A clone FDC */ -#define FDC_TYPE_82077 0x90 /* FIFO + perpendicular support */ +#define FDC_NONE 0x00 +#define FDC_UNKNOWN 0x10 +#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */ +#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */ +#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */ +#define FDC_82077_ORIG 0x50 /* Original version of 82077AA, sans LOCK */ +#define FDC_82077 0x52 /* 82077AA-1 */ +#define FD_RESET_DELAY 20 #endif diff -u --recursive --new-file v1.1.40/linux/include/linux/fs.h linux/include/linux/fs.h --- v1.1.40/linux/include/linux/fs.h Sat Aug 6 21:32:07 1994 +++ linux/include/linux/fs.h Sun Aug 7 00:46:58 1994 @@ -422,6 +422,7 @@ extern int check_disk_change(dev_t dev); extern void invalidate_inodes(dev_t dev); extern void invalidate_buffers(dev_t dev); +extern int floppy_is_wp(int minor); extern void sync_inodes(dev_t dev); extern void sync_dev(dev_t dev); extern int fsync_dev(dev_t dev); @@ -446,6 +447,7 @@ extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]); extern void ll_rw_page(int rw, int dev, int nr, char * buffer); extern void ll_rw_swap_file(int rw, int dev, unsigned int *b, int nb, char *buffer); +extern int is_read_only(int dev); extern void brelse(struct buffer_head * buf); extern void set_blocksize(dev_t dev, int size); extern struct buffer_head * bread(dev_t dev, int block, int size); diff -u --recursive --new-file v1.1.40/linux/include/linux/string.h linux/include/linux/string.h --- v1.1.40/linux/include/linux/string.h Mon Jul 18 14:48:30 1994 +++ linux/include/linux/string.h Sun Aug 7 14:34:59 1994 @@ -11,430 +11,27 @@ extern "C" { #endif -/* - * This string-include defines all string functions as inline - * functions. Use gcc. It also assumes ds=es=data space, this should be - * normal. Most of the string-functions are rather heavily hand-optimized, - * see especially strtok,strstr,str[c]spn. They should work, but are not - * very easy to understand. Everything is done entirely within the register - * set, making the functions fast and clean. String instructions have been - * used through-out, making for "slightly" unclear code :-) - * - * Copyright (C) 1991, 1992 Linus Torvalds - */ - -extern inline char * strcpy(char * dest,const char *src) -{ -__asm__ __volatile__( - "cld\n" - "1:\tlodsb\n\t" - "stosb\n\t" - "testb %%al,%%al\n\t" - "jne 1b" - : /* no output */ - :"S" (src),"D" (dest):"si","di","ax","memory"); -return dest; -} +#include /* inline functions for i386.. */ -extern inline char * strncpy(char * dest,const char *src,size_t count) -{ -__asm__ __volatile__( - "cld\n" - "1:\tdecl %2\n\t" - "js 2f\n\t" - "lodsb\n\t" - "stosb\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n\t" - "rep\n\t" - "stosb\n" - "2:" - : /* no output */ - :"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory"); -return dest; -} - -extern inline char * strcat(char * dest,const char * src) -{ -__asm__ __volatile__( - "cld\n\t" - "repne\n\t" - "scasb\n\t" - "decl %1\n" - "1:\tlodsb\n\t" - "stosb\n\t" - "testb %%al,%%al\n\t" - "jne 1b" - : /* no output */ - :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx"); -return dest; -} - -extern inline char * strncat(char * dest,const char * src,size_t count) -{ -__asm__ __volatile__( - "cld\n\t" - "repne\n\t" - "scasb\n\t" - "decl %1\n\t" - "movl %4,%3\n" - "1:\tdecl %3\n\t" - "js 2f\n\t" - "lodsb\n\t" - "stosb\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n" - "2:\txorl %2,%2\n\t" - "stosb" - : /* no output */ - :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count) - :"si","di","ax","cx","memory"); -return dest; -} - -extern inline int strcmp(const char * cs,const char * ct) -{ -register int __res; -__asm__ __volatile__( - "cld\n" - "1:\tlodsb\n\t" - "scasb\n\t" - "jne 2f\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n\t" - "xorl %%eax,%%eax\n\t" - "jmp 3f\n" - "2:\tsbbl %%eax,%%eax\n\t" - "orb $1,%%eax\n" - "3:" - :"=a" (__res):"S" (cs),"D" (ct):"si","di"); -return __res; -} - -extern inline int strncmp(const char * cs,const char * ct,size_t count) -{ -register int __res; -__asm__ __volatile__( - "cld\n" - "1:\tdecl %3\n\t" - "js 2f\n\t" - "lodsb\n\t" - "scasb\n\t" - "jne 3f\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n" - "2:\txorl %%eax,%%eax\n\t" - "jmp 4f\n" - "3:\tsbbl %%eax,%%eax\n\t" - "orb $1,%%al\n" - "4:" - :"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx"); -return __res; -} - -extern inline char * strchr(const char * s,char c) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" - "movb %%al,%%ah\n" - "1:\tlodsb\n\t" - "cmpb %%ah,%%al\n\t" - "je 2f\n\t" - "testb %%al,%%al\n\t" - "jne 1b\n\t" - "movl $1,%1\n" - "2:\tmovl %1,%0\n\t" - "decl %0" - :"=a" (__res):"S" (s),"0" (c):"si"); -return __res; -} - -extern inline char * strrchr(const char * s,char c) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" - "movb %%al,%%ah\n" - "1:\tlodsb\n\t" - "cmpb %%ah,%%al\n\t" - "jne 2f\n\t" - "leal -1(%%esi),%0\n" - "2:\ttestb %%al,%%al\n\t" - "jne 1b" - :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si"); -return __res; -} - -extern inline size_t strspn(const char * cs, const char * ct) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" - "movl %4,%%edi\n\t" - "repne\n\t" - "scasb\n\t" - "notl %%ecx\n\t" - "decl %%ecx\n\t" - "movl %%ecx,%%edx\n" - "1:\tlodsb\n\t" - "testb %%al,%%al\n\t" - "je 2f\n\t" - "movl %4,%%edi\n\t" - "movl %%edx,%%ecx\n\t" - "repne\n\t" - "scasb\n\t" - "je 1b\n" - "2:\tdecl %0" - :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) - :"ax","cx","dx","di"); -return __res-cs; -} - -extern inline size_t strcspn(const char * cs, const char * ct) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" - "movl %4,%%edi\n\t" - "repne\n\t" - "scasb\n\t" - "notl %%ecx\n\t" - "decl %%ecx\n\t" - "movl %%ecx,%%edx\n" - "1:\tlodsb\n\t" - "testb %%al,%%al\n\t" - "je 2f\n\t" - "movl %4,%%edi\n\t" - "movl %%edx,%%ecx\n\t" - "repne\n\t" - "scasb\n\t" - "jne 1b\n" - "2:\tdecl %0" - :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) - :"ax","cx","dx","di"); -return __res-cs; -} - -extern inline char * strpbrk(const char * cs,const char * ct) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" - "movl %4,%%edi\n\t" - "repne\n\t" - "scasb\n\t" - "notl %%ecx\n\t" - "decl %%ecx\n\t" - "movl %%ecx,%%edx\n" - "1:\tlodsb\n\t" - "testb %%al,%%al\n\t" - "je 2f\n\t" - "movl %4,%%edi\n\t" - "movl %%edx,%%ecx\n\t" - "repne\n\t" - "scasb\n\t" - "jne 1b\n\t" - "decl %0\n\t" - "jmp 3f\n" - "2:\txorl %0,%0\n" - "3:" - :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct) - :"ax","cx","dx","di"); -return __res; -} - -extern inline char * strstr(const char * cs,const char * ct) -{ -register char * __res; -__asm__ __volatile__( - "cld\n\t" \ - "movl %4,%%edi\n\t" - "repne\n\t" - "scasb\n\t" - "notl %%ecx\n\t" - "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */ - "movl %%ecx,%%edx\n" - "1:\tmovl %4,%%edi\n\t" - "movl %%esi,%%eax\n\t" - "movl %%edx,%%ecx\n\t" - "repe\n\t" - "cmpsb\n\t" - "je 2f\n\t" /* also works for empty string, see above */ - "xchgl %%eax,%%esi\n\t" - "incl %%esi\n\t" - "cmpb $0,-1(%%eax)\n\t" - "jne 1b\n\t" - "xorl %%eax,%%eax\n\t" - "2:" - :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct) - :"cx","dx","di","si"); -return __res; -} - -extern inline size_t strlen(const char * s) -{ -register int __res; -__asm__ __volatile__( - "cld\n\t" - "repne\n\t" - "scasb\n\t" - "notl %0\n\t" - "decl %0" - :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di"); -return __res; -} - -extern char * ___strtok; - -extern inline char * strtok(char * s,const char * ct) -{ -register char * __res; -__asm__ __volatile__( - "testl %1,%1\n\t" - "jne 1f\n\t" - "testl %0,%0\n\t" - "je 8f\n\t" - "movl %0,%1\n" - "1:\txorl %0,%0\n\t" - "movl $-1,%%ecx\n\t" - "xorl %%eax,%%eax\n\t" - "cld\n\t" - "movl %4,%%edi\n\t" - "repne\n\t" - "scasb\n\t" - "notl %%ecx\n\t" - "decl %%ecx\n\t" - "je 7f\n\t" /* empty delimeter-string */ - "movl %%ecx,%%edx\n" - "2:\tlodsb\n\t" - "testb %%al,%%al\n\t" - "je 7f\n\t" - "movl %4,%%edi\n\t" - "movl %%edx,%%ecx\n\t" - "repne\n\t" - "scasb\n\t" - "je 2b\n\t" - "decl %1\n\t" - "cmpb $0,(%1)\n\t" - "je 7f\n\t" - "movl %1,%0\n" - "3:\tlodsb\n\t" - "testb %%al,%%al\n\t" - "je 5f\n\t" - "movl %4,%%edi\n\t" - "movl %%edx,%%ecx\n\t" - "repne\n\t" - "scasb\n\t" - "jne 3b\n\t" - "decl %1\n\t" - "cmpb $0,(%1)\n\t" - "je 5f\n\t" - "movb $0,(%1)\n\t" - "incl %1\n\t" - "jmp 6f\n" - "5:\txorl %1,%1\n" - "6:\tcmpb $0,(%0)\n\t" - "jne 7f\n\t" - "xorl %0,%0\n" - "7:\ttestl %0,%0\n\t" - "jne 8f\n\t" - "movl %0,%1\n" - "8:" - :"=b" (__res),"=S" (___strtok) - :"0" (___strtok),"1" (s),"g" (ct) - :"ax","cx","dx","di","memory"); -return __res; -} - -extern inline void * memcpy(void * to, const void * from, size_t n) -{ -__asm__ __volatile__( - "cld\n\t" - "movl %%edx, %%ecx\n\t" - "shrl $2,%%ecx\n\t" - "rep ; movsl\n\t" - "testb $1,%%dl\n\t" - "je 1f\n\t" - "movsb\n" - "1:\ttestb $2,%%dl\n\t" - "je 2f\n\t" - "movsw\n" - "2:\n" - : /* no output */ - :"d" (n),"D" ((long) to),"S" ((long) from) - : "cx","di","si","memory"); -return (to); -} - -extern inline void * memmove(void * dest,const void * src, size_t n) -{ -if (dest36) + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) return 0; - c = (type & ZEROPAD) ? '0' : ' ' ; - if (type&SIGN && num<0) { - sign='-'; - num = -num; - } else - sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0); - if (sign) size--; - if (type&SPECIAL) - if (base==16) size -= 2; - else if (base==8) size--; - i=0; - if (num==0) + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) tmp[i++]='0'; - else while (num!=0) - tmp[i++]=digits[do_div(num,base)]; - if (i>precision) precision=i; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; size -= precision; if (!(type&(ZEROPAD+LEFT))) while(size-->0) *str++ = ' '; if (sign) *str++ = sign; - if (type&SPECIAL) + if (type & SPECIAL) if (base==8) *str++ = '0'; else if (base==16) { *str++ = '0'; *str++ = digits[33]; } - if (!(type&LEFT)) - while(size-->0) + if (!(type & LEFT)) + while (size-- > 0) *str++ = c; - while(i0) + while (i-- > 0) *str++ = tmp[i]; - while(size-->0) + while (size-- > 0) *str++ = ' '; return str; } @@ -119,10 +134,10 @@ int vsprintf(char *buf, const char *fmt, va_list args) { int len; - int i; + unsigned long num; + int i, base; char * str; char *s; - int *ip; int flags; /* flags to number() */ @@ -183,6 +198,9 @@ ++fmt; } + /* default base */ + base = 10; + switch (*fmt) { case 'c': if (!(flags & LEFT)) @@ -191,7 +209,7 @@ *str++ = (unsigned char) va_arg(args, int); while (--field_width > 0) *str++ = ' '; - break; + continue; case 's': s = va_arg(args, char *); @@ -210,12 +228,7 @@ *str++ = *s++; while (len < field_width--) *str++ = ' '; - break; - - case 'o': - str = number(str, va_arg(args, unsigned long), 8, - field_width, precision, flags); - break; + continue; case 'p': if (field_width == -1) { @@ -225,26 +238,34 @@ str = number(str, (unsigned long) va_arg(args, void *), 16, field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; break; - case 'x': - flags |= SMALL; case 'X': - str = number(str, va_arg(args, unsigned long), 16, - field_width, precision, flags); + flags |= LARGE; + case 'x': + base = 16; break; case 'd': case 'i': flags |= SIGN; case 'u': - str = number(str, va_arg(args, unsigned long), 10, - field_width, precision, flags); - break; - - case 'n': - ip = va_arg(args, int *); - *ip = (str - buf); break; default: @@ -254,8 +275,20 @@ *str++ = *fmt; else --fmt; - break; + continue; } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') + if (flags & SIGN) + num = va_arg(args, short); + else + num = va_arg(args, unsigned short); + else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); } *str = '\0'; return str-buf;